diff --git a/src/modules/SD2/@@Temp_VC120/ScriptDev2.vcxproj b/src/modules/SD2/@@Temp_VC120/ScriptDev2.vcxproj new file mode 100644 index 000000000..493d57563 --- /dev/null +++ b/src/modules/SD2/@@Temp_VC120/ScriptDev2.vcxproj @@ -0,0 +1,891 @@ + + + + + Debug + Win32 + + + Debug + x64 + + + Release + Win32 + + + Release + x64 + + + + ScriptDev2 + {4295C8A9-79B7-4354-8064-F05FB9CA0C96} + ScriptDev2 + Win32Proj + + + + DynamicLibrary + MultiByte + v120 + + + DynamicLibrary + MultiByte + v120 + + + DynamicLibrary + MultiByte + v120 + + + DynamicLibrary + MultiByte + v120 + + + + + + + + + + + + + + + + + + + + + + + ..\..\..\..\bin\win32_debug\ + .\ScriptDev2__$(Platform)_$(Configuration)\ + true + ..\..\..\..\bin\x64_debug\ + .\ScriptDev2__$(Platform)_$(Configuration)\ + true + ..\..\..\..\bin\win32_release\ + .\ScriptDev2__$(Platform)_$(Configuration)\ + false + ..\..\..\..\bin\x64_release\ + .\ScriptDev2__$(Platform)_$(Configuration)\ + false + AllRules.ruleset + + + AllRules.ruleset + + + AllRules.ruleset + + + AllRules.ruleset + + + mangosscript + mangosscript + mangosscript + mangosscript + + + + Disabled + ..\..\..\..\dep\include\;..\..\..\shared\;..\..\..\framework\;..\..\..\game\;..\include\;..\base\;..\..\..\..\dep\ACE_wrappers\;%(AdditionalIncludeDirectories) + WIN32;_DEBUG;MANGOS_DEBUG;_WINDOWS;_USRDLL;SCRIPT;%(PreprocessorDefinitions) + true + EnableFastChecks + MultiThreadedDebugDLL + Use + precompiled.h + Level3 + ProgramDatabase + true + true + true + /Zm200 %(AdditionalOptions) + + + mangosd.lib;ACEd.lib;framework.lib;%(AdditionalDependencies) + ..\..\..\..\win\VC120\mangosd__Win32_Debug;..\..\..\..\win\VC120\framework__Win32_Debug;..\..\..\..\dep\lib\win32_debug\;%(AdditionalLibraryDirectories) + true + Windows + false + + + $(OutDir)mangosscript.lib + MachineX86 + + + + + X64 + + + Disabled + ..\..\..\..\dep\include\;..\..\..\shared\;..\..\..\framework\;..\..\..\game\;..\include\;..\base\;..\..\..\..\dep\ACE_wrappers\;%(AdditionalIncludeDirectories) + WIN32;_DEBUG;MANGOS_DEBUG;_WINDOWS;_USRDLL;SCRIPT;%(PreprocessorDefinitions) + true + EnableFastChecks + MultiThreadedDebugDLL + Use + precompiled.h + Level3 + ProgramDatabase + true + true + true + /Zm200 %(AdditionalOptions) + + + mangosd.lib;ACEd.lib;framework.lib;%(AdditionalDependencies) + ..\..\..\..\win\VC120\mangosd__x64_Debug;..\..\..\..\win\VC120\framework__x64_Debug;..\..\..\..\dep\lib\x64_Debug\;%(AdditionalLibraryDirectories) + true + Windows + false + + + $(OutDir)mangosscript.lib + MachineX64 + + + + + /Zm200 %(AdditionalOptions) + ..\..\..\..\dep\include\;..\..\..\shared\;..\..\..\framework\;..\..\..\game\;..\include\;..\base\;..\..\..\..\dep\ACE_wrappers\;%(AdditionalIncludeDirectories) + WIN32;NDEBUG;_WINDOWS;_USRDLL;SCRIPT;_SECURE_SCL=0;%(PreprocessorDefinitions) + false + MultiThreadedDLL + Use + precompiled.h + Level3 + ProgramDatabase + true + true + true + + + mangosd.lib;ACE.lib;framework.lib;%(AdditionalDependencies) + ..\..\..\..\win\VC120\mangosd__Win32_Release;..\..\..\..\win\VC120\framework__Win32_Release;..\..\..\..\dep\lib\win32_release\;%(AdditionalLibraryDirectories) + true + Windows + true + true + false + + + $(OutDir)mangosscript.lib + + + + + X64 + + + /Zm200 %(AdditionalOptions) + ..\..\..\..\dep\include\;..\..\..\shared\;..\..\..\framework\;..\..\..\game\;..\include\;..\base\;..\..\..\..\dep\ACE_wrappers\;%(AdditionalIncludeDirectories) + WIN32;NDEBUG;_WINDOWS;_USRDLL;SCRIPT;_SECURE_SCL=0;%(PreprocessorDefinitions) + MultiThreadedDLL + Use + precompiled.h + Level3 + ProgramDatabase + true + true + true + + + mangosd.lib;ACE.lib;framework.lib;%(AdditionalDependencies) + ..\..\..\..\win\VC120\mangosd__x64_Release;..\..\..\..\win\VC120\framework__x64_Release;..\..\..\..\dep\lib\x64_release\;%(AdditionalLibraryDirectories) + true + Windows + true + true + false + + + $(OutDir)mangosscript.lib + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Create + Create + Create + Create + + + + + + NotUsing + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Extracting revision + cd "$(SolutionDir)" +"$(SolutionDir)..\..\..\win\VC120\genrevision__Win32_$(Configuration)\genrevision.exe" + + .svn/entries;%(AdditionalInputs) + revision.h;%(Outputs) + Extracting revision + cd "$(SolutionDir)" +"$(SolutionDir)..\..\..\win\VC120\genrevision__Win32_$(Configuration)\genrevision.exe" + + .svn/entries;%(AdditionalInputs) + revision.h;%(Outputs) + Extracting revision + cd "$(SolutionDir)" +"$(SolutionDir)..\..\..\win\VC120\genrevision__Win32_$(Configuration)\genrevision.exe" + + .svn/entries;%(AdditionalInputs) + revision.h;%(Outputs) + Extracting revision + cd "$(SolutionDir)" +"$(SolutionDir)..\..\..\win\VC120\genrevision__Win32_$(Configuration)\genrevision.exe" + + .svn/entries;%(AdditionalInputs) + revision.h;%(Outputs) + + + + + + + + + \ No newline at end of file diff --git a/src/modules/SD2/@@Temp_VC120/ScriptDev2.vcxproj.filters b/src/modules/SD2/@@Temp_VC120/ScriptDev2.vcxproj.filters new file mode 100644 index 000000000..d19c23750 --- /dev/null +++ b/src/modules/SD2/@@Temp_VC120/ScriptDev2.vcxproj.filters @@ -0,0 +1,2249 @@ + + + + + {3b1307a9-bc58-4773-9a93-0e9ceff55c82} + + + {b7b35df3-4231-4370-aed5-5859ab4c312e} + + + {ced0a1e2-6867-4f01-a762-390a3d1f01d3} + + + {af3d4b4d-a585-41c4-ba95-79669a614ea8} + + + {ca2551e0-68d0-4331-88f3-ab0cdaa2bf9a} + + + {043f49c3-d844-4833-b5bc-bf88638c47b5} + + + {1bb5cf47-7950-4b49-8e5d-9ee37108f2c5} + + + {434e1621-9e58-44d9-b5a6-d0f5dea495b4} + + + {45c59280-02fb-482f-859b-bd47cc230f0f} + + + {5631f38e-66c8-4efd-ad3a-2834fbb49a6e} + + + {9887f0b1-d8ca-49a5-b1a7-aaf30ed4452f} + + + {31605090-12b9-4d42-a5e9-1cbfac8ab384} + + + {920d1832-b347-4afd-bd05-4c943e5555f0} + + + {08fb5b22-710d-46d6-9893-f567aa75a5e6} + + + {cef5c3c5-f434-4b9f-9bad-8c106d46c755} + + + {cc0c8231-75d8-456e-bca8-7d0a122eee52} + + + {66bc2cec-8372-4191-991c-1c23275fa13c} + + + {af465469-5a16-4908-aa08-39c77d02cd88} + + + {6ffd42e5-ac49-4635-8087-77ad2b7b0866} + + + {dd4995a8-6d86-4f21-82bc-a18acc2c688f} + + + {2a3a25e5-51f1-4ab3-a1a0-1b9ec9c45d1a} + + + {7a028ad9-afa1-42ea-b147-20a40c5e716c} + + + {66401dac-d94c-4ed6-b912-229dda0906d6} + + + {385f26dc-dcaf-4022-921d-3a562617bf93} + + + {25be39dd-536d-450d-ac94-6372d37a4fae} + + + {1d718eaa-2a8d-4cb9-9abf-17fcf0d91f83} + + + {f00c08b4-1c1d-4379-8b0c-2a1a51c14bb2} + + + {c929f54b-22dd-49b4-862c-92dfa9a2d1f7} + + + {60b91cfe-7dff-40de-9957-41f8d03144cc} + + + {22eca3e6-662f-4c53-9344-6e749c09c86b} + + + {070541ef-a813-43f5-a8a6-e6674fe1a47e} + + + {5cda7bda-399b-46b7-8bc2-5786f0202fa2} + + + {226d4dc8-830b-4249-b28a-313ac21080a9} + + + {404b30fd-bdaa-44d1-ab3a-384886ebb8ec} + + + {5833dbce-1fa8-49eb-b678-145a2d58a067} + + + {dc38d3fe-d1fe-49f2-bd8f-758cd270dd62} + + + {ad315c26-01fe-4431-96bf-7b3dc3402e1a} + + + {b8416f00-9151-4ab5-abe1-bc06af42d736} + + + {4df3cb5e-8de9-4082-a35a-72a2c589779c} + + + {2552ab59-1f73-4778-a6db-6f03e405622d} + + + {8206776b-4d43-431d-bca5-5f6cff157ea4} + + + {708d3668-ff97-46b8-a75e-6f5d20ee3ec8} + + + {02135a7c-33cc-444e-af90-48a5193e5a6f} + + + {094d5051-4784-4b79-8875-a87d4ed6d5b1} + + + {dd6326cc-bcd9-4642-a049-7911cda0bc85} + + + {014ada9a-13bf-4540-a0ce-5132bdc36fcf} + + + {9feb59d0-e6bd-4328-9a50-7ddb9852936e} + + + {00f03f8d-4af1-40d9-a95a-e2dc1a81ca7d} + + + {de2e959a-1ef4-41ef-9235-15a1f3f8a19d} + + + {8f8a60e0-223e-4517-918c-243e523a7892} + + + {416ccfc8-a1bb-43a5-b202-d7656981094a} + + + {ba031a13-787e-41da-bcaf-1986fdea8069} + + + {17496c60-f5f8-4e29-ab2b-d480296e0eb4} + + + {397252e0-5267-4571-b9e1-cb45a1bbd1ab} + + + {0412262e-b05b-4827-adef-97507204cb69} + + + {e775610e-fa3c-4355-9c4c-473a6a5e57d5} + + + {d85193fb-5107-4a82-86b9-fff6c2ffb537} + + + {6089ced5-7090-4294-bd63-c036335880be} + + + {6c5f7fd4-671b-4cba-aa95-7758b93dc844} + + + {71e87e58-3b30-41e7-8f8a-16886508a4d9} + + + {3cf8e088-da94-4561-993f-2970908d6642} + + + {bc0156b2-6544-4ebc-9ed6-fe45ac1902a4} + + + {b59c73a8-c480-43b2-8a2e-65f5a54978eb} + + + {5eb6a84a-97e6-477e-97e3-23681f29f675} + + + {d7e0e45a-fd89-48a6-8d24-f4922058160e} + + + {71617682-fe62-4d7c-a2de-7181fb3f46f6} + + + {9c5d9a0f-8967-4cf6-a047-3b724f81dc29} + + + {88c327d5-0768-465f-b954-30eca0d31f45} + + + {b20efa19-1e16-42e4-bc29-a9c8e282d17c} + + + {fec1fbfa-5119-4bb0-98bc-f8d4583521f7} + + + {1cb43523-dc33-4c49-a5b7-6e700fe0ecd6} + + + {7289cc07-6956-4b09-8dc5-753c82abe8c9} + + + {e834cf7b-88ad-4bae-9fc8-8e027136c63b} + + + {42501e94-f91e-40be-a02a-472b5ca1e22c} + + + {aac6b8be-6055-4d7d-a50a-41650b8d464e} + + + {e7ee3113-f4d0-4e28-acc7-dc80f42d41df} + + + {e603d4b3-3c2f-4011-aa2b-040243163d3f} + + + {36c79ad7-420c-4909-8da3-c90d3cc83b31} + + + {d47410b0-3184-40e2-8704-cea9b97e20ed} + + + {e35abead-2389-4eb9-88bd-70e6fe5636b5} + + + {1b2f0d99-82b0-4a96-aaac-c485830eb1b3} + + + {fd594031-4dc2-40de-b37b-93063ee6e3e9} + + + {ef27a557-e12d-4b02-9e86-c258329f201e} + + + {b7e4ad7d-8515-4981-ae27-9e542c223618} + + + {04b1d59c-38b6-4b5c-a50d-b9ff1d39bb30} + + + {ee364414-732a-4aad-820f-a5b73cb8b093} + + + {638d5426-652e-4d1f-9abe-3ed95d7e7e69} + + + {a21893a5-0f34-4a73-b0aa-49b175822834} + + + {728148f8-18a0-4606-a2ec-2c9eae4e5e7e} + + + {cfb08e82-63e6-49ca-bff2-533f8647797b} + + + {2be3084e-c5c0-4e2f-bbb4-2ebb22bfff19} + + + {6dc08e92-3e86-4a7f-bab9-9a8cec6a201b} + + + {01da56d1-ee04-455c-846c-4e53869314f6} + + + {5e79ca74-cd29-47ff-a08e-3fada858ddd7} + + + {32b92545-4821-4cc8-ae39-f631b0176d46} + + + {044e4dbc-e2fc-437c-b359-b6fb5a3439ff} + + + {4cbcf193-4d98-4518-a9c4-6c8b4bedbf44} + + + {5c32221e-3532-4c32-bdba-9a85213006af} + + + {2df99085-aee2-4202-b4d1-38a5cc35f3e2} + + + {0a109c31-6dd3-4030-8d6c-6c0e147481be} + + + {ddada980-2eed-4f2b-a9f7-dbb9cca7ca3d} + + + {d6843352-6425-48da-85b6-6b26f6217ba8} + + + {6a68396b-343f-4c79-938c-93741acdeb41} + + + {974fc97c-e585-442c-b35e-b21a17804619} + + + {ba709b56-d5db-4730-9591-79b8f9788ca1} + + + {1fa90250-9c5b-450c-be64-d9eee910c4a8} + + + {7b3c1c18-03e9-493e-a364-5beb1a6936eb} + + + {575420cc-581f-4d08-a9da-7814f221ab47} + + + {9c5997ee-accc-4737-815f-b031d422577b} + + + {579f2f5c-acf1-410a-bce6-d353d6912546} + + + {04e4571d-55ee-4f2e-ac4e-46fc2610341f} + + + {79a3e62b-bd96-4d16-8c23-bda17f968d19} + + + {ca7213e9-56c1-4084-a48b-19ff57dee433} + + + {2fd46769-c050-4e47-9533-461ef8695d6a} + + + {90f3df6a-a1d0-4a6e-b8d7-d1d993fc795b} + + + + + base + + + base + + + base + + + base + + + scripts\battlegrounds + + + scripts\eastern_kingdoms + + + scripts\eastern_kingdoms + + + scripts\eastern_kingdoms + + + scripts\eastern_kingdoms + + + scripts\eastern_kingdoms + + + scripts\eastern_kingdoms + + + scripts\eastern_kingdoms + + + scripts\eastern_kingdoms + + + scripts\eastern_kingdoms + + + scripts\eastern_kingdoms + + + scripts\eastern_kingdoms + + + scripts\eastern_kingdoms + + + scripts\eastern_kingdoms + + + scripts\eastern_kingdoms + + + scripts\eastern_kingdoms + + + scripts\eastern_kingdoms + + + scripts\eastern_kingdoms + + + scripts\eastern_kingdoms + + + scripts\eastern_kingdoms + + + scripts\eastern_kingdoms + + + scripts\eastern_kingdoms + + + scripts\eastern_kingdoms + + + scripts\eastern_kingdoms + + + scripts\eastern_kingdoms + + + scripts\eastern_kingdoms + + + scripts\eastern_kingdoms + + + scripts\eastern_kingdoms + + + scripts\eastern_kingdoms + + + scripts\eastern_kingdoms + + + scripts\eastern_kingdoms\bastion_of_twilight + + + scripts\eastern_kingdoms\bastion_of_twilight + + + scripts\eastern_kingdoms\bastion_of_twilight + + + scripts\eastern_kingdoms\bastion_of_twilight + + + scripts\eastern_kingdoms\bastion_of_twilight + + + scripts\eastern_kingdoms\bastion_of_twilight + + + scripts\eastern_kingdoms\blackrock_mountain\blackrock_caverns + + + scripts\eastern_kingdoms\blackrock_mountain\blackrock_caverns + + + scripts\eastern_kingdoms\blackrock_mountain\blackrock_caverns + + + scripts\eastern_kingdoms\blackrock_mountain\blackrock_caverns + + + scripts\eastern_kingdoms\blackrock_mountain\blackrock_caverns + + + scripts\eastern_kingdoms\blackrock_mountain\blackrock_caverns + + + scripts\eastern_kingdoms\blackrock_mountain\blackrock_depths + + + scripts\eastern_kingdoms\blackrock_mountain\blackrock_depths + + + scripts\eastern_kingdoms\blackrock_mountain\blackrock_depths + + + scripts\eastern_kingdoms\blackrock_mountain\blackrock_depths + + + scripts\eastern_kingdoms\blackrock_mountain\blackrock_depths + + + scripts\eastern_kingdoms\blackrock_mountain\blackrock_depths + + + scripts\eastern_kingdoms\blackrock_mountain\blackrock_depths + + + scripts\eastern_kingdoms\blackrock_mountain\blackrock_spire + + + scripts\eastern_kingdoms\blackrock_mountain\blackrock_spire + + + scripts\eastern_kingdoms\blackrock_mountain\blackrock_spire + + + scripts\eastern_kingdoms\blackrock_mountain\blackrock_spire + + + scripts\eastern_kingdoms\blackrock_mountain\blackwing_descent + + + scripts\eastern_kingdoms\blackrock_mountain\blackwing_descent + + + scripts\eastern_kingdoms\blackrock_mountain\blackwing_descent + + + scripts\eastern_kingdoms\blackrock_mountain\blackwing_descent + + + scripts\eastern_kingdoms\blackrock_mountain\blackwing_descent + + + scripts\eastern_kingdoms\blackrock_mountain\blackwing_descent + + + scripts\eastern_kingdoms\blackrock_mountain\blackwing_descent + + + scripts\eastern_kingdoms\blackrock_mountain\blackwing_lair + + + scripts\eastern_kingdoms\blackrock_mountain\blackwing_lair + + + scripts\eastern_kingdoms\blackrock_mountain\blackwing_lair + + + scripts\eastern_kingdoms\blackrock_mountain\blackwing_lair + + + scripts\eastern_kingdoms\blackrock_mountain\blackwing_lair + + + scripts\eastern_kingdoms\blackrock_mountain\blackwing_lair + + + scripts\eastern_kingdoms\blackrock_mountain\blackwing_lair + + + scripts\eastern_kingdoms\blackrock_mountain\blackwing_lair + + + scripts\eastern_kingdoms\blackrock_mountain\blackwing_lair + + + scripts\eastern_kingdoms\blackrock_mountain\blackwing_lair + + + scripts\eastern_kingdoms\blackrock_mountain\molten_core + + + scripts\eastern_kingdoms\blackrock_mountain\molten_core + + + scripts\eastern_kingdoms\blackrock_mountain\molten_core + + + scripts\eastern_kingdoms\blackrock_mountain\molten_core + + + scripts\eastern_kingdoms\blackrock_mountain\molten_core + + + scripts\eastern_kingdoms\blackrock_mountain\molten_core + + + scripts\eastern_kingdoms\blackrock_mountain\molten_core + + + scripts\eastern_kingdoms\blackrock_mountain\molten_core + + + scripts\eastern_kingdoms\blackrock_mountain\molten_core + + + scripts\eastern_kingdoms\blackrock_mountain\molten_core + + + scripts\eastern_kingdoms\blackrock_mountain\molten_core + + + scripts\eastern_kingdoms\blackrock_mountain\molten_core + + + scripts\eastern_kingdoms\deadmines + + + scripts\eastern_kingdoms\deadmines + + + scripts\eastern_kingdoms\deadmines + + + scripts\eastern_kingdoms\gnomeregan + + + scripts\eastern_kingdoms\gnomeregan + + + scripts\eastern_kingdoms\gnomeregan + + + scripts\eastern_kingdoms\grim_batol + + + scripts\eastern_kingdoms\grim_batol + + + scripts\eastern_kingdoms\grim_batol + + + scripts\eastern_kingdoms\grim_batol + + + scripts\eastern_kingdoms\grim_batol + + + scripts\eastern_kingdoms\karazhan + + + scripts\eastern_kingdoms\karazhan + + + scripts\eastern_kingdoms\karazhan + + + scripts\eastern_kingdoms\karazhan + + + scripts\eastern_kingdoms\karazhan + + + scripts\eastern_kingdoms\karazhan + + + scripts\eastern_kingdoms\karazhan + + + scripts\eastern_kingdoms\karazhan + + + scripts\eastern_kingdoms\karazhan + + + scripts\eastern_kingdoms\karazhan + + + scripts\eastern_kingdoms\karazhan + + + scripts\eastern_kingdoms\karazhan + + + scripts\eastern_kingdoms\karazhan + + + scripts\eastern_kingdoms\magisters_terrace + + + scripts\eastern_kingdoms\magisters_terrace + + + scripts\eastern_kingdoms\magisters_terrace + + + scripts\eastern_kingdoms\magisters_terrace + + + scripts\eastern_kingdoms\magisters_terrace + + + scripts\eastern_kingdoms\magisters_terrace + + + scripts\eastern_kingdoms\scarlet_enclave + + + scripts\eastern_kingdoms\scarlet_enclave + + + scripts\eastern_kingdoms\scarlet_monastery + + + scripts\eastern_kingdoms\scarlet_monastery + + + scripts\eastern_kingdoms\scarlet_monastery + + + scripts\eastern_kingdoms\scarlet_monastery + + + scripts\eastern_kingdoms\scarlet_monastery + + + scripts\eastern_kingdoms\scholomance + + + scripts\eastern_kingdoms\scholomance + + + scripts\eastern_kingdoms\scholomance + + + scripts\eastern_kingdoms\shadowfang_keep + + + scripts\eastern_kingdoms\shadowfang_keep + + + scripts\eastern_kingdoms\shadowfang_keep + + + scripts\eastern_kingdoms\stratholme + + + scripts\eastern_kingdoms\stratholme + + + scripts\eastern_kingdoms\stratholme + + + scripts\eastern_kingdoms\stratholme + + + scripts\eastern_kingdoms\stratholme + + + scripts\eastern_kingdoms\stratholme + + + scripts\eastern_kingdoms\stratholme + + + scripts\eastern_kingdoms\sunken_temple + + + scripts\eastern_kingdoms\sunken_temple + + + scripts\eastern_kingdoms\sunwell_plateau + + + scripts\eastern_kingdoms\sunwell_plateau + + + scripts\eastern_kingdoms\sunwell_plateau + + + scripts\eastern_kingdoms\sunwell_plateau + + + scripts\eastern_kingdoms\sunwell_plateau + + + scripts\eastern_kingdoms\sunwell_plateau + + + scripts\eastern_kingdoms\sunwell_plateau + + + scripts\eastern_kingdoms\throne_of_the_tides + + + scripts\eastern_kingdoms\throne_of_the_tides + + + scripts\eastern_kingdoms\throne_of_the_tides + + + scripts\eastern_kingdoms\throne_of_the_tides + + + scripts\eastern_kingdoms\throne_of_the_tides + + + scripts\eastern_kingdoms\uldaman + + + scripts\eastern_kingdoms\uldaman + + + scripts\eastern_kingdoms\uldaman + + + scripts\eastern_kingdoms\zulaman + + + scripts\eastern_kingdoms\zulaman + + + scripts\eastern_kingdoms\zulaman + + + scripts\eastern_kingdoms\zulaman + + + scripts\eastern_kingdoms\zulaman + + + scripts\eastern_kingdoms\zulaman + + + scripts\eastern_kingdoms\zulaman + + + scripts\eastern_kingdoms\zulaman + + + scripts\eastern_kingdoms\zulgurub + + + scripts\eastern_kingdoms\zulgurub + + + scripts\eastern_kingdoms\zulgurub + + + scripts\eastern_kingdoms\zulgurub + + + scripts\eastern_kingdoms\zulgurub + + + scripts\eastern_kingdoms\zulgurub + + + scripts\eastern_kingdoms\zulgurub + + + scripts\eastern_kingdoms\zulgurub + + + scripts\eastern_kingdoms\zulgurub + + + scripts\eastern_kingdoms\zulgurub + + + scripts\eastern_kingdoms\zulgurub + + + scripts\examples + + + scripts\examples + + + scripts\examples + + + scripts\examples + + + scripts\kalimdor + + + scripts\kalimdor + + + scripts\kalimdor + + + scripts\kalimdor + + + scripts\kalimdor + + + scripts\kalimdor + + + scripts\kalimdor + + + scripts\kalimdor + + + scripts\kalimdor + + + scripts\kalimdor + + + scripts\kalimdor + + + scripts\kalimdor + + + scripts\kalimdor + + + scripts\kalimdor + + + scripts\kalimdor + + + scripts\kalimdor + + + scripts\kalimdor + + + scripts\kalimdor + + + scripts\kalimdor + + + scripts\kalimdor + + + scripts\kalimdor + + + scripts\kalimdor + + + scripts\kalimdor + + + scripts\kalimdor + + + scripts\kalimdor + + + scripts\kalimdor + + + scripts\kalimdor\blackfathom_deeps + + + scripts\kalimdor\caverns_of_time\culling_of_stratholme + + + scripts\kalimdor\caverns_of_time\culling_of_stratholme + + + scripts\kalimdor\caverns_of_time\dark_portal + + + scripts\kalimdor\caverns_of_time\dark_portal + + + scripts\kalimdor\caverns_of_time\dark_portal + + + scripts\kalimdor\caverns_of_time\dark_portal + + + scripts\kalimdor\caverns_of_time\dark_portal + + + scripts\kalimdor\caverns_of_time\dragon_soul + + + scripts\kalimdor\caverns_of_time\dragon_soul + + + scripts\kalimdor\caverns_of_time\dragon_soul + + + scripts\kalimdor\caverns_of_time\dragon_soul + + + scripts\kalimdor\caverns_of_time\dragon_soul + + + scripts\kalimdor\caverns_of_time\dragon_soul + + + scripts\kalimdor\caverns_of_time\dragon_soul + + + scripts\kalimdor\caverns_of_time\dragon_soul + + + scripts\kalimdor\caverns_of_time\dragon_soul + + + scripts\kalimdor\caverns_of_time\end_of_time + + + scripts\kalimdor\caverns_of_time\end_of_time + + + scripts\kalimdor\caverns_of_time\hour_of_twilight + + + scripts\kalimdor\caverns_of_time\hour_of_twilight + + + scripts\kalimdor\caverns_of_time\hour_of_twilight + + + scripts\kalimdor\caverns_of_time\hour_of_twilight + + + scripts\kalimdor\caverns_of_time\hyjal_summit + + + scripts\kalimdor\caverns_of_time\hyjal_summit + + + scripts\kalimdor\caverns_of_time\hyjal_summit + + + scripts\kalimdor\caverns_of_time\hyjal_summit + + + scripts\kalimdor\caverns_of_time\old_hillsbrad + + + scripts\kalimdor\caverns_of_time\old_hillsbrad + + + scripts\kalimdor\caverns_of_time\well_of_eternity + + + scripts\kalimdor\caverns_of_time\well_of_eternity + + + scripts\kalimdor\caverns_of_time\well_of_eternity + + + scripts\kalimdor\caverns_of_time\well_of_eternity + + + scripts\kalimdor\dire_maul + + + scripts\kalimdor\dire_maul + + + scripts\kalimdor\firelands + + + scripts\kalimdor\firelands + + + scripts\kalimdor\firelands + + + scripts\kalimdor\firelands + + + scripts\kalimdor\firelands + + + scripts\kalimdor\firelands + + + scripts\kalimdor\firelands + + + scripts\kalimdor\firelands + + + scripts\kalimdor\lost_city_of_tolvir + + + scripts\kalimdor\lost_city_of_tolvir + + + scripts\kalimdor\lost_city_of_tolvir + + + scripts\kalimdor\lost_city_of_tolvir + + + scripts\kalimdor\lost_city_of_tolvir + + + scripts\kalimdor\maraudon + + + scripts\kalimdor\onyxias_lair + + + scripts\kalimdor\onyxias_lair + + + scripts\kalimdor\razorfen_downs + + + scripts\kalimdor\razorfen_kraul + + + scripts\kalimdor\razorfen_kraul + + + scripts\kalimdor\ruins_of_ahnqiraj + + + scripts\kalimdor\ruins_of_ahnqiraj + + + scripts\kalimdor\ruins_of_ahnqiraj + + + scripts\kalimdor\ruins_of_ahnqiraj + + + scripts\kalimdor\ruins_of_ahnqiraj + + + scripts\kalimdor\ruins_of_ahnqiraj + + + scripts\kalimdor\ruins_of_ahnqiraj + + + scripts\kalimdor\ruins_of_ahnqiraj + + + scripts\kalimdor\temple_of_ahnqiraj + + + scripts\kalimdor\temple_of_ahnqiraj + + + scripts\kalimdor\temple_of_ahnqiraj + + + scripts\kalimdor\temple_of_ahnqiraj + + + scripts\kalimdor\temple_of_ahnqiraj + + + scripts\kalimdor\temple_of_ahnqiraj + + + scripts\kalimdor\temple_of_ahnqiraj + + + scripts\kalimdor\temple_of_ahnqiraj + + + scripts\kalimdor\temple_of_ahnqiraj + + + scripts\kalimdor\temple_of_ahnqiraj + + + scripts\kalimdor\temple_of_ahnqiraj + + + scripts\kalimdor\throne_of_the_four_winds + + + scripts\kalimdor\throne_of_the_four_winds + + + scripts\kalimdor\throne_of_the_four_winds + + + scripts\kalimdor\vortex_pinnacle + + + scripts\kalimdor\vortex_pinnacle + + + scripts\kalimdor\vortex_pinnacle + + + scripts\kalimdor\vortex_pinnacle + + + scripts\kalimdor\wailing_caverns + + + scripts\kalimdor\wailing_caverns + + + scripts\kalimdor\zulfarrak + + + scripts\kalimdor\zulfarrak + + + scripts\kalimdor\zulfarrak + + + scripts\maelstrom + + + scripts\maelstrom + + + scripts\maelstrom + + + scripts\maelstrom\stonecore + + + scripts\maelstrom\stonecore + + + scripts\maelstrom\stonecore + + + scripts\maelstrom\stonecore + + + scripts\maelstrom\stonecore + + + scripts\northrend + + + scripts\northrend + + + scripts\northrend + + + scripts\northrend + + + scripts\northrend + + + scripts\northrend + + + scripts\northrend + + + scripts\northrend + + + scripts\northrend + + + scripts\northrend\azjol-nerub\ahnkahet + + + scripts\northrend\azjol-nerub\ahnkahet + + + scripts\northrend\azjol-nerub\ahnkahet + + + scripts\northrend\azjol-nerub\ahnkahet + + + scripts\northrend\azjol-nerub\ahnkahet + + + scripts\northrend\azjol-nerub\ahnkahet + + + scripts\northrend\azjol-nerub\azjol-nerub + + + scripts\northrend\azjol-nerub\azjol-nerub + + + scripts\northrend\azjol-nerub\azjol-nerub + + + scripts\northrend\azjol-nerub\azjol-nerub + + + scripts\northrend\crusaders_coliseum\trial_of_the_champion + + + scripts\northrend\crusaders_coliseum\trial_of_the_champion + + + scripts\northrend\crusaders_coliseum\trial_of_the_champion + + + scripts\northrend\crusaders_coliseum\trial_of_the_crusader + + + scripts\northrend\crusaders_coliseum\trial_of_the_crusader + + + scripts\northrend\crusaders_coliseum\trial_of_the_crusader + + + scripts\northrend\crusaders_coliseum\trial_of_the_crusader + + + scripts\northrend\crusaders_coliseum\trial_of_the_crusader + + + scripts\northrend\crusaders_coliseum\trial_of_the_crusader + + + scripts\northrend\crusaders_coliseum\trial_of_the_crusader + + + scripts\northrend\draktharon_keep + + + scripts\northrend\draktharon_keep + + + scripts\northrend\draktharon_keep + + + scripts\northrend\draktharon_keep + + + scripts\northrend\gundrak + + + scripts\northrend\gundrak + + + scripts\northrend\gundrak + + + scripts\northrend\gundrak + + + scripts\northrend\gundrak + + + scripts\northrend\gundrak + + + scripts\northrend\icecrown_citadel\icecrown_citadel + + + scripts\northrend\icecrown_citadel\icecrown_citadel + + + scripts\northrend\icecrown_citadel\icecrown_citadel + + + scripts\northrend\icecrown_citadel\icecrown_citadel + + + scripts\northrend\icecrown_citadel\icecrown_citadel + + + scripts\northrend\icecrown_citadel\icecrown_citadel + + + scripts\northrend\icecrown_citadel\icecrown_citadel + + + scripts\northrend\icecrown_citadel\icecrown_citadel + + + scripts\northrend\icecrown_citadel\icecrown_citadel + + + scripts\northrend\icecrown_citadel\icecrown_citadel + + + scripts\northrend\icecrown_citadel\icecrown_citadel + + + scripts\northrend\icecrown_citadel\icecrown_citadel + + + scripts\northrend\icecrown_citadel\icecrown_citadel + + + scripts\northrend\icecrown_citadel\frozen_halls\pit_of_saron + + + scripts\northrend\icecrown_citadel\frozen_halls\pit_of_saron + + + scripts\northrend\icecrown_citadel\frozen_halls\pit_of_saron + + + scripts\northrend\icecrown_citadel\frozen_halls\pit_of_saron + + + scripts\northrend\icecrown_citadel\frozen_halls\pit_of_saron + + + scripts\northrend\icecrown_citadel\frozen_halls\halls_of_reflection + + + scripts\northrend\icecrown_citadel\frozen_halls\halls_of_reflection + + + scripts\northrend\icecrown_citadel\frozen_halls\halls_of_reflection + + + scripts\northrend\icecrown_citadel\frozen_halls\halls_of_reflection + + + scripts\northrend\icecrown_citadel\frozen_halls\halls_of_reflection + + + scripts\northrend\icecrown_citadel\frozen_halls\forge_of_souls + + + scripts\northrend\icecrown_citadel\frozen_halls\forge_of_souls + + + scripts\northrend\icecrown_citadel\frozen_halls\forge_of_souls + + + scripts\northrend\naxxramas + + + scripts\northrend\naxxramas + + + scripts\northrend\naxxramas + + + scripts\northrend\naxxramas + + + scripts\northrend\naxxramas + + + scripts\northrend\naxxramas + + + scripts\northrend\naxxramas + + + scripts\northrend\naxxramas + + + scripts\northrend\naxxramas + + + scripts\northrend\naxxramas + + + scripts\northrend\naxxramas + + + scripts\northrend\naxxramas + + + scripts\northrend\naxxramas + + + scripts\northrend\naxxramas + + + scripts\northrend\naxxramas + + + scripts\northrend\naxxramas + + + scripts\northrend\nexus\eye_of_eternity + + + scripts\northrend\nexus\eye_of_eternity + + + scripts\northrend\nexus\nexus + + + scripts\northrend\nexus\nexus + + + scripts\northrend\nexus\nexus + + + scripts\northrend\nexus\nexus + + + scripts\northrend\nexus\nexus + + + scripts\northrend\nexus\oculus + + + scripts\northrend\nexus\oculus + + + scripts\northrend\nexus\oculus + + + scripts\northrend\nexus\oculus + + + scripts\northrend\nexus\oculus + + + scripts\northrend\obsidian_sanctum + + + scripts\northrend\obsidian_sanctum + + + scripts\northrend\ruby_sanctum + + + scripts\northrend\ruby_sanctum + + + scripts\northrend\ruby_sanctum + + + scripts\northrend\ruby_sanctum + + + scripts\northrend\ruby_sanctum + + + scripts\northrend\ulduar\halls_of_lightning + + + scripts\northrend\ulduar\halls_of_lightning + + + scripts\northrend\ulduar\halls_of_lightning + + + scripts\northrend\ulduar\halls_of_lightning + + + scripts\northrend\ulduar\halls_of_lightning + + + scripts\northrend\ulduar\halls_of_stone + + + scripts\northrend\ulduar\halls_of_stone + + + scripts\northrend\ulduar\halls_of_stone + + + scripts\northrend\ulduar\halls_of_stone + + + scripts\northrend\ulduar\ulduar + + + scripts\northrend\ulduar\ulduar + + + scripts\northrend\ulduar\ulduar + + + scripts\northrend\ulduar\ulduar + + + scripts\northrend\ulduar\ulduar + + + scripts\northrend\ulduar\ulduar + + + scripts\northrend\ulduar\ulduar + + + scripts\northrend\ulduar\ulduar + + + scripts\northrend\ulduar\ulduar + + + scripts\northrend\ulduar\ulduar + + + scripts\northrend\ulduar\ulduar + + + scripts\northrend\ulduar\ulduar + + + scripts\northrend\ulduar\ulduar + + + scripts\northrend\ulduar\ulduar + + + scripts\northrend\ulduar\ulduar + + + scripts\northrend\ulduar\ulduar + + + scripts\northrend\utgarde_keep\utgarde_keep + + + scripts\northrend\utgarde_keep\utgarde_keep + + + scripts\northrend\utgarde_keep\utgarde_keep + + + scripts\northrend\utgarde_keep\utgarde_keep + + + scripts\northrend\utgarde_keep\utgarde_keep + + + scripts\northrend\utgarde_keep\utgarde_pinnacle + + + scripts\northrend\utgarde_keep\utgarde_pinnacle + + + scripts\northrend\utgarde_keep\utgarde_pinnacle + + + scripts\northrend\utgarde_keep\utgarde_pinnacle + + + scripts\northrend\utgarde_keep\utgarde_pinnacle + + + scripts\northrend\vault_of_archavon + + + scripts\northrend\vault_of_archavon + + + scripts\northrend\vault_of_archavon + + + scripts\northrend\vault_of_archavon + + + scripts\northrend\vault_of_archavon + + + scripts\northrend\violet_hold + + + scripts\northrend\violet_hold + + + scripts\northrend\violet_hold + + + scripts\northrend\violet_hold + + + scripts\outland + + + scripts\outland + + + scripts\outland + + + scripts\outland + + + scripts\outland + + + scripts\outland + + + scripts\outland + + + scripts\outland + + + scripts\outland + + + scripts\outland + + + scripts\outland\auchindoun\auchenai_crypts + + + scripts\outland\auchindoun\auchenai_crypts + + + scripts\outland\auchindoun\mana_tombs + + + scripts\outland\auchindoun\mana_tombs + + + scripts\outland\auchindoun\sethekk_halls + + + scripts\outland\auchindoun\sethekk_halls + + + scripts\outland\auchindoun\sethekk_halls + + + scripts\outland\auchindoun\sethekk_halls + + + scripts\outland\auchindoun\shadow_labyrinth + + + scripts\outland\auchindoun\shadow_labyrinth + + + scripts\outland\auchindoun\shadow_labyrinth + + + scripts\outland\auchindoun\shadow_labyrinth + + + scripts\outland\auchindoun\shadow_labyrinth + + + scripts\outland\black_temple + + + scripts\outland\black_temple + + + scripts\outland\black_temple + + + scripts\outland\black_temple + + + scripts\outland\black_temple + + + scripts\outland\black_temple + + + scripts\outland\black_temple + + + scripts\outland\black_temple + + + scripts\outland\black_temple + + + scripts\outland\black_temple + + + scripts\outland\black_temple + + + scripts\outland\coilfang_reservoir\serpent_shrine + + + scripts\outland\coilfang_reservoir\serpent_shrine + + + scripts\outland\coilfang_reservoir\serpent_shrine + + + scripts\outland\coilfang_reservoir\serpent_shrine + + + scripts\outland\coilfang_reservoir\serpent_shrine + + + scripts\outland\coilfang_reservoir\serpent_shrine + + + scripts\outland\coilfang_reservoir\serpent_shrine + + + scripts\outland\coilfang_reservoir\slave_pens + + + scripts\outland\coilfang_reservoir\steam_vault + + + scripts\outland\coilfang_reservoir\steam_vault + + + scripts\outland\coilfang_reservoir\steam_vault + + + scripts\outland\coilfang_reservoir\steam_vault + + + scripts\outland\coilfang_reservoir\underbog + + + scripts\outland\gruuls_lair + + + scripts\outland\gruuls_lair + + + scripts\outland\gruuls_lair + + + scripts\outland\hellfire_citadel\blood_furnace + + + scripts\outland\hellfire_citadel\blood_furnace + + + scripts\outland\hellfire_citadel\blood_furnace + + + scripts\outland\hellfire_citadel\blood_furnace + + + scripts\outland\hellfire_citadel\hellfire_ramparts + + + scripts\outland\hellfire_citadel\hellfire_ramparts + + + scripts\outland\hellfire_citadel\hellfire_ramparts + + + scripts\outland\hellfire_citadel\hellfire_ramparts + + + scripts\outland\hellfire_citadel\magtheridons_lair + + + scripts\outland\hellfire_citadel\magtheridons_lair + + + scripts\outland\hellfire_citadel\shattered_halls + + + scripts\outland\hellfire_citadel\shattered_halls + + + scripts\outland\hellfire_citadel\shattered_halls + + + scripts\outland\hellfire_citadel\shattered_halls + + + scripts\outland\tempest_keep\arcatraz + + + scripts\outland\tempest_keep\arcatraz + + + scripts\outland\tempest_keep\arcatraz + + + scripts\outland\tempest_keep\arcatraz + + + scripts\outland\tempest_keep\arcatraz + + + scripts\outland\tempest_keep\botanica + + + scripts\outland\tempest_keep\botanica + + + scripts\outland\tempest_keep\botanica + + + scripts\outland\tempest_keep\the_eye + + + scripts\outland\tempest_keep\the_eye + + + scripts\outland\tempest_keep\the_eye + + + scripts\outland\tempest_keep\the_eye + + + scripts\outland\tempest_keep\the_eye + + + scripts\outland\tempest_keep\the_mechanar + + + scripts\outland\tempest_keep\the_mechanar + + + scripts\outland\tempest_keep\the_mechanar + + + scripts\world + + + scripts\world + + + scripts\world + + + scripts\world + + + scripts\world + + + scripts\world + + + scripts\world + + + scripts\world + + + scripts\world + + + scripts\world + + + include + + + include + + + include + + + include + + + system + + + system + + + system + + + + + + base + + + base + + + base + + + base + + + scripts\eastern_kingdoms\bastion_of_twilight + + + scripts\eastern_kingdoms\blackrock_mountain\blackrock_caverns + + + scripts\eastern_kingdoms\blackrock_mountain\blackrock_depths + + + scripts\eastern_kingdoms\blackrock_mountain\blackrock_spire + + + scripts\eastern_kingdoms\blackrock_mountain\blackwing_descent + + + scripts\eastern_kingdoms\blackrock_mountain\blackwing_lair + + + scripts\eastern_kingdoms\blackrock_mountain\molten_core + + + scripts\eastern_kingdoms\deadmines + + + scripts\eastern_kingdoms\gnomeregan + + + scripts\eastern_kingdoms\grim_batol + + + scripts\eastern_kingdoms\karazhan + + + scripts\eastern_kingdoms\magisters_terrace + + + scripts\eastern_kingdoms\scarlet_enclave + + + scripts\eastern_kingdoms\scarlet_monastery + + + scripts\eastern_kingdoms\scholomance + + + scripts\eastern_kingdoms\shadowfang_keep + + + scripts\eastern_kingdoms\stratholme + + + scripts\eastern_kingdoms\sunken_temple + + + scripts\eastern_kingdoms\sunwell_plateau + + + scripts\eastern_kingdoms\throne_of_the_tides + + + scripts\eastern_kingdoms\uldaman + + + scripts\eastern_kingdoms\zulaman + + + scripts\eastern_kingdoms\zulgurub + + + scripts\kalimdor\blackfathom_deeps + + + scripts\kalimdor\caverns_of_time\culling_of_stratholme + + + scripts\kalimdor\caverns_of_time\dark_portal + + + scripts\kalimdor\caverns_of_time\dragon_soul + + + scripts\kalimdor\caverns_of_time\end_of_time + + + scripts\kalimdor\caverns_of_time\hour_of_twilight + + + scripts\kalimdor\caverns_of_time\hyjal_summit + + + scripts\kalimdor\caverns_of_time\hyjal_summit + + + scripts\kalimdor\caverns_of_time\old_hillsbrad + + + scripts\kalimdor\caverns_of_time\well_of_eternity + + + scripts\kalimdor\dire_maul + + + scripts\kalimdor\firelands + + + scripts\kalimdor\lost_city_of_tolvir + + + scripts\kalimdor\onyxias_lair + + + scripts\kalimdor\razorfen_kraul + + + scripts\kalimdor\ruins_of_ahnqiraj + + + scripts\kalimdor\temple_of_ahnqiraj + + + scripts\kalimdor\throne_of_the_four_winds + + + scripts\kalimdor\vortex_pinnacle + + + scripts\kalimdor\wailing_caverns + + + scripts\kalimdor\zulfarrak + + + scripts\maelstrom\stonecore + + + scripts\northrend\azjol-nerub\ahnkahet + + + scripts\northrend\azjol-nerub\azjol-nerub + + + scripts\northrend\crusaders_coliseum\trial_of_the_champion + + + scripts\northrend\crusaders_coliseum\trial_of_the_crusader + + + scripts\northrend\draktharon_keep + + + scripts\northrend\gundrak + + + scripts\northrend\icecrown_citadel\frozen_halls\forge_of_souls + + + scripts\northrend\icecrown_citadel\frozen_halls\halls_of_reflection + + + scripts\northrend\icecrown_citadel\frozen_halls\pit_of_saron + + + scripts\northrend\icecrown_citadel\icecrown_citadel + + + scripts\northrend\naxxramas + + + scripts\northrend\nexus\eye_of_eternity + + + scripts\northrend\nexus\nexus + + + scripts\northrend\nexus\oculus + + + scripts\northrend\obsidian_sanctum + + + scripts\northrend\ruby_sanctum + + + scripts\northrend\ulduar\halls_of_lightning + + + scripts\northrend\ulduar\halls_of_stone + + + scripts\northrend\ulduar\ulduar + + + scripts\northrend\utgarde_keep\utgarde_keep + + + scripts\northrend\utgarde_keep\utgarde_pinnacle + + + scripts\northrend\vault_of_archavon + + + scripts\northrend\violet_hold + + + scripts\outland\auchindoun\sethekk_halls + + + scripts\outland\auchindoun\shadow_labyrinth + + + scripts\outland\black_temple + + + scripts\outland\coilfang_reservoir\serpent_shrine + + + scripts\outland\coilfang_reservoir\steam_vault + + + scripts\outland\gruuls_lair + + + scripts\outland\hellfire_citadel\blood_furnace + + + scripts\outland\hellfire_citadel\hellfire_ramparts + + + scripts\outland\hellfire_citadel\magtheridons_lair + + + scripts\outland\hellfire_citadel\shattered_halls + + + scripts\outland\tempest_keep\arcatraz + + + scripts\outland\tempest_keep\the_eye + + + scripts\outland\tempest_keep\the_mechanar + + + scripts\world + + + include + + + include + + + include + + + include + + + include + + + system + + + system + + + + + + + + + + \ No newline at end of file diff --git a/src/modules/SD2/CMakeLists.txt b/src/modules/SD2/CMakeLists.txt new file mode 100644 index 000000000..55b8e1fa5 --- /dev/null +++ b/src/modules/SD2/CMakeLists.txt @@ -0,0 +1,516 @@ +# +# Copyright (C) 2005-2015 MaNGOS +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +# +# ***** END GPL LICENSE BLOCK ***** +# +# World of Warcraft, and all World of Warcraft or Warcraft art, images, +# and lore are copyrighted by Blizzard Entertainment, Inc. + +include(MacroMangosSourceGroup) + +set(mangosscript_PCH_H "${CMAKE_CURRENT_SOURCE_DIR}/include/precompiled.h") +if(PCH) + set(mangosscript_PCH_CPP "${CMAKE_CURRENT_SOURCE_DIR}/include/precompiled.cpp") +endif() + +include_directories( + "${CMAKE_CURRENT_SOURCE_DIR}/base" + "${CMAKE_CURRENT_SOURCE_DIR}/include" + "${CMAKE_SOURCE_DIR}/src/shared" + "${CMAKE_SOURCE_DIR}/src/framework" + "${CMAKE_SOURCE_DIR}/src/game/Server" + "${CMAKE_SOURCE_DIR}/src/game/BattleGround" + "${CMAKE_SOURCE_DIR}/src/game/WorldHandlers" + "${CMAKE_SOURCE_DIR}/src/game/Object" + "${CMAKE_SOURCE_DIR}/src/game/References" + "${CMAKE_SOURCE_DIR}/src/game/MotionGenerators" + "${CMAKE_SOURCE_DIR}/src/game/vmap" + "${CMAKE_SOURCE_DIR}/dep/include" + "${CMAKE_BINARY_DIR}" + "${CMAKE_BINARY_DIR}/src/shared" + "${ACE_INCLUDE_DIR}" + "${MYSQL_INCLUDE_DIR}" +) + +#----------------------------------------------------------------------------- +# Define the scriptdev2 library +set(sources + sd2_revision_nr.h + sd2_revision_sql.h + ScriptDevMgr.cpp + ScriptDevMgr.h + base/escort_ai.cpp + base/escort_ai.h + base/follower_ai.cpp + base/follower_ai.h + base/guard_ai.cpp + base/guard_ai.h + base/pet_ai.cpp + base/pet_ai.h + + ${mangosscript_PCH_H} + ${mangosscript_PCH_CPP} + include/sc_creature.cpp + include/sc_creature.h + include/sc_grid_searchers.cpp + include/sc_grid_searchers.h + include/sc_instance.cpp + include/sc_instance.h + include/sc_gossip.h + + system/ScriptLoader.cpp + system/ScriptLoader.h + system/system.cpp + system/system.h +) + +set(mangosscript_LIB_SRCS ${sources}) + +#Battleground Scripts +file(GLOB sources_battlegrounds scripts/battlegrounds/*.cpp scripts/battlegrounds/*.h) + LIST(APPEND mangosscript_LIB_SRCS ${sources_battlegrounds}) + source_group("Battleground Scripts" FILES ${sources_battlegrounds}) + +#World Scripts +file(GLOB sources_world scripts/world/*.cpp scripts/world/*.h) + LIST(APPEND mangosscript_LIB_SRCS ${sources_world}) + source_group("World Scripts" FILES ${sources_world}) + +#Eastern Kingdom Scripts +file(GLOB sources_eastern_kingdoms scripts/eastern_kingdoms/*.cpp scripts/eastern_kingdoms/*.h) + LIST(APPEND mangosscript_LIB_SRCS ${sources_eastern_kingdoms}) + source_group("Eastern Kingdoms Scripts" FILES ${sources_eastern_kingdoms}) + +#Kalimdor Scripts +file(GLOB sources_kalimdor scripts/kalimdor/*.cpp scripts/kalimdor/*.h) + LIST(APPEND mangosscript_LIB_SRCS ${sources_kalimdor}) + source_group("Kalimdor Scripts" FILES ${sources_kalimdor}) + +#Outlands Scripts +file(GLOB sources_outlands scripts/outland/*.cpp scripts/outland/*.h) + LIST(APPEND mangosscript_LIB_SRCS ${sources_outlands}) + source_group("Outlands Scripts" FILES ${sources_outlands}) + +#Northrend Scripts +file(GLOB sources_northrend scripts/northrend/*.cpp scripts/northrend/*.h) + LIST(APPEND mangosscript_LIB_SRCS ${sources_northrend}) + source_group("Northrend Scripts" FILES ${sources_northrend}) + +#Instance: Blackrock Depths Scripts +file(GLOB sources_instance_ek_brd scripts/eastern_kingdoms/blackrock_mountain/blackrock_depths/*.cpp scripts/eastern_kingdoms/blackrock_mountain/blackrock_depths/*.h) + LIST(APPEND mangosscript_LIB_SRCS ${sources_instance_ek_brd}) + source_group("Eastern Kingdoms Scripts\\Instances\\Blackrock Depths" FILES ${sources_instance_ek_brd}) + +#Instance: Blackrock Spire Scripts +file(GLOB sources_instance_ek_brs scripts/eastern_kingdoms/blackrock_mountain/blackrock_spire/*.cpp scripts/eastern_kingdoms/blackrock_mountain/blackrock_spire/*.h) + LIST(APPEND mangosscript_LIB_SRCS ${sources_instance_ek_brs}) + source_group("Eastern Kingdoms Scripts\\Instances\\Blackrock Spire" FILES ${sources_instance_ek_brs}) + +#Instance: Deadmines Scripts +file(GLOB sources_instance_ek_tdm scripts/eastern_kingdoms/deadmines/*.cpp scripts/eastern_kingdoms/deadmines/*.h) + LIST(APPEND mangosscript_LIB_SRCS ${sources_instance_ek_tdm}) + source_group("Eastern Kingdoms Scripts\\Instances\\Deadmines" FILES ${sources_instance_ek_tdm}) + +#Instance: Gnomeregan Scripts +file(GLOB sources_instance_ek_gno scripts/eastern_kingdoms/gnomeregan/*.cpp scripts/eastern_kingdoms/gnomeregan/*.h) + LIST(APPEND mangosscript_LIB_SRCS ${sources_instance_ek_gno}) + source_group("Eastern Kingdoms Scripts\\Instances\\Gnomeregan" FILES ${sources_instance_ek_gno}) + +#Instance: Scarlet Monastery Scripts +file(GLOB sources_instance_ek_sm scripts/eastern_kingdoms/scarlet_monastery/*.cpp scripts/eastern_kingdoms/scarlet_monastery/*.h) + LIST(APPEND mangosscript_LIB_SRCS ${sources_instance_ek_sm}) + source_group("Eastern Kingdoms Scripts\\Instances\\Scarlet Monastery" FILES ${sources_instance_ek_sm}) + +#Instance: Scholomance Scripts +file(GLOB sources_instance_ek_scholo scripts/eastern_kingdoms/scholomance/*.cpp scripts/eastern_kingdoms/scholomance/*.h) + LIST(APPEND mangosscript_LIB_SRCS ${sources_instance_ek_scholo}) + source_group("Eastern Kingdoms Scripts\\Instances\\Scholomance" FILES ${sources_instance_ek_scholo}) + +#Instance: Uldaman Scripts +file(GLOB sources_instance_ek_uld scripts/eastern_kingdoms/uldaman/*.cpp scripts/eastern_kingdoms/uldaman/*.h) + LIST(APPEND mangosscript_LIB_SRCS ${sources_instance_ek_uld}) + source_group("Eastern Kingdoms Scripts\\Instances\\Uldaman" FILES ${sources_instance_ek_uld}) + +#Instance: Shadowfang Keep Scripts +file(GLOB sources_instance_ek_sfk scripts/eastern_kingdoms/shadowfang_keep/*.cpp scripts/eastern_kingdoms/shadowfang_keep/*.h) + LIST(APPEND mangosscript_LIB_SRCS ${sources_instance_ek_sfk}) + source_group("Eastern Kingdoms Scripts\\Instances\\Shadowfang Keep" FILES ${sources_instance_ek_sfk}) + +#Instance: Sunken Temple Scripts +file(GLOB sources_instance_ek_st scripts/eastern_kingdoms/sunken_temple/*.cpp scripts/eastern_kingdoms/sunken_temple/*.h) + LIST(APPEND mangosscript_LIB_SRCS ${sources_instance_ek_st}) + source_group("Eastern Kingdoms Scripts\\Instances\\Sunken Temple" FILES ${sources_instance_ek_st}) + +#Instance: Stratholme Scripts +file(GLOB sources_instance_ek_strat scripts/eastern_kingdoms/stratholme/*.cpp scripts/eastern_kingdoms/stratholme/*.h) + LIST(APPEND mangosscript_LIB_SRCS ${sources_instance_ek_strat}) + source_group("Eastern Kingdoms Scripts\\Instances\\Stratholme" FILES ${sources_instance_ek_strat}) + +#Instance: Magisters Terrace Scripts +file(GLOB sources_instance_ek_mt scripts/eastern_kingdoms/magisters_terrace/*.cpp scripts/eastern_kingdoms/magisters_terrace/*.h) + LIST(APPEND mangosscript_LIB_SRCS ${sources_instance_ek_mt}) + source_group("Eastern Kingdoms Scripts\\Instances\\Magisters Terrace" FILES ${sources_instance_ek_mt}) + +#Raid: Karazhan Scripts +file(GLOB sources_raid_ek_kara scripts/eastern_kingdoms/karazhan/*.cpp scripts/eastern_kingdoms/karazhan/*.h) + LIST(APPEND mangosscript_LIB_SRCS ${sources_raid_ek_kara}) + source_group("Eastern Kingdoms Scripts\\Raids\\Karazhan" FILES ${sources_raid_ek_kara}) + +#Raid: Sunwell Plateau Scripts +file(GLOB sources_raid_ek_sp scripts/eastern_kingdoms/sunwell_plateau/*.cpp scripts/eastern_kingdoms/sunwell_plateau/*.h) + LIST(APPEND mangosscript_LIB_SRCS ${sources_raid_ek_sp}) + source_group("Eastern Kingdoms Scripts\\Raids\\Sunwell Plateau" FILES ${sources_raid_ek_sp}) + +#Raid: Zul'Aman Scripts +file(GLOB sources_raid_ek_za scripts/eastern_kingdoms/zulaman/*.cpp scripts/eastern_kingdoms/zulaman/*.h) + LIST(APPEND mangosscript_LIB_SRCS ${sources_raid_ek_za}) + source_group("Eastern Kingdoms Scripts\\Raids\\Zul'Aman" FILES ${sources_raid_ek_za}) + +#Raid: Molten Core Scripts +file(GLOB sources_raid_ek_mc scripts/eastern_kingdoms/blackrock_mountain/molten_core/*.cpp scripts/eastern_kingdoms/blackrock_mountain/molten_core/*.h) + LIST(APPEND mangosscript_LIB_SRCS ${sources_raid_ek_mc}) + source_group("Eastern Kingdoms Scripts\\Raids\\Molten Core" FILES ${sources_raid_ek_mc}) + +#Raid: Blackwing Lair Scripts +file(GLOB sources_raid_ek_bwl scripts/eastern_kingdoms/blackrock_mountain/blackwing_lair/*.cpp scripts/eastern_kingdoms/blackrock_mountain/blackwing_lair/*.h) + LIST(APPEND mangosscript_LIB_SRCS ${sources_raid_ek_bwl}) + source_group("Eastern Kingdoms Scripts\\Raids\\Blackwing Lair" FILES ${sources_raid_ek_bwl}) + +#Raid: Zul Gurub Scripts +file(GLOB sources_raid_ek_zg scripts/eastern_kingdoms/zulgurub/*.cpp scripts/eastern_kingdoms/zulgurub/*.h) + LIST(APPEND mangosscript_LIB_SRCS ${sources_raid_ek_zg}) + source_group("Eastern Kingdoms Scripts\\Raids\\Zul Gurub" FILES ${sources_raid_ek_zg}) + +#Instance: Blackfathom Deeps Scripts +file(GLOB sources_instance_k_bdf scripts/kalimdor/blackfathom_deeps/*.cpp scripts/kalimdor/blackfathom_deeps/*.h) + LIST(APPEND mangosscript_LIB_SRCS ${sources_instance_k_bdf}) + source_group("Kalimdor Scripts\\Instances\\Blackfathom Deeps" FILES ${sources_instance_k_bdf}) + +#Instance: Dire Maul Scripts +file(GLOB sources_instance_k_dm scripts/kalimdor/dire_maul/*.cpp scripts/kalimdor/dire_maul/*.h) + LIST(APPEND mangosscript_LIB_SRCS ${sources_instance_k_dm}) + source_group("Kalimdor Scripts\\Instances\\Dire Maul" FILES ${sources_instance_k_dm}) + +#Instance: Maraudon Scripts +file(GLOB sources_instance_k_mara scripts/kalimdor/maraudon/*.cpp scripts/kalimdor/maraudon/*.h) + LIST(APPEND mangosscript_LIB_SRCS ${sources_instance_k_mara}) + source_group("Kalimdor Scripts\\Instances\\Maraudon Scripts" FILES ${sources_instance_k_mara}) + +#Instance: Razorfen Downs Scripts +file(GLOB sources_instance_k_rfd scripts/kalimdor/razorfen_downs/*.cpp scripts/kalimdor/razorfen_downs/*.h) + LIST(APPEND mangosscript_LIB_SRCS ${sources_instance_k_rfd}) + source_group("Kalimdor Scripts\\Instances\\Razorfen Downs" FILES ${sources_instance_k_rfd}) + +#Instance: Razorfen Kraul Scripts +file(GLOB sources_instance_k_rfk scripts/kalimdor/razorfen_kraul/*.cpp scripts/kalimdor/razorfen_kraul/*.h) + LIST(APPEND mangosscript_LIB_SRCS ${sources_instance_k_rfk}) + source_group("Kalimdor Scripts\\Instances\\Razorfen Kraul" FILES ${sources_instance_k_rfk}) + +#Instance: Wailing Caverns Scripts +file(GLOB sources_instance_k_wc scripts/kalimdor/wailing_caverns/*.cpp scripts/kalimdor/wailing_caverns/*.h) + LIST(APPEND mangosscript_LIB_SRCS ${sources_instance_k_wc}) + source_group("Kalimdor Scripts\\Instances\\Wailing Caverns" FILES ${sources_instance_k_wc}) + +#Instance: Caverns of Time - Black Morass +file(GLOB sources_instance_k_cot_bm scripts/kalimdor/caverns_of_time/dark_portal/*.cpp scripts/kalimdor/caverns_of_time/dark_portal/*.h) + LIST(APPEND mangosscript_LIB_SRCS ${sources_instance_k_cot_bm}) + source_group("Kalimdor Scripts\\Instances\\Caverns of Time\\Black Morass" FILES ${sources_instance_k_cot_bm}) + +#Instance: Caverns of Time - Old Hillsbrad +file(GLOB sources_instance_k_cot_oh scripts/kalimdor/caverns_of_time/old_hillsbrad/*.cpp scripts/kalimdor/caverns_of_time/old_hillsbrad/*.h) + LIST(APPEND mangosscript_LIB_SRCS ${sources_instance_k_cot_oh}) + source_group("Kalimdor Scripts\\Instances\\Caverns of Time\\Old Hillsbrad" FILES ${sources_instance_k_cot_oh}) + +#Instance: Caverns of Time - Culling of Stratholme +file(GLOB sources_instance_k_cot_cos scripts/kalimdor/caverns_of_time/culling_of_stratholme/*.cpp scripts/kalimdor/caverns_of_time/culling_of_stratholme/*.h) + LIST(APPEND mangosscript_LIB_SRCS ${sources_instance_k_cot_cos}) + source_group("Kalimdor Scripts\\Instances\\Caverns of Time\\Culling of Stratholme" FILES ${sources_instance_k_cot_cos}) + +#Instance: Zul Farrak Scripts +file(GLOB sources_instance_k_zf scripts/kalimdor/zulfarrak/*.cpp scripts/kalimdor/zulfarrak/*.h) + LIST(APPEND mangosscript_LIB_SRCS ${sources_instance_k_zf}) + source_group("Kalimdor Scripts\\Instances\\Zul Farrak" FILES ${sources_instance_k_zf}) + +#Raid: Caverns of Time - Mount Hyjal +file(GLOB sources_instance_k_cot_mh scripts/kalimdor/caverns_of_time/hyjal/*.cpp scripts/kalimdor/caverns_of_time/hyjal/*.h) + LIST(APPEND mangosscript_LIB_SRCS ${sources_instance_k_cot_mh}) + source_group("Kalimdor Scripts\\Raids\\Caverns of Time\\Mount Hyjal" FILES ${sources_instance_k_cot_mh}) + +#Raid: Onyxias Lair Scripts +file(GLOB sources_raid_k_ony scripts/kalimdor/onyxias_lair/*.cpp scripts/kalimdor/onyxias_lair/*.h) + LIST(APPEND mangosscript_LIB_SRCS ${sources_raid_k_ony}) + source_group("Kalimdor Scripts\\Raids\\Onyxias Lair" FILES ${sources_raid_k_ony}) + +#Raid: Ruins of AQ Scripts +file(GLOB sources_raid_k_raq scripts/kalimdor/ruins_of_ahnqiraj/*.cpp scripts/kalimdor/ruins_of_ahnqiraj/*.h) + LIST(APPEND mangosscript_LIB_SRCS ${sources_raid_k_raq}) + source_group("Kalimdor Scripts\\Raids\\Ruins of AQ" FILES ${sources_raid_k_raq}) + +#Raid: Temple of AQ Scripts +file(GLOB sources_raid_k_taq scripts/kalimdor/temple_of_ahnqiraj/*.cpp scripts/kalimdor/temple_of_ahnqiraj/*.h) + LIST(APPEND mangosscript_LIB_SRCS ${sources_raid_k_taq}) + source_group("Kalimdor Scripts\\Raids\\Temple of AQ" FILES ${sources_raid_k_taq}) + +#Instance: Auchindoun - Auchenai Crypts +file(GLOB sources_instance_ol_aac scripts/outland/auchindoun/auchenai_crypts/*.cpp scripts/outland/auchindoun/auchenai_crypts/*.h) + LIST(APPEND mangosscript_LIB_SRCS ${sources_instance_ol_aac}) + source_group("Outlands Scripts\\Instances\\Auchindoun\\Auchenai Crypts" FILES ${sources_instance_ol_aac}) + +#Instance: Auchindoun - Mana Tombs +file(GLOB sources_instance_ol_amt scripts/outland/auchindoun/mana_tombs/*.cpp scripts/outland/auchindoun/mana_tombs/*.h) + LIST(APPEND mangosscript_LIB_SRCS ${sources_instance_ol_amt}) + source_group("Outlands Scripts\\Instances\\Auchindoun\\Mana Tombs" FILES ${sources_instance_ol_amt}) + +#Instance: Auchindoun - Sethekk Halls +file(GLOB sources_instance_ol_ash scripts/outland/auchindoun/sethekk_halls/*.cpp scripts/outland/auchindoun/sethekk_halls/*.h) + LIST(APPEND mangosscript_LIB_SRCS ${sources_instance_ol_ash}) + source_group("Outlands Scripts\\Instances\\Auchindoun\\Sethekk Halls" FILES ${sources_instance_ol_ash}) + +#Instance: Auchindoun - Shadow Labyrinth +file(GLOB sources_instance_ol_asl scripts/outland/auchindoun/shadow_labyrinth/*.cpp scripts/outland/auchindoun/shadow_labyrinth/*.h) + LIST(APPEND mangosscript_LIB_SRCS ${sources_instance_ol_asl}) + source_group("Outlands Scripts\\Instances\\Auchindoun\\Shadow Labyrinth" FILES ${sources_instance_ol_asl}) + +#Instance: Coilfang Reservoir - Slave Pens +file(GLOB sources_instance_ol_csp scripts/outland/coilfang_reservoir/slave_pens/*.cpp scripts/outland/coilfang_reservoir/slave_pens/*.h) + LIST(APPEND mangosscript_LIB_SRCS ${sources_instance_ol_csp}) + source_group("Outlands Scripts\\Instances\\Coilfang Reservoir\\Slave Pens" FILES ${sources_instance_ol_csp}) + +#Instance: Coilfang Reservoir - Steam Vaults +file(GLOB sources_instance_ol_cst scripts/outland/coilfang_reservoir/steam_vault/*.cpp scripts/outland/coilfang_reservoir/steam_vault/*.h) + LIST(APPEND mangosscript_LIB_SRCS ${sources_instance_ol_cst}) + source_group("Outlands Scripts\\Instances\\Coilfang Reservoir\\Steam Vaults" FILES ${sources_instance_ol_cst}) + +#Instance: Coilfang Reservoir - Underbog +file(GLOB sources_instance_ol_cu scripts/outland/coilfang_reservoir/underbog/*.cpp scripts/outland/coilfang_reservoir/underbog/*.h) + LIST(APPEND mangosscript_LIB_SRCS ${sources_instance_ol_cu}) + source_group("Outlands Scripts\\Instances\\Coilfang Reservoir\\Underbog" FILES ${sources_instance_ol_cu}) + +#Instance: Hellfire Citadel - Blood Furnace +file(GLOB sources_raid_ol_hbf scripts/outland/hellfire_citadel/blood_furnace/*.cpp scripts/outland/hellfire_citadel/blood_furnace/*.h) + LIST(APPEND mangosscript_LIB_SRCS ${sources_raid_ol_hbf}) + source_group("Outlands Scripts\\Instances\\Hellfire Citadel\\Blood Furnace" FILES ${sources_raid_ol_hbf}) + +#Instance: Hellfire Citadel - Hellfire Ramparts +file(GLOB sources_raid_ol_hhr scripts/outland/hellfire_citadel/hellfire_ramparts/*.cpp scripts/outland/hellfire_citadel/hellfire_ramparts/*.h) + LIST(APPEND mangosscript_LIB_SRCS ${sources_raid_ol_hhr}) + source_group("Outlands Scripts\\Instances\\Hellfire Citadel\\Hellfire Ramparts" FILES ${sources_raid_ol_hhr}) + +#Instance: Hellfire Citadel - Shattered Halls +file(GLOB sources_raid_ol_hsh scripts/outland/hellfire_citadel/shattered_halls/*.cpp scripts/outland/hellfire_citadel/shattered_halls/*.h) + LIST(APPEND mangosscript_LIB_SRCS ${sources_raid_ol_hsh}) + source_group("Outlands Scripts\\Instances\\Hellfire Citadel\\Shattered Halls" FILES ${sources_raid_ol_hsh}) + +#Instance: Tempest Keep - Arcatraz +file(GLOB sources_raid_ol_ta scripts/outland/tempest_keep/arcatraz/*.cpp scripts/outland/tempest_keep/arcatraz/*.h) + LIST(APPEND mangosscript_LIB_SRCS ${sources_raid_ol_ta}) + source_group("Outlands Scripts\\Instances\\Tempest Keep\\Arcatraz" FILES ${sources_raid_ol_ta}) + +#Instance: Tempest Keep - Botanica +file(GLOB sources_raid_ol_tb scripts/outland/tempest_keep/botanica/*.cpp scripts/outland/tempest_keep/botanica/*.h) + LIST(APPEND mangosscript_LIB_SRCS ${sources_raid_ol_tb}) + source_group("Outlands Scripts\\Instances\\Tempest Keep\\Botanica" FILES ${sources_raid_ol_tb}) + +#Instance: Tempest Keep - The Mechanar +file(GLOB sources_raid_ol_ttm scripts/outland/tempest_keep/the_mechanar/*.cpp scripts/outland/tempest_keep/the_mechanar/*.h) + LIST(APPEND mangosscript_LIB_SRCS ${sources_raid_ol_ttm}) + source_group("Outlands Scripts\\Instances\\Tempest Keep\\The Mechanar" FILES ${sources_raid_ol_ttm}) + +#Raid: Hellfire Citadel - Magtheridons Lair +file(GLOB sources_raid_ol_hml scripts/outland/hellfire_citadel/magtheridons_lair/*.cpp scripts/outland/hellfire_citadel/magtheridons_lair/*.h) + LIST(APPEND mangosscript_LIB_SRCS ${sources_raid_ol_hml}) + source_group("Outlands Scripts\\Raids\\Hellfire Citadel\\Magtheridons Lair" FILES ${sources_raid_ol_hml}) + +#Raid: Tempest Keep - The Eye +file(GLOB sources_raid_ol_tte scripts/outland/tempest_keep/the_eye/*.cpp scripts/outland/tempest_keep/the_eye/*.h) + LIST(APPEND mangosscript_LIB_SRCS ${sources_raid_ol_tte}) + source_group("Outlands Scripts\\Raids\\Tempest Keep\\The Eye" FILES ${sources_raid_ol_tte}) + +#Raid: Coilfang Reservoir - Serpentshrine Cavern +file(GLOB sources_raid_ol_csc scripts/outland/coilfang_reservoir/serpent_shrine/*.cpp scripts/outland/coilfang_reservoir/serpent_shrine/*.h) + LIST(APPEND mangosscript_LIB_SRCS ${sources_raid_ol_csc}) + source_group("Outlands Scripts\\Raids\\Coilfang Reservoir\\Serpentshrine Cavern" FILES ${sources_raid_ol_csc}) + +#Raid: Black Temple +file(GLOB sources_raid_ol_bt scripts/outland/black_temple/*.cpp scripts/outland/black_temple/*.h) + LIST(APPEND mangosscript_LIB_SRCS ${sources_raid_ol_bt}) + source_group("Outlands Scripts\\Raids\\Black Temple" FILES ${sources_raid_ol_bt}) + +#Raid: Gruul's Lair +file(GLOB sources_raid_ol_gl scripts/outland/gruuls_lair/*.cpp scripts/outland/gruuls_lair/*.h) + LIST(APPEND mangosscript_LIB_SRCS ${sources_raid_ol_gl}) + source_group("Outlands Scripts\\Raids\\Gruul's Lair" FILES ${sources_raid_ol_gl}) + +#Instance: Azjol Nerub +file(GLOB sources_instance_nr_aznj scripts/northrend/azjol-nerub/azjol-nerub/*.cpp scripts/northrend/azjol-nerub/azjol-nerub/*.h) + LIST(APPEND mangosscript_LIB_SRCS ${sources_instance_nr_aznj}) + source_group("Northrend Scripts\\Instances\\Azjol Nerub" FILES ${sources_instance_nr_aznj}) + +#Instance: Ahn'Kahet +file(GLOB sources_instance_nr_ahnk scripts/northrend/azjol-nerub/ahnkahet/*.cpp scripts/northrend/azjol-nerub/ahnkahet/*.h) + LIST(APPEND mangosscript_LIB_SRCS ${sources_instance_nr_ahnk}) + source_group("Northrend Scripts\\Instances\\Ahn'Kahet" FILES ${sources_instance_nr_ahnk}) + +#Instance: Trial of the Champion +file(GLOB sources_instance_nr_totch scripts/northrend/crusaders_coliseum/trial_of_the_champion/*.cpp scripts/northrend/crusaders_coliseum/trial_of_the_champion/*.h) + LIST(APPEND mangosscript_LIB_SRCS ${sources_instance_nr_totch}) + source_group("Northrend Scripts\\Instances\\Trial of the Champion" FILES ${sources_instance_nr_totch}) + +#Instance: Drak'Tharon Keep +file(GLOB sources_instance_nr_dtk scripts/northrend/draktharon_keep/*.cpp scripts/northrend/draktharon_keep/*.h) + LIST(APPEND mangosscript_LIB_SRCS ${sources_instance_nr_dtk}) + source_group("Northrend Scripts\\Instances\\Drak'Tharon Keep" FILES ${sources_instance_nr_dtk}) + +#Instance: Gundrak +file(GLOB sources_instance_nr_gdk scripts/northrend/gundrak/*.cpp scripts/northrend/gundrak/*.h) + LIST(APPEND mangosscript_LIB_SRCS ${sources_instance_nr_gdk}) + source_group("Northrend Scripts\\Instances\\Gundrak" FILES ${sources_instance_nr_gdk}) + +#Instance: Frozen Halls: Forge of Souls +file(GLOB sources_instance_nr_fh_fos scripts/northrend/icecrown_citadel/frozen_halls/forge_of_souls/*.cpp scripts/northrend/icecrown_citadel/frozen_halls/forge_of_souls/*.h) + LIST(APPEND mangosscript_LIB_SRCS ${sources_instance_nr_fh_fos}) + source_group("Northrend Scripts\\Instances\\Forge of Souls" FILES ${sources_instance_nr_fh_fos}) + +#Instance: Frozen Halls: Halls of Reflection +file(GLOB sources_instance_nr_fh_hor scripts/northrend/icecrown_citadel/frozen_halls/halls_of_reflection/*.cpp scripts/northrend/icecrown_citadel/frozen_halls/halls_of_reflection/*.h) + LIST(APPEND mangosscript_LIB_SRCS ${sources_instance_nr_fh_hor}) + source_group("Northrend Scripts\\Instances\\Halls of Reflection" FILES ${sources_instance_nr_fh_hor}) + +#Instance: Frozen Halls: Pit of Saron +file(GLOB sources_instance_nr_fh_pos scripts/northrend/icecrown_citadel/frozen_halls/pit_of_saron/*.cpp scripts/northrend/icecrown_citadel/frozen_halls/pit_of_saron/*.h) + LIST(APPEND mangosscript_LIB_SRCS ${sources_instance_nr_fh_pos}) + source_group("Northrend Scripts\\Instances\\Pit of Saron" FILES ${sources_instance_nr_fh_pos}) + +#Instance: The Nexus +file(GLOB sources_instance_nr_nex scripts/northrend/nexus/nexus/*.cpp scripts/northrend/nexus/nexus/*.h) + LIST(APPEND mangosscript_LIB_SRCS ${sources_instance_nr_nex}) + source_group("Northrend Scripts\\Instances\\The Nexus" FILES ${sources_instance_nr_nex}) + +#Instance: The Oculus +file(GLOB sources_instance_nr_oculus scripts/northrend/nexus/oculus/*.cpp scripts/northrend/nexus/oculus/*.h) + LIST(APPEND mangosscript_LIB_SRCS ${sources_instance_nr_oculus}) + source_group("Northrend Scripts\\Instances\\The Oculus" FILES ${sources_instance_nr_oculus}) + +#Instance: Halls of Lightning +file(GLOB sources_instance_nr_uld_hol scripts/northrend/ulduar/halls_of_lightning/*.cpp scripts/northrend/ulduar/halls_of_lightning/*.h) + LIST(APPEND mangosscript_LIB_SRCS ${sources_instance_nr_uld_hol}) + source_group("Northrend Scripts\\Instances\\Halls of Lightning" FILES ${sources_instance_nr_uld_hol}) + +#Instance: Halls of Stone +file(GLOB sources_instance_nr_uld_hos scripts/northrend/ulduar/halls_of_stone/*.cpp scripts/northrend/ulduar/halls_of_stone/*.h) + LIST(APPEND mangosscript_LIB_SRCS ${sources_instance_nr_uld_hos}) + source_group("Northrend Scripts\\Instances\\Halls of Stone" FILES ${sources_instance_nr_uld_hos}) + +#Instance: Utgarde Keep +file(GLOB sources_instance_nr_uk scripts/northrend/utgarde_keep/utgarde_keep/*.cpp scripts/northrend/utgarde_keep/utgarde_keep/*.h) + LIST(APPEND mangosscript_LIB_SRCS ${sources_instance_nr_uk}) + source_group("Northrend Scripts\\Instances\\Utgarde Keep" FILES ${sources_instance_nr_uk}) + +#Instance: Utgarde Pinnacle +file(GLOB sources_instance_nr_up scripts/northrend/utgarde_keep/utgarde_pinnacle/*.cpp scripts/northrend/utgarde_keep/utgarde_pinnacle/*.h) + LIST(APPEND mangosscript_LIB_SRCS ${sources_instance_nr_up}) + source_group("Northrend Scripts\\Instances\\Utgarde Pinnacle" FILES ${sources_instance_nr_up}) + +#Instance: Violet Hold +file(GLOB sources_instance_nr_vh scripts/northrend/violet_hold/*.cpp scripts/northrend/violet_hold/*.h) + LIST(APPEND mangosscript_LIB_SRCS ${sources_instance_nr_vh}) + source_group("Northrend Scripts\\Instances\\Violet Hold" FILES ${sources_instance_nr_vh}) + +#Raid: Naxxramas +file(GLOB sources_raid_nr_naxx scripts/northrend/naxxramas/*.cpp scripts/northrend/naxxramas/*.h) + LIST(APPEND mangosscript_LIB_SRCS ${sources_raid_nr_naxx}) + source_group("Northrend Scripts\\Raids\\Naxxramas" FILES ${sources_raid_nr_naxx}) + +#Raid: Vault of Archavon +file(GLOB sources_raid_nr_voa scripts/northrend/vault_of_archavon/*.cpp scripts/northrend/vault_of_archavon/*.h) + LIST(APPEND mangosscript_LIB_SRCS ${sources_raid_nr_voa}) + source_group("Northrend Scripts\\Raids\\Vault of Archavon" FILES ${sources_raid_nr_voa}) + +#Raid: Obsidian Sanctum +file(GLOB sources_raid_nr_os scripts/northrend/obsidian_sanctum/*.cpp scripts/northrend/obsidian_sanctum/*.h) + LIST(APPEND mangosscript_LIB_SRCS ${sources_raid_nr_os}) + source_group("Northrend Scripts\\Raids\\Obsidian Sanctum" FILES ${sources_raid_nr_os}) + +#Raid: Ruby Sanctum +file(GLOB sources_raid_nr_rs scripts/northrend/ruby_sanctum/*.cpp scripts/northrend/ruby_sanctum/*.h) + LIST(APPEND mangosscript_LIB_SRCS ${sources_raid_nr_rs}) + source_group("Northrend Scripts\\Raids\\Ruby Sanctum" FILES ${sources_raid_nr_rs}) + +#Raid: Icecrown Citadel +file(GLOB sources_raid_nr_icc scripts/northrend/icecrown_citadel/icecrown_citadel/*.cpp scripts/northrend/icecrown_citadel/icecrown_citadel/*.h) + LIST(APPEND mangosscript_LIB_SRCS ${sources_raid_nr_icc}) + source_group("Northrend Scripts\\Raids\\Icecrown Citadel" FILES ${sources_raid_nr_icc}) + +#Raid: Trial of the Crusader +file(GLOB sources_raid_nr_totc scripts/northrend/crusaders_coliseum/trial_of_the_crusader/*.cpp scripts/northrend/crusaders_coliseum/trial_of_the_crusader/*.h) + LIST(APPEND mangosscript_LIB_SRCS ${sources_raid_nr_totc}) + source_group("Northrend Scripts\\Raids\\Trial of the Crusader" FILES ${sources_raid_nr_totc}) + +#Raid: Eye of Eternity +file(GLOB sources_raid_nr_eoe scripts/northrend/nexus/eye_of_eternity/*.cpp scripts/northrend/nexus/eye_of_eternity/*.h) + LIST(APPEND mangosscript_LIB_SRCS ${sources_raid_nr_eoe}) + source_group("Northrend Scripts\\Raids\\Eye of Eternity" FILES ${sources_raid_nr_eoe}) + +#Raid: Ulduar +file(GLOB sources_raid_nr_uld scripts/northrend/ulduar/ulduar/*.cpp scripts/northrend/ulduar/ulduar/*.h) + LIST(APPEND mangosscript_LIB_SRCS ${sources_raid_nr_uld}) + source_group("Northrend Scripts\\Raids\\Ulduar" FILES ${sources_raid_nr_uld}) + +#WorldScript: Scarlet Enclave +file(GLOB sources_world_scarlet_enclave scripts/eastern_kingdoms/scarlet_enclave/*.cpp scripts/eastern_kingdoms/scarlet_enclave/*.h) + LIST(APPEND mangosscript_LIB_SRCS ${sources_world_scarlet_enclave}) + source_group("Eastern Kingdoms Scripts\\World\\Scarlet Enclave" FILES ${sources_world_scarlet_enclave}) + +add_library(mangosscript STATIC + ${mangosscript_PCH_CPP} + ${mangosscript_LIB_SRCS} +) + +# Generate precompiled header +if(PCH) + ADD_CXX_PCH(mangosscript ${mangosscript_PCH_H} ${mangosscript_PCH_CPP}) +endif() + +if(NOT ACE_USE_EXTERNAL) + add_dependencies(mangosscript ace) +endif() + +if(UNIX) + set(mangosscript_LINK_FLAGS "-pthread") + if(APPLE) + set(mangosscript_LINK_FLAGS "-framework Carbon ${mangosscript_LINK_FLAGS}") + # Needed for the linking because of the missing symbols + set(mangosscript_LINK_FLAGS "-Wl,-undefined -Wl,dynamic_lookup ${mangosscript_LINK_FLAGS}") + endif() + + if(APPLE) + set(mangosscript_PROPERTIES INSTALL_NAME_DIR "${LIBS_DIR}") + else() + set(mangosscript_PROPERTIES INSTALL_RPATH ${LIBS_DIR}) + endif() + + # Run out of build tree + set(mangosscript_PROPERTIES + ${mangosscript_PROPERTIES} + BUILD_WITH_INSTALL_RPATH OFF + ) + + set_target_properties(mangosscript PROPERTIES + LINK_FLAGS ${mangosscript_LINK_FLAGS} + ${mangosscript_PROPERTIES} + ) +endif() diff --git a/src/modules/SD2/README b/src/modules/SD2/README new file mode 100644 index 000000000..17d091332 --- /dev/null +++ b/src/modules/SD2/README @@ -0,0 +1,40 @@ +== ScriptDev2 README == + + Copyright (C) 2006 - 2013 ScriptDev2 + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +== Welcome to ScriptDev2 == + +ScriptDev2 is a script library, an extention of the scripting capabilities +that comes with MaNGOS (https://getmangos.eu/), written in C++ and is +compatible with Windows and Linux. SQL needed for database support both +MySQL and PostgreSQL. + +This script library provides unique scripts for NPCs, gameobjects, events +and other that need unique implementation. + +Once ScriptDev2 is compiled it is automatically run by MaNGOS on server +startup. + +For further information on ScriptDev2, please visit our project web site +at http://www.scriptdev2.com/ + +Documentation on various development related topics can be found in the +../doc/ sub directory as well as on the web site. + +The required SQL files for creating the database backend are included in +the ../sql/ sub directory. If you are updating from an older ScriptDev2 +version, make sure to take a look at the SQL files provided in the +../sql/updates/ sub directory. diff --git a/src/modules/SD2/ScriptDevMgr.cpp b/src/modules/SD2/ScriptDevMgr.cpp new file mode 100644 index 000000000..89701a0a0 --- /dev/null +++ b/src/modules/SD2/ScriptDevMgr.cpp @@ -0,0 +1,643 @@ +/** + * ScriptDev2 is an extension for mangos providing enhanced features for + * area triggers, creatures, game objects, instances, items, and spells beyond + * the default database scripting in mangos. + * + * Copyright (C) 2006-2013 ScriptDev2 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * World of Warcraft, and all World of Warcraft or Warcraft art, images, + * and lore are copyrighted by Blizzard Entertainment, Inc. + */ + +#include "precompiled.h" +#include "Config/Config.h" +#include "config-sd2.h" +#include "Database/DatabaseEnv.h" +#include "DBCStores.h" +#include "ObjectMgr.h" +#include "ProgressBar.h" +#include "system/ScriptLoader.h" +#include "system/system.h" +#include "ScriptDevMgr.h" +#include "Spell.h" + +typedef std::vector SDScriptVec; +int num_sc_scripts; +SDScriptVec m_scripts; + +Config SD2Config; + +void FillSpellSummary(); + +void LoadDatabase() +{ + std::string strSD2DBinfo = SD2Config.GetStringDefault("ScriptDev2DatabaseInfo", ""); + + if (strSD2DBinfo.empty()) + { + script_error_log("Missing Scriptdev2 database info from configuration file. Load database aborted."); + return; + } + + // Initialize connection to DB + if (SD2Database.Initialize(strSD2DBinfo.c_str())) + { + outstring_log("SD2: ScriptDev2 database initialized."); + outstring_log("\n"); + + pSystemMgr.LoadVersion(); + pSystemMgr.LoadScriptTexts(); + pSystemMgr.LoadScriptTextsCustom(); + pSystemMgr.LoadScriptGossipTexts(); + pSystemMgr.LoadScriptWaypoints(); + } + else + { + script_error_log("Unable to connect to Database. Load database aborted."); + return; + } + + SD2Database.HaltDelayThread(); +} + +struct TSpellSummary +{ + uint8 Targets; // set of enum SelectTarget + uint8 Effects; // set of enum SelectEffect +} extern* SpellSummary; + + + // Free Spell Summary + + // Free resources before library unload + + + + + // ScriptDev2 startup + + // Get configuration file + + // Set SD2 Error Log File + + + // Check config file version + + + // Load database (must be called after SD2Config.SetSource). + + + // Resize script ids to needed ammount of assigned ScriptNames (from core) + + + + // Check existance scripts for all registered by core script names + + +//********************************* +//*** Functions used globally *** + +/** + * Function that does script text + * + * @param iTextEntry Entry of the text, stored in SD2-database + * @param pSource Source of the text + * @param pTarget Can be NULL (depending on CHAT_TYPE of iTextEntry). Possible target for the text + */ +void DoScriptText(int32 iTextEntry, WorldObject* pSource, Unit* pTarget) +{ + if (!pSource) + { + script_error_log("DoScriptText entry %i, invalid Source pointer.", iTextEntry); + return; + } + + if (iTextEntry >= 0) + { + script_error_log("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; + } + + DoDisplayText(pSource, iTextEntry, pTarget); + // TODO - maybe add some call-stack like error output if above function returns false +} + +/** + * Function that either simulates or does script text for a map + * + * @param iTextEntry Entry of the text, stored in SD2-database, only type CHAT_TYPE_ZONE_YELL supported + * @param uiCreatureEntry Id of the creature of whom saying will be simulated + * @param pMap Given Map on which the map-wide text is displayed + * @param pCreatureSource Can be NULL. If pointer to Creature is given, then the creature does the map-wide text + * @param pTarget Can be NULL. Possible target for the text + */ +void DoOrSimulateScriptTextForMap(int32 iTextEntry, uint32 uiCreatureEntry, Map* pMap, Creature* pCreatureSource /*=NULL*/, Unit* pTarget /*=NULL*/) +{ + if (!pMap) + { + script_error_log("DoOrSimulateScriptTextForMap entry %i, invalid Map pointer.", iTextEntry); + return; + } + + if (iTextEntry >= 0) + { + script_error_log("DoOrSimulateScriptTextForMap with source entry %u for map %u attempts to process text entry %i, but text entry must be negative.", uiCreatureEntry, pMap->GetId(), iTextEntry); + return; + } + + CreatureInfo const* pInfo = GetCreatureTemplateStore(uiCreatureEntry); + if (!pInfo) + { + script_error_log("DoOrSimulateScriptTextForMap has invalid source entry %u for map %u.", uiCreatureEntry, pMap->GetId()); + return; + } + + MangosStringLocale const* pData = GetMangosStringData(iTextEntry); + if (!pData) + { + script_error_log("DoOrSimulateScriptTextForMap with source entry %u for map %u could not find text entry %i.", uiCreatureEntry, pMap->GetId(), iTextEntry); + return; + } + + debug_log("SD2: DoOrSimulateScriptTextForMap: text entry=%i, Sound=%u, Type=%u, Language=%u, Emote=%u", + iTextEntry, pData->SoundId, pData->Type, pData->Language, pData->Emote); + + if (pData->Type != CHAT_TYPE_ZONE_YELL) + { + script_error_log("DoSimulateScriptTextForMap entry %i has not supported chat type %u.", iTextEntry, pData->Type); + return; + } + + if (pData->SoundId) + { + pMap->PlayDirectSoundToMap(pData->SoundId); + } + + if (pCreatureSource) // If provided pointer for sayer, use direct version + { + pMap->MonsterYellToMap(pCreatureSource->GetObjectGuid(), iTextEntry, pData->Language, pTarget); + } + else // Simulate yell + { + pMap->MonsterYellToMap(pInfo, iTextEntry, pData->Language, pTarget); + } +} + +//********************************* +//*** Functions used internally *** + +void Script::RegisterSelf(bool bReportError) +{ + if (uint32 id = GetScriptId(Name.c_str())) + { + m_scripts[id] = this; + ++num_sc_scripts; + } + else + { + if (bReportError) + { + script_error_log("Script registering but ScriptName %s is not assigned in database. Script will not be used.", Name.c_str()); + } + + delete this; + } +} + +//******************************** +//*** Functions to be Exported *** + +void SD2::FreeScriptLibrary() +{ + // Free Spell Summary + delete []SpellSummary; + + // Free resources before library unload + for (SDScriptVec::const_iterator itr = m_scripts.begin(); itr != m_scripts.end(); ++itr) + { + delete *itr; + } + m_scripts.clear(); + num_sc_scripts = 0; + setScriptLibraryErrorFile(NULL, NULL); +} + +void SD2::InitScriptLibrary() +{ + // ScriptDev2 startup + outstring_log(" ___ _ _ ___ ___ "); + outstring_log(" / __| __ _ _(_)_ __| |_| \\ _____ _|_ )"); + outstring_log(" \\__ \\/ _| '_| | '_ \\ _| |) / -_) V // / "); + outstring_log(" |___/\\__|_| |_| .__/\\__|___/\\___|\\_//___|"); + outstring_log(" |_| "); + outstring_log(" http://scriptdev2.com/\n"); + + // Get configuration file + bool configFailure = false; + if (!SD2Config.SetSource(MANGOSD_CONFIG_LOCATION)) + { + configFailure = true; + } + else + { + outstring_log("SD2: Using configuration file %s", MANGOSD_CONFIG_LOCATION); + } + + // Set SD2 Error Log File + std::string sd2LogFile = SD2Config.GetStringDefault("SD2ErrorLogFile", "scriptdev2-errors.log"); + setScriptLibraryErrorFile(sd2LogFile.c_str(), "SD2"); + if (configFailure) + { + script_error_log("Unable to open configuration file. Database will be unaccessible. Configuration values will use default."); + } + + // Check config file version + if (SD2Config.GetIntDefault("ConfVersion", 0) != MANGOSD_CONFIG_VERSION) + { + script_error_log("Configuration file version doesn't match expected version. Some config variables may be wrong or missing."); + } + outstring_log("\n"); + + // Load database (must be called after SD2Config.SetSource). + LoadDatabase(); + outstring_log("SD2: Loading C++ scripts"); + BarGoLink bar(1); + bar.step(); + + // Resize script ids to needed ammount of assigned ScriptNames (from core) + m_scripts.resize(GetScriptIdsCount(), NULL); + FillSpellSummary(); + AddScripts(); + + // Check existance scripts for all registered by core script names + for (uint32 i = 1; i < GetScriptIdsCount(); ++i) + { + if (!m_scripts[i]) + { + script_error_log("No script found for ScriptName '%s'.", GetScriptName(i)); + } + } + outstring_log(">> Loaded %i C++ Scripts.", num_sc_scripts); +} + +char const* SD2::GetScriptLibraryVersion() +{ + return strSD2Version.c_str(); +} + +bool SD2::GossipHello(Player* pPlayer, Creature* pCreature) +{ + Script* pTempScript = m_scripts[pCreature->GetScriptId()]; + + if (!pTempScript || !pTempScript->pGossipHello) + { + return false; + } + + pPlayer->PlayerTalkClass->ClearMenus(); + + return pTempScript->pGossipHello(pPlayer, pCreature); +} + +bool SD2::GOGossipHello(Player* pPlayer, GameObject* pGo) +{ + Script* pTempScript = m_scripts[pGo->GetGOInfo()->ScriptId]; + + if (!pTempScript || !pTempScript->pGossipHelloGO) + { + return false; + } + + pPlayer->PlayerTalkClass->ClearMenus(); + + return pTempScript->pGossipHelloGO(pPlayer, pGo); +} + +bool SD2::GossipSelect(Player* pPlayer, Creature* pCreature, uint32 uiSender, uint32 uiAction) +{ + debug_log("SD2: Gossip selection, sender: %u, action: %u", uiSender, uiAction); + + Script* pTempScript = m_scripts[pCreature->GetScriptId()]; + + if (!pTempScript || !pTempScript->pGossipSelect) + { + return false; + } + + pPlayer->PlayerTalkClass->ClearMenus(); + + return pTempScript->pGossipSelect(pPlayer, pCreature, uiSender, uiAction); +} + +bool SD2::GOGossipSelect(Player* pPlayer, GameObject* pGo, uint32 uiSender, uint32 uiAction) +{ + debug_log("SD2: GO Gossip selection, sender: %u, action: %u", uiSender, uiAction); + + Script* pTempScript = m_scripts[pGo->GetGOInfo()->ScriptId]; + + if (!pTempScript || !pTempScript->pGossipSelectGO) + { + return false; + } + + pPlayer->PlayerTalkClass->ClearMenus(); + + return pTempScript->pGossipSelectGO(pPlayer, pGo, uiSender, uiAction); +} + +bool SD2::GossipSelectWithCode(Player* pPlayer, Creature* pCreature, uint32 uiSender, uint32 uiAction, const char* sCode) +{ + debug_log("SD2: Gossip selection with code, sender: %u, action: %u", uiSender, uiAction); + + Script* pTempScript = m_scripts[pCreature->GetScriptId()]; + + if (!pTempScript || !pTempScript->pGossipSelectWithCode) + { + return false; + } + + pPlayer->PlayerTalkClass->ClearMenus(); + + return pTempScript->pGossipSelectWithCode(pPlayer, pCreature, uiSender, uiAction, sCode); +} + +bool SD2::GOGossipSelectWithCode(Player* pPlayer, GameObject* pGo, uint32 uiSender, uint32 uiAction, const char* sCode) +{ + debug_log("SD2: GO Gossip selection with code, sender: %u, action: %u", uiSender, uiAction); + + Script* pTempScript = m_scripts[pGo->GetGOInfo()->ScriptId]; + + if (!pTempScript || !pTempScript->pGossipSelectGOWithCode) + { + return false; + } + + pPlayer->PlayerTalkClass->ClearMenus(); + + return pTempScript->pGossipSelectGOWithCode(pPlayer, pGo, uiSender, uiAction, sCode); +} + +bool SD2::QuestAccept(Player* pPlayer, Creature* pCreature, const Quest* pQuest) +{ + Script* pTempScript = m_scripts[pCreature->GetScriptId()]; + + if (!pTempScript || !pTempScript->pQuestAcceptNPC) + { + return false; + } + + pPlayer->PlayerTalkClass->ClearMenus(); + + return pTempScript->pQuestAcceptNPC(pPlayer, pCreature, pQuest); +} + +bool SD2::QuestRewarded(Player* pPlayer, Creature* pCreature, Quest const* pQuest) +{ + Script* pTempScript = m_scripts[pCreature->GetScriptId()]; + + if (!pTempScript || !pTempScript->pQuestRewardedNPC) + { + return false; + } + + pPlayer->PlayerTalkClass->ClearMenus(); + + return pTempScript->pQuestRewardedNPC(pPlayer, pCreature, pQuest); +} + +uint32 SD2::GetNPCDialogStatus(Player* pPlayer, Creature* pCreature) +{ + Script* pTempScript = m_scripts[pCreature->GetScriptId()]; + + if (!pTempScript || !pTempScript->pDialogStatusNPC) + { + return DIALOG_STATUS_UNDEFINED; + } + + pPlayer->PlayerTalkClass->ClearMenus(); + + return pTempScript->pDialogStatusNPC(pPlayer, pCreature); +} + +uint32 SD2::GetGODialogStatus(Player* pPlayer, GameObject* pGo) +{ + Script* pTempScript = m_scripts[pGo->GetGOInfo()->ScriptId]; + + if (!pTempScript || !pTempScript->pDialogStatusGO) + { + return DIALOG_STATUS_UNDEFINED; + } + + pPlayer->PlayerTalkClass->ClearMenus(); + + return pTempScript->pDialogStatusGO(pPlayer, pGo); +} + +bool SD2::ItemQuestAccept(Player* pPlayer, Item* pItem, Quest const* pQuest) +{ + Script* pTempScript = m_scripts[pItem->GetProto()->ScriptId]; + + if (!pTempScript || !pTempScript->pQuestAcceptItem) + { + return false; + } + + pPlayer->PlayerTalkClass->ClearMenus(); + + return pTempScript->pQuestAcceptItem(pPlayer, pItem, pQuest); +} + +bool SD2::GOUse(Player* pPlayer, GameObject* pGo) +{ + Script* pTempScript = m_scripts[pGo->GetGOInfo()->ScriptId]; + + if (!pTempScript || !pTempScript->pGOUse) + { + return false; + } + + return pTempScript->pGOUse(pPlayer, pGo); +} + +bool SD2::GOQuestAccept(Player* pPlayer, GameObject* pGo, const Quest* pQuest) +{ + Script* pTempScript = m_scripts[pGo->GetGOInfo()->ScriptId]; + + if (!pTempScript || !pTempScript->pQuestAcceptGO) + { + return false; + } + + pPlayer->PlayerTalkClass->ClearMenus(); + + return pTempScript->pQuestAcceptGO(pPlayer, pGo, pQuest); +} + +bool SD2::GOQuestRewarded(Player* pPlayer, GameObject* pGo, Quest const* pQuest) +{ + Script* pTempScript = m_scripts[pGo->GetGOInfo()->ScriptId]; + + if (!pTempScript || !pTempScript->pQuestRewardedGO) + { + return false; + } + + pPlayer->PlayerTalkClass->ClearMenus(); + + return pTempScript->pQuestRewardedGO(pPlayer, pGo, pQuest); +} + +bool SD2::AreaTrigger(Player* pPlayer, AreaTriggerEntry const* atEntry) +{ + Script* pTempScript = m_scripts[GetAreaTriggerScriptId(atEntry->id)]; + + if (!pTempScript || !pTempScript->pAreaTrigger) + { + return false; + } + + return pTempScript->pAreaTrigger(pPlayer, atEntry); +} + +bool SD2::NpcSpellClick(Player* pPlayer, Creature* pClickedCreature, uint32 uiSpellId) +{ + Script* pTempScript = m_scripts[pClickedCreature->GetScriptId()]; + + if (!pTempScript || !pTempScript->pNpcSpellClick) + return false; + + return pTempScript->pNpcSpellClick(pPlayer, pClickedCreature, uiSpellId); +} + +bool SD2::ProcessEvent(uint32 uiEventId, Object* pSource, Object* pTarget, bool bIsStart) +{ + Script* pTempScript = m_scripts[GetEventIdScriptId(uiEventId)]; + + if (!pTempScript || !pTempScript->pProcessEventId) + { + return false; + } + + // bIsStart may be false, when event is from taxi node events (arrival=false, departure=true) + return pTempScript->pProcessEventId(uiEventId, pSource, pTarget, bIsStart); +} + +CreatureAI* SD2::GetCreatureAI(Creature* pCreature) +{ + Script* pTempScript = m_scripts[pCreature->GetScriptId()]; + + if (!pTempScript || !pTempScript->GetAI) + { + return NULL; + } + + return pTempScript->GetAI(pCreature); +} + +bool SD2::ItemUse(Player* pPlayer, Item* pItem, SpellCastTargets const& targets) +{ + Script* pTempScript = m_scripts[pItem->GetProto()->ScriptId]; + + if (!pTempScript || !pTempScript->pItemUse) + { + return false; + } + + return pTempScript->pItemUse(pPlayer, pItem, targets); +} + +bool SD2::EffectDummyCreature(Unit* pCaster, uint32 spellId, SpellEffectIndex effIndex, Creature* pTarget, ObjectGuid originalCasterGuid) +{ + Script* pTempScript = m_scripts[pTarget->GetScriptId()]; + + if (!pTempScript || !pTempScript->pEffectDummyNPC) + { + return false; + } + + return pTempScript->pEffectDummyNPC(pCaster, spellId, effIndex, pTarget, originalCasterGuid); +} + +bool SD2::EffectDummyGameObject(Unit* pCaster, uint32 spellId, SpellEffectIndex effIndex, GameObject* pTarget, ObjectGuid originalCasterGuid) +{ + Script* pTempScript = m_scripts[pTarget->GetGOInfo()->ScriptId]; + + if (!pTempScript || !pTempScript->pEffectDummyGO) + { + return false; + } + + return pTempScript->pEffectDummyGO(pCaster, spellId, effIndex, pTarget, originalCasterGuid); +} + +bool SD2::EffectDummyItem(Unit* pCaster, uint32 spellId, SpellEffectIndex effIndex, Item* pTarget, ObjectGuid originalCasterGuid) +{ + Script* pTempScript = m_scripts[pTarget->GetProto()->ScriptId]; + + if (!pTempScript || !pTempScript->pEffectDummyItem) + { + return false; + } + + return pTempScript->pEffectDummyItem(pCaster, spellId, effIndex, pTarget, originalCasterGuid); +} + +bool SD2::EffectScriptEffectCreature(Unit* pCaster, uint32 spellId, SpellEffectIndex effIndex, Creature* pTarget, ObjectGuid originalCasterGuid) +{ + Script* pTempScript = m_scripts[pTarget->GetScriptId()]; + + if (!pTempScript || !pTempScript->pEffectScriptEffectNPC) + { + return false; + } + + return pTempScript->pEffectScriptEffectNPC(pCaster, spellId, effIndex, pTarget, originalCasterGuid); +} + +bool SD2::AuraDummy(Aura const* pAura, bool bApply) +{ + Script* pTempScript = m_scripts[((Creature*)pAura->GetTarget())->GetScriptId()]; + + if (!pTempScript || !pTempScript->pEffectAuraDummy) + { + return false; + } + + return pTempScript->pEffectAuraDummy(pAura, bApply); +} + +InstanceData* SD2::CreateInstanceData(Map* pMap) +{ + Script* pTempScript = m_scripts[pMap->GetScriptId()]; + + if (!pTempScript || !pTempScript->GetInstanceData) + { + return NULL; + } + + return pTempScript->GetInstanceData(pMap); +} + +#ifdef WIN32 +# include +BOOL APIENTRY DllMain(HANDLE hModule, DWORD ul_reason_for_call, LPVOID lpReserved) +{ + return true; +} +#endif diff --git a/src/modules/SD2/ScriptDevMgr.h b/src/modules/SD2/ScriptDevMgr.h new file mode 100644 index 000000000..bb2d4e424 --- /dev/null +++ b/src/modules/SD2/ScriptDevMgr.h @@ -0,0 +1,182 @@ +/** + * ScriptDev2 is an extension for mangos providing enhanced features for + * area triggers, creatures, game objects, instances, items, and spells beyond + * the default database scripting in mangos. + * + * Copyright (C) 2006-2013 ScriptDev2 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * World of Warcraft, and all World of Warcraft or Warcraft art, images, + * and lore are copyrighted by Blizzard Entertainment, Inc. + */ + +#ifndef SC_SCRIPTMGR_H +#define SC_SCRIPTMGR_H + +#include "Common.h" +#include "DBCStructure.h" + +class Player; +class Creature; +class CreatureAI; +class InstanceData; +class Quest; +class Item; +class GameObject; +class SpellCastTargets; +class Map; +class Unit; +class WorldObject; +class Aura; +class Object; +class ObjectGuid; + +// ********************************************************* +// **************** Functions used by core ***************** + +class SD2 +{ +public: + static void FreeScriptLibrary(); + static void InitScriptLibrary(); + static char const* GetScriptLibraryVersion(); + + static CreatureAI* GetCreatureAI(Creature* pCreature); + static InstanceData* CreateInstanceData(Map* pMap); + + static bool GossipHello(Player*, Creature*); + static bool GOGossipHello(Player*, GameObject*); + static bool GossipSelect(Player*, Creature*, uint32, uint32); + static bool GOGossipSelect(Player*, GameObject*, uint32, uint32); + static bool GossipSelectWithCode(Player*, Creature*, uint32, uint32, const char*); + static bool GOGossipSelectWithCode(Player*, GameObject*, uint32, uint32, const char*); + static bool QuestAccept(Player*, Creature*, Quest const*); + static bool GOQuestAccept(Player*, GameObject*, Quest const*); + static bool ItemQuestAccept(Player*, Item*, Quest const*); + static bool QuestRewarded(Player*, Creature*, Quest const*); + static bool GOQuestRewarded(Player*, GameObject*, Quest const*); + static uint32 GetNPCDialogStatus(Player*, Creature*); + static uint32 GetGODialogStatus(Player*, GameObject*); + static bool GOUse(Player*, GameObject*); + static bool ItemUse(Player*, Item*, SpellCastTargets const&); + static bool AreaTrigger(Player*, AreaTriggerEntry const*); + static bool NpcSpellClick(Player* pPlayer, Creature* pClickedCreature, uint32 uiSpellId); + static bool ProcessEvent(uint32, Object*, Object*, bool); + static bool EffectDummyCreature(Unit*, uint32, SpellEffectIndex, Creature*, ObjectGuid); + static bool EffectDummyGameObject(Unit*, uint32, SpellEffectIndex, GameObject*, ObjectGuid); + static bool EffectDummyItem(Unit*, uint32, SpellEffectIndex, Item*, ObjectGuid); + static bool EffectScriptEffectCreature(Unit*, uint32, SpellEffectIndex, Creature*, ObjectGuid); + static bool AuraDummy(Aura const*, bool); +}; + +// ********************************************************* +// ************** Some defines used globally *************** + +// Basic defines +#define VISIBLE_RANGE (166.0f) // MAX visible range (size of grid) +#define DEFAULT_TEXT "" + +/* Escort Factions + * TODO: find better namings and definitions. + * N=Neutral, A=Alliance, H=Horde. + * NEUTRAL or FRIEND = Hostility to player surroundings (not a good definition) + * ACTIVE or PASSIVE = Hostility to environment surroundings. + */ +enum EscortFaction +{ + 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 +}; + +// ********************************************************* +// ************* Some structures used globally ************* + +struct Script +{ + Script() : + pGossipHello(NULL), pGossipHelloGO(NULL), pGossipSelect(NULL), pGossipSelectGO(NULL), + pGossipSelectWithCode(NULL), pGossipSelectGOWithCode(NULL), + pDialogStatusNPC(NULL), pDialogStatusGO(NULL), + pQuestAcceptNPC(NULL), pQuestAcceptGO(NULL), pQuestAcceptItem(NULL), + pQuestRewardedNPC(NULL), pQuestRewardedGO(NULL), + pGOUse(NULL), pItemUse(NULL), pAreaTrigger(NULL), pNpcSpellClick(NULL), pProcessEventId(NULL), + pEffectDummyNPC(NULL), pEffectDummyGO(NULL), pEffectDummyItem(NULL), pEffectScriptEffectNPC(NULL), + pEffectAuraDummy(NULL), GetAI(NULL), GetInstanceData(NULL) + {} + + std::string Name; + + bool (*pGossipHello)(Player*, Creature*); + bool (*pGossipHelloGO)(Player*, GameObject*); + bool (*pGossipSelect)(Player*, Creature*, uint32, uint32); + bool (*pGossipSelectGO)(Player*, GameObject*, uint32, uint32); + bool (*pGossipSelectWithCode)(Player*, Creature*, uint32, uint32, const char*); + bool (*pGossipSelectGOWithCode)(Player*, GameObject*, uint32, uint32, const char*); + uint32(*pDialogStatusNPC)(Player*, Creature*); + uint32(*pDialogStatusGO)(Player*, GameObject*); + bool (*pQuestAcceptNPC)(Player*, Creature*, Quest const*); + bool (*pQuestAcceptGO)(Player*, GameObject*, Quest const*); + bool (*pQuestAcceptItem)(Player*, Item*, Quest const*); + bool (*pQuestRewardedNPC)(Player*, Creature*, Quest const*); + bool (*pQuestRewardedGO)(Player*, GameObject*, Quest const*); + bool (*pGOUse)(Player*, GameObject*); + bool (*pItemUse)(Player*, Item*, SpellCastTargets const&); + bool (*pAreaTrigger)(Player*, AreaTriggerEntry const*); + bool (*pNpcSpellClick)(Player*, Creature*, uint32); + bool (*pProcessEventId)(uint32, Object*, Object*, bool); + bool (*pEffectDummyNPC)(Unit*, uint32, SpellEffectIndex, Creature*, ObjectGuid); + bool (*pEffectDummyGO)(Unit*, uint32, SpellEffectIndex, GameObject*, ObjectGuid); + bool (*pEffectDummyItem)(Unit*, uint32, SpellEffectIndex, Item*, ObjectGuid); + bool (*pEffectScriptEffectNPC)(Unit*, uint32, SpellEffectIndex, Creature*, ObjectGuid); + bool (*pEffectAuraDummy)(const Aura*, bool); + + CreatureAI* (*GetAI)(Creature*); + InstanceData* (*GetInstanceData)(Map*); + + void RegisterSelf(bool bReportError = true); +}; + +// ********************************************************* +// ************* Some functions used globally ************** + +// Generic scripting text function +void DoScriptText(int32 iTextEntry, WorldObject* pSource, Unit* pTarget = NULL); +void DoOrSimulateScriptTextForMap(int32 iTextEntry, uint32 uiCreatureEntry, Map* pMap, Creature* pCreatureSource = NULL, Unit* pTarget = NULL); + +// ********************************************************* +// **************** Internal hook mechanics **************** + +#if COMPILER == COMPILER_GNU || COMPILER == COMPILER_CLANG +#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 + +#endif diff --git a/src/modules/SD2/base/escort_ai.cpp b/src/modules/SD2/base/escort_ai.cpp new file mode 100644 index 000000000..a6717792e --- /dev/null +++ b/src/modules/SD2/base/escort_ai.cpp @@ -0,0 +1,661 @@ +/** + * ScriptDev2 is an extension for mangos providing enhanced features for + * area triggers, creatures, game objects, instances, items, and spells beyond + * the default database scripting in mangos. + * + * Copyright (C) 2006-2013 ScriptDev2 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * World of Warcraft, and all World of Warcraft or Warcraft art, images, + * and lore are copyrighted by Blizzard Entertainment, Inc. + */ + +/* ScriptData +SDName: EscortAI +SD%Complete: 100 +SDComment: +SDCategory: Npc +EndScriptData */ + +#include "precompiled.h" +#include "escort_ai.h" +#include "../system/system.h" + +const float MAX_PLAYER_DISTANCE = 66.0f; + +enum +{ + POINT_LAST_POINT = 0xFFFFFF, + POINT_HOME = 0xFFFFFE +}; + +npc_escortAI::npc_escortAI(Creature* pCreature) : ScriptedAI(pCreature), + m_uiWPWaitTimer(2500), + m_uiPlayerCheckTimer(1000), + m_uiEscortState(STATE_ESCORT_NONE), + m_pQuestForEscort(NULL), + m_bIsRunning(false), + m_bCanInstantRespawn(false), + m_bCanReturnToStart(false) +{} + +void npc_escortAI::GetAIInformation(ChatHandler& reader) +{ + std::ostringstream oss; + + oss << "EscortAI "; + if (m_playerGuid) + { + oss << "started for " << m_playerGuid.GetString() << " "; + } + if (m_pQuestForEscort) + { + oss << "started with quest " << m_pQuestForEscort->GetQuestId(); + } + + if (HasEscortState(STATE_ESCORT_ESCORTING)) + { + oss << "\nEscortFlags: Escorting" << (HasEscortState(STATE_ESCORT_RETURNING) ? ", Returning" : "") << (HasEscortState(STATE_ESCORT_PAUSED) ? ", Paused" : ""); + + if (CurrentWP != WaypointList.end()) + { + oss << "\nNext Waypoint Id = " << CurrentWP->uiId << " Position: " << CurrentWP->fX << " " << CurrentWP->fY << " " << CurrentWP->fZ; + } + } + + reader.PSendSysMessage(oss.str().c_str()); +} + +bool npc_escortAI::IsVisible(Unit* pWho) const +{ + if (!pWho) + { + return false; + } + + return m_creature->IsWithinDist(pWho, VISIBLE_RANGE) && pWho->IsVisibleForOrDetect(m_creature, m_creature, true); +} + +void npc_escortAI::AttackStart(Unit* pWho) +{ + if (!pWho) + { + return; + } + + if (m_creature->Attack(pWho, true)) + { + m_creature->AddThreat(pWho); + m_creature->SetInCombatWith(pWho); + pWho->SetInCombatWith(m_creature); + + if (m_creature->GetMotionMaster()->GetCurrentMovementGeneratorType() == POINT_MOTION_TYPE) + { + m_creature->GetMotionMaster()->MovementExpired(); + } + + if (IsCombatMovement()) + { + m_creature->GetMotionMaster()->MoveChase(pWho); + } + } +} + +void npc_escortAI::EnterCombat(Unit* pEnemy) +{ + // Store combat start position + m_creature->SetCombatStartPosition(m_creature->GetPositionX(), m_creature->GetPositionY(), m_creature->GetPositionZ()); + + if (!pEnemy) + { + return; + } + + Aggro(pEnemy); +} + +void npc_escortAI::Aggro(Unit* /*pEnemy*/) {} + +// see followerAI +bool npc_escortAI::AssistPlayerInCombat(Unit* pWho) +{ + if (!pWho->getVictim()) + { + return false; + } + + // experimental (unknown) flag not present + if (!(m_creature->GetCreatureInfo()->CreatureTypeFlags & CREATURE_TYPEFLAGS_CAN_ASSIST)) + { + return false; + } + + // unit state prevents (similar check is done in CanInitiateAttack which also include checking unit_flags. We skip those here) + if (m_creature->hasUnitState(UNIT_STAT_STUNNED | UNIT_STAT_DIED)) + { + return false; + } + + // victim of pWho is 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)) + { + // already fighting someone? + if (!m_creature->getVictim()) + { + AttackStart(pWho); + return true; + } + else + { + pWho->SetInCombatWith(m_creature); + m_creature->AddThreat(pWho); + return true; + } + } + + return false; +} + +void npc_escortAI::MoveInLineOfSight(Unit* pWho) +{ + if (pWho->IsTargetableForAttack() && pWho->isInAccessablePlaceFor(m_creature)) + { + // AssistPlayerInCombat can start attack, so return if true + if (HasEscortState(STATE_ESCORT_ESCORTING) && AssistPlayerInCombat(pWho)) + { + return; + } + + if (!m_creature->CanInitiateAttack()) + { + 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); + if (m_creature->IsWithinDistInMap(pWho, fAttackRadius) && m_creature->IsWithinLOSInMap(pWho)) + { + if (!m_creature->getVictim()) + { + pWho->RemoveSpellsCausingAura(SPELL_AURA_MOD_STEALTH); + AttackStart(pWho); + } + else if (m_creature->GetMap()->IsDungeon()) + { + pWho->SetInCombatWith(m_creature); + m_creature->AddThreat(pWho); + } + } + } + } +} + +void npc_escortAI::JustDied(Unit* /*pKiller*/) +{ + if (!HasEscortState(STATE_ESCORT_ESCORTING) || !m_playerGuid || !m_pQuestForEscort) + { + return; + } + + if (Player* pPlayer = GetPlayerForEscort()) + { + if (Group* pGroup = pPlayer->GetGroup()) + { + for (GroupReference* pRef = pGroup->GetFirstMember(); pRef != NULL; pRef = pRef->next()) + { + if (Player* pMember = pRef->getSource()) + { + if (pMember->GetQuestStatus(m_pQuestForEscort->GetQuestId()) == QUEST_STATUS_INCOMPLETE) + { + pMember->FailQuest(m_pQuestForEscort->GetQuestId()); + } + } + } + } + else + { + if (pPlayer->GetQuestStatus(m_pQuestForEscort->GetQuestId()) == QUEST_STATUS_INCOMPLETE) + { + pPlayer->FailQuest(m_pQuestForEscort->GetQuestId()); + } + } + } +} + +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; + + Reset(); +} + +void npc_escortAI::EnterEvadeMode() +{ + m_creature->RemoveAllAurasOnEvade(); + m_creature->DeleteThreatList(); + m_creature->CombatStop(true); + m_creature->SetLootRecipient(NULL); + + if (HasEscortState(STATE_ESCORT_ESCORTING)) + { + // We have left our path + if (m_creature->GetMotionMaster()->GetCurrentMovementGeneratorType() != POINT_MOTION_TYPE) + { + debug_log("SD2: EscortAI has left combat and is now returning to CombatStartPosition."); + + AddEscortState(STATE_ESCORT_RETURNING); + + float fPosX, fPosY, fPosZ; + m_creature->GetCombatStartPosition(fPosX, fPosY, fPosZ); + m_creature->GetMotionMaster()->MovePoint(POINT_LAST_POINT, fPosX, fPosY, fPosZ); + } + } + 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()) + { + Player* pMember = pRef->getSource(); + if (pMember && m_creature->IsWithinDistInMap(pMember, MAX_PLAYER_DISTANCE)) + { + return true; + } + } + } + else + { + if (m_creature->IsWithinDistInMap(pPlayer, MAX_PLAYER_DISTANCE)) + { + return true; + } + } + } + return false; +} + +// Returns false, if npc is despawned +bool npc_escortAI::MoveToNextWaypoint() +{ + // Do nothing if escorting is paused + if (HasEscortState(STATE_ESCORT_PAUSED)) + { + return true; + } + + // Final Waypoint reached (and final wait time waited) + if (CurrentWP == WaypointList.end()) + { + debug_log("SD2: 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("SD2: EscortAI are returning home to spawn location: %u, %f, %f, %f", POINT_HOME, fRetX, fRetY, fRetZ); + return true; + } + + if (m_bCanInstantRespawn) + { + m_creature->SetDeathState(JUST_DIED); + m_creature->Respawn(); + } + else + { + m_creature->ForcedDespawn(); + } + + return false; + } + + m_creature->GetMotionMaster()->MovePoint(CurrentWP->uiId, CurrentWP->fX, CurrentWP->fY, CurrentWP->fZ); + debug_log("SD2: EscortAI start waypoint %u (%f, %f, %f).", CurrentWP->uiId, CurrentWP->fX, CurrentWP->fY, CurrentWP->fZ); + + WaypointStart(CurrentWP->uiId); + + m_uiWPWaitTimer = 0; + + return true; +} + +void npc_escortAI::UpdateAI(const uint32 uiDiff) +{ + // Waypoint Updating + if (HasEscortState(STATE_ESCORT_ESCORTING) && !m_creature->getVictim() && m_uiWPWaitTimer && !HasEscortState(STATE_ESCORT_RETURNING)) + { + if (m_uiWPWaitTimer <= uiDiff) + { + if (!MoveToNextWaypoint()) + { + return; + } + } + else + { + m_uiWPWaitTimer -= uiDiff; + } + } + + // Check if player or any member of his group is within range + if (HasEscortState(STATE_ESCORT_ESCORTING) && m_playerGuid && !m_creature->getVictim() && !HasEscortState(STATE_ESCORT_RETURNING)) + { + if (m_uiPlayerCheckTimer < uiDiff) + { + if (!HasEscortState(STATE_ESCORT_PAUSED) && !IsPlayerOrGroupInRange()) + { + debug_log("SD2: EscortAI failed because player/group was to far away or not found"); + + if (m_bCanInstantRespawn) + { + m_creature->SetDeathState(JUST_DIED); + m_creature->Respawn(); + } + else + { + m_creature->ForcedDespawn(); + } + + return; + } + + m_uiPlayerCheckTimer = 1000; + } + else + { + m_uiPlayerCheckTimer -= uiDiff; + } + } + + UpdateEscortAI(uiDiff); +} + +void npc_escortAI::UpdateEscortAI(const uint32 /*uiDiff*/) +{ + // Check if we have a current target + if (!m_creature->SelectHostileTarget() || !m_creature->getVictim()) + { + return; + } + + 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("SD2: EscortAI has returned to original position before combat"); + + m_creature->SetWalk(!m_bIsRunning); + RemoveEscortState(STATE_ESCORT_RETURNING); + } + else if (uiPointId == POINT_HOME) + { + debug_log("SD2: EscortAI has returned to original home location and will continue from beginning of waypoint list."); + + CurrentWP = WaypointList.begin(); + m_uiWPWaitTimer = 0; + } + else + { + // Make sure that we are still on the right waypoint + if (CurrentWP->uiId != uiPointId) + { + script_error_log("EscortAI for Npc %u reached waypoint out of order %u, expected %u.", m_creature->GetEntry(), uiPointId, CurrentWP->uiId); + return; + } + + debug_log("SD2: EscortAI waypoint %u reached.", CurrentWP->uiId); + + // In case we were moving while in combat, we should evade back to this position + m_creature->SetCombatStartPosition(m_creature->GetPositionX(), m_creature->GetPositionY(), m_creature->GetPositionZ()); + + // Call WP function + WaypointReached(CurrentWP->uiId); + + m_uiWPWaitTimer = CurrentWP->uiWaitTime; + + ++CurrentWP; + } + + if (!m_uiWPWaitTimer) + { + // Continue WP Movement if Can + if (HasEscortState(STATE_ESCORT_ESCORTING) && !HasEscortState(STATE_ESCORT_PAUSED | STATE_ESCORT_RETURNING) && !m_creature->getVictim()) + { + MoveToNextWaypoint(); + } + else + { + m_uiWPWaitTimer = 1; + } + } +} + +/*void npc_escortAI::AddWaypoint(uint32 id, float x, float y, float z, uint32 WaitTimeMs) +{ + Escort_Waypoint t(id, x, y, z, WaitTimeMs); + + WaypointList.push_back(t); +}*/ + +void npc_escortAI::FillPointMovementListForCreature() +{ + std::vector const& pPointsEntries = pSystemMgr.GetPointMoveList(m_creature->GetEntry()); + + if (pPointsEntries.empty()) + { + return; + } + + std::vector::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::SetCurrentWaypoint(uint32 uiPointId) +{ + if (!(HasEscortState(STATE_ESCORT_PAUSED))) // Only when paused + { + return; + } + + if (uiPointId == CurrentWP->uiId) // Already here + { + return; + } + + bool bFoundWaypoint = false; + for (std::list::iterator itr = WaypointList.begin(); itr != WaypointList.end(); ++itr) + { + if (itr->uiId == uiPointId) + { + CurrentWP = itr; // Set to found itr + bFoundWaypoint = true; + break; + } + } + + if (!bFoundWaypoint) + { + debug_log("SD2: EscortAI current waypoint tried to set to id %u, but doesn't exist in WaypointList", uiPointId); + return; + } + + m_uiWPWaitTimer = 1; + + debug_log("SD2: EscortAI current waypoint set to id %u", CurrentWP->uiId); +} + +void npc_escortAI::SetRun(bool bRun) +{ + if (bRun) + { + if (!m_bIsRunning) + { + m_creature->SetWalk(false); + } + else + { + debug_log("SD2: EscortAI attempt to set run mode, but is already running."); + } + } + else + { + if (m_bIsRunning) + { + m_creature->SetWalk(true); + } + else + { + debug_log("SD2: EscortAI attempt to set walk mode, but is already walking."); + } + } + m_bIsRunning = bRun; +} + +// TODO: get rid of this many variables passed in function. +void npc_escortAI::Start(bool bRun, const Player* pPlayer, const Quest* pQuest, bool bInstantRespawn, bool bCanLoopPath) +{ + if (m_creature->getVictim()) + { + script_error_log("EscortAI attempt to Start while in combat."); + return; + } + + if (HasEscortState(STATE_ESCORT_ESCORTING)) + { + script_error_log("EscortAI attempt to Start while already escorting."); + return; + } + + if (!WaypointList.empty()) + { + WaypointList.clear(); + } + + FillPointMovementListForCreature(); + + if (WaypointList.empty()) + { + error_db_log("SD2: EscortAI Start with 0 waypoints (possible missing entry in script_waypoint)."); + return; + } + + // set variables + m_bIsRunning = bRun; + + m_playerGuid = pPlayer ? pPlayer->GetObjectGuid() : ObjectGuid(); + m_pQuestForEscort = pQuest; + + m_bCanInstantRespawn = bInstantRespawn; + m_bCanReturnToStart = bCanLoopPath; + + if (m_bCanReturnToStart && m_bCanInstantRespawn) + { + debug_log("SD2: 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("SD2: EscortAI start with WAYPOINT_MOTION_TYPE, changed to MoveIdle."); + } + + // disable npcflags + m_creature->SetUInt32Value(UNIT_NPC_FLAGS, UNIT_NPC_FLAG_NONE); + + debug_log("SD2: EscortAI started with " SIZEFMTD " waypoints. Run = %d, PlayerGuid = %s", WaypointList.size(), m_bIsRunning, m_playerGuid.GetString().c_str()); + + CurrentWP = WaypointList.begin(); + + // Set initial speed + m_creature->SetWalk(!m_bIsRunning); + + AddEscortState(STATE_ESCORT_ESCORTING); + + JustStartedEscort(); +} + +void npc_escortAI::SetEscortPaused(bool bPaused) +{ + if (!HasEscortState(STATE_ESCORT_ESCORTING)) + { + return; + } + + if (bPaused) + { + AddEscortState(STATE_ESCORT_PAUSED); + } + else + { + RemoveEscortState(STATE_ESCORT_PAUSED); + } +} diff --git a/src/modules/SD2/base/escort_ai.h b/src/modules/SD2/base/escort_ai.h new file mode 100644 index 000000000..c07415749 --- /dev/null +++ b/src/modules/SD2/base/escort_ai.h @@ -0,0 +1,136 @@ +/** + * ScriptDev2 is an extension for mangos providing enhanced features for + * area triggers, creatures, game objects, instances, items, and spells beyond + * the default database scripting in mangos. + * + * Copyright (C) 2006-2013 ScriptDev2 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * World of Warcraft, and all World of Warcraft or Warcraft art, images, + * and lore are copyrighted by Blizzard Entertainment, Inc. + */ + +#ifndef SC_ESCORTAI_H +#define SC_ESCORTAI_H + +// Remove this include, when EscortAI stores uint32 quest-id instead of Quest* +#include "ObjectMgr.h" + +struct Escort_Waypoint +{ + Escort_Waypoint(uint32 uiId, float fX, float fY, float fZ, uint32 uiWaitTime) : + uiId(uiId), + fX(fX), + fY(fY), + fZ(fZ), + uiWaitTime(uiWaitTime) + {} + + uint32 uiId; + float fX; + float fY; + float fZ; + uint32 uiWaitTime; +}; + +enum EscortState +{ + STATE_ESCORT_NONE = 0x000, // nothing in progress + STATE_ESCORT_ESCORTING = 0x001, // escort are in progress + 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 npc_escortAI : public ScriptedAI +{ + public: + explicit npc_escortAI(Creature* pCreature); + ~npc_escortAI() {} + + void GetAIInformation(ChatHandler& reader) override; + + virtual void Aggro(Unit*) override; + + virtual void Reset() override = 0; + + // CreatureAI functions + bool IsVisible(Unit*) const override; + + void AttackStart(Unit*) override; + + void EnterCombat(Unit*) override; + + void MoveInLineOfSight(Unit*) override; + + void JustDied(Unit*) override; + + void JustRespawned() override; + + void EnterEvadeMode() override; + + void UpdateAI(const uint32) override; // the "internal" update, calls UpdateEscortAI() + + // Called when an AI Event is received + void ReceiveAIEvent(AIEventType /*eventType*/, Creature* /*pSender*/, Unit* /*pInvoker*/, uint32 /*uiMiscValue*/) override {} + + virtual void UpdateEscortAI(const uint32); // used when it's needed to add code in update (abilities, scripted events, etc) + + void MovementInform(uint32, uint32) override; + + // 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 bRun = false, const Player* pPlayer = NULL, 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); } + + // update current point + void SetCurrentWaypoint(uint32 uiPointId); + + protected: + Player* GetPlayerForEscort() { return m_creature->GetMap()->GetPlayer(m_playerGuid); } + virtual void JustStartedEscort() {} + + private: + bool AssistPlayerInCombat(Unit* pWho); + bool IsPlayerOrGroupInRange(); + bool MoveToNextWaypoint(); + void FillPointMovementListForCreature(); + + void AddEscortState(uint32 uiEscortState) { m_uiEscortState |= uiEscortState; } + void RemoveEscortState(uint32 uiEscortState) { m_uiEscortState &= ~uiEscortState; } + + ObjectGuid m_playerGuid; + uint32 m_uiWPWaitTimer; + uint32 m_uiPlayerCheckTimer; + uint32 m_uiEscortState; + + const Quest* m_pQuestForEscort; // generally passed in Start() when regular escort script. + + std::list WaypointList; + std::list::iterator CurrentWP; + + bool m_bIsRunning; // all creatures are walking by default (has flag SPLINEFLAG_WALKMODE) + bool m_bCanInstantRespawn; // if creature should respawn instantly after escort over (if not, database respawntime are used) + bool m_bCanReturnToStart; // if creature can walk same path (loop) without despawn. Not for regular escort quests. +}; +#endif diff --git a/src/modules/SD2/base/follower_ai.cpp b/src/modules/SD2/base/follower_ai.cpp new file mode 100644 index 000000000..b786f4373 --- /dev/null +++ b/src/modules/SD2/base/follower_ai.cpp @@ -0,0 +1,457 @@ +/** + * ScriptDev2 is an extension for mangos providing enhanced features for + * area triggers, creatures, game objects, instances, items, and spells beyond + * the default database scripting in mangos. + * + * Copyright (C) 2006-2013 ScriptDev2 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * World of Warcraft, and all World of Warcraft or Warcraft art, images, + * and lore are copyrighted by Blizzard Entertainment, Inc. + */ + +/* ScriptData +SDName: FollowerAI +SD%Complete: 60 +SDComment: This AI is under development +SDCategory: Npc +EndScriptData */ + +#include "precompiled.h" +#include "follower_ai.h" + +const float MAX_PLAYER_DISTANCE = 100.0f; + +enum +{ + POINT_COMBAT_START = 0xFFFFFF +}; + +FollowerAI::FollowerAI(Creature* pCreature) : ScriptedAI(pCreature), + m_uiUpdateFollowTimer(2500), + m_uiFollowState(STATE_FOLLOW_NONE), + m_pQuestForFollow(NULL) +{} + +void FollowerAI::AttackStart(Unit* pWho) +{ + if (!pWho) + { + return; + } + + if (m_creature->Attack(pWho, true)) + { + m_creature->AddThreat(pWho); + m_creature->SetInCombatWith(pWho); + pWho->SetInCombatWith(m_creature); + + 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) +bool FollowerAI::AssistPlayerInCombat(Unit* pWho) +{ + if (!pWho->getVictim()) + { + return false; + } + + // experimental (unknown) flag not present + if (!(m_creature->GetCreatureInfo()->CreatureTypeFlags & CREATURE_TYPEFLAGS_CAN_ASSIST)) + { + return false; + } + + // unit state prevents (similar check is done in CanInitiateAttack which also include checking unit_flags. We skip those here) + if (m_creature->hasUnitState(UNIT_STAT_STUNNED | UNIT_STAT_DIED)) + { + return false; + } + + // victim of pWho is 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)) + { + // already fighting someone? + if (!m_creature->getVictim()) + { + AttackStart(pWho); + return true; + } + else + { + pWho->SetInCombatWith(m_creature); + m_creature->AddThreat(pWho); + return true; + } + } + + return false; +} + +void FollowerAI::MoveInLineOfSight(Unit* pWho) +{ + if (pWho->IsTargetableForAttack() && pWho->isInAccessablePlaceFor(m_creature)) + { + // AssistPlayerInCombat can start attack, so return if true + if (HasFollowState(STATE_FOLLOW_INPROGRESS) && AssistPlayerInCombat(pWho)) + { + return; + } + + if (!m_creature->CanInitiateAttack()) + { + 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); + if (m_creature->IsWithinDistInMap(pWho, fAttackRadius) && m_creature->IsWithinLOSInMap(pWho)) + { + if (!m_creature->getVictim()) + { + pWho->RemoveSpellsCausingAura(SPELL_AURA_MOD_STEALTH); + AttackStart(pWho); + } + else if (m_creature->GetMap()->IsDungeon()) + { + pWho->SetInCombatWith(m_creature); + m_creature->AddThreat(pWho); + } + } + } + } +} + +void FollowerAI::JustDied(Unit* /*pKiller*/) +{ + if (!HasFollowState(STATE_FOLLOW_INPROGRESS) || !m_leaderGuid || !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()) + { + if (Player* pMember = pRef->getSource()) + { + if (pMember->GetQuestStatus(m_pQuestForFollow->GetQuestId()) == QUEST_STATUS_INCOMPLETE) + { + pMember->FailQuest(m_pQuestForFollow->GetQuestId()); + } + } + } + } + else + { + if (pPlayer->GetQuestStatus(m_pQuestForFollow->GetQuestId()) == QUEST_STATUS_INCOMPLETE) + { + pPlayer->FailQuest(m_pQuestForFollow->GetQuestId()); + } + } + } +} + +void FollowerAI::JustRespawned() +{ + m_uiFollowState = STATE_FOLLOW_NONE; + + if (!IsCombatMovement()) + { + SetCombatMovement(true); + } + + Reset(); +} + +void FollowerAI::EnterEvadeMode() +{ + m_creature->RemoveAllAurasOnEvade(); + m_creature->DeleteThreatList(); + m_creature->CombatStop(true); + m_creature->SetLootRecipient(NULL); + + if (HasFollowState(STATE_FOLLOW_INPROGRESS)) + { + debug_log("SD2: FollowerAI left combat, returning to CombatStartPosition."); + + if (m_creature->GetMotionMaster()->GetCurrentMovementGeneratorType() == CHASE_MOTION_TYPE) + { + float fPosX, fPosY, fPosZ; + m_creature->GetCombatStartPosition(fPosX, fPosY, fPosZ); + m_creature->GetMotionMaster()->MovePoint(POINT_COMBAT_START, fPosX, fPosY, fPosZ); + } + } + else + { + if (m_creature->GetMotionMaster()->GetCurrentMovementGeneratorType() == CHASE_MOTION_TYPE) + { + m_creature->GetMotionMaster()->MoveTargetedHome(); + } + } + + Reset(); +} + +void FollowerAI::UpdateAI(const uint32 uiDiff) +{ + if (HasFollowState(STATE_FOLLOW_INPROGRESS) && !m_creature->getVictim()) + { + if (m_uiUpdateFollowTimer < uiDiff) + { + if (HasFollowState(STATE_FOLLOW_COMPLETE) && !HasFollowState(STATE_FOLLOW_POSTEVENT)) + { + debug_log("SD2: FollowerAI is set completed, despawns."); + m_creature->ForcedDespawn(); + return; + } + + bool bIsMaxRangeExceeded = true; + + if (Player* pPlayer = GetLeaderForFollower()) + { + if (HasFollowState(STATE_FOLLOW_RETURNING)) + { + debug_log("SD2: 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()) + { + Player* pMember = pRef->getSource(); + + if (pMember && m_creature->IsWithinDistInMap(pMember, MAX_PLAYER_DISTANCE)) + { + bIsMaxRangeExceeded = false; + break; + } + } + } + else + { + if (m_creature->IsWithinDistInMap(pPlayer, MAX_PLAYER_DISTANCE)) + { + bIsMaxRangeExceeded = false; + } + } + } + + if (bIsMaxRangeExceeded) + { + debug_log("SD2: 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 (!m_creature->SelectHostileTarget() || !m_creature->getVictim()) + { + 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()) + { + if (!HasFollowState(STATE_FOLLOW_PAUSED)) + { + AddFollowState(STATE_FOLLOW_RETURNING); + } + } + else + { + m_creature->ForcedDespawn(); + } + } +} + +void FollowerAI::StartFollow(Player* pLeader, uint32 uiFactionForFollower, const Quest* pQuest) +{ + if (m_creature->getVictim()) + { + debug_log("SD2: FollowerAI attempt to StartFollow while in combat."); + return; + } + + if (HasFollowState(STATE_FOLLOW_INPROGRESS)) + { + script_error_log("FollowerAI attempt to StartFollow while already following."); + return; + } + + // set variables + m_leaderGuid = pLeader->GetObjectGuid(); + + if (uiFactionForFollower) + { + m_creature->SetFactionTemporary(uiFactionForFollower, TEMPFACTION_RESTORE_RESPAWN); + } + + m_pQuestForFollow = pQuest; + + if (m_creature->GetMotionMaster()->GetCurrentMovementGeneratorType() == WAYPOINT_MOTION_TYPE) + { + m_creature->GetMotionMaster()->Clear(); + m_creature->GetMotionMaster()->MoveIdle(); + debug_log("SD2: 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("SD2: FollowerAI start follow %s (Guid %s)", pLeader->GetName(), m_leaderGuid.GetString().c_str()); +} + +Player* FollowerAI::GetLeaderForFollower() +{ + if (Player* pLeader = m_creature->GetMap()->GetPlayer(m_leaderGuid)) + { + if (pLeader->IsAlive()) + { + return pLeader; + } + else + { + if (Group* pGroup = pLeader->GetGroup()) + { + 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("SD2: FollowerAI GetLeader changed and returned new leader."); + m_leaderGuid = pMember->GetObjectGuid(); + return pMember; + } + } + } + } + } + + debug_log("SD2: FollowerAI GetLeader can not find suitable leader."); + return NULL; +} + +void FollowerAI::SetFollowComplete(bool bWithEndEvent) +{ + if (m_creature->GetMotionMaster()->GetCurrentMovementGeneratorType() == FOLLOW_MOTION_TYPE) + { + m_creature->StopMoving(); + m_creature->GetMotionMaster()->Clear(); + m_creature->GetMotionMaster()->MoveIdle(); + } + + if (bWithEndEvent) + { + AddFollowState(STATE_FOLLOW_POSTEVENT); + } + else + { + 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->GetMotionMaster()->GetCurrentMovementGeneratorType() == FOLLOW_MOTION_TYPE) + { + m_creature->StopMoving(); + m_creature->GetMotionMaster()->Clear(); + m_creature->GetMotionMaster()->MoveIdle(); + } + } + else + { + RemoveFollowState(STATE_FOLLOW_PAUSED); + + if (Player* pLeader = GetLeaderForFollower()) + { + m_creature->GetMotionMaster()->MoveFollow(pLeader, PET_FOLLOW_DIST, PET_FOLLOW_ANGLE); + } + } +} diff --git a/src/modules/SD2/base/follower_ai.h b/src/modules/SD2/base/follower_ai.h new file mode 100644 index 000000000..d7fb016ac --- /dev/null +++ b/src/modules/SD2/base/follower_ai.h @@ -0,0 +1,86 @@ +/** + * ScriptDev2 is an extension for mangos providing enhanced features for + * area triggers, creatures, game objects, instances, items, and spells beyond + * the default database scripting in mangos. + * + * Copyright (C) 2006-2013 ScriptDev2 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * World of Warcraft, and all World of Warcraft or Warcraft art, images, + * and lore are copyrighted by Blizzard Entertainment, Inc. + */ + +#ifndef SC_FOLLOWERAI_H +#define SC_FOLLOWERAI_H + +enum FollowState +{ + STATE_FOLLOW_NONE = 0x000, + STATE_FOLLOW_INPROGRESS = 0x001, // must always have this state for any follow + STATE_FOLLOW_RETURNING = 0x002, // when returning to combat start after being in combat + STATE_FOLLOW_PAUSED = 0x004, // disables following + STATE_FOLLOW_COMPLETE = 0x008, // follow is completed and may end + 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 FollowerAI : public ScriptedAI +{ + public: + explicit FollowerAI(Creature* pCreature); + ~FollowerAI() {} + + // virtual void WaypointReached(uint32 uiPointId) = 0; + + void MovementInform(uint32 uiMotionType, uint32 uiPointId) override; + + void AttackStart(Unit*) override; + + void MoveInLineOfSight(Unit*) override; + + void EnterEvadeMode() override; + + void JustDied(Unit*) override; + + void JustRespawned() override; + + void UpdateAI(const uint32) override; // 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); + + ObjectGuid m_leaderGuid; + uint32 m_uiUpdateFollowTimer; + uint32 m_uiFollowState; + + const Quest* m_pQuestForFollow; // normally we have a quest +}; + +#endif diff --git a/src/modules/SD2/base/guard_ai.cpp b/src/modules/SD2/base/guard_ai.cpp new file mode 100644 index 000000000..773ce5132 --- /dev/null +++ b/src/modules/SD2/base/guard_ai.cpp @@ -0,0 +1,274 @@ +/** + * ScriptDev2 is an extension for mangos providing enhanced features for + * area triggers, creatures, game objects, instances, items, and spells beyond + * the default database scripting in mangos. + * + * Copyright (C) 2006-2013 ScriptDev2 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * World of Warcraft, and all World of Warcraft or Warcraft art, images, + * and lore are copyrighted by Blizzard Entertainment, Inc. + */ + +/* 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 + +guardAI::guardAI(Creature* pCreature) : ScriptedAI(pCreature), + m_uiGlobalCooldown(0), + m_uiBuffTimer(0) +{} + +void guardAI::Reset() +{ + m_uiGlobalCooldown = 0; + m_uiBuffTimer = 0; // Rebuff as soon as we can +} + +void guardAI::Aggro(Unit* pWho) +{ + if (m_creature->GetEntry() == NPC_CENARION_INFANTRY) + { + switch (urand(0, 2)) + { + case 0: + DoScriptText(SAY_GUARD_SIL_AGGRO1, m_creature, pWho); + break; + case 1: + DoScriptText(SAY_GUARD_SIL_AGGRO2, m_creature, pWho); + break; + case 2: + DoScriptText(SAY_GUARD_SIL_AGGRO3, m_creature, pWho); + break; + } + } + + if (const SpellEntry* pSpellInfo = m_creature->ReachWithSpellAttack(pWho)) + { + DoCastSpell(pWho, pSpellInfo); + } +} + +void guardAI::JustDied(Unit* pKiller) +{ + // Send Zone Under Attack message to the LocalDefense and WorldDefense Channels + if (Player* pPlayer = pKiller->GetCharmerOrOwnerPlayerOrPlayerItself()) + { + m_creature->SendZoneUnderAttackMessage(pPlayer); + } +} + +void guardAI::UpdateAI(const uint32 uiDiff) +{ + // Always decrease our global cooldown first + if (m_uiGlobalCooldown > uiDiff) + { + m_uiGlobalCooldown -= uiDiff; + } + else + { + m_uiGlobalCooldown = 0; + } + + // Buff timer (only buff when we are alive and not in combat + if (m_creature->IsAlive() && !m_creature->IsInCombat()) + { + if (m_uiBuffTimer < uiDiff) + { + // Find a spell that targets friendly and applies an aura (these are generally buffs) + const SpellEntry* pSpellInfo = SelectSpell(m_creature, -1, -1, SELECT_TARGET_ANY_FRIEND, 0, 0, 0, 0, SELECT_EFFECT_AURA); + + if (pSpellInfo && !m_uiGlobalCooldown) + { + // Cast the buff spell + DoCastSpell(m_creature, pSpellInfo); + + // Set our global cooldown + m_uiGlobalCooldown = GENERIC_CREATURE_COOLDOWN; + + // Set our timer to 10 minutes before rebuff + m_uiBuffTimer = 600000; + } // Try again in 30 seconds + else + { + m_uiBuffTimer = 30000; + } + } + else + { + m_uiBuffTimer -= uiDiff; + } + } + + // Return since we have no target + if (!m_creature->SelectHostileTarget() || !m_creature->getVictim()) + { + return; + } + + // Make sure our attack is ready and we arn't currently casting + if (m_creature->isAttackReady() && !m_creature->IsNonMeleeSpellCasted(false)) + { + // If we are within range melee the target + if (m_creature->CanReachWithMeleeAttack(m_creature->getVictim())) + { + bool bHealing = false; + const SpellEntry* pSpellInfo = NULL; + + // Select a healing spell if less than 30% hp + if (m_creature->GetHealthPercent() < 30.0f) + { + pSpellInfo = 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 (pSpellInfo) + { + bHealing = true; + } + else + { + pSpellInfo = 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 (pSpellInfo && !urand(0, 4) && !m_uiGlobalCooldown) + { + // Cast the spell + if (bHealing) + { + DoCastSpell(m_creature, pSpellInfo); + } + else + { + DoCastSpell(m_creature->getVictim(), pSpellInfo); + } + + // Set our global cooldown + m_uiGlobalCooldown = GENERIC_CREATURE_COOLDOWN; + } + else + { + m_creature->AttackerStateUpdate(m_creature->getVictim()); + } + + m_creature->resetAttackTimer(); + } + } + else + { + // Only run this code if we arn't already casting + if (!m_creature->IsNonMeleeSpellCasted(false)) + { + bool bHealing = false; + const SpellEntry* pSpellInfo = NULL; + + // Select a healing spell if less than 30% hp ONLY 33% of the time + if (m_creature->GetHealthPercent() < 30.0f && !urand(0, 2)) + { + pSpellInfo = 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 (pSpellInfo) + { + bHealing = true; + } + else + { + pSpellInfo = SelectSpell(m_creature->getVictim(), -1, -1, SELECT_TARGET_ANY_ENEMY, 0, 0, ATTACK_DISTANCE, 0, SELECT_EFFECT_DONTCARE); + } + + // Found a spell, check if we arn't on cooldown + if (pSpellInfo && !m_uiGlobalCooldown) + { + // If we are currently moving stop us and set the movement generator + if (m_creature->GetMotionMaster()->GetCurrentMovementGeneratorType() != IDLE_MOTION_TYPE) + { + m_creature->GetMotionMaster()->Clear(false); + m_creature->GetMotionMaster()->MoveIdle(); + } + + // Cast spell + if (bHealing) + { + DoCastSpell(m_creature, pSpellInfo); + } + else + { + DoCastSpell(m_creature->getVictim(), pSpellInfo); + } + + // Set our global cooldown + m_uiGlobalCooldown = GENERIC_CREATURE_COOLDOWN; + } // If no spells available and we arn't moving run to target + else if (m_creature->GetMotionMaster()->GetCurrentMovementGeneratorType() != CHASE_MOTION_TYPE) + { + // Cancel our current spell and then mutate new movement generator + m_creature->InterruptNonMeleeSpells(false); + m_creature->GetMotionMaster()->Clear(false); + m_creature->GetMotionMaster()->MoveChase(m_creature->getVictim()); + } + } + } +} + +void guardAI::DoReplyToTextEmote(uint32 uiTextEmote) +{ + switch (uiTextEmote) + { + case TEXTEMOTE_KISS: + m_creature->HandleEmote(EMOTE_ONESHOT_BOW); + break; + case TEXTEMOTE_WAVE: + m_creature->HandleEmote(EMOTE_ONESHOT_WAVE); + break; + case TEXTEMOTE_SALUTE: + m_creature->HandleEmote(EMOTE_ONESHOT_SALUTE); + break; + case TEXTEMOTE_SHY: + m_creature->HandleEmote(EMOTE_ONESHOT_FLEX); + break; + case TEXTEMOTE_RUDE: + case TEXTEMOTE_CHICKEN: + m_creature->HandleEmote(EMOTE_ONESHOT_POINT); + break; + } +} + +void guardAI_orgrimmar::ReceiveEmote(Player* pPlayer, uint32 uiTextEmote) +{ + if (pPlayer->GetTeam() == HORDE) + { + DoReplyToTextEmote(uiTextEmote); + } +} + +void guardAI_stormwind::ReceiveEmote(Player* pPlayer, uint32 uiTextEmote) +{ + if (pPlayer->GetTeam() == ALLIANCE) + { + DoReplyToTextEmote(uiTextEmote); + } +} diff --git a/src/modules/SD2/base/guard_ai.h b/src/modules/SD2/base/guard_ai.h new file mode 100644 index 000000000..9c8311ed0 --- /dev/null +++ b/src/modules/SD2/base/guard_ai.h @@ -0,0 +1,83 @@ +/** + * ScriptDev2 is an extension for mangos providing enhanced features for + * area triggers, creatures, game objects, instances, items, and spells beyond + * the default database scripting in mangos. + * + * Copyright (C) 2006-2013 ScriptDev2 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * World of Warcraft, and all World of Warcraft or Warcraft art, images, + * and lore are copyrighted by Blizzard Entertainment, Inc. + */ + +#ifndef SC_GUARDAI_H +#define SC_GUARDAI_H + +enum +{ + GENERIC_CREATURE_COOLDOWN = 5000, + + SAY_GUARD_SIL_AGGRO1 = -1000198, + SAY_GUARD_SIL_AGGRO2 = -1000199, + SAY_GUARD_SIL_AGGRO3 = -1000200, + + NPC_CENARION_INFANTRY = 15184 +}; + +enum eShattrathGuard +{ + SPELL_BANISHED_SHATTRATH_A = 36642, + SPELL_BANISHED_SHATTRATH_S = 36671, + SPELL_BANISH_TELEPORT = 36643, + SPELL_EXILE = 39533 +}; + +struct guardAI : public ScriptedAI +{ + public: + explicit guardAI(Creature* pCreature); + ~guardAI() {} + + uint32 m_uiGlobalCooldown; // This variable acts like the global cooldown that players have (1.5 seconds) + uint32 m_uiBuffTimer; // This variable keeps track of buffs + + void Reset() override; + + void Aggro(Unit* pWho) override; + + void JustDied(Unit* /*pKiller*/) override; + + void UpdateAI(const uint32 uiDiff) override; + + // Commonly used for guards in main cities + void DoReplyToTextEmote(uint32 uiTextEmote); +}; + +struct guardAI_orgrimmar : public guardAI +{ + guardAI_orgrimmar(Creature* pCreature) : guardAI(pCreature) {} + + void ReceiveEmote(Player* pPlayer, uint32 uiTextEmote) override; +}; + +struct guardAI_stormwind : public guardAI +{ + guardAI_stormwind(Creature* pCreature) : guardAI(pCreature) {} + + void ReceiveEmote(Player* pPlayer, uint32 uiTextEmote) override; +}; + +#endif diff --git a/src/modules/SD2/base/pet_ai.cpp b/src/modules/SD2/base/pet_ai.cpp new file mode 100644 index 000000000..1a41c9425 --- /dev/null +++ b/src/modules/SD2/base/pet_ai.cpp @@ -0,0 +1,171 @@ +/** + * ScriptDev2 is an extension for mangos providing enhanced features for + * area triggers, creatures, game objects, instances, items, and spells beyond + * the default database scripting in mangos. + * + * Copyright (C) 2006-2013 ScriptDev2 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * World of Warcraft, and all World of Warcraft or Warcraft art, images, + * and lore are copyrighted by Blizzard Entertainment, Inc. + */ + +/* ScriptData +SDName: ScriptedPetAI +SD%Complete: 50 +SDComment: Intended to be used with Guardian/Protector/Minipets. Little/no control over when pet enter/leave combat. Must be considered to be under development. +SDCategory: Npc +EndScriptData */ + +#include "precompiled.h" +#include "pet_ai.h" + +ScriptedPetAI::ScriptedPetAI(Creature* pCreature) : CreatureAI(pCreature) +{} + +bool ScriptedPetAI::IsVisible(Unit* pWho) const +{ + return pWho && m_creature->IsWithinDist(pWho, VISIBLE_RANGE) + && pWho->IsVisibleForOrDetect(m_creature, m_creature, true); +} + +void ScriptedPetAI::MoveInLineOfSight(Unit* pWho) +{ + if (m_creature->getVictim()) + { + return; + } + + if (!m_creature->GetCharmInfo() || !m_creature->GetCharmInfo()->HasReactState(REACT_AGGRESSIVE)) + { + return; + } + + if (m_creature->CanInitiateAttack() && pWho->IsTargetableForAttack() && + m_creature->IsHostileTo(pWho) && pWho->isInAccessablePlaceFor(m_creature)) + { + if (!m_creature->CanFly() && m_creature->GetDistanceZ(pWho) > CREATURE_Z_ATTACK_RANGE) + { + return; + } + + if (m_creature->IsWithinDistInMap(pWho, m_creature->GetAttackDistance(pWho)) && m_creature->IsWithinLOSInMap(pWho)) + { + pWho->RemoveSpellsCausingAura(SPELL_AURA_MOD_STEALTH); + AttackStart(pWho); + } + } +} + +void ScriptedPetAI::AttackStart(Unit* pWho) +{ + if (pWho && m_creature->Attack(pWho, true)) + { + m_creature->GetMotionMaster()->MoveChase(pWho); + } +} + +void ScriptedPetAI::AttackedBy(Unit* pAttacker) +{ + if (m_creature->getVictim()) + { + return; + } + + if (m_creature->GetCharmInfo() && !m_creature->GetCharmInfo()->HasReactState(REACT_PASSIVE) && + m_creature->CanReachWithMeleeAttack(pAttacker)) + { + AttackStart(pAttacker); + } +} + +void ScriptedPetAI::ResetPetCombat() +{ + Unit* pOwner = m_creature->GetCharmerOrOwner(); + + if (pOwner && m_creature->GetCharmInfo() && m_creature->GetCharmInfo()->HasCommandState(COMMAND_FOLLOW)) + { + m_creature->GetMotionMaster()->MoveFollow(pOwner, PET_FOLLOW_DIST, PET_FOLLOW_ANGLE); + } + else + { + m_creature->GetMotionMaster()->Clear(false); + m_creature->GetMotionMaster()->MoveIdle(); + } + + m_creature->AttackStop(); + + debug_log("SD2: ScriptedPetAI reset pet combat and stop attack."); + Reset(); +} + +void ScriptedPetAI::UpdatePetAI(const uint32 /*uiDiff*/) +{ + DoMeleeAttackIfReady(); +} + +void ScriptedPetAI::UpdateAI(const uint32 uiDiff) +{ + if (!m_creature->IsAlive()) // should not be needed, IsAlive is checked in mangos before calling UpdateAI + { + return; + } + + // UpdateAllies() is done in the generic PetAI in Mangos, but we can't do this from script side. + // Unclear what side effects this has, but is something to be resolved from Mangos. + + if (m_creature->getVictim()) // in combat + { + if (!m_creature->getVictim()->IsTargetableForAttack()) + { + // target no longer valid for pet, so either attack stops or new target are selected + // doesn't normally reach this, because of how petAi is designed in Mangos. CombatStop + // are called before this update diff, and then pet will already have no victim. + ResetPetCombat(); + return; + } + + // update when in combat + UpdatePetAI(uiDiff); + } + else if (m_creature->GetCharmInfo()) + { + Unit* pOwner = m_creature->GetCharmerOrOwner(); + + if (!pOwner) + { + return; + } + + if (pOwner->IsInCombat() && !m_creature->GetCharmInfo()->HasReactState(REACT_PASSIVE)) + { + // Not correct in all cases. + // When mob initiate attack by spell, pet should not start attack before spell landed. + AttackStart(pOwner->getAttackerForHelper()); + } + else if (m_creature->GetCharmInfo()->HasCommandState(COMMAND_FOLLOW)) + { + // not following, so start follow + if (!m_creature->hasUnitState(UNIT_STAT_FOLLOW)) + { + m_creature->GetMotionMaster()->MoveFollow(pOwner, PET_FOLLOW_DIST, PET_FOLLOW_ANGLE); + } + + // update when not in combat + UpdatePetOOCAI(uiDiff); + } + } +} diff --git a/src/modules/SD2/base/pet_ai.h b/src/modules/SD2/base/pet_ai.h new file mode 100644 index 000000000..4037beb1a --- /dev/null +++ b/src/modules/SD2/base/pet_ai.h @@ -0,0 +1,60 @@ +/** + * ScriptDev2 is an extension for mangos providing enhanced features for + * area triggers, creatures, game objects, instances, items, and spells beyond + * the default database scripting in mangos. + * + * Copyright (C) 2006-2013 ScriptDev2 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * World of Warcraft, and all World of Warcraft or Warcraft art, images, + * and lore are copyrighted by Blizzard Entertainment, Inc. + */ + +#ifndef SC_PET_H +#define SC_PET_H + +// Using CreatureAI for now. Might change later and use PetAI (need to export for dll first) +class ScriptedPetAI : public CreatureAI +{ + public: + explicit ScriptedPetAI(Creature* pCreature); + ~ScriptedPetAI() {} + + void MoveInLineOfSight(Unit* /*pWho*/) override; + + void AttackStart(Unit* /*pWho*/) override; + + void AttackedBy(Unit* /*pAttacker*/) override; + + bool IsVisible(Unit* /*pWho*/) const override; + + void KilledUnit(Unit* /*pVictim*/) override {} + + void OwnerKilledUnit(Unit* /*pVictim*/) override {} + + void UpdateAI(const uint32 uiDiff) override; + + virtual void Reset() {} + + virtual void UpdatePetAI(const uint32 uiDiff); // while in combat + + virtual void UpdatePetOOCAI(const uint32 /*uiDiff*/) {} // when not in combat + + protected: + void ResetPetCombat(); +}; + +#endif diff --git a/src/modules/SD2/config-sd2.h b/src/modules/SD2/config-sd2.h new file mode 100644 index 000000000..0627e1037 --- /dev/null +++ b/src/modules/SD2/config-sd2.h @@ -0,0 +1,38 @@ +/** + * ScriptDev2 is an extension for mangos providing enhanced features for + * area triggers, creatures, game objects, instances, items, and spells beyond + * the default database scripting in mangos. + * + * Copyright (C) 2006-2013 ScriptDev2 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * World of Warcraft, and all World of Warcraft or Warcraft art, images, + * and lore are copyrighted by Blizzard Entertainment, Inc. + */ + +#ifndef SC_CONFIG_H +#define SC_CONFIG_H + +#include "Platform/CompilerDefs.h" +//#include "revision.h" +#include "sd2_revision_nr.h" +#include "SystemConfig.h" + +#ifndef SCRIPTDEV2_VERSION + #define SCRIPTDEV2_VERSION "Revision [" SD2_REVISION_NR "] (" REVISION_ID ") " REVISION_DATE " " REVISION_TIME +#endif + +#endif diff --git a/src/modules/SD2/docs/License.md b/src/modules/SD2/docs/License.md new file mode 100644 index 000000000..ddf4fcd95 --- /dev/null +++ b/src/modules/SD2/docs/License.md @@ -0,0 +1,344 @@ +GNU GENERAL PUBLIC LICENSE +========================== + +Version 2, June 1991 + +Copyright (c) 1989, 1981 [Free Software Foundation, Inc.][1] + +Everyone is permitted to copy and distribute verbatim copies of this license +document, but changing it is not allowed. + +## Preamble + +The licenses for most software are designed to take away your freedom to share +and change it. By contrast, the GNU General Public License is intended to +guarantee your freedom to share and change free software -- to make sure the +software is free for all its users. This General Public License applies to most +of the Free Software Foundation's software and to any other program whose +authors commit to using it [^1]. You can apply it to your programs, too. + +When we speak of free software, we are referring to freedom, not price. Our +General Public Licenses are designed to make sure that you have the freedom to +distribute copies of free software (and charge for this service if you wish), +that you receive source code or can get it if you want it, that you can change +the software or use pieces of it in new free programs; and that you know you +can do these things. + +To protect your rights, we need to make restrictions that forbid anyone to deny +you these rights or to ask you to surrender the rights. These restrictions +translate to certain responsibilities for you if you distribute copies of the +software, or if you modify it. + +For example, if you distribute copies of such a program, whether gratis or for +a fee, you must give the recipients all the rights that you have. You must make +sure that they, too, receive or can get the source code. And you must show them +these terms so they know their rights. + +We protect your rights with two steps: (1) copyright the software, and (2) offer +you this license which gives you legal permission to copy, distribute and/or +modify the software. + +Also, for each author's protection and ours, we want to make certain that +everyone understands that there is no warranty for this free software. If the +software is modified by someone else and passed on, we want its recipients to +know that what they have is not the original, so that any problems introduced +by others will not reflect on the original authors' reputations. + +Finally, any free program is threatened constantly by software patents. We wish +to avoid the danger that redistributors of a free program will individually +obtain patent licenses, in effect making the program proprietary. To prevent +this, we have made it clear that any patent must be licensed for everyone's +free use or not licensed at all. + +The precise terms and conditions for copying, distribution and modification +follow. + +## TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + +### 0. + +This License applies to any program or other work which contains a notice +placed by the copyright holder saying it may be distributed under the terms of +this General Public License. The "Program", below, refers to any such program +or work, and a "work based on the Program" means either the Program or any +derivative work under copyright law: that is to say, a work containing the +Program or a portion of it, either verbatim or with modifications and/or +translated into another language. (Hereinafter, translation is included +without limitation in the term "modification".) Each licensee is addressed +as "you". + +Activities other than copying, distribution and modification are not covered by +this License; they are outside its scope. The act of running the Program is not +restricted, and the output from the Program is covered only if its contents +constitute a work based on the Program (independent of having been made by +running the Program). Whether that is true depends on what the Program does. + +### 1. + +You may copy and distribute verbatim copies of the Program's source code as you +receive it, in any medium, provided that you conspicuously and appropriately +publish on each copy an appropriate copyright notice and disclaimer of warranty; +keep intact all the notices that refer to this License and to the absence of +any warranty; and give any other recipients of the Program a copy of this +License along with the Program. + +You may charge a fee for the physical act of transferring a copy, and you may +at your option offer warranty protection in exchange for a fee. + +### 2. + +You may modify your copy or copies of the Program or any portion of it, thus +forming a work based on the Program, and copy and distribute such modifications +or work under the terms of Section 1 above, provided that you also meet all of +these conditions: + +* **a)** You must cause the modified files to carry prominent notices stating + that you changed the files and the date of any change. +* **b)** You must cause any work that you distribute or publish, that in whole + or in part contains or is derived from the Program or any part thereof, to be + licensed as a whole at no charge to all third parties under the terms of this + License. +* **c)** If the modified program normally reads commands interactively when run, + you must cause it, when started running for such interactive use in the most + ordinary way, to print or display an announcement including an appropriate + copyright notice and a notice that there is no warranty (or else, saying that + you provide a warranty) and that users may redistribute the program under + these conditions, and telling the user how to view a copy of this License. + (Exception: if the Program itself is interactive but does not normally print + such an announcement, your work based on the Program is not required to print + an announcement.) + +These requirements apply to the modified work as a whole. If identifiable +sections of that work are not derived from the Program, and can be reasonably +considered independent and separate works in themselves, then this License, +and its terms, do not apply to those sections when you distribute them as +separate works. But when you distribute the same sections as part of a whole +which is a work based on the Program, the distribution of the whole must be on +the terms of this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote it. + +Thus, it is not the intent of this section to claim rights or contest your +rights to work written entirely by you; rather, the intent is to exercise the +right to control the distribution of derivative or collective works based on +the Program. + +In addition, mere aggregation of another work not based on the Program with the +Program (or with a work based on the Program) on a volume of a storage or +distribution medium does not bring the other work under the scope of this +License. + +### 3. + +You may copy and distribute the Program (or a work based on it, under Section 2) +in object code or executable form under the terms of Sections 1 and 2 above +provided that you also do one of the following: + +* **a)** Accompany it with the complete corresponding machine-readable source + code, which must be distributed under the terms of Sections 1 and 2 above on + a medium customarily used for software interchange; or, +* **b)** Accompany it with a written offer, valid for at least three years, to + give any third party, for a charge no more than your cost of physically + performing source distribution, a complete machine-readable copy of the + corresponding source code, to be distributed under the terms of Sections 1 + and 2 above on a medium customarily used for software interchange; or, +* **c)** Accompany it with the information you received as to the offer to + distribute corresponding source code. (This alternative is allowed only for + noncommercial distribution and only if you received the program in object code + or executable form with such an offer, in accord with Subsection b above.) + +The source code for a work means the preferred form of the work for making +modifications to it. For an executable work, complete source code means all +the source code for all modules it contains, plus any associated interface +definition files, plus the scripts used to control compilation and installation +of the executable. However, as a special exception, the source code distributed +need not include anything that is normally distributed (in either source or +binary form) with the major components (compiler, kernel, and so on) of the +operating system on which the executable runs, unless that component itself +accompanies the executable. + +If distribution of executable or object code is made by offering access to copy +from a designated place, then offering equivalent access to copy the source +code from the same place counts as distribution of the source code, even though +third parties are not compelled to copy the source along with the object code. + +### 4. + +You may not copy, modify, sublicense, or distribute the Program except as +expressly provided under this License. Any attempt otherwise to copy, modify, +sublicense or distribute the Program is void, and will automatically terminate +your rights under this License. However, parties who have received copies, or +rights, from you under this License will not have their licenses terminated so +long as such parties remain in full compliance. + +### 5. + +You are not required to accept this License, since you have not signed it. +However, nothing else grants you permission to modify or distribute the Program +or its derivative works. These actions are prohibited by law if you do not +accept this License. Therefore, by modifying or distributing the Program (or any +work based on the Program), you indicate your acceptance of this License to do +so, and all its terms and conditions for copying, distributing or modifying the +Program or works based on it. + +### 6. + +Each time you redistribute the Program (or any work based on the Program), the +recipient automatically receives a license from the original licensor to copy, +distribute or modify the Program subject to these terms and conditions. You may +not impose any further restrictions on the recipients' exercise of the rights +granted herein. You are not responsible for enforcing compliance by third +parties to this License. + +### 7. + +If, as a consequence of a court judgment or allegation of patent infringement +or for any other reason (not limited to patent issues), conditions are imposed +on you (whether by court order, agreement or otherwise) that contradict the +conditions of this License, they do not excuse you from the conditions of this +License. If you cannot distribute so as to satisfy simultaneously your obligations +under this License and any other pertinent obligations, then as a consequence +you may not distribute the Program at all. For example, if a patent license +would not permit royalty-free redistribution of the Program by all those who +receive copies directly or indirectly through you, then the only way you could +satisfy both it and this License would be to refrain entirely from distribution +of the Program. + +If any portion of this section is held invalid or unenforceable under any +particular circumstance, the balance of the section is intended to apply and the +section as a whole is intended to apply in other circumstances. + +It is not the purpose of this section to induce you to infringe any patents or +other property right claims or to contest validity of any such claims; this +section has the sole purpose of protecting the integrity of the free software +distribution system, which is implemented by public license practices. Many +people have made generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that system; it is +up to the author/donor to decide if he or she is willing to distribute software +through any other system and a licensee cannot impose that choice. + +This section is intended to make thoroughly clear what is believed to be a +consequence of the rest of this License. + +### 8. + +If the distribution and/or use of the Program is restricted in certain countries +either by patents or by copyrighted interfaces, the original copyright holder +who places the Program under this License may add an explicit geographical +distribution limitation excluding those countries, so that distribution is +permitted only in or among countries not thus excluded. In such case, this +License incorporates the limitation as if written in the body of this License. + +### 9. + +The Free Software Foundation may publish revised and/or new versions of the +General Public License from time to time. Such new versions will be similar in +spirit to the present version, but may differ in detail to address new problems +or concerns. + +Each version is given a distinguishing version number. If the Program specifies +a version number of this License which applies to it and "any later version", +you have the option of following the terms and conditions either of that version +or of any later version published by the Free Software Foundation. If the Program +does not specify a version number of this License, you may choose any version +ever published by the Free Software Foundation. + +### 10. + +If you wish to incorporate parts of the Program into other free programs whose +distribution conditions are different, write to the author to ask for +permission. For software which is copyrighted by the Free Software Foundation, +write to the Free Software Foundation; we sometimes make exceptions for this. +Our decision will be guided by the two goals of preserving the free status of +all derivatives of our free software and of promoting the sharing and reuse of +software generally. + +## NO WARRANTY + +### 11. + +BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE +PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE +STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE +PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, +INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND +FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND +PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, +YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + +### 12. + +IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY +COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE +PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, +SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY +TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING +RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF +THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER +PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. + +## END OF TERMS AND CONDITIONS + +### How to Apply These Terms to Your New Programs + +If you develop a new program, and you want it to be of the greatest possible +use to the public, the best way to achieve this is to make it free software +which everyone can redistribute and change under these terms. + +To do so, attach the following notices to the program. It is safest to attach +them to the start of each source file to most effectively convey the exclusion +of warranty; and each file should have at least the "copyright" line and a +pointer to where the full notice is found. + + + Copyright (C) + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along + with this program; if not, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + +Also add information on how to contact you by electronic and paper mail. + +If the program is interactive, make it output a short notice like this when it +starts in an interactive mode: + + Gnomovision version 69, Copyright (C) year name of author + Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, the commands you use may be +called something other than `show w' and `show c'; they could even be +mouse-clicks or menu items--whatever suits your program. + +You should also get your employer (if you work as a programmer) or your school, +if any, to sign a "copyright disclaimer" for the program, if necessary. Here +is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the program + `Gnomovision' (which makes passes at compilers) written by James Hacker. + + , 1 April 1989 + Ty Coon, President of Vice + +This General Public License does not permit incorporating your program into +proprietary programs. If your program is a subroutine library, you may consider +it more useful to permit linking proprietary applications with the library. If +this is what you want to do, use the GNU Lesser General Public License instead +of this License. + +[^1]: Some other Free Software Foundation software is covered by the GNU Lesser + General Public License instead. + +[1]: http://fsf.org/ "Free Software Foundation, Inc." +[2]: http://www.gnu.org/licenses/ "Licenses - GNU Project" diff --git a/src/modules/SD2/docs/SQL_guide.md b/src/modules/SD2/docs/SQL_guide.md new file mode 100644 index 000000000..722bb2e87 --- /dev/null +++ b/src/modules/SD2/docs/SQL_guide.md @@ -0,0 +1,257 @@ +Introduction to Database content for SD2 +================================================ + +This guide is intended to help people + +* to understand which information of the database is used with SD2 +* who want to contribute their patches as complete as possible + +All SQL-related files are located in the ScriptDev2/SQL and subsequent directories. + +SQL-Files +--------- + +Files that contain full SD2-Database content +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +For a script we usually have to take care of these files: + +* mangos_scriptname_full.sql ++ +This file is applied to the world database (default: mangos), and contains the script names ++ +* scriptdev2_script_full.sql ++ +This file is applied to the sd2 database (default: scriptdev2), and contains texts, gossip-items and waypoints + +Patchfiles for incremental Updates +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Patches for the databases are stored in the files: + +* Updates/rXXXX_mangos.sql ++ +This file contains the changes that should be done with the patch to the world-database ++ +* Updates/rXXXX_scriptdev2.sql ++ +This file contains the changes that should be done with the patch to the scriptdev2-database + +World-Database +-------------- + +ScriptNames of NPCs: +~~~~~~~~~~~~~~~~~~~~ + +If we need to assign a ScriptName to a NPC (GameObject-Scripts are similar) the statement is: + +----------- +UPDATE creature_template SET ScriptName='npc_and_his_name' WHERE entry=XYZ; +----------- +or +----------- +UPDATE creature_template SET ScriptName='npc_something_identifying' WHERE entry IN (XYZ, ZYX); +----------- + +'Remark:' For creatures with many difficulty entries, only the one for normal difficulty needs the ScriptName. + +ScriptNames for scripted_areatrigger: +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +For Areatriggers (or scripted_event) we usally cannot use UPDATE, hence we need to DELETE possible old entries first: + +----------- +DELETE FROM scripted_areatrigger WHERE entry=XYZ; +INSERT INTO scripted_areatrigger VALUES (XYZ, at_some_place); +----------- + +ScriptDev2-Database +------------------- + +entry-Format for texts and for gossip-texts: +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +The to be used entry is a combination of the number depending on the map and a counter. + +* This is for texts: -1 +* For gossip-texts: -3 ++ +where is the ID of the map for instances, or 000 for all other maps. + +Example: Text on WorldMap +^^^^^^^^^^^^^^^^^^^^^^^^^ + +Let's say we want to add a new text to a NPC on Kalimdor (no instance), +then we need to look which is the last text entry of the format -1000XYZ +(this one can be found in scriptdev2_script_full.sql). + +On the moment where I write this guide this is: + +---------- +(-1000589,'Kroshius live? Kroshius crush!',0,1,0,0,'SAY_KROSHIUS_REVIVE'); +---------- + +so our first text entry will be -1000590. + +Example: Gossip-Item in Instance +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +Let's say we want to add a new gossip item to a NPC in Culling of Stratholme, this map has the ID 595. +At this moment there is already some gossip_text, and the last one is + +------------ +(-3595005,'So how does the Infinite Dragonflight plan to interfere?','chromie GOSSIP_ITEM_INN_3'); +------------ + +so our first gossip-text entry will be -3595006. + +Format for texts +~~~~~~~~~~~~~~~~ + +The format is `(entry,content_default,sound,type,language,emote,comment)` with these meanings: + +entry:: should now be clear ;) +content_default:: is the text (in english) enclosed with '. + + There are a few placeholders that can be used: + + +[horizontal] +%s;; self, is the name of the Unit saying the text + +The $-placeholders work only if you use DoScriptText with a 'target'. +$N, $n;; the [N, n]ame of the target +$C, $c;; the [C, c]lass of the target +$R, $r;; the [R, r]ace of the target +$GA:B; ;; if the target is male then A else B is displayed, Example: ++ +-------------------------------- +'Time to teach you a lesson in manners, little $Gboy:girl;!' +-------------------------------- ++ +Remember to escape [red]#\'# with [red]#\'#, Example: ++ +-------------------------------- +'That \'s my favourite chocolate bar'. +-------------------------------- +sound:: is the sound ID that shall be played on saying, they are stored in SoundEntries.dbc ++ +[quote, Ntsc] +_____________________________ +Sound Ids are stored within the SoundEntries.dbc file. Within that dbc file you will find a reference to the actual file that is played. We cannot help you with reading these files so please do not ask how. +_____________________________ ++ +type:: is the type of the text, there are these possibilities: ++ +------------- +0 CHAT_TYPE_SAY - 'white' text +1 CHAT_TYPE_YELL - 'red' text +2 CHAT_TYPE_TEXT_EMOTE - 'yellow' emote-text (no ... ) +3 CHAT_TYPE_BOSS_EMOTE - 'big yellow' emote-text displayed in the center of the screen +4 CHAT_TYPE_WHISPER - whisper, needs a target +5 CHAT_TYPE_BOSS_WHISPER - whipser, needs a target +6 CHAT_TYPE_ZONE_YELL - 'red' text, displayed to everyone in the zone +-------------- ++ +language:: is the language of the text (like LANG_GNOMISH), see +enum Language+ in `game/SharedDefines.h` -- usually zero (LANG_UNIVERSAL) +emote:: is the emote the npc shall perform on saying the text, can be found in +enum Emote+ in `game/SharedDefines.h` +comment:: is a comment to this text, usually the used enum of the script, like SAY_KROSHIUS_REVIVE, if this enum is not identifying the npc, then the name of the npc is put before. + +Format for gossip-texts +~~~~~~~~~~~~~~~~~~~~~~~ + +The format for gossip texts is `(entry,content_default,comment)` + +The fields have the same meaning as for script-texts. + +Format for waypoints +~~~~~~~~~~~~~~~~~~~~ + +The format for waypoints is `(entry,pointid,location_x,location_y,location_z,waittime,point_comment)` with these meanings: + +entry:: is the entry of the scripted NPC +pointid:: is the ID of the point, usally starting with 01, and increasing +location_*:: describes the position of the waypoint +waittime:: is the time, the mob will wait after reaching _this_ waypoint, before he continues to go to the next +point_comment:: is used to note if something special is happening at this point, like quest credit + + +Creating the Patch +------------------ + +There are different ways to get to a patch, I prefer this workflow: + +For the scriptdev2 database (patch files): +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Open scriptdev2_script_full.txt + +scroll to the right place for the needed SQL-statements, to note the entry. + +(for texts depending on mapId, and to the last counter, for waypoints behind the last inserted waypoint) + +Example for normal world text: +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +Assume the last entry in your map (here world-map) was: + +-------------- +(-1000589,'Kroshius live? Kroshius crush!',0,1,0,0,'SAY_KROSHIUS_REVIVE'); +-------------- + +Now create a new file: Updates/r0000_scriptdev2.sql +Add there: + +----------- +DELETE FROM script_texts WHERE entry=-1000590; +INSERT INTO script_texts (entry,content_default,sound,type,language,emote,comment) VALUES +(-1000590,'My fancy aggro-text',0,1,0,0,'boss_hogger SAY_AGGRO'); +----------- +or +----------- +DELETE FROM script_texts WHERE entry BETWEEN -1000592 AND -1000590; +INSERT INTO script_texts (entry,content_default,sound,type,language,emote,comment) VALUES +(-1000590,'My fancy aggro-text1',0,1,0,0,'boss_hogger SAY_AGGRO1'), +(-1000591,'My fancy aggro-text2',0,1,0,0,'boss_hogger SAY_AGGRO2'), +(-1000592,'My fancy aggro-text3',0,1,0,0,'boss_hogger SAY_AGGRO3'); +----------- + +Hint: the INSERT statements can also be copied from the scriptdev2_script_full.sql + +Example for waypoints: +^^^^^^^^^^^^^^^^^^^^^^ + +The required SQL code to add a waypoint is: + +---------- +DELETE FROM script_waypoint WHERE entry=; +INSERT INTO script_waypoint VALUES +(, 1, 4013.51,6390.33, 29.970, 0, ' - start escort'), +(, 2, 4060.51,6400.33, 20.970, 0, ' - finish escort'); +---------- + +When the Update file is done, append an additional empty line + +And test these lines for correctness! + +For the scriptdev2 database (full files): +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +If everything works alright, and you finally intend to prepare the full-patch, copy the SQL-Code that is needed to the proper place in scriptdev2_script_full.sql, +(for a new npc add an empty line), and change the semicolon to a comma: + +Example for world text: +^^^^^^^^^^^^^^^^^^^^^^ + +----------- +(-1000589,'Kroshius live? Kroshius crush!',0,1,0,0,'SAY_KROSHIUS_REVIVE'), + +(-1000590,'My fancy aggro-text',0,1,0,0,'boss_hogger SAY_AGGRO'); +----------- + +The waypoints are added behind the last waypoint, after an empty line. + +For the world database: +~~~~~~~~~~~~~~~~~~~~~~~ + +Create a new file: Updates/r0000_mangos.sql + +In this file put the needed statements for your ScriptNames, append an empty line, convert lineendings to Unix, and then test it for correctness. + +If everything is alright, open mangos_scriptname_full.sql and go to the right place (this is usally sorted alphabetically by zone). + +Insert the needed statement where it fits (usally again ordered alphabetically) + +After this is done, Create a patch including the (untracked) files in Update/ + +then you have all information in the created patch, and anyone who wants to test your patch just needs to apply the created files from Updates/ diff --git a/src/modules/SD2/docs/Script_Layout.md b/src/modules/SD2/docs/Script_Layout.md new file mode 100644 index 000000000..efacc62e5 --- /dev/null +++ b/src/modules/SD2/docs/Script_Layout.md @@ -0,0 +1,31 @@ +Script source code layout +========================= +In order to make it easier to find scripts, we have agreed on a fixed naming scheme +for directories and scripts. + +Directories +----------- +* **battlegrounds**: contains scripts used in the Alterac Valley, Arathi Basin and + Warsong Gulch battlegrounds. +* **eastern_kingdoms**: contains scripts for area triggers, creatures, dungeons, + instances, etc. related to the Eastern Kingdoms continent. Instances located on + Eastern Kingdoms are grouped in sub-directories by instance name. +* **kalimdor**: contains scripts for area triggers, creatures, dungeons, instances, + etc. related to the Kalimdor continent. Instances located on Kalimdor are grouped + in sub-directories by instance name. +* **world**: contains scripts which are used on every map, and not limited to one + specific zone. This includes scripts for area triggers, game objects, items, and + some creatures which can be found over the world. Also, scripts for spells are + stored here. +Contains scripts for anything that is not related to a specified zone. + +Naming Conventions +------------------ +Source files should be named `type_objectname.cpp` where + +* *type* is replaced by the type of object, +* and *objectname* is replaced by the name of the object, creature, item, or area + that this script will be used by. + +`AddSC` functions used for registering scripts to the server core should use the +form of `void AddSC_filename(void);`. diff --git a/src/modules/SD2/docs/Text-tables.md b/src/modules/SD2/docs/Text-tables.md new file mode 100644 index 000000000..dc2291757 --- /dev/null +++ b/src/modules/SD2/docs/Text-tables.md @@ -0,0 +1,85 @@ +Texts Documentation +=================== +In order for scripts to have a centralized storage for texts, text tables have been +added to the database. Any script can access and use texts from these tables. + +An additional table is available for custom scripts. + +For each table ranges of valid identifiers have been define + +* entry `-1` to `-999999`: reserved EventAI in *mangos*, +* entry `-1000000` to `-1999999`: script text entries, +* entry `-2000000` to `-2999999`: text entries for custom scripts, +* entry `-3000000` to `-3999999`: texts for scripted gossip texts. + +Text entries not using identifiers from the defined ranges will result in startup +errors. + +Database structure +------------------ +`custom_texts`, `gossip_texts`, and `script_texts` share an indentical table +structure, thus making it very easy to add new text entries. + +Field name | Description +--------------- | -------------------------------------------------------------- +entry | A unique *negative* identifier to the text entry. +content_default | The default text to be displayed in English. +content_loc1 | Korean localization of `content_default`. +content_loc2 | French localization of `content_default`. +content_loc3 | German localization of `content_default`. +content_loc4 | Chinese localization of `content_default`. +content_loc5 | Taiwanese localization of `content_default`. +content_loc6 | Spanish Spain localization of `content_default`. +content_loc7 | Spanish Latin America localization of `content_default`. +content_loc8 | Russian localization of `content_default`. +sound | A sound from SoundEntries.dbc to be played. +type | Type of text (Say/Yell/Text emote/Whisper/Boss whisper/zone yell). +language | A text language from Languages.dbc +emote | An emote from Emotes.dbc. Only source of text will play this emote (not target, if target are defined in DoScriptText) +comment | This is a comment using the Creature ID of NPC using it. + +*Note*: `sound`, `type`, `language` and `emote` exist only in the tables +`script_texts` and `custom_texts`. + +*Note*: Fields `content_loc1` to `content_loc8` are `NULL` values by default and +are handled by separate localization projects. + +Text Types (`type`) +------------------- +Below is the list of current text types that texts tables can handle. + +ID | Internal name | Description +-- | ---------------------- | ---------------------------------- +0 | CHAT_TYPE_SAY | Displayed as a Say (Speech Bubble). +1 | CHAT_TYPE_YELL | Displayed as a Yell (Red Speech Bubble) and usually has a matching Sound ID. +2 | CHAT_TYPE_TEXT_EMOTE | Displayed as a text emote in orange in the chat log. +3 | CHAT_TYPE_BOSS_EMOTE | Displayed as a text emote in orange in the chat log (Used only for specific Bosses). +4 | CHAT_TYPE_WHISPER | Displayed as a whisper to the player in the chat log. +5 | CHAT_TYPE_BOSS_WHISPER | Displayed as a whisper to the player in the chat log (Used only for specific Bosses). +6 | CHAT_TYPE_ZONE_YELL | Same as CHAT_TYPE_YELL but will display to all players in current zone. + +Language Types (`language`) +--------------------------- +This is the race language that the text is native to. Below is the list of +current language types that are allowed. + +ID | Internal Name | Description +--- | ------------- | -------------------------------------------------------- +0 | UNIVERSAL | Understood by *all* races. +1 | ORCISH | Understood *only* by Horde races. +2 | DARNASSIAN | Understood *only* by the Night Elf race. +3 | TAURAHE | Understood *only* by the Tauren race. +6 | DWARVISH | Understood *only* by the Dwarf race. +7 | COMMON | Understood *only* by Alliance races. +8 | DEMONIC | Understood *only* by the Demon race (Not Implemented). +9 | TITAN | This language was used by Sargeras to speak with other Titians (Not Implemented). +10 | THALASSIAN | Understood *only* by the Blood Elf race. +11 | DRACONIC | Understood *only* by the Dragon race. +12 | KALIMAG | Text will display as Kalimag (not readable by players, language of all elementals) +13 | GNOMISH | Understood *only* by the Gnome race. +14 | TROLL | Understood *only* by the Troll race. +33 | GUTTERSPEAK | Understood *only* by the Undead race. +35 | DRAENEI | Understood *only* by the Draenai Race. +36 | ZOMBIE | (not currently used?) +37 | GNOMISH BINARY| Understood *only* by Alliance when drinking Binary Brew +38 | GOBLIN BINARY | Understood *only* by Horde when drinking Binary Brew diff --git a/src/modules/SD2/include/precompiled.cpp b/src/modules/SD2/include/precompiled.cpp new file mode 100644 index 000000000..f43b4f1d3 --- /dev/null +++ b/src/modules/SD2/include/precompiled.cpp @@ -0,0 +1,26 @@ +/** + * ScriptDev2 is an extension for mangos providing enhanced features for + * area triggers, creatures, game objects, instances, items, and spells beyond + * the default database scripting in mangos. + * + * Copyright (C) 2006-2013 ScriptDev2 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * World of Warcraft, and all World of Warcraft or Warcraft art, images, + * and lore are copyrighted by Blizzard Entertainment, Inc. + */ + +#include "precompiled.h" diff --git a/src/modules/SD2/include/precompiled.h b/src/modules/SD2/include/precompiled.h new file mode 100644 index 000000000..55ddc1a60 --- /dev/null +++ b/src/modules/SD2/include/precompiled.h @@ -0,0 +1,42 @@ +/** + * ScriptDev2 is an extension for mangos providing enhanced features for + * area triggers, creatures, game objects, instances, items, and spells beyond + * the default database scripting in mangos. + * + * Copyright (C) 2006-2013 ScriptDev2 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * World of Warcraft, and all World of Warcraft or Warcraft art, images, + * and lore are copyrighted by Blizzard Entertainment, Inc. + */ + +#ifndef SC_PRECOMPILED_H +#define SC_PRECOMPILED_H + +#include "../ScriptDevMgr.h" +#include "Object.h" +#include "Unit.h" +#include "Creature.h" +#include "CreatureAI.h" +#include "GameObject.h" +#include "sc_creature.h" +#include "sc_gossip.h" +#include "sc_grid_searchers.h" +#include "sc_instance.h" +#include "SpellAuras.h" + + +#endif diff --git a/src/modules/SD2/include/sc_creature.cpp b/src/modules/SD2/include/sc_creature.cpp new file mode 100644 index 000000000..f94285658 --- /dev/null +++ b/src/modules/SD2/include/sc_creature.cpp @@ -0,0 +1,727 @@ +/** + * ScriptDev2 is an extension for mangos providing enhanced features for + * area triggers, creatures, game objects, instances, items, and spells beyond + * the default database scripting in mangos. + * + * Copyright (C) 2006-2013 ScriptDev2 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * World of Warcraft, and all World of Warcraft or Warcraft art, images, + * and lore are copyrighted by Blizzard Entertainment, Inc. + */ + +#include "precompiled.h" +#include "Item.h" +#include "Spell.h" +#include "WorldPacket.h" +#include "ObjectMgr.h" +#include "Cell.h" +#include "CellImpl.h" +#include "GridNotifiers.h" +#include "GridNotifiersImpl.h" + +// Spell summary for ScriptedAI::SelectSpell +struct TSpellSummary +{ + uint8 Targets; // set of enum SelectTarget + uint8 Effects; // set of enum SelectEffect +}* SpellSummary; + +ScriptedAI::ScriptedAI(Creature* pCreature) : CreatureAI(pCreature), + m_uiEvadeCheckCooldown(2500) +{} + +/// This function shows if combat movement is enabled, overwrite for more info +void ScriptedAI::GetAIInformation(ChatHandler& reader) +{ + reader.PSendSysMessage("ScriptedAI, combat movement is %s", reader.GetOnOffStr(IsCombatMovement())); +} + +/// Return if the creature can "see" pWho +bool ScriptedAI::IsVisible(Unit* pWho) const +{ + if (!pWho) + { + return false; + } + + return m_creature->IsWithinDist(pWho, VISIBLE_RANGE) && pWho->IsVisibleForOrDetect(m_creature, m_creature, true); +} + +/** + * This function triggers the creature attacking pWho, depending on conditions like: + * - Can the creature start an attack? + * - Is pWho hostile to the creature? + * - Can the creature reach pWho? + * - Is pWho in aggro-range? + * If the creature can attack pWho, it will if it has no victim. + * Inside dungeons, the creature will get into combat with pWho, even if it has already a victim + */ +void ScriptedAI::MoveInLineOfSight(Unit* pWho) +{ + if (m_creature->CanInitiateAttack() && pWho->IsTargetableForAttack() && + m_creature->IsHostileTo(pWho) && pWho->isInAccessablePlaceFor(m_creature)) + { + if (!m_creature->CanFly() && m_creature->GetDistanceZ(pWho) > CREATURE_Z_ATTACK_RANGE) + { + return; + } + + if (m_creature->IsWithinDistInMap(pWho, m_creature->GetAttackDistance(pWho)) && m_creature->IsWithinLOSInMap(pWho)) + { + if (!m_creature->getVictim()) + { + pWho->RemoveSpellsCausingAura(SPELL_AURA_MOD_STEALTH); + AttackStart(pWho); + } + else if (m_creature->GetMap()->IsDungeon()) + { + pWho->SetInCombatWith(m_creature); + m_creature->AddThreat(pWho); + } + } + } +} + +/** + * This function sets the TargetGuid for the creature if required + * Also it will handle the combat movement (chase movement), depending on SetCombatMovement(bool) + */ +void ScriptedAI::AttackStart(Unit* pWho) +{ + if (!m_creature->CanAttackByItself()) + return; + + if (pWho && m_creature->Attack(pWho, true)) // The Attack function also uses basic checks if pWho can be attacked + { + m_creature->AddThreat(pWho); + m_creature->SetInCombatWith(pWho); + pWho->SetInCombatWith(m_creature); + + HandleMovementOnAttackStart(pWho); + } +} + +/** + * This function only calls Aggro, which is to be used for scripting purposes + */ +void ScriptedAI::EnterCombat(Unit* pEnemy) +{ + if (pEnemy) + { + Aggro(pEnemy); + } +} + +/** + * Main update function, by default let the creature behave as expected by a mob (threat management and melee dmg) + * Always handle here threat-management with m_creature->SelectHostileTarget() + * Handle (if required) melee attack with DoMeleeAttackIfReady() + * This is usally overwritten to support timers for ie spells + */ +void ScriptedAI::UpdateAI(const uint32 /*uiDiff*/) +{ + // Check if we have a current target + if (!m_creature->SelectHostileTarget() || !m_creature->getVictim()) + { + return; + } + + DoMeleeAttackIfReady(); + + Unit* victim = m_creature->getVictim(); + + const SpellEntry* potentialSpell = m_creature->ReachWithSpellAttack(victim); + if (potentialSpell) + m_creature->CastSpell(victim, potentialSpell->Id, true); +} + +/** + * This function cleans up the combat state if the creature evades + * It will: + * - Drop Auras + * - Drop all threat + * - Stop combat + * - Move the creature home + * - Clear tagging for loot + * - call Reset() + */ +void ScriptedAI::EnterEvadeMode() +{ + m_creature->RemoveAllAurasOnEvade(); + m_creature->DeleteThreatList(); + m_creature->CombatStop(true); + + if (m_creature->IsAlive()) + { + m_creature->GetMotionMaster()->MoveTargetedHome(); + } + + m_creature->SetLootRecipient(NULL); + + Reset(); +} + +/// This function calls Reset() to reset variables as expected +void ScriptedAI::JustRespawned() +{ + Reset(); +} + +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(); + m_creature->StopMoving(); +} + +void ScriptedAI::DoStopAttack() +{ + if (m_creature->getVictim()) + { + m_creature->AttackStop(); + } +} + +void ScriptedAI::DoCast(Unit* pTarget, uint32 uiSpellId, bool bTriggered) +{ + if (m_creature->IsNonMeleeSpellCasted(false) && !bTriggered) + { + return; + } + + m_creature->CastSpell(pTarget, uiSpellId, bTriggered); +} + +void ScriptedAI::DoCastSpell(Unit* pTarget, SpellEntry const* pSpellInfo, bool bTriggered) +{ + if (m_creature->IsNonMeleeSpellCasted(false) && !bTriggered) + { + return; + } + + m_creature->CastSpell(pTarget, pSpellInfo, bTriggered); +} + +void ScriptedAI::DoPlaySoundToSet(WorldObject* pSource, uint32 uiSoundId) +{ + if (!pSource) + { + return; + } + + if (!GetSoundEntriesStore()->LookupEntry(uiSoundId)) + { + script_error_log("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); +} + +SpellEntry const* ScriptedAI::SelectSpell(Unit* pTarget, int32 uiSchool, int32 iMechanic, SelectTarget selectTargets, uint32 uiPowerCostMin, uint32 uiPowerCostMax, float fRangeMin, float fRangeMax, SelectEffect selectEffects) +{ + // No target so we can't cast + if (!pTarget) + { + return NULL; + } + + // Silenced so we can't cast + if (m_creature->HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_SILENCED)) + { + return NULL; + } + + // Using the extended script system we first create a list of viable spells + SpellEntry const* apSpell[4]; + memset(apSpell, 0, sizeof(SpellEntry*) * 4); + + uint32 uiSpellCount = 0; + + SpellEntry const* pTempSpell; + SpellRangeEntry const* pTempRange; + + // Check if each spell is viable(set it to null if not) + for (uint8 i = 0; i < 4; ++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 (iMechanic >= 0 && pTempSpell->GetMechanic() != (uint32)iMechanic) + { + continue; + } + + // Make sure that the spell uses the requested amount of power + if (uiPowerCostMin && pTempSpell->GetManaCost() < uiPowerCostMin) + { + continue; + } + + if (uiPowerCostMax && pTempSpell->GetManaCost() > uiPowerCostMax) + { + continue; + } + + // Continue if we don't have the mana to actually cast this spell + if (pTempSpell->GetManaCost() > 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 && pTempRange->maxRange < fRangeMin) + { + continue; + } + + if (fRangeMax && pTempRange->maxRange > fRangeMax) + { + continue; + } + + // Check if our target is in range + if (m_creature->IsWithinDistInMap(pTarget, pTempRange->minRange) || !m_creature->IsWithinDistInMap(pTarget, pTempRange->maxRange)) + { + 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[urand(0, uiSpellCount - 1)]; +} + +bool ScriptedAI::CanCast(Unit* pTarget, SpellEntry const* pSpellEntry, bool bTriggered) +{ + // No target so we can't cast + if (!pTarget || !pSpellEntry) + { + return false; + } + + // Silenced so we can't cast + if (!bTriggered && m_creature->HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_SILENCED)) + { + return false; + } + + // Check for power + if (!bTriggered && m_creature->GetPower((Powers)pSpellEntry->powerType) < pSpellEntry->GetManaCost()) + { + return false; + } + + SpellRangeEntry const* pTempRange = GetSpellRangeStore()->LookupEntry(pSpellEntry->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 (!m_creature->IsInRange(pTarget, pTempRange->minRange, pTempRange->maxRange)) + { + return false; + } + + return true; +} + +void FillSpellSummary() +{ + SpellSummary = new TSpellSummary[GetSpellStore()->GetNumRows()]; + + SpellEntry const* pTempSpell; + + 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 (uint8 j = 0; j < 3; ++j) + { + SpellEffectEntry const* pSpellEffect = pTempSpell->GetSpellEffect(SpellEffectIndex(j)); + if (!pSpellEffect) + continue; + + // Spell targets self + if (pTempSpell->GetEffectImplicitTargetAByIndex(SpellEffectIndex(j)) == TARGET_SELF) + { + SpellSummary[i].Targets |= 1 << (SELECT_TARGET_SELF - 1); + } + + // Spell targets a single enemy + if (pTempSpell->GetEffectImplicitTargetAByIndex(SpellEffectIndex(j)) == TARGET_CHAIN_DAMAGE || + pTempSpell->GetEffectImplicitTargetAByIndex(SpellEffectIndex(j)) == TARGET_CURRENT_ENEMY_COORDINATES) + { + SpellSummary[i].Targets |= 1 << (SELECT_TARGET_SINGLE_ENEMY - 1); + } + + // Spell targets AoE at enemy + if (pTempSpell->GetEffectImplicitTargetAByIndex(SpellEffectIndex(j)) == TARGET_ALL_ENEMY_IN_AREA || + pTempSpell->GetEffectImplicitTargetAByIndex(SpellEffectIndex(j)) == TARGET_ALL_ENEMY_IN_AREA_INSTANT || + pTempSpell->GetEffectImplicitTargetAByIndex(SpellEffectIndex(j)) == TARGET_CASTER_COORDINATES || + pTempSpell->GetEffectImplicitTargetAByIndex(SpellEffectIndex(j)) == TARGET_ALL_ENEMY_IN_AREA_CHANNELED) + { + SpellSummary[i].Targets |= 1 << (SELECT_TARGET_AOE_ENEMY - 1); + } + + // Spell targets an enemy + if (pTempSpell->GetEffectImplicitTargetAByIndex(SpellEffectIndex(j)) == TARGET_CHAIN_DAMAGE || + pTempSpell->GetEffectImplicitTargetAByIndex(SpellEffectIndex(j)) == TARGET_CURRENT_ENEMY_COORDINATES || + pTempSpell->GetEffectImplicitTargetAByIndex(SpellEffectIndex(j)) == TARGET_ALL_ENEMY_IN_AREA || + pTempSpell->GetEffectImplicitTargetAByIndex(SpellEffectIndex(j)) == TARGET_ALL_ENEMY_IN_AREA_INSTANT || + pTempSpell->GetEffectImplicitTargetAByIndex(SpellEffectIndex(j)) == TARGET_CASTER_COORDINATES || + pTempSpell->GetEffectImplicitTargetAByIndex(SpellEffectIndex(j)) == TARGET_ALL_ENEMY_IN_AREA_CHANNELED) + { + SpellSummary[i].Targets |= 1 << (SELECT_TARGET_ANY_ENEMY - 1); + } + + // Spell targets a single friend(or self) + if (pTempSpell->GetEffectImplicitTargetAByIndex(SpellEffectIndex(j)) == TARGET_SELF || + pTempSpell->GetEffectImplicitTargetAByIndex(SpellEffectIndex(j)) == TARGET_SINGLE_FRIEND || + pTempSpell->GetEffectImplicitTargetAByIndex(SpellEffectIndex(j)) == TARGET_SINGLE_PARTY) + { + SpellSummary[i].Targets |= 1 << (SELECT_TARGET_SINGLE_FRIEND - 1); + } + + // Spell targets aoe friends + if (pTempSpell->GetEffectImplicitTargetAByIndex(SpellEffectIndex(j)) == TARGET_ALL_PARTY_AROUND_CASTER || + pTempSpell->GetEffectImplicitTargetAByIndex(SpellEffectIndex(j)) == TARGET_AREAEFFECT_PARTY || + pTempSpell->GetEffectImplicitTargetAByIndex(SpellEffectIndex(j)) == TARGET_CASTER_COORDINATES) + { + SpellSummary[i].Targets |= 1 << (SELECT_TARGET_AOE_FRIEND - 1); + } + + // Spell targets any friend(or self) + if (pTempSpell->GetEffectImplicitTargetAByIndex(SpellEffectIndex(j)) == TARGET_SELF || + pTempSpell->GetEffectImplicitTargetAByIndex(SpellEffectIndex(j)) == TARGET_SINGLE_FRIEND || + pTempSpell->GetEffectImplicitTargetAByIndex(SpellEffectIndex(j)) == TARGET_SINGLE_PARTY || + pTempSpell->GetEffectImplicitTargetAByIndex(SpellEffectIndex(j)) == TARGET_ALL_PARTY_AROUND_CASTER || + pTempSpell->GetEffectImplicitTargetAByIndex(SpellEffectIndex(j)) == TARGET_AREAEFFECT_PARTY || + pTempSpell->GetEffectImplicitTargetAByIndex(SpellEffectIndex(j)) == TARGET_CASTER_COORDINATES) + { + SpellSummary[i].Targets |= 1 << (SELECT_TARGET_ANY_FRIEND - 1); + } + + // Make sure that this spell includes a damage effect + if (pSpellEffect->Effect == SPELL_EFFECT_SCHOOL_DAMAGE || + pSpellEffect->Effect == SPELL_EFFECT_INSTAKILL || + pSpellEffect->Effect == SPELL_EFFECT_ENVIRONMENTAL_DAMAGE || + pSpellEffect->Effect == 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 (pSpellEffect->Effect == SPELL_EFFECT_HEAL || + pSpellEffect->Effect == SPELL_EFFECT_HEAL_MAX_HEALTH || + pSpellEffect->Effect == SPELL_EFFECT_HEAL_MECHANICAL || + (pSpellEffect->Effect == SPELL_EFFECT_APPLY_AURA && pTempSpell->GetEffectApplyAuraNameByIndex(SpellEffectIndex(j)) == 8)) + { + SpellSummary[i].Effects |= 1 << (SELECT_EFFECT_HEALING - 1); + } + + // Make sure that this spell applies an aura + if (pSpellEffect->Effect == SPELL_EFFECT_APPLY_AURA) + { + SpellSummary[i].Effects |= 1 << (SELECT_EFFECT_AURA - 1); + } + } + } +} + +void ScriptedAI::DoResetThreat() +{ + if (!m_creature->CanHaveThreatList() || m_creature->GetThreatManager().isThreatListEmpty()) + { + script_error_log("DoResetThreat called for creature that either can not have threat list or has empty threat list (m_creature entry = %d)", m_creature->GetEntry()); + return; + } + + ThreatList const& tList = m_creature->GetThreatManager().getThreatList(); + for (ThreatList::const_iterator itr = tList.begin(); itr != tList.end(); ++itr) + { + Unit* pUnit = m_creature->GetMap()->GetUnit((*itr)->getUnitGuid()); + + if (pUnit && m_creature->GetThreatManager().getThreat(pUnit)) + { + m_creature->GetThreatManager().modifyThreatPercent(pUnit, -100); + } + } +} + +void ScriptedAI::DoTeleportPlayer(Unit* pUnit, float fX, float fY, float fZ, float fO) +{ + if (!pUnit) + { + return; + } + + if (pUnit->GetTypeId() != TYPEID_PLAYER) + { + script_error_log("%s tried to teleport non-player (%s) to x: %f y:%f z: %f o: %f. Aborted.", m_creature->GetGuidStr().c_str(), pUnit->GetGuidStr().c_str(), fX, fY, fZ, fO); + return; + } + + ((Player*)pUnit)->TeleportTo(pUnit->GetMapId(), fX, fY, fZ, fO, TELE_TO_NOT_LEAVE_COMBAT); +} + +Unit* ScriptedAI::DoSelectLowestHpFriendly(float fRange, uint32 uiMinHPDiff) +{ + Unit* pUnit = NULL; + + MaNGOS::MostHPMissingInRangeCheck u_check(m_creature, fRange, uiMinHPDiff); + MaNGOS::UnitLastSearcher searcher(pUnit, u_check); + + Cell::VisitGridObjects(m_creature, searcher, fRange); + + return pUnit; +} + +std::list ScriptedAI::DoFindFriendlyCC(float fRange) +{ + std::list pList; + + MaNGOS::FriendlyCCedInRangeCheck u_check(m_creature, fRange); + MaNGOS::CreatureListSearcher searcher(pList, u_check); + + Cell::VisitGridObjects(m_creature, searcher, fRange); + + return pList; +} + +std::list ScriptedAI::DoFindFriendlyMissingBuff(float fRange, uint32 uiSpellId) +{ + std::list pList; + + MaNGOS::FriendlyMissingBuffInRangeCheck u_check(m_creature, fRange, uiSpellId); + MaNGOS::CreatureListSearcher searcher(pList, u_check); + + Cell::VisitGridObjects(m_creature, searcher, fRange); + + return pList; +} + +Player* ScriptedAI::GetPlayerAtMinimumRange(float fMinimumRange) +{ + Player* pPlayer = NULL; + + MaNGOS::AnyPlayerInObjectRangeCheck check(m_creature, fMinimumRange); + MaNGOS::PlayerSearcher searcher(pPlayer, check); + + Cell::VisitWorldObjects(m_creature, searcher, fMinimumRange); + + return pPlayer; +} + +void ScriptedAI::SetEquipmentSlots(bool bLoadDefault, int32 iMainHand, int32 iOffHand, int32 iRanged) +{ + if (bLoadDefault) + { + m_creature->LoadEquipment(m_creature->GetCreatureInfo()->equipmentId, true); + return; + } + + if (iMainHand >= 0) + { + m_creature->SetVirtualItem(VIRTUAL_ITEM_SLOT_0, iMainHand); + } + + if (iOffHand >= 0) + { + m_creature->SetVirtualItem(VIRTUAL_ITEM_SLOT_1, iOffHand); + } + + if (iRanged >= 0) + { + m_creature->SetVirtualItem(VIRTUAL_ITEM_SLOT_2, iRanged); + } +} + +// 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. +enum +{ + NPC_BROODLORD = 12017, + NPC_VOID_REAVER = 19516, + NPC_JAN_ALAI = 23578, + NPC_SARTHARION = 28860, + NPC_TALON_KING_IKISS = 18473, + NPC_KARGATH_BLADEFIST = 16808, + NPC_ANUBARAK = 29120, + NPC_SINDRAGOSA = 36853, + NPC_ZARITHRIAN = 39746, +}; + +bool ScriptedAI::EnterEvadeIfOutOfCombatArea(const uint32 uiDiff) +{ + if (m_uiEvadeCheckCooldown < uiDiff) + { + m_uiEvadeCheckCooldown = 2500; + } + else + { + 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) + if (fZ > 448.60f) + { return false; } + break; + case NPC_VOID_REAVER: // void reaver (calculate from center of room) + if (m_creature->GetDistance2d(432.59f, 371.93f) < 105.0f) + { return false; } + break; + case NPC_JAN_ALAI: // jan'alai (calculate by Z) + if (fZ > 12.0f) + { return false; } + break; + case NPC_SARTHARION: // sartharion (calculate box) + if (fX > 3218.86f && fX < 3275.69f && fY < 572.40f && fY > 484.68f) + { return false; } + break; + case NPC_TALON_KING_IKISS: + { + float fX, fY, fZ; + m_creature->GetRespawnCoord(fX, fY, fZ); + if (m_creature->GetDistance2d(fX, fY) < 70.0f) + { return false; } + break; + } + case NPC_KARGATH_BLADEFIST: + if (fX < 255.0f && fX > 205.0f) + { return false; } + break; + case NPC_ANUBARAK: + if (fY < 281.0f && fY > 228.0f) + return false; + break; + case NPC_SINDRAGOSA: + if (fX > 4314.0f) + return false; + break; + case NPC_ZARITHRIAN: + if (fZ > 87.0f) + return false; + break; + default: + script_error_log("EnterEvadeIfOutOfCombatArea used for creature entry %u, but does not have any definition.", m_creature->GetEntry()); + return false; + } + + EnterEvadeMode(); + return true; +} + +void Scripted_NoMovementAI::GetAIInformation(ChatHandler& reader) +{ + reader.PSendSysMessage("Subclass of Scripted_NoMovementAI"); +} + +void Scripted_NoMovementAI::AttackStart(Unit* pWho) +{ + if (!m_creature->CanAttackByItself()) + return; + + if (pWho && m_creature->Attack(pWho, true)) + { + m_creature->AddThreat(pWho); + m_creature->SetInCombatWith(pWho); + pWho->SetInCombatWith(m_creature); + + DoStartNoMovement(pWho); + } +} diff --git a/src/modules/SD2/include/sc_creature.h b/src/modules/SD2/include/sc_creature.h new file mode 100644 index 000000000..3b103cc08 --- /dev/null +++ b/src/modules/SD2/include/sc_creature.h @@ -0,0 +1,255 @@ +/** + * ScriptDev2 is an extension for mangos providing enhanced features for + * area triggers, creatures, game objects, instances, items, and spells beyond + * the default database scripting in mangos. + * + * Copyright (C) 2006-2013 ScriptDev2 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * World of Warcraft, and all World of Warcraft or Warcraft art, images, + * and lore are copyrighted by Blizzard Entertainment, Inc. + */ + +#ifndef SC_CREATURE_H +#define SC_CREATURE_H + +#include "Chat.h" +#include "DBCStores.h" // Mostly only used the Lookup acces, but a few cases really do use the DBC-Stores + +// Spell targets used by SelectSpell +enum SelectTarget +{ + SELECT_TARGET_DONTCARE = 0, // All target types allowed + + SELECT_TARGET_SELF, // Only Self casting + + SELECT_TARGET_SINGLE_ENEMY, // Only Single Enemy + SELECT_TARGET_AOE_ENEMY, // Only AoE Enemy + SELECT_TARGET_ANY_ENEMY, // AoE or Single Enemy + + SELECT_TARGET_SINGLE_FRIEND, // Only Single Friend + SELECT_TARGET_AOE_FRIEND, // Only AoE Friend + SELECT_TARGET_ANY_FRIEND, // AoE or Single Friend +}; + +// Spell Effects used by SelectSpell +enum SelectEffect +{ + SELECT_EFFECT_DONTCARE = 0, // All spell effects allowed + SELECT_EFFECT_DAMAGE, // Spell does damage + SELECT_EFFECT_HEALING, // Spell does healing + SELECT_EFFECT_AURA, // Spell applies an aura +}; + +enum SCEquip +{ + EQUIP_NO_CHANGE = -1, + EQUIP_UNEQUIP = 0 +}; + +/// Documentation of CreatureAI functions can be found in MaNGOS source +// Only list them here again to ensure that the interface between SD2 and the core is not changed unnoticed +struct ScriptedAI : public CreatureAI +{ + public: + explicit ScriptedAI(Creature* pCreature); + ~ScriptedAI() {} + + // ************* + // CreatureAI Functions + // ************* + + // == Information about AI ======================== + // Get information about the AI + void GetAIInformation(ChatHandler& reader) override; + + // == Reactions At ================================= + + // Called if IsVisible(Unit* pWho) is true at each relative pWho move + void MoveInLineOfSight(Unit* pWho) override; + + // Called for reaction at enter to combat if not in combat yet (enemy can be NULL) + void EnterCombat(Unit* pEnemy) override; + + // Called at stoping attack by any attacker + void EnterEvadeMode() override; + + // Called when reached home after MoveTargetHome (in evade) + void JustReachedHome() override {} + + // Called at any Heal received + void HealedBy(Unit* /*pHealer*/, uint32& /*uiHealedAmount*/) override {} + + // Called at any Damage to any victim (before damage apply) + void DamageDeal(Unit* /*pDoneTo*/, uint32& /*uiDamage*/) override {} + + // Called at any Damage from any attacker (before damage apply) + void DamageTaken(Unit* /*pDealer*/, uint32& /*uiDamage*/) override {} + + // Called at creature death + void JustDied(Unit* /*pKiller*/) override {} + + // Called when the corpse of this creature gets removed + void CorpseRemoved(uint32& /*uiRespawnDelay*/) override {} + + // Called when a summoned creature is killed + void SummonedCreatureJustDied(Creature* /*pSummoned*/) override {} + + // Called at creature killing another unit + void KilledUnit(Unit* /*pVictim*/) override {} + + // Called when owner of m_creature (if m_creature is PROTECTOR_PET) kills a unit + void OwnerKilledUnit(Unit* /*pVictim*/) override {} + + // Called when the creature successfully summons a creature + void JustSummoned(Creature* /*pSummoned*/) override {} + + // Called when the creature successfully summons a gameobject + void JustSummoned(GameObject* /*pGo*/) override {} + + // Called when a summoned creature gets TemporarySummon::UnSummon ed + void SummonedCreatureDespawn(Creature* /*pSummoned*/) override {} + + // Called when hit by a spell + void SpellHit(Unit* /*pCaster*/, const SpellEntry* /*pSpell*/) override {} + + // Called when spell hits creature's target + void SpellHitTarget(Unit* /*pTarget*/, const SpellEntry* /*pSpell*/) override {} + + // Called when the creature is target of hostile action: swing, hostile spell landed, fear/etc) + /// This will by default result in reattacking, if the creature has no victim + void AttackedBy(Unit* pAttacker) override { CreatureAI::AttackedBy(pAttacker); } + + // Called when creature is respawned (for reseting variables) + void JustRespawned() override; + + // Called at waypoint reached or point movement finished + void MovementInform(uint32 /*uiMovementType*/, uint32 /*uiData*/) override {} + + // Called if a temporary summoned of m_creature reach a move point + void SummonedMovementInform(Creature* /*pSummoned*/, uint32 /*uiMotionType*/, uint32 /*uiData*/) override {} + + // Called at text emote receive from player + void ReceiveEmote(Player* /*pPlayer*/, uint32 /*uiEmote*/) override {} + + // Called at each attack of m_creature by any victim + void AttackStart(Unit* pWho) override; + + // Called at World update tick + void UpdateAI(const uint32) override; + + // == State checks ================================= + + // Check if unit is visible for MoveInLineOfSight + bool IsVisible(Unit* pWho) const override; + + // Called when victim entered water and creature can not enter water + bool canReachByRangeAttack(Unit* pWho) override { return CreatureAI::canReachByRangeAttack(pWho); } + + // ************* + // Variables + // ************* + + // ************* + // Pure virtual functions + // ************* + + /** + * This is a SD2 internal function, that every AI must implement + * Usally used to reset combat variables + * Called by default on creature evade and respawn + * In most scripts also called in the constructor of the AI + */ + virtual void Reset() = 0; + + /// Called at creature EnterCombat with an enemy + /** + * This is a SD2 internal function + * Called by default on creature EnterCombat with an enemy + */ + virtual void Aggro(Unit* /*pWho*/) {} + + // ************* + // 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 Id + void DoCast(Unit* pTarget, uint32 uiSpellId, bool bTriggered = false); + + // 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 uiSoundId); + + // Drops all threat to 0%. Does not remove enemies from the threat list + void DoResetThreat(); + + // Teleports a player without dropping threat (only teleports to same map) + void DoTeleportPlayer(Unit* pUnit, 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 DoFindFriendlyCC(float fRange); + + // Returns a list of all friendly units missing a specific buff within range + std::list 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); + + // Returns spells that meet the specified criteria from the creatures spell list + SpellEntry const* SelectSpell(Unit* pTarget, int32 uiSchool, int32 iMechanic, SelectTarget selectTargets, uint32 uiPowerCostMin, uint32 uiPowerCostMax, float fRangeMin, float fRangeMax, SelectEffect selectEffect); + + // Checks if you can cast the specified spell + bool CanCast(Unit* pTarget, SpellEntry const* pSpell, bool bTriggered = false); + + void SetEquipmentSlots(bool bLoadDefault, int32 iMainHand = EQUIP_NO_CHANGE, int32 iOffHand = EQUIP_NO_CHANGE, int32 iRanged = EQUIP_NO_CHANGE); + + bool EnterEvadeIfOutOfCombatArea(const uint32 uiDiff); + + private: + uint32 m_uiEvadeCheckCooldown; +}; + +struct Scripted_NoMovementAI : public ScriptedAI +{ + Scripted_NoMovementAI(Creature* pCreature) : ScriptedAI(pCreature) + { + SetCombatMovement(false); + } + + void GetAIInformation(ChatHandler& reader) override; + + // Called at each attack of m_creature by any victim + void AttackStart(Unit* pWho) override; +}; + +#endif diff --git a/src/modules/SD2/include/sc_gossip.h b/src/modules/SD2/include/sc_gossip.h new file mode 100644 index 000000000..6cc38d650 --- /dev/null +++ b/src/modules/SD2/include/sc_gossip.h @@ -0,0 +1,180 @@ +/** + * ScriptDev2 is an extension for mangos providing enhanced features for + * area triggers, creatures, game objects, instances, items, and spells beyond + * the default database scripting in mangos. + * + * Copyright (C) 2006-2013 ScriptDev2 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * World of Warcraft, and all World of Warcraft or Warcraft art, images, + * and lore are copyrighted by Blizzard Entertainment, Inc. + */ + +#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" +#define GOSSIP_TEXT_WINDRIDER "The wind rider master" +#define GOSSIP_TEXT_GRYPHON "The gryphon master" +#define GOSSIP_TEXT_BATHANDLER "The bat handler" +#define GOSSIP_TEXT_HIPPOGRYPH "The hippogryph master" +#define GOSSIP_TEXT_ZEPPLINMASTER "The zeppelin master" +#define GOSSIP_TEXT_DEEPRUNTRAM "The Deeprun Tram" +#define GOSSIP_TEXT_FERRY "The Rut'theran Ferry" +#define GOSSIP_TEXT_FLIGHTMASTER "The flight master" +#define GOSSIP_TEXT_AUCTIONHOUSE "The auction house" +#define GOSSIP_TEXT_GUILDMASTER "The guild master" +#define GOSSIP_TEXT_INN "The inn" +#define GOSSIP_TEXT_MAILBOX "The mailbox" +#define GOSSIP_TEXT_STABLEMASTER "The stable master" +#define GOSSIP_TEXT_WEAPONMASTER "The weapon master" +#define GOSSIP_TEXT_OFFICERS "The officers' lounge" +#define GOSSIP_TEXT_BATTLEMASTER "The battlemaster" +#define GOSSIP_TEXT_BARBER "Barber" +#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" +#define GOSSIP_TEXT_PRIEST "Priest" +#define GOSSIP_TEXT_ROGUE "Rogue" +#define GOSSIP_TEXT_WARRIOR "Warrior" +#define GOSSIP_TEXT_PALADIN "Paladin" +#define GOSSIP_TEXT_SHAMAN "Shaman" +#define GOSSIP_TEXT_MAGE "Mage" +#define GOSSIP_TEXT_WARLOCK "Warlock" + +#define GOSSIP_TEXT_ALCHEMY "Alchemy" +#define GOSSIP_TEXT_BLACKSMITHING "Blacksmithing" +#define GOSSIP_TEXT_COOKING "Cooking" +#define GOSSIP_TEXT_ENCHANTING "Enchanting" +#define GOSSIP_TEXT_ENGINEERING "Engineering" +#define GOSSIP_TEXT_FIRSTAID "First Aid" +#define GOSSIP_TEXT_HERBALISM "Herbalism" +#define GOSSIP_TEXT_LEATHERWORKING "Leatherworking" +#define GOSSIP_TEXT_TAILORING "Tailoring" +#define GOSSIP_TEXT_MINING "Mining" +#define GOSSIP_TEXT_FISHING "Fishing" +#define GOSSIP_TEXT_SKINNING "Skinning" +#define GOSSIP_TEXT_JEWELCRAFTING "Jewelcrafting" +#define GOSSIP_TEXT_INSCRIPTION "Inscription" + +enum +{ + // Skill defines + TRADESKILL_ALCHEMY = 1, + TRADESKILL_BLACKSMITHING = 2, + TRADESKILL_COOKING = 3, + TRADESKILL_ENCHANTING = 4, + TRADESKILL_ENGINEERING = 5, + TRADESKILL_FIRSTAID = 6, + TRADESKILL_HERBALISM = 7, + TRADESKILL_LEATHERWORKING = 8, + TRADESKILL_POISONS = 9, + TRADESKILL_TAILORING = 10, + TRADESKILL_MINING = 11, + TRADESKILL_FISHING = 12, + TRADESKILL_SKINNING = 13, + TRADESKILL_JEWLCRAFTING = 14, + TRADESKILL_INSCRIPTION = 15, + + TRADESKILL_LEVEL_NONE = 0, + TRADESKILL_LEVEL_APPRENTICE = 1, + TRADESKILL_LEVEL_JOURNEYMAN = 2, + TRADESKILL_LEVEL_EXPERT = 3, + TRADESKILL_LEVEL_ARTISAN = 4, + TRADESKILL_LEVEL_MASTER = 5, + TRADESKILL_LEVEL_GRAND_MASTER = 6, + + // Gossip defines + GOSSIP_ACTION_TRADE = 1, + GOSSIP_ACTION_TRAIN = 2, + GOSSIP_ACTION_TAXI = 3, + GOSSIP_ACTION_GUILD = 4, + GOSSIP_ACTION_BATTLE = 5, + GOSSIP_ACTION_BANK = 6, + GOSSIP_ACTION_INN = 7, + GOSSIP_ACTION_HEAL = 8, + GOSSIP_ACTION_TABARD = 9, + GOSSIP_ACTION_AUCTION = 10, + 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, + GOSSIP_SENDER_SEC_PROFTRAIN = 4, + GOSSIP_SENDER_SEC_CLASSTRAIN = 5, + GOSSIP_SENDER_SEC_BATTLEINFO = 6, + GOSSIP_SENDER_SEC_BANK = 7, + GOSSIP_SENDER_SEC_INN = 8, + GOSSIP_SENDER_SEC_MAILBOX = 9, + GOSSIP_SENDER_SEC_STABLEMASTER = 10 +}; + +extern uint32 GetSkillLevel(Player* pPlayer, uint32 uiSkill); + +// Defined fuctions to use with player. + +// This fuction add's a menu item, +// Icon Id +// Text +// Sender(this is to identify the current Menu with this item) +// Option id (identifies this Menu Item) +// Text to be displayed in pop up box +// Money value in pop up box +// Coded +#define ADD_GOSSIP_ITEM(uiIcon, chrText, uiSender, uiOptionId) PlayerTalkClass->GetGossipMenu().AddMenuItem(uiIcon, chrText, uiSender, uiOptionId, "", 0) +#define ADD_GOSSIP_ITEM_ID(uiIcon, iTextId, uiSender, uiOptionId) PlayerTalkClass->GetGossipMenu().AddMenuItem(uiIcon, iTextId, uiSender, uiOptionId, 0, 0) +#define ADD_GOSSIP_ITEM_EXTENDED(uiIcon, chrText, uiSender, uiOptionId, chrBoxMessage, uiBoxMoney, bCode) PlayerTalkClass->GetGossipMenu().AddMenuItem(uiIcon, chrText, uiSender, uiOptionId, chrBoxMessage, uiBoxMoney, bCode) + +// This fuction Sends the current menu to show to client +// uiTextId - NPCTEXTID (uint32) +// guid - npc guid (ObjectGuid) +#define SEND_GOSSIP_MENU(uiTextId, guid) PlayerTalkClass->SendGossipMenu(uiTextId, guid) + +// Closes the Menu +#define CLOSE_GOSSIP_MENU() PlayerTalkClass->CloseGossip() + +// Fuctions to send NPC lists +// a - is always the npc guid (ObjectGuid) +#define SEND_VENDORLIST(a) GetSession()->SendListInventory(a) +#define SEND_TRAINERLIST(a) GetSession()->SendTrainerList(a) +#define SEND_BANKERLIST(a) GetSession()->SendShowBank(a) +#define SEND_TABARDLIST(a) GetSession()->SendTabardVendorActivate(a) +#define SEND_TAXILIST(a) GetSession()->SendTaxiStatus(a) + +#endif diff --git a/src/modules/SD2/include/sc_grid_searchers.cpp b/src/modules/SD2/include/sc_grid_searchers.cpp new file mode 100644 index 000000000..6ee04747d --- /dev/null +++ b/src/modules/SD2/include/sc_grid_searchers.cpp @@ -0,0 +1,73 @@ +/** + * ScriptDev2 is an extension for mangos providing enhanced features for + * area triggers, creatures, game objects, instances, items, and spells beyond + * the default database scripting in mangos. + * + * Copyright (C) 2006-2013 ScriptDev2 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * World of Warcraft, and all World of Warcraft or Warcraft art, images, + * and lore are copyrighted by Blizzard Entertainment, Inc. + */ + +#include "precompiled.h" + +#include "Cell.h" +#include "CellImpl.h" +#include "GridNotifiers.h" +#include "GridNotifiersImpl.h" + +// return closest GO in grid, with range from pSource +GameObject* GetClosestGameObjectWithEntry(WorldObject* pSource, uint32 uiEntry, float fMaxSearchRange) +{ + GameObject* pGo = NULL; + + MaNGOS::NearestGameObjectEntryInObjectRangeCheck go_check(*pSource, uiEntry, fMaxSearchRange); + MaNGOS::GameObjectLastSearcher searcher(pGo, go_check); + + Cell::VisitGridObjects(pSource, searcher, fMaxSearchRange); + + return pGo; +} + +// return closest creature alive in grid, with range from pSource +Creature* GetClosestCreatureWithEntry(WorldObject* pSource, uint32 uiEntry, float fMaxSearchRange, bool bOnlyAlive/*=true*/, bool bOnlyDead/*=false*/) +{ + Creature* pCreature = NULL; + + MaNGOS::NearestCreatureEntryWithLiveStateInObjectRangeCheck creature_check(*pSource, uiEntry, bOnlyAlive, bOnlyDead, fMaxSearchRange); + MaNGOS::CreatureLastSearcher searcher(pCreature, creature_check); + + Cell::VisitGridObjects(pSource, searcher, fMaxSearchRange); + + return pCreature; +} + +void GetGameObjectListWithEntryInGrid(std::list& lList , WorldObject* pSource, uint32 uiEntry, float fMaxSearchRange) +{ + MaNGOS::GameObjectEntryInPosRangeCheck check(*pSource, uiEntry, pSource->GetPositionX(), pSource->GetPositionY(), pSource->GetPositionZ(), fMaxSearchRange); + MaNGOS::GameObjectListSearcher searcher(lList, check); + + Cell::VisitGridObjects(pSource, searcher, fMaxSearchRange); +} + +void GetCreatureListWithEntryInGrid(std::list& lList, WorldObject* pSource, uint32 uiEntry, float fMaxSearchRange) +{ + MaNGOS::AllCreaturesOfEntryInRangeCheck check(pSource, uiEntry, fMaxSearchRange); + MaNGOS::CreatureListSearcher searcher(lList, check); + + Cell::VisitGridObjects(pSource, searcher, fMaxSearchRange); +} diff --git a/src/modules/SD2/include/sc_grid_searchers.h b/src/modules/SD2/include/sc_grid_searchers.h new file mode 100644 index 000000000..56a7426ca --- /dev/null +++ b/src/modules/SD2/include/sc_grid_searchers.h @@ -0,0 +1,63 @@ +/** + * ScriptDev2 is an extension for mangos providing enhanced features for + * area triggers, creatures, game objects, instances, items, and spells beyond + * the default database scripting in mangos. + * + * Copyright (C) 2006-2013 ScriptDev2 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * World of Warcraft, and all World of Warcraft or Warcraft art, images, + * and lore are copyrighted by Blizzard Entertainment, Inc. + */ + +#ifndef SC_GRIDSEARCH_H +#define SC_GRIDSEARCH_H + +#include "Object.h" +class GameObject; +class Creature; + +struct ObjectDistanceOrder : public std::binary_function +{ + 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 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); + } +}; + +GameObject* GetClosestGameObjectWithEntry(WorldObject* pSource, uint32 uiEntry, float fMaxSearchRange); +Creature* GetClosestCreatureWithEntry(WorldObject* pSource, uint32 uiEntry, float fMaxSearchRange, bool bOnlyAlive = true, bool bOnlyDead = false); + +void GetGameObjectListWithEntryInGrid(std::list& lList , WorldObject* pSource, uint32 uiEntry, float fMaxSearchRange); +void GetCreatureListWithEntryInGrid(std::list& lList, WorldObject* pSource, uint32 uiEntry, float fMaxSearchRange); + +#endif diff --git a/src/modules/SD2/include/sc_instance.cpp b/src/modules/SD2/include/sc_instance.cpp new file mode 100644 index 000000000..bbde4e946 --- /dev/null +++ b/src/modules/SD2/include/sc_instance.cpp @@ -0,0 +1,420 @@ +/** + * ScriptDev2 is an extension for mangos providing enhanced features for + * area triggers, creatures, game objects, instances, items, and spells beyond + * the default database scripting in mangos. + * + * Copyright (C) 2006-2013 ScriptDev2 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * World of Warcraft, and all World of Warcraft or Warcraft art, images, + * and lore are copyrighted by Blizzard Entertainment, Inc. + */ + +#include "precompiled.h" + +/** + Function that uses a door or a button + + @param guid The ObjectGuid of the Door/ Button that will be used + @param uiWithRestoreTime (in seconds) if == 0 autoCloseTime will be used (if not 0 by default in *_template) + @param bUseAlternativeState Use to alternative state + */ +void ScriptedInstance::DoUseDoorOrButton(ObjectGuid guid, uint32 uiWithRestoreTime, bool bUseAlternativeState) +{ + if (!guid) + { + return; + } + + if (GameObject* pGo = instance->GetGameObject(guid)) + { + if (pGo->GetGoType() == GAMEOBJECT_TYPE_DOOR || pGo->GetGoType() == GAMEOBJECT_TYPE_BUTTON || pGo->GetGoType() == GAMEOBJECT_TYPE_TRAPDOOR) + { + if (pGo->getLootState() == GO_READY) + { + pGo->UseDoorOrButton(uiWithRestoreTime, bUseAlternativeState); + } + else if (pGo->getLootState() == GO_ACTIVATED) + { + pGo->ResetDoorOrButton(); + } + } + else + { + script_error_log("Script call DoUseDoorOrButton, but gameobject entry %u is type %u.", pGo->GetEntry(), pGo->GetGoType()); + } + } +} + +/// Function that uses a door or button that is stored in m_mGoEntryGuidStore +void ScriptedInstance::DoUseDoorOrButton(uint32 uiEntry, uint32 uiWithRestoreTime /*= 0*/, bool bUseAlternativeState /*= false*/) +{ + EntryGuidMap::iterator find = m_mGoEntryGuidStore.find(uiEntry); + if (find != m_mGoEntryGuidStore.end()) + { + DoUseDoorOrButton(find->second, uiWithRestoreTime, bUseAlternativeState); + } + else + // Output log, possible reason is not added GO to storage, or not yet loaded + { + debug_log("SD2: Script call DoUseDoorOrButton(by Entry), but no gameobject of entry %u was created yet, or it was not stored by script for map %u.", uiEntry, instance->GetId()); + } +} + +/** + Function that respawns a despawned GameObject with given time + + @param guid The ObjectGuid of the GO that will be respawned + @param uiTimeToDespawn (in seconds) Despawn the GO after this time, default is a minute + */ +void ScriptedInstance::DoRespawnGameObject(ObjectGuid guid, uint32 uiTimeToDespawn) +{ + if (!guid) + { + return; + } + + if (GameObject* pGo = instance->GetGameObject(guid)) + { + // not expect any of these should ever be handled + if (pGo->GetGoType() == GAMEOBJECT_TYPE_FISHINGNODE || pGo->GetGoType() == GAMEOBJECT_TYPE_DOOR || + pGo->GetGoType() == GAMEOBJECT_TYPE_BUTTON) + { + return; + } + + if (pGo->isSpawned()) + { + return; + } + + pGo->SetRespawnTime(uiTimeToDespawn); + pGo->Refresh(); + } +} + +/// Function that uses a door or button that is stored in m_mGoEntryGuidStore +void ScriptedInstance::DoToggleGameObjectFlags(uint32 uiEntry, uint32 uiGOflags, bool bApply) +{ + EntryGuidMap::iterator find = m_mGoEntryGuidStore.find(uiEntry); + if (find != m_mGoEntryGuidStore.end()) + { + DoToggleGameObjectFlags(find->second, uiGOflags, bApply); + } + else + // Output log, possible reason is not added GO to storage, or not yet loaded + { + debug_log("SD2: Script call ToogleTameObjectFlags (by Entry), but no gameobject of entry %u was created yet, or it was not stored by script for map %u.", uiEntry, instance->GetId()); + } +} + +/** + Function that toggles the GO-flags of a GameObject + + @param guid The ObjectGuid of the GO that will be respawned + @param uiGOflags Which GO-flags to toggle + @param bApply should the GO-flags be applied or removed? + */ +void ScriptedInstance::DoToggleGameObjectFlags(ObjectGuid guid, uint32 uiGOflags, bool bApply) +{ + if (!guid) + { + return; + } + + if (GameObject* pGo = instance->GetGameObject(guid)) + { + if (bApply) + { + pGo->SetFlag(GAMEOBJECT_FLAGS, uiGOflags); + } + else + { + pGo->RemoveFlag(GAMEOBJECT_FLAGS, uiGOflags); + } + } +} + +/// Function that respawns a despawned GO that is stored in m_mGoEntryGuidStore +void ScriptedInstance::DoRespawnGameObject(uint32 uiEntry, uint32 uiTimeToDespawn) +{ + EntryGuidMap::iterator find = m_mGoEntryGuidStore.find(uiEntry); + if (find != m_mGoEntryGuidStore.end()) + { + DoRespawnGameObject(find->second, uiTimeToDespawn); + } + else + // Output log, possible reason is not added GO to storage, or not yet loaded; + { + debug_log("SD2: Script call DoRespawnGameObject(by Entry), but no gameobject of entry %u was created yet, or it was not stored by script for map %u.", uiEntry, instance->GetId()); + } +} + +/** + Helper function to update a world state for all players in the map + + @param uiStateId The WorldState that will be set for all players in the map + @param uiStateData The Value to which the State will be set to + */ +void ScriptedInstance::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) + { + if (Player* pPlayer = itr->getSource()) + { + pPlayer->SendUpdateWorldState(uiStateId, uiStateData); + } + } + } + else + { + debug_log("SD2: DoUpdateWorldState attempt send data but no players in map."); + } +} + +/// Get the first found Player* (with requested properties) in the map. Can return NULL. +Player* ScriptedInstance::GetPlayerInMap(bool bOnlyAlive /*=false*/, bool bCanBeGamemaster /*=true*/) +{ + Map::PlayerList const& lPlayers = instance->GetPlayers(); + + for (Map::PlayerList::const_iterator itr = lPlayers.begin(); itr != lPlayers.end(); ++itr) + { + Player* pPlayer = itr->getSource(); + if (pPlayer && (!bOnlyAlive || pPlayer->IsAlive()) && (bCanBeGamemaster || !pPlayer->isGameMaster())) + { + return pPlayer; + } + } + + return NULL; +} + +/// Returns a pointer to a loaded GameObject that was stored in m_mGoEntryGuidStore. Can return NULL +GameObject* ScriptedInstance::GetSingleGameObjectFromStorage(uint32 uiEntry) +{ + EntryGuidMap::iterator find = m_mGoEntryGuidStore.find(uiEntry); + if (find != m_mGoEntryGuidStore.end()) + { + return instance->GetGameObject(find->second); + } + + // Output log, possible reason is not added GO to map, or not yet loaded; + script_error_log("Script requested gameobject with entry %u, but no gameobject of this entry was created yet, or it was not stored by script for map %u.", uiEntry, instance->GetId()); + + return NULL; +} + +/// Returns a pointer to a loaded Creature that was stored in m_mGoEntryGuidStore. Can return NULL +Creature* ScriptedInstance::GetSingleCreatureFromStorage(uint32 uiEntry, bool bSkipDebugLog /*=false*/) +{ + EntryGuidMap::iterator find = m_mNpcEntryGuidStore.find(uiEntry); + if (find != m_mNpcEntryGuidStore.end()) + { + return instance->GetCreature(find->second); + } + + // Output log, possible reason is not added GO to map, or not yet loaded; + if (!bSkipDebugLog) + { + script_error_log("Script requested creature with entry %u, but no npc of this entry was created yet, or it was not stored by script for map %u.", uiEntry, instance->GetId()); + } + + return NULL; +} + +/** + Helper function to start a timed achievement criteria for players in the map + + @param criteriaType The Type that is required to complete the criteria, see enum AchievementCriteriaTypes in MaNGOS + @param uiTimedCriteriaMiscId The ID that identifies how the criteria is started + */ +void ScriptedInstance::DoStartTimedAchievement(AchievementCriteriaTypes criteriaType, uint32 uiTimedCriteriaMiscId) +{ + Map::PlayerList const& lPlayers = instance->GetPlayers(); + + if (!lPlayers.isEmpty()) + { + for (Map::PlayerList::const_iterator itr = lPlayers.begin(); itr != lPlayers.end(); ++itr) + { + if (Player* pPlayer = itr->getSource()) + pPlayer->StartTimedAchievementCriteria(criteriaType, uiTimedCriteriaMiscId); + } + } + else + debug_log("SD2: DoStartTimedAchievement attempt start achievements but no players in map."); +} + +/** + Constructor for DialogueHelper + + @param pDialogueArray The static const array of DialogueEntry holding the information about the dialogue. This array MUST be terminated by {0,0,0} + */ +DialogueHelper::DialogueHelper(DialogueEntry const* pDialogueArray) : + m_pInstance(NULL), + m_pDialogueArray(pDialogueArray), + m_pCurrentEntry(NULL), + m_pDialogueTwoSideArray(NULL), + m_pCurrentEntryTwoSide(NULL), + m_uiTimer(0), + m_bIsFirstSide(true), + m_bCanSimulate(false) +{} + +/** + Constructor for DialogueHelper (Two Sides) + + @param pDialogueTwoSideArray The static const array of DialogueEntryTwoSide holding the information about the dialogue. This array MUST be terminated by {0,0,0,0,0} + */ +DialogueHelper::DialogueHelper(DialogueEntryTwoSide const* pDialogueTwoSideArray) : + m_pInstance(NULL), + m_pDialogueArray(NULL), + m_pCurrentEntry(NULL), + m_pDialogueTwoSideArray(pDialogueTwoSideArray), + m_pCurrentEntryTwoSide(NULL), + m_uiTimer(0), + m_bIsFirstSide(true), + m_bCanSimulate(false) +{} + +/** + Function to start a (part of a) dialogue + + @param iTextEntry The TextEntry of the dialogue that will be started (must be always the entry of first side) + */ +void DialogueHelper::StartNextDialogueText(int32 iTextEntry) +{ + // Find iTextEntry + bool bFound = false; + + if (m_pDialogueArray) // One Side + { + for (DialogueEntry const* pEntry = m_pDialogueArray; pEntry->iTextEntry; ++pEntry) + { + if (pEntry->iTextEntry == iTextEntry) + { + m_pCurrentEntry = pEntry; + bFound = true; + break; + } + } + } + else // Two Sides + { + for (DialogueEntryTwoSide const* pEntry = m_pDialogueTwoSideArray; pEntry->iTextEntry; ++pEntry) + { + if (pEntry->iTextEntry == iTextEntry) + { + m_pCurrentEntryTwoSide = pEntry; + bFound = true; + break; + } + } + } + + if (!bFound) + { + script_error_log("Script call DialogueHelper::StartNextDialogueText, but textEntry %i is not in provided dialogue (on map id %u)", iTextEntry, m_pInstance ? m_pInstance->instance->GetId() : 0); + return; + } + + DoNextDialogueStep(); +} + +/// Internal helper function to do the actual say of a DialogueEntry +void DialogueHelper::DoNextDialogueStep() +{ + // Last Dialogue Entry done? + if ((m_pCurrentEntry && !m_pCurrentEntry->iTextEntry) || (m_pCurrentEntryTwoSide && !m_pCurrentEntryTwoSide->iTextEntry)) + { + m_uiTimer = 0; + return; + } + + // Get Text, SpeakerEntry and Timer + int32 iTextEntry = 0; + uint32 uiSpeakerEntry = 0; + + if (m_pDialogueArray) // One Side + { + uiSpeakerEntry = m_pCurrentEntry->uiSayerEntry; + iTextEntry = m_pCurrentEntry->iTextEntry; + + m_uiTimer = m_pCurrentEntry->uiTimer; + } + else // Two Sides + { + // Second Entries can be 0, if they are the entry from first side will be taken + uiSpeakerEntry = !m_bIsFirstSide && m_pCurrentEntryTwoSide->uiSayerEntryAlt ? m_pCurrentEntryTwoSide->uiSayerEntryAlt : m_pCurrentEntryTwoSide->uiSayerEntry; + iTextEntry = !m_bIsFirstSide && m_pCurrentEntryTwoSide->iTextEntryAlt ? m_pCurrentEntryTwoSide->iTextEntryAlt : m_pCurrentEntryTwoSide->iTextEntry; + + m_uiTimer = m_pCurrentEntryTwoSide->uiTimer; + } + + // Simulate Case + if (uiSpeakerEntry && iTextEntry < 0) + { + // Use Speaker if directly provided + Creature* pSpeaker = GetSpeakerByEntry(uiSpeakerEntry); + if (m_pInstance && !pSpeaker) // Get Speaker from instance + { + if (m_bCanSimulate) // Simulate case + { + m_pInstance->DoOrSimulateScriptTextForThisInstance(iTextEntry, uiSpeakerEntry); + } + else + { + pSpeaker = m_pInstance->GetSingleCreatureFromStorage(uiSpeakerEntry); + } + } + + if (pSpeaker) + { + DoScriptText(iTextEntry, pSpeaker); + } + } + + JustDidDialogueStep(m_pDialogueArray ? m_pCurrentEntry->iTextEntry : m_pCurrentEntryTwoSide->iTextEntry); + + // Increment position + if (m_pDialogueArray) + { + ++m_pCurrentEntry; + } + else + { + ++m_pCurrentEntryTwoSide; + } +} + +/// Call this function within any DialogueUpdate method. This is required for saying next steps in a dialogue +void DialogueHelper::DialogueUpdate(uint32 uiDiff) +{ + if (m_uiTimer) + { + if (m_uiTimer <= uiDiff) + { + DoNextDialogueStep(); + } + else + { + m_uiTimer -= uiDiff; + } + } +} diff --git a/src/modules/SD2/include/sc_instance.h b/src/modules/SD2/include/sc_instance.h new file mode 100644 index 000000000..ff50e5edc --- /dev/null +++ b/src/modules/SD2/include/sc_instance.h @@ -0,0 +1,156 @@ +/** + * ScriptDev2 is an extension for mangos providing enhanced features for + * area triggers, creatures, game objects, instances, items, and spells beyond + * the default database scripting in mangos. + * + * Copyright (C) 2006-2013 ScriptDev2 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * World of Warcraft, and all World of Warcraft or Warcraft art, images, + * and lore are copyrighted by Blizzard Entertainment, Inc. + */ + +#ifndef SC_INSTANCE_H +#define SC_INSTANCE_H + +#include "InstanceData.h" +#include "Map.h" + +enum EncounterState +{ + NOT_STARTED = 0, + IN_PROGRESS = 1, + FAIL = 2, + DONE = 3, + SPECIAL = 4 +}; + +#define OUT_SAVE_INST_DATA debug_log("SD2: Saving Instance Data for Instance %s (Map %d, Instance Id %d)", instance->GetMapName(), instance->GetId(), instance->GetInstanceId()) +#define OUT_SAVE_INST_DATA_COMPLETE debug_log("SD2: Saving Instance Data for Instance %s (Map %d, Instance Id %d) completed.", instance->GetMapName(), instance->GetId(), instance->GetInstanceId()) +#define OUT_LOAD_INST_DATA(a) debug_log("SD2: Loading Instance Data for Instance %s (Map %d, Instance Id %d). Input is '%s'", instance->GetMapName(), instance->GetId(), instance->GetInstanceId(), a) +#define OUT_LOAD_INST_DATA_COMPLETE debug_log("SD2: Instance Data for Instance %s (Map %d, Instance Id: %d) is loaded.", instance->GetMapName(), instance->GetId(), instance->GetInstanceId()) +#define OUT_LOAD_INST_DATA_FAIL script_error_log("Unable to load Instance Data for Instance %s (Map %d, Instance Id: %d).", instance->GetMapName(), instance->GetId(), instance->GetInstanceId()) + +class ScriptedInstance : public InstanceData +{ + public: + ScriptedInstance(Map* pMap) : InstanceData(pMap) {} + ~ScriptedInstance() {} + + // Default accessor functions + GameObject* GetSingleGameObjectFromStorage(uint32 uiEntry); + Creature* GetSingleCreatureFromStorage(uint32 uiEntry, bool bSkipDebugLog = false); + + // Change active state of doors or buttons + void DoUseDoorOrButton(ObjectGuid guid, uint32 uiWithRestoreTime = 0, bool bUseAlternativeState = false); + void DoUseDoorOrButton(uint32 uiEntry, uint32 uiWithRestoreTime = 0, bool bUseAlternativeState = false); + + // Respawns a GO having negative spawntimesecs in gameobject-table + void DoRespawnGameObject(ObjectGuid guid, uint32 uiTimeToDespawn = MINUTE); + void DoRespawnGameObject(uint32 uiEntry, uint32 uiTimeToDespawn = MINUTE); + + // Toggle the flags of a GO + void DoToggleGameObjectFlags(ObjectGuid guid, uint32 uiGOflags, bool bApply); + void DoToggleGameObjectFlags(uint32 uiEntry, uint32 uiGOflags, bool bApply); + + // Sends world state update to all players in instance + void DoUpdateWorldState(uint32 uiStateId, uint32 uiStateData); + + // Get a Player from map + Player* GetPlayerInMap(bool bOnlyAlive = false, bool bCanBeGamemaster = true); + + /// Wrapper for simulating map-wide text in this instance. It is expected that the Creature is stored in m_mNpcEntryGuidStore if loaded. + void DoOrSimulateScriptTextForThisInstance(int32 iTextEntry, uint32 uiCreatureEntry) + { + // Prevent debug output in GetSingleCreatureFromStorage + DoOrSimulateScriptTextForMap(iTextEntry, uiCreatureEntry, instance, GetSingleCreatureFromStorage(uiCreatureEntry, true)); + } + + // Starts a timed achievement criteria for all players in instance + void DoStartTimedAchievement(AchievementCriteriaTypes criteriaType, uint32 uiTimedCriteriaMiscId); + + protected: + // Storage for GO-Guids and NPC-Guids + typedef std::map EntryGuidMap; + EntryGuidMap m_mGoEntryGuidStore; ///< Store unique GO-Guids by entry + EntryGuidMap m_mNpcEntryGuidStore; ///< Store unique NPC-Guids by entry +}; + +// Class for world maps (May need additional zone-wide functions later on) +class ScriptedMap : public ScriptedInstance +{ + public: + ScriptedMap(Map* pMap) : ScriptedInstance(pMap) {} +}; + +/// A static const array of this structure must be handled to DialogueHelper +struct DialogueEntry +{ + int32 iTextEntry; ///< To be said text entry + uint32 uiSayerEntry; ///< Entry of the mob who should say + uint32 uiTimer; ///< Time delay until next text of array is said (0 stops) +}; + +/// A static const array of this structure must be handled to DialogueHelper +struct DialogueEntryTwoSide +{ + int32 iTextEntry; ///< To be said text entry (first side) + uint32 uiSayerEntry; ///< Entry of the mob who should say (first side) + int32 iTextEntryAlt; ///< To be said text entry (second side) + uint32 uiSayerEntryAlt; ///< Entry of the mob who should say (second side) + uint32 uiTimer; ///< Time delay until next text of array is said (0 stops) +}; + +/// Helper class handling a dialogue given as static const array of DialogueEntry or DialogueEntryTwoSide +class DialogueHelper +{ + public: + // The array MUST be terminated by {0,0,0} + DialogueHelper(DialogueEntry const* pDialogueArray); + // The array MUST be terminated by {0,0,0,0,0} + DialogueHelper(DialogueEntryTwoSide const* aDialogueTwoSide); + + /// Function to initialize the dialogue helper for instances. If not used with instances, GetSpeakerByEntry MUST be overwritten to obtain the speakers + void InitializeDialogueHelper(ScriptedInstance* pInstance, bool bCanSimulateText = false) { m_pInstance = pInstance; m_bCanSimulate = bCanSimulateText; } + /// Set if take first entries or second entries + void SetDialogueSide(bool bIsFirstSide) { m_bIsFirstSide = bIsFirstSide; } + + void StartNextDialogueText(int32 iTextEntry); + + void DialogueUpdate(uint32 uiDiff); + + protected: + /// Will be called when a dialogue step was done + virtual void JustDidDialogueStep(int32 /*iEntry*/) {} + /// Will be called to get a speaker, MUST be implemented if not used in instances + virtual Creature* GetSpeakerByEntry(uint32 /*uiEntry*/) { return NULL; } + + private: + void DoNextDialogueStep(); + + ScriptedInstance* m_pInstance; + + DialogueEntry const* m_pDialogueArray; + DialogueEntry const* m_pCurrentEntry; + DialogueEntryTwoSide const* m_pDialogueTwoSideArray; + DialogueEntryTwoSide const* m_pCurrentEntryTwoSide; + + uint32 m_uiTimer; + bool m_bIsFirstSide; + bool m_bCanSimulate; +}; + +#endif diff --git a/src/modules/SD2/scripts/battlegrounds/battleground.cpp b/src/modules/SD2/scripts/battlegrounds/battleground.cpp new file mode 100644 index 000000000..1f6f4426f --- /dev/null +++ b/src/modules/SD2/scripts/battlegrounds/battleground.cpp @@ -0,0 +1,132 @@ +/** + * ScriptDev2 is an extension for mangos providing enhanced features for + * area triggers, creatures, game objects, instances, items, and spells beyond + * the default database scripting in mangos. + * + * Copyright (C) 2006-2013 ScriptDev2 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * World of Warcraft, and all World of Warcraft or Warcraft art, images, + * and lore are copyrighted by Blizzard Entertainment, Inc. + */ + +/** + * ScriptData + * SDName: Battleground + * SD%Complete: 100 + * SDComment: Spirit guides in battlegrounds will revive all players every 30 sec. + * SDCategory: Battlegrounds + * EndScriptData + */ + +#include "precompiled.h" + +/** + * Script Info + * + * Spirit guides in battlegrounds resurrecting many players at once every 30 + * seconds through a channeled spell, which gets autocasted the whole time. + * + * If a spirit guide despawns all players around him will get teleported to + * the next spirit guide. + * + * this script handles gossipHello and JustDied also allows autocast of the + * channeled spell. + */ + +enum +{ + SPELL_SPIRIT_HEAL_CHANNEL = 22011, // Spirit Heal Channel + + SPELL_SPIRIT_HEAL = 22012, // Spirit Heal + SPELL_SPIRIT_HEAL_MANA = 44535, // in battlegrounds player get this no-mana-cost-buff + + SPELL_WAITING_TO_RESURRECT = 2584 // players who cancel this aura don't want a resurrection +}; + +struct npc_spirit_guideAI : public ScriptedAI +{ + npc_spirit_guideAI(Creature* pCreature) : ScriptedAI(pCreature) + { + pCreature->SetActiveObjectState(true); + Reset(); + } + + void Reset() override {} + + void UpdateAI(const uint32 /*uiDiff*/) override + { + // auto cast the whole time this spell + if (!m_creature->GetCurrentSpell(CURRENT_CHANNELED_SPELL)) + { + m_creature->CastSpell(m_creature, SPELL_SPIRIT_HEAL_CHANNEL, false); + } + } + + void CorpseRemoved(uint32&) override + { + // TODO: would be better to cast a dummy spell + Map* pMap = m_creature->GetMap(); + + if (!pMap || !pMap->IsBattleGround()) + { + return; + } + + Map::PlayerList const& PlayerList = pMap->GetPlayers(); + + for (Map::PlayerList::const_iterator itr = PlayerList.begin(); itr != PlayerList.end(); ++itr) + { + Player* pPlayer = itr->getSource(); + if (!pPlayer || !pPlayer->IsWithinDistInMap(m_creature, 20.0f) || !pPlayer->HasAura(SPELL_WAITING_TO_RESURRECT)) + { + continue; + } + + // repop player again - now this node won't be counted and another node is searched + pPlayer->RepopAtGraveyard(); + } + } + + void SpellHitTarget(Unit* pUnit, const SpellEntry* pSpellEntry) override + { + if (pSpellEntry->Id == SPELL_SPIRIT_HEAL && pUnit->GetTypeId() == TYPEID_PLAYER + && pUnit->HasAura(SPELL_WAITING_TO_RESURRECT)) + { pUnit->CastSpell(pUnit, SPELL_SPIRIT_HEAL_MANA, true); } + } +}; + +bool GossipHello_npc_spirit_guide(Player* pPlayer, Creature* /*pCreature*/) +{ + pPlayer->CastSpell(pPlayer, SPELL_WAITING_TO_RESURRECT, true); + return true; +} + +CreatureAI* GetAI_npc_spirit_guide(Creature* pCreature) +{ + return new npc_spirit_guideAI(pCreature); +} + +void AddSC_battleground() +{ + Script* pNewScript; + + pNewScript = new Script; + pNewScript->Name = "npc_spirit_guide"; + pNewScript->GetAI = &GetAI_npc_spirit_guide; + pNewScript->pGossipHello = &GossipHello_npc_spirit_guide; + pNewScript->RegisterSelf(); +} diff --git a/src/modules/SD2/scripts/eastern_kingdoms/alterac_mountains.cpp b/src/modules/SD2/scripts/eastern_kingdoms/alterac_mountains.cpp new file mode 100644 index 000000000..13776374e --- /dev/null +++ b/src/modules/SD2/scripts/eastern_kingdoms/alterac_mountains.cpp @@ -0,0 +1,44 @@ +/** + * ScriptDev2 is an extension for mangos providing enhanced features for + * area triggers, creatures, game objects, instances, items, and spells beyond + * the default database scripting in mangos. + * + * Copyright (C) 2006-2013 ScriptDev2 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * World of Warcraft, and all World of Warcraft or Warcraft art, images, + * and lore are copyrighted by Blizzard Entertainment, Inc. + */ + +/** + * ScriptData + * SDName: Alterac_Mountains + * SD%Complete: 0 + * SDComment: Placeholder + * SDCategory: Alterac Mountains + * EndScriptData + */ + +/** + * ContentData + * EndContentData + */ + +#include "precompiled.h" + +void AddSC_alterac_mountains() +{ +} diff --git a/src/modules/SD2/scripts/eastern_kingdoms/arathi_highlands.cpp b/src/modules/SD2/scripts/eastern_kingdoms/arathi_highlands.cpp new file mode 100644 index 000000000..7f9dbd9c5 --- /dev/null +++ b/src/modules/SD2/scripts/eastern_kingdoms/arathi_highlands.cpp @@ -0,0 +1,279 @@ +/** + * ScriptDev2 is an extension for mangos providing enhanced features for + * area triggers, creatures, game objects, instances, items, and spells beyond + * the default database scripting in mangos. + * + * Copyright (C) 2006-2013 ScriptDev2 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * World of Warcraft, and all World of Warcraft or Warcraft art, images, + * and lore are copyrighted by Blizzard Entertainment, Inc. + */ + +/** + * ScriptData + * SDName: Arathi Highlands + * SD%Complete: 100 + * SDComment: Quest support: 660, 665 + * SDCategory: Arathi Highlands + * EndScriptData + */ + +/** + * ContentData + * npc_professor_phizzlethorpe + * npc_kinelory + * EndContentData + */ + +#include "precompiled.h" +#include "escort_ai.h" + +/*###### +## npc_professor_phizzlethorpe +######*/ + +enum +{ + SAY_PROGRESS_1 = -1000264, + SAY_PROGRESS_2 = -1000265, + SAY_PROGRESS_3 = -1000266, + EMOTE_PROGRESS_4 = -1000267, + SAY_AGGRO = -1000268, + SAY_PROGRESS_5 = -1000269, + SAY_PROGRESS_6 = -1000270, + SAY_PROGRESS_7 = -1000271, + EMOTE_PROGRESS_8 = -1000272, + SAY_PROGRESS_9 = -1000273, + + QUEST_SUNKEN_TREASURE = 665, + ENTRY_VENGEFUL_SURGE = 2776 +}; + +struct npc_professor_phizzlethorpeAI : public npc_escortAI +{ + npc_professor_phizzlethorpeAI(Creature* pCreature) : npc_escortAI(pCreature) { Reset(); } + + void Reset() override { } + + void WaypointReached(uint32 uiPointId) override + { + Player* pPlayer = GetPlayerForEscort(); + + if (!pPlayer) + { + return; + } + + switch (uiPointId) + { + case 4: + DoScriptText(SAY_PROGRESS_2, m_creature, pPlayer); + break; + case 5: + DoScriptText(SAY_PROGRESS_3, m_creature, pPlayer); + break; + case 8: + DoScriptText(EMOTE_PROGRESS_4, m_creature); + break; + case 9: + m_creature->SummonCreature(ENTRY_VENGEFUL_SURGE, -2056.41f, -2144.01f, 20.59f, 5.70f, TEMPSUMMON_TIMED_OOC_OR_CORPSE_DESPAWN, 600000); + m_creature->SummonCreature(ENTRY_VENGEFUL_SURGE, -2050.17f, -2140.02f, 19.54f, 5.17f, TEMPSUMMON_TIMED_OOC_OR_CORPSE_DESPAWN, 600000); + break; + case 10: + DoScriptText(SAY_PROGRESS_5, m_creature, pPlayer); + break; + case 11: + DoScriptText(SAY_PROGRESS_6, m_creature, pPlayer); + SetRun(); + break; + case 19: + DoScriptText(SAY_PROGRESS_7, m_creature, pPlayer); + break; + case 20: + DoScriptText(EMOTE_PROGRESS_8, m_creature); + DoScriptText(SAY_PROGRESS_9, m_creature, pPlayer); + pPlayer->GroupEventHappens(QUEST_SUNKEN_TREASURE, m_creature); + break; + } + } + + void Aggro(Unit* /*pWho*/) override + { + DoScriptText(SAY_AGGRO, m_creature); + } + + void JustSummoned(Creature* pSummoned) override + { + pSummoned->AI()->AttackStart(m_creature); + } +}; + +bool QuestAccept_npc_professor_phizzlethorpe(Player* pPlayer, Creature* pCreature, const Quest* pQuest) +{ + if (pQuest->GetQuestId() == QUEST_SUNKEN_TREASURE) + { + pCreature->SetFactionTemporary(FACTION_ESCORT_N_NEUTRAL_PASSIVE, TEMPFACTION_RESTORE_RESPAWN); + DoScriptText(SAY_PROGRESS_1, pCreature, pPlayer); + + if (npc_professor_phizzlethorpeAI* pEscortAI = dynamic_cast(pCreature->AI())) + { + pEscortAI->Start(false, pPlayer, pQuest, true); + } + } + return true; +} + +CreatureAI* GetAI_npc_professor_phizzlethorpe(Creature* pCreature) +{ + return new npc_professor_phizzlethorpeAI(pCreature); +} + +/*###### +## npc_kinelory +######*/ + +enum +{ + SAY_START = -1000948, + SAY_REACH_BOTTOM = -1000949, + SAY_AGGRO_KINELORY = -1000950, + SAY_AGGRO_JORELL = -1000951, + SAY_WATCH_BACK = -1000952, + EMOTE_BELONGINGS = -1000953, + SAY_DATA_FOUND = -1000954, + SAY_ESCAPE = -1000955, + SAY_FINISH = -1000956, + EMOTE_HAND_PACK = -1000957, + + // ToDo: find the healing spell id! + SPELL_BEAR_FORM = 4948, + + NPC_JORELL = 2733, + NPC_QUAE = 2712, + + QUEST_HINTS_NEW_PLAGUE = 660 +}; + +struct npc_kineloryAI : public npc_escortAI +{ + npc_kineloryAI(Creature* pCreature) : npc_escortAI(pCreature) { Reset(); } + + uint32 m_uiBearFormTimer; + + void Reset() override + { + m_uiBearFormTimer = urand(1000, 5000); + } + + void WaypointReached(uint32 uiPointId) override + { + switch (uiPointId) + { + case 9: + DoScriptText(SAY_REACH_BOTTOM, m_creature); + break; + case 16: + DoScriptText(SAY_WATCH_BACK, m_creature); + DoScriptText(EMOTE_BELONGINGS, m_creature); + break; + case 17: + DoScriptText(SAY_DATA_FOUND, m_creature); + break; + case 18: + DoScriptText(SAY_ESCAPE, m_creature); + if (Player* pPlayer = GetPlayerForEscort()) + { m_creature->SetFacingToObject(pPlayer); } + SetRun(); + break; + case 33: + DoScriptText(SAY_FINISH, m_creature); + if (Creature* pQuae = GetClosestCreatureWithEntry(m_creature, NPC_QUAE, 10.0f)) + { + DoScriptText(EMOTE_HAND_PACK, m_creature, pQuae); + m_creature->SetFacingToObject(pQuae); + } + break; + case 34: + if (Player* pPlayer = GetPlayerForEscort()) + { pPlayer->GroupEventHappens(QUEST_HINTS_NEW_PLAGUE, m_creature); } + break; + } + } + + void Aggro(Unit* pWho) override + { + if (pWho->GetEntry() == NPC_JORELL) + { DoScriptText(SAY_AGGRO_JORELL, pWho, m_creature); } + else if (roll_chance_i(10)) + { DoScriptText(SAY_AGGRO_KINELORY, m_creature); } + } + + void ReceiveAIEvent(AIEventType eventType, Creature* /*pSender*/, Unit* pInvoker, uint32 uiMiscValue) override + { + if (eventType == AI_EVENT_START_ESCORT && pInvoker->GetTypeId() == TYPEID_PLAYER) + { + DoScriptText(SAY_START, m_creature); + Start(false, (Player*)pInvoker, GetQuestTemplateStore(uiMiscValue), true); + } + } + + void UpdateEscortAI(const uint32 uiDiff) override + { + if (!m_creature->SelectHostileTarget() || !m_creature->getVictim()) + { return; } + + if (m_uiBearFormTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature, SPELL_BEAR_FORM) == CAST_OK) + { m_uiBearFormTimer = urand(25000, 30000); } + } + else + { m_uiBearFormTimer -= uiDiff; } + + DoMeleeAttackIfReady(); + } +}; + +CreatureAI* GetAI_npc_kinelory(Creature* pCreature) +{ + return new npc_kineloryAI(pCreature); +} + +bool QuestAccept_npc_kinelory(Player* pPlayer, Creature* pCreature, const Quest* pQuest) +{ + if (pQuest->GetQuestId() == QUEST_HINTS_NEW_PLAGUE) + { pCreature->AI()->SendAIEvent(AI_EVENT_START_ESCORT, pPlayer, pCreature, pQuest->GetQuestId()); } + + return true; +} + +void AddSC_arathi_highlands() +{ + Script* pNewScript; + + pNewScript = new Script; + pNewScript->Name = "npc_professor_phizzlethorpe"; + pNewScript->GetAI = &GetAI_npc_professor_phizzlethorpe; + pNewScript->pQuestAcceptNPC = &QuestAccept_npc_professor_phizzlethorpe; + pNewScript->RegisterSelf(); + + pNewScript = new Script; + pNewScript->Name = "npc_kinelory"; + pNewScript->GetAI = &GetAI_npc_kinelory; + pNewScript->pQuestAcceptNPC = &QuestAccept_npc_kinelory; + pNewScript->RegisterSelf(); +} diff --git a/src/modules/SD2/scripts/eastern_kingdoms/bastion_of_twilight/ascendant_council.cpp b/src/modules/SD2/scripts/eastern_kingdoms/bastion_of_twilight/ascendant_council.cpp new file mode 100644 index 000000000..82a854bb5 --- /dev/null +++ b/src/modules/SD2/scripts/eastern_kingdoms/bastion_of_twilight/ascendant_council.cpp @@ -0,0 +1,28 @@ +/* Copyright (C) 2006 - 2013 ScriptDev2 + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +/* ScriptData +SDName: ascendant_council +SD%Complete: 0 +SDComment: Placeholder +SDCategory: Bastion of Twilight +EndScriptData */ + +#include "precompiled.h" + +void AddSC_ascendant_council() +{ +} diff --git a/src/modules/SD2/scripts/eastern_kingdoms/bastion_of_twilight/bastion_of_twilight.h b/src/modules/SD2/scripts/eastern_kingdoms/bastion_of_twilight/bastion_of_twilight.h new file mode 100644 index 000000000..6b5f9ef47 --- /dev/null +++ b/src/modules/SD2/scripts/eastern_kingdoms/bastion_of_twilight/bastion_of_twilight.h @@ -0,0 +1,3 @@ +/* Copyright (C) 2006 - 2013 ScriptDev2 + * This program is free software licensed under GPL version 2 + * Please see the included DOCS/LICENSE.TXT for more information */ diff --git a/src/modules/SD2/scripts/eastern_kingdoms/bastion_of_twilight/boss_chogall.cpp b/src/modules/SD2/scripts/eastern_kingdoms/bastion_of_twilight/boss_chogall.cpp new file mode 100644 index 000000000..7512a7d04 --- /dev/null +++ b/src/modules/SD2/scripts/eastern_kingdoms/bastion_of_twilight/boss_chogall.cpp @@ -0,0 +1,28 @@ +/* Copyright (C) 2006 - 2013 ScriptDev2 + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +/* ScriptData +SDName: boss_chogall +SD%Complete: 0 +SDComment: Placeholder +SDCategory: Bastion of Twilight +EndScriptData */ + +#include "precompiled.h" + +void AddSC_boss_chogall() +{ +} diff --git a/src/modules/SD2/scripts/eastern_kingdoms/bastion_of_twilight/boss_halfus_wyrmbreaker.cpp b/src/modules/SD2/scripts/eastern_kingdoms/bastion_of_twilight/boss_halfus_wyrmbreaker.cpp new file mode 100644 index 000000000..aff3077d8 --- /dev/null +++ b/src/modules/SD2/scripts/eastern_kingdoms/bastion_of_twilight/boss_halfus_wyrmbreaker.cpp @@ -0,0 +1,28 @@ +/* Copyright (C) 2006 - 2013 ScriptDev2 + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +/* ScriptData +SDName: boss_halfus_wyrmbreaker +SD%Complete: 0 +SDComment: Placeholder +SDCategory: Bastion of Twilight +EndScriptData */ + +#include "precompiled.h" + +void AddSC_boss_halfus_wyrmbreaker() +{ +} diff --git a/src/modules/SD2/scripts/eastern_kingdoms/bastion_of_twilight/boss_sinestra.cpp b/src/modules/SD2/scripts/eastern_kingdoms/bastion_of_twilight/boss_sinestra.cpp new file mode 100644 index 000000000..2bb955691 --- /dev/null +++ b/src/modules/SD2/scripts/eastern_kingdoms/bastion_of_twilight/boss_sinestra.cpp @@ -0,0 +1,28 @@ +/* Copyright (C) 2006 - 2013 ScriptDev2 + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +/* ScriptData +SDName: boss_sinestra +SD%Complete: 0 +SDComment: Placeholder +SDCategory: Bastion of Twilight +EndScriptData */ + +#include "precompiled.h" + +void AddSC_boss_sinestra() +{ +} diff --git a/src/modules/SD2/scripts/eastern_kingdoms/bastion_of_twilight/boss_valiona_and_theralion.cpp b/src/modules/SD2/scripts/eastern_kingdoms/bastion_of_twilight/boss_valiona_and_theralion.cpp new file mode 100644 index 000000000..7aa75ad33 --- /dev/null +++ b/src/modules/SD2/scripts/eastern_kingdoms/bastion_of_twilight/boss_valiona_and_theralion.cpp @@ -0,0 +1,28 @@ +/* Copyright (C) 2006 - 2013 ScriptDev2 + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +/* ScriptData +SDName: boss_valiona_and_theralion +SD%Complete: 0 +SDComment: Placeholder +SDCategory: Bastion of Twilight +EndScriptData */ + +#include "precompiled.h" + +void AddSC_boss_valiona_and_theralion() +{ +} diff --git a/src/modules/SD2/scripts/eastern_kingdoms/bastion_of_twilight/instance_bastion_of_twilight.cpp b/src/modules/SD2/scripts/eastern_kingdoms/bastion_of_twilight/instance_bastion_of_twilight.cpp new file mode 100644 index 000000000..4211e97e9 --- /dev/null +++ b/src/modules/SD2/scripts/eastern_kingdoms/bastion_of_twilight/instance_bastion_of_twilight.cpp @@ -0,0 +1,28 @@ +/* Copyright (C) 2006 - 2013 ScriptDev2 + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along 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_bastion_of_twilight +SD%Complete: 0 +SDComment: Placeholder +SDCategory: Bastion of Twilight +EndScriptData */ + +#include "precompiled.h" + +void AddSC_instance_bastion_of_twilight() +{ +} diff --git a/src/modules/SD2/scripts/eastern_kingdoms/blackrock_mountain/blackrock_caverns/blackrock_caverns.h b/src/modules/SD2/scripts/eastern_kingdoms/blackrock_mountain/blackrock_caverns/blackrock_caverns.h new file mode 100644 index 000000000..f7bee097e --- /dev/null +++ b/src/modules/SD2/scripts/eastern_kingdoms/blackrock_mountain/blackrock_caverns/blackrock_caverns.h @@ -0,0 +1,47 @@ +/* Copyright (C) 2006 - 2013 ScriptDev2 + * This program is free software licensed under GPL version 2 + * Please see the included DOCS/LICENSE.TXT for more information */ + +#ifndef DEF_BLACKROCK_CAVERNS_H +#define DEF_BLACKROCK_CAVERNS_H + +enum +{ + MAX_ENCOUNTER = 5, + + TYPE_ROMOGG = 0, + TYPE_CORLA = 1, + TYPE_KARSH = 2, + TYPE_BEAUTY = 3, + TYPE_OBSIDIUS = 4, + + NPC_ROMOGG = 39665, + NPC_CORLA = 39679, + NPC_KARSH = 39698, + NPC_BEAUTY = 39700, + NPC_OBSIDIUS = 39705, +}; + +class instance_blackrock_caverns : public ScriptedInstance +{ + public: + instance_blackrock_caverns(Map* pMap); + + void Initialize() override; + + void OnCreatureCreate(Creature* pCreature) override; + void OnObjectCreate(GameObject* pGo) override; + + void SetData(uint32 uiType, uint32 uiData) override; + uint32 GetData(uint32 uiType) const override; + + const char* Save() const override { return m_strInstData.c_str(); } + void Load(const char* chrIn) override; + + private: + + uint32 m_auiEncounter[MAX_ENCOUNTER]; + std::string m_strInstData; +}; + +#endif diff --git a/src/modules/SD2/scripts/eastern_kingdoms/blackrock_mountain/blackrock_caverns/boss_beauty.cpp b/src/modules/SD2/scripts/eastern_kingdoms/blackrock_mountain/blackrock_caverns/boss_beauty.cpp new file mode 100644 index 000000000..df6ebda30 --- /dev/null +++ b/src/modules/SD2/scripts/eastern_kingdoms/blackrock_mountain/blackrock_caverns/boss_beauty.cpp @@ -0,0 +1,92 @@ +/* Copyright (C) 2006 - 2013 ScriptDev2 + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +/* ScriptData +SDName: boss_beauty +SD%Complete: 10 +SDComment: Placeholder +SDCategory: Blackrock Caverns +EndScriptData */ + +#include "precompiled.h" +#include "blackrock_caverns.h" + +enum +{ + // ToDo: add spells and yells here +}; + +struct boss_beautyAI : public ScriptedAI +{ + boss_beautyAI(Creature* pCreature) : ScriptedAI(pCreature) + { + m_pInstance = (ScriptedInstance*)pCreature->GetInstanceData(); + m_bIsRegularMode = pCreature->GetMap()->IsRegularDifficulty(); + Reset(); + } + + ScriptedInstance* m_pInstance; + bool m_bIsRegularMode; + + void Reset() override + { + } + + void Aggro(Unit* pWho) override + { + if (m_pInstance) + m_pInstance->SetData(TYPE_BEAUTY, IN_PROGRESS); + } + + void JustDied(Unit* pKiller) override + { + if (m_pInstance) + m_pInstance->SetData(TYPE_BEAUTY, DONE); + } + + void KilledUnit(Unit* pVictim) override + { + } + + void JustReachedHome() override + { + if (m_pInstance) + m_pInstance->SetData(TYPE_BEAUTY, FAIL); + } + + void UpdateAI(const uint32 uiDiff) override + { + if (!m_creature->SelectHostileTarget() || !m_creature->getVictim()) + return; + + DoMeleeAttackIfReady(); + } +}; + +CreatureAI* GetAI_boss_beauty(Creature* pCreature) +{ + return new boss_beautyAI(pCreature); +} + +void AddSC_boss_beauty() +{ + Script* pNewScript; + + pNewScript = new Script; + pNewScript->Name = "boss_beauty"; + pNewScript->GetAI = &GetAI_boss_beauty; + pNewScript->RegisterSelf(); +} diff --git a/src/modules/SD2/scripts/eastern_kingdoms/blackrock_mountain/blackrock_caverns/boss_corla.cpp b/src/modules/SD2/scripts/eastern_kingdoms/blackrock_mountain/blackrock_caverns/boss_corla.cpp new file mode 100644 index 000000000..d5fd98c52 --- /dev/null +++ b/src/modules/SD2/scripts/eastern_kingdoms/blackrock_mountain/blackrock_caverns/boss_corla.cpp @@ -0,0 +1,92 @@ +/* Copyright (C) 2006 - 2013 ScriptDev2 + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +/* ScriptData +SDName: boss_corla +SD%Complete: 10 +SDComment: Placeholder +SDCategory: Blackrock Caverns +EndScriptData */ + +#include "precompiled.h" +#include "blackrock_caverns.h" + +enum +{ + // ToDo: add spells and yells here +}; + +struct boss_corlaAI : public ScriptedAI +{ + boss_corlaAI(Creature* pCreature) : ScriptedAI(pCreature) + { + m_pInstance = (ScriptedInstance*)pCreature->GetInstanceData(); + m_bIsRegularMode = pCreature->GetMap()->IsRegularDifficulty(); + Reset(); + } + + ScriptedInstance* m_pInstance; + bool m_bIsRegularMode; + + void Reset() override + { + } + + void Aggro(Unit* pWho) override + { + if (m_pInstance) + m_pInstance->SetData(TYPE_CORLA, IN_PROGRESS); + } + + void JustDied(Unit* pKiller) override + { + if (m_pInstance) + m_pInstance->SetData(TYPE_CORLA, DONE); + } + + void KilledUnit(Unit* pVictim) override + { + } + + void JustReachedHome() override + { + if (m_pInstance) + m_pInstance->SetData(TYPE_CORLA, FAIL); + } + + void UpdateAI(const uint32 uiDiff) override + { + if (!m_creature->SelectHostileTarget() || !m_creature->getVictim()) + return; + + DoMeleeAttackIfReady(); + } +}; + +CreatureAI* GetAI_boss_corla(Creature* pCreature) +{ + return new boss_corlaAI(pCreature); +} + +void AddSC_boss_corla() +{ + Script* pNewScript; + + pNewScript = new Script; + pNewScript->Name = "boss_corla"; + pNewScript->GetAI = &GetAI_boss_corla; + pNewScript->RegisterSelf(); +} diff --git a/src/modules/SD2/scripts/eastern_kingdoms/blackrock_mountain/blackrock_caverns/boss_karsh_steelbender.cpp b/src/modules/SD2/scripts/eastern_kingdoms/blackrock_mountain/blackrock_caverns/boss_karsh_steelbender.cpp new file mode 100644 index 000000000..174f63da1 --- /dev/null +++ b/src/modules/SD2/scripts/eastern_kingdoms/blackrock_mountain/blackrock_caverns/boss_karsh_steelbender.cpp @@ -0,0 +1,92 @@ +/* Copyright (C) 2006 - 2013 ScriptDev2 + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +/* ScriptData +SDName: boss_karsh_steelbender +SD%Complete: 10 +SDComment: Placeholder +SDCategory: Blackrock Caverns +EndScriptData */ + +#include "precompiled.h" +#include "blackrock_caverns.h" + +enum +{ + // ToDo: add spells and yells here +}; + +struct boss_karsh_steelbenderAI : public ScriptedAI +{ + boss_karsh_steelbenderAI(Creature* pCreature) : ScriptedAI(pCreature) + { + m_pInstance = (ScriptedInstance*)pCreature->GetInstanceData(); + m_bIsRegularMode = pCreature->GetMap()->IsRegularDifficulty(); + Reset(); + } + + ScriptedInstance* m_pInstance; + bool m_bIsRegularMode; + + void Reset() override + { + } + + void Aggro(Unit* pWho) override + { + if (m_pInstance) + m_pInstance->SetData(TYPE_KARSH, IN_PROGRESS); + } + + void JustDied(Unit* pKiller) override + { + if (m_pInstance) + m_pInstance->SetData(TYPE_KARSH, DONE); + } + + void KilledUnit(Unit* pVictim) override + { + } + + void JustReachedHome() override + { + if (m_pInstance) + m_pInstance->SetData(TYPE_KARSH, FAIL); + } + + void UpdateAI(const uint32 uiDiff) override + { + if (!m_creature->SelectHostileTarget() || !m_creature->getVictim()) + return; + + DoMeleeAttackIfReady(); + } +}; + +CreatureAI* GetAI_boss_karsh_steelbender(Creature* pCreature) +{ + return new boss_karsh_steelbenderAI(pCreature); +} + +void AddSC_boss_karsh_steelbender() +{ + Script* pNewScript; + + pNewScript = new Script; + pNewScript->Name = "boss_karsh_steelbender"; + pNewScript->GetAI = &GetAI_boss_karsh_steelbender; + pNewScript->RegisterSelf(); +} diff --git a/src/modules/SD2/scripts/eastern_kingdoms/blackrock_mountain/blackrock_caverns/boss_lord_obsidius.cpp b/src/modules/SD2/scripts/eastern_kingdoms/blackrock_mountain/blackrock_caverns/boss_lord_obsidius.cpp new file mode 100644 index 000000000..9580f2767 --- /dev/null +++ b/src/modules/SD2/scripts/eastern_kingdoms/blackrock_mountain/blackrock_caverns/boss_lord_obsidius.cpp @@ -0,0 +1,92 @@ +/* Copyright (C) 2006 - 2013 ScriptDev2 + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with 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_obsidius +SD%Complete: 10 +SDComment: Placeholder +SDCategory: Blackrock Caverns +EndScriptData */ + +#include "precompiled.h" +#include "blackrock_caverns.h" + +enum +{ + // ToDo: add spells and yells here +}; + +struct boss_lord_obsidiusAI : public ScriptedAI +{ + boss_lord_obsidiusAI(Creature* pCreature) : ScriptedAI(pCreature) + { + m_pInstance = (ScriptedInstance*)pCreature->GetInstanceData(); + m_bIsRegularMode = pCreature->GetMap()->IsRegularDifficulty(); + Reset(); + } + + ScriptedInstance* m_pInstance; + bool m_bIsRegularMode; + + void Reset() override + { + } + + void Aggro(Unit* pWho) override + { + if (m_pInstance) + m_pInstance->SetData(TYPE_OBSIDIUS, IN_PROGRESS); + } + + void JustDied(Unit* pKiller) override + { + if (m_pInstance) + m_pInstance->SetData(TYPE_OBSIDIUS, DONE); + } + + void KilledUnit(Unit* pVictim) override + { + } + + void JustReachedHome() override + { + if (m_pInstance) + m_pInstance->SetData(TYPE_OBSIDIUS, FAIL); + } + + void UpdateAI(const uint32 uiDiff) override + { + if (!m_creature->SelectHostileTarget() || !m_creature->getVictim()) + return; + + DoMeleeAttackIfReady(); + } +}; + +CreatureAI* GetAI_boss_lord_obsidius(Creature* pCreature) +{ + return new boss_lord_obsidiusAI(pCreature); +} + +void AddSC_boss_lord_obsidius() +{ + Script* pNewScript; + + pNewScript = new Script; + pNewScript->Name = "boss_lord_obsidius"; + pNewScript->GetAI = &GetAI_boss_lord_obsidius; + pNewScript->RegisterSelf(); +} diff --git a/src/modules/SD2/scripts/eastern_kingdoms/blackrock_mountain/blackrock_caverns/boss_romogg.cpp b/src/modules/SD2/scripts/eastern_kingdoms/blackrock_mountain/blackrock_caverns/boss_romogg.cpp new file mode 100644 index 000000000..bdc44deb0 --- /dev/null +++ b/src/modules/SD2/scripts/eastern_kingdoms/blackrock_mountain/blackrock_caverns/boss_romogg.cpp @@ -0,0 +1,92 @@ +/* Copyright (C) 2006 - 2013 ScriptDev2 + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +/* ScriptData +SDName: boss_romogg +SD%Complete: 10 +SDComment: Placeholder +SDCategory: Blackrock Caverns +EndScriptData */ + +#include "precompiled.h" +#include "blackrock_caverns.h" + +enum +{ + // ToDo: add spells and yells here +}; + +struct boss_romoggAI : public ScriptedAI +{ + boss_romoggAI(Creature* pCreature) : ScriptedAI(pCreature) + { + m_pInstance = (ScriptedInstance*)pCreature->GetInstanceData(); + m_bIsRegularMode = pCreature->GetMap()->IsRegularDifficulty(); + Reset(); + } + + ScriptedInstance* m_pInstance; + bool m_bIsRegularMode; + + void Reset() override + { + } + + void Aggro(Unit* pWho) override + { + if (m_pInstance) + m_pInstance->SetData(TYPE_ROMOGG, IN_PROGRESS); + } + + void JustDied(Unit* pKiller) override + { + if (m_pInstance) + m_pInstance->SetData(TYPE_ROMOGG, DONE); + } + + void KilledUnit(Unit* pVictim) override + { + } + + void JustReachedHome() override + { + if (m_pInstance) + m_pInstance->SetData(TYPE_ROMOGG, FAIL); + } + + void UpdateAI(const uint32 uiDiff) override + { + if (!m_creature->SelectHostileTarget() || !m_creature->getVictim()) + return; + + DoMeleeAttackIfReady(); + } +}; + +CreatureAI* GetAI_boss_romogg(Creature* pCreature) +{ + return new boss_romoggAI(pCreature); +} + +void AddSC_boss_romogg() +{ + Script* pNewScript; + + pNewScript = new Script; + pNewScript->Name = "boss_romogg"; + pNewScript->GetAI = &GetAI_boss_romogg; + pNewScript->RegisterSelf(); +} diff --git a/src/modules/SD2/scripts/eastern_kingdoms/blackrock_mountain/blackrock_caverns/instance_blackrock_caverns.cpp b/src/modules/SD2/scripts/eastern_kingdoms/blackrock_mountain/blackrock_caverns/instance_blackrock_caverns.cpp new file mode 100644 index 000000000..8a3e50f33 --- /dev/null +++ b/src/modules/SD2/scripts/eastern_kingdoms/blackrock_mountain/blackrock_caverns/instance_blackrock_caverns.cpp @@ -0,0 +1,119 @@ +/* Copyright (C) 2006 - 2013 ScriptDev2 + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along 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_caverns +SD%Complete: 10 +SDComment: Placeholder +SDCategory: Blackrock Caverns +EndScriptData */ + +#include "precompiled.h" +#include "blackrock_caverns.h" + +instance_blackrock_caverns::instance_blackrock_caverns(Map* pMap) : ScriptedInstance(pMap) +{ + Initialize(); +}; + +void instance_blackrock_caverns::Initialize() +{ + memset(&m_auiEncounter, 0, sizeof(m_auiEncounter)); +} + +void instance_blackrock_caverns::OnCreatureCreate(Creature* pCreature) +{ + // ToDo: add Creature references here +} + +void instance_blackrock_caverns::OnObjectCreate(GameObject* pGo) +{ + // ToDo: add Gameobject references here +} + +void instance_blackrock_caverns::SetData(uint32 uiType, uint32 uiData) +{ + switch (uiType) + { + case TYPE_ROMOGG: + case TYPE_CORLA: + case TYPE_KARSH: + case TYPE_BEAUTY: + case TYPE_OBSIDIUS: + m_auiEncounter[uiType] = uiData; + 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_strInstData = saveStream.str(); + + SaveToDB(); + OUT_SAVE_INST_DATA_COMPLETE; + } +} + +uint32 instance_blackrock_caverns::GetData(uint32 uiType) const +{ + if (uiType < MAX_ENCOUNTER) + return m_auiEncounter[uiType]; + + return 0; +} + +void instance_blackrock_caverns::Load(const char* chrIn) +{ + if (!chrIn) + { + 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]; + + 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_blackrock_caverns(Map* pMap) +{ + return new instance_blackrock_caverns(pMap); +} + +void AddSC_instance_blackrock_caverns() +{ + Script* pNewScript; + + pNewScript = new Script; + pNewScript->Name = "instance_blackrock_caverns"; + pNewScript->GetInstanceData = &GetInstanceData_instance_blackrock_caverns; + pNewScript->RegisterSelf(); +} diff --git a/src/modules/SD2/scripts/eastern_kingdoms/blackrock_mountain/blackrock_depths/blackrock_depths.cpp b/src/modules/SD2/scripts/eastern_kingdoms/blackrock_mountain/blackrock_depths/blackrock_depths.cpp new file mode 100644 index 000000000..7beaf367d --- /dev/null +++ b/src/modules/SD2/scripts/eastern_kingdoms/blackrock_mountain/blackrock_depths/blackrock_depths.cpp @@ -0,0 +1,1113 @@ +/* Copyright (C) 2006 - 2013 ScriptDev2 + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along 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: 80 +SDComment: Quest support: 4001, 4322, 4342, 7604, 9015. +SDCategory: Blackrock Depths +EndScriptData */ + +/* ContentData +go_shadowforge_brazier +go_relic_coffer_door +at_ring_of_law +npc_grimstone +npc_kharan_mighthammer +npc_marshal_windsor +npc_dughal_stormwing +npc_tobias_seecher +boss_doomrel +EndContentData */ + +#include "precompiled.h" +#include "blackrock_depths.h" +#include "escort_ai.h" + +/*###### +## go_shadowforge_brazier +######*/ + +bool GOUse_go_shadowforge_brazier(Player* /*pPlayer*/, GameObject* pGo) +{ + if (ScriptedInstance* pInstance = (ScriptedInstance*)pGo->GetInstanceData()) + { + if (pInstance->GetData(TYPE_LYCEUM) == IN_PROGRESS) + pInstance->SetData(TYPE_LYCEUM, DONE); + else + pInstance->SetData(TYPE_LYCEUM, IN_PROGRESS); + } + return false; +} + +/*###### +## go_relic_coffer_door +######*/ + +bool GOUse_go_relic_coffer_door(Player* /*pPlayer*/, GameObject* pGo) +{ + if (ScriptedInstance* pInstance = (ScriptedInstance*)pGo->GetInstanceData()) + { + // check if the event is already done + if (pInstance->GetData(TYPE_VAULT) != DONE && pInstance->GetData(TYPE_VAULT) != IN_PROGRESS) + pInstance->SetData(TYPE_VAULT, SPECIAL); + } + + return false; +} + +/*###### +## npc_grimstone +######*/ + +/* Notes about this event: + * Visual: Npc Grimstone should use some visual spell when appear/ disappear / opening/ closing doors + * Texts: The texts and their positions need confirmation + * Event timer might also need adjustment + * Quest-Event: This needs to be clearified - there is some suggestion, that Theldren&Adds also might come as first wave. + */ + +enum +{ + SAY_START_1 = -1230004, + SAY_START_2 = -1230005, + SAY_OPEN_EAST_GATE = -1230006, + SAY_SUMMON_BOSS_1 = -1230007, + SAY_SUMMON_BOSS_2 = -1230008, + SAY_OPEN_NORTH_GATE = -1230009, + + NPC_GRIMSTONE = 10096, + DATA_BANNER_BEFORE_EVENT = 5, + + // 4 or 6 in total? 1+2+1 / 2+2+2 / 3+3. Depending on this, code should be changed. + MAX_MOB_AMOUNT = 4, + MAX_THELDREN_ADDS = 4, + MAX_POSSIBLE_THELDREN_ADDS = 8, + + SPELL_SUMMON_THELRIN_DND = 27517, + /* Other spells used by Grimstone + SPELL_ASHCROMBES_TELEPORT_A = 15742 + SPELL_ASHCROMBES_TELEPORT_B = 6422, + SPELL_ARENA_FLASH_A = 15737, + SPELL_ARENA_FLASH_B = 15739, + */ + + QUEST_THE_CHALLENGE = 9015, + NPC_THELDREN_QUEST_CREDIT = 16166, +}; + +enum SpawnPosition +{ + POS_EAST = 0, + POS_NORTH = 1, + POS_GRIMSTONE = 2, +}; + +static const float aSpawnPositions[3][4] = +{ + {608.960f, -235.322f, -53.907f, 1.857f}, // Ring mob spawn position + {644.300f, -175.989f, -53.739f, 3.418f}, // Ring boss spawn position + {625.559f, -205.618f, -52.735f, 2.609f} // Grimstone spawn position +}; + +static const uint32 aGladiator[MAX_POSSIBLE_THELDREN_ADDS] = {NPC_LEFTY, NPC_ROTFANG, NPC_SNOKH, NPC_MALGEN, NPC_KORV, NPC_REZZNIK, NPC_VAJASHNI, NPC_VOLIDA}; +static const uint32 aRingMob[] = {NPC_WORM, NPC_STINGER, NPC_SCREECHER, NPC_THUNDERSNOUT, NPC_CREEPER, NPC_BEETLE}; +static const uint32 aRingBoss[] = {NPC_GOROSH, NPC_GRIZZLE, NPC_EVISCERATOR, NPC_OKTHOR, NPC_ANUBSHIAH, NPC_HEDRUM}; + +enum Phases +{ + PHASE_MOBS = 0, + PHASE_BOSS = 2, + PHASE_GLADIATORS = 3, +}; + +bool AreaTrigger_at_ring_of_law(Player* pPlayer, AreaTriggerEntry const* pAt) +{ + if (instance_blackrock_depths* pInstance = (instance_blackrock_depths*)pPlayer->GetInstanceData()) + { + if (pInstance->GetData(TYPE_RING_OF_LAW) == IN_PROGRESS || pInstance->GetData(TYPE_RING_OF_LAW) == DONE || pInstance->GetData(TYPE_RING_OF_LAW) == SPECIAL) + return false; + + if (pPlayer->isGameMaster()) + return false; + + pInstance->SetData(TYPE_RING_OF_LAW, pInstance->GetData(TYPE_RING_OF_LAW) == DATA_BANNER_BEFORE_EVENT ? SPECIAL : IN_PROGRESS); + + pPlayer->SummonCreature(NPC_GRIMSTONE, aSpawnPositions[POS_GRIMSTONE][0], aSpawnPositions[POS_GRIMSTONE][1], aSpawnPositions[POS_GRIMSTONE][2], aSpawnPositions[POS_GRIMSTONE][3], TEMPSUMMON_DEAD_DESPAWN, 0); + pInstance->SetArenaCenterCoords(pAt->x, pAt->y, pAt->z); + + return false; + } + return false; +} + +/*###### +## npc_grimstone +######*/ + +struct npc_grimstoneAI : public npc_escortAI +{ + npc_grimstoneAI(Creature* pCreature) : npc_escortAI(pCreature) + { + m_pInstance = (instance_blackrock_depths*)pCreature->GetInstanceData(); + m_uiMobSpawnId = urand(0, 5); + // Select MAX_THELDREN_ADDS(4) random adds for Theldren encounter + uint8 uiCount = 0; + for (uint8 i = 0; i < MAX_POSSIBLE_THELDREN_ADDS && uiCount < MAX_THELDREN_ADDS; ++i) + { + if (urand(0, 1) || i >= MAX_POSSIBLE_THELDREN_ADDS - MAX_THELDREN_ADDS + uiCount) + { + m_uiGladiatorId[uiCount] = aGladiator[i]; + ++uiCount; + } + } + + Reset(); + } + + instance_blackrock_depths* m_pInstance; + + uint8 m_uiEventPhase; + uint32 m_uiEventTimer; + + uint8 m_uiMobSpawnId; + uint8 m_uiMobDeadCount; + + Phases m_uiPhase; + + uint32 m_uiGladiatorId[MAX_THELDREN_ADDS]; + + GuidList m_lSummonedGUIDList; + + void Reset() override + { + m_creature->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE); + + m_uiEventTimer = 1000; + m_uiEventPhase = 0; + m_uiMobDeadCount = 0; + + m_uiPhase = PHASE_MOBS; + } + + void JustSummoned(Creature* pSummoned) override + { + if (!m_pInstance) + return; + + // Ring mob or boss summoned + float fX, fY, fZ; + float fcX, fcY, fcZ; + m_pInstance->GetArenaCenterCoords(fX, fY, fZ); + m_creature->GetRandomPoint(fX, fY, fZ, 10.0f, fcX, fcY, fcZ); + pSummoned->GetMotionMaster()->MovePoint(1, fcX, fcY, fcZ); + + m_lSummonedGUIDList.push_back(pSummoned->GetObjectGuid()); + } + + void DoChallengeQuestCredit() + { + Map::PlayerList const& PlayerList = m_creature->GetMap()->GetPlayers(); + + for (Map::PlayerList::const_iterator itr = PlayerList.begin(); itr != PlayerList.end(); ++itr) + { + Player* pPlayer = itr->getSource(); + if (pPlayer && pPlayer->GetQuestStatus(QUEST_THE_CHALLENGE) == QUEST_STATUS_INCOMPLETE) + pPlayer->KilledMonsterCredit(NPC_THELDREN_QUEST_CREDIT); + } + } + + void SummonedCreatureJustDied(Creature* /*pSummoned*/) override + { + ++m_uiMobDeadCount; + + switch (m_uiPhase) + { + case PHASE_MOBS: // Ring mob killed + if (m_uiMobDeadCount == MAX_MOB_AMOUNT) + { + m_uiEventTimer = 5000; + m_uiMobDeadCount = 0; + } + break; + case PHASE_BOSS: // Ring boss killed + // One Boss + if (m_uiMobDeadCount == 1) + { + m_uiEventTimer = 5000; + m_uiMobDeadCount = 0; + } + break; + case PHASE_GLADIATORS: // Theldren and his band killed + // Adds + Theldren + if (m_uiMobDeadCount == MAX_THELDREN_ADDS + 1) + { + m_uiEventTimer = 5000; + m_uiMobDeadCount = 0; + DoChallengeQuestCredit(); + } + break; + } + } + + void SummonRingMob(uint32 uiEntry, SpawnPosition uiPosition) + { + float fX, fY, fZ; + m_creature->GetRandomPoint(aSpawnPositions[uiPosition][0], aSpawnPositions[uiPosition][1], aSpawnPositions[uiPosition][2], 2.0f, fX, fY, fZ); + m_creature->SummonCreature(uiEntry, fX, fY, fZ, 0, TEMPSUMMON_DEAD_DESPAWN, 0); + } + + void WaypointReached(uint32 uiPointId) override + { + switch (uiPointId) + { + case 0: // Middle reached first time + DoScriptText(urand(0, 1) ? SAY_START_1 : SAY_START_2, m_creature); + SetEscortPaused(true); + m_uiEventTimer = 5000; + break; + case 1: // Reached wall again + DoScriptText(SAY_OPEN_EAST_GATE, m_creature); + SetEscortPaused(true); + m_uiEventTimer = 5000; + break; + case 2: // walking along the wall, while door opened + SetEscortPaused(true); + break; + case 3: // Middle reached second time + DoScriptText(urand(0, 1) ? SAY_SUMMON_BOSS_1 : SAY_SUMMON_BOSS_2, m_creature); + break; + case 4: // Reached North Gate + DoScriptText(SAY_OPEN_NORTH_GATE, m_creature); + SetEscortPaused(true); + m_uiEventTimer = 5000; + break; + case 5: + if (m_pInstance) + { + m_pInstance->SetData(TYPE_RING_OF_LAW, DONE); + debug_log("SD2: npc_grimstone: event reached end and set complete."); + } + break; + } + } + + void UpdateEscortAI(const uint32 uiDiff) override + { + if (!m_pInstance) + return; + + if (m_pInstance->GetData(TYPE_RING_OF_LAW) == FAIL) + { + // Reset Doors + if (m_uiEventPhase >= 9) // North Gate is opened + { + m_pInstance->DoUseDoorOrButton(GO_ARENA_2); + m_pInstance->DoUseDoorOrButton(GO_ARENA_4); + } + else if (m_uiEventPhase >= 4) // East Gate is opened + { + m_pInstance->DoUseDoorOrButton(GO_ARENA_1); + m_pInstance->DoUseDoorOrButton(GO_ARENA_4); + } + + // Despawn Summoned Mobs + for (GuidList::const_iterator itr = m_lSummonedGUIDList.begin(); itr != m_lSummonedGUIDList.end(); ++itr) + { + if (Creature* pSummoned = m_creature->GetMap()->GetCreature(*itr)) + pSummoned->ForcedDespawn(); + } + m_lSummonedGUIDList.clear(); + + // Despawn NPC + m_creature->ForcedDespawn(); + return; + } + + if (m_uiEventTimer) + { + if (m_uiEventTimer <= uiDiff) + { + switch (m_uiEventPhase) + { + case 0: + // Shortly after spawn, start walking + // DoScriptText(-1000000, m_creature); // no more text on spawn + m_pInstance->DoUseDoorOrButton(GO_ARENA_4); + Start(false); + SetEscortPaused(false); + m_uiEventTimer = 0; + break; + case 1: + // Start walking towards wall + SetEscortPaused(false); + m_uiEventTimer = 0; + break; + case 2: + m_uiEventTimer = 2000; + break; + case 3: + // Open East Gate + m_pInstance->DoUseDoorOrButton(GO_ARENA_1); + m_uiEventTimer = 3000; + break; + case 4: + SetEscortPaused(false); + m_creature->SetVisibility(VISIBILITY_OFF); + // Summon Ring Mob(s) + SummonRingMob(aRingMob[m_uiMobSpawnId], POS_EAST); + m_uiEventTimer = 8000; + break; + case 5: + // Summon Ring Mob(s) + SummonRingMob(aRingMob[m_uiMobSpawnId], POS_EAST); + SummonRingMob(aRingMob[m_uiMobSpawnId], POS_EAST); + m_uiEventTimer = 8000; + break; + case 6: + // Summon Ring Mob(s) + SummonRingMob(aRingMob[m_uiMobSpawnId], POS_EAST); + m_uiEventTimer = 0; + break; + case 7: + // Summoned Mobs are dead, continue event + m_creature->SetVisibility(VISIBILITY_ON); + m_pInstance->DoUseDoorOrButton(GO_ARENA_1); + // DoScriptText(-1000000, m_creature); // after killed the mobs, no say here + SetEscortPaused(false); + m_uiEventTimer = 0; + break; + case 8: + // Open North Gate + m_pInstance->DoUseDoorOrButton(GO_ARENA_2); + m_uiEventTimer = 5000; + break; + case 9: + // Summon Boss + m_creature->SetVisibility(VISIBILITY_OFF); + // If banner summoned after start, then summon Thelden after the creatures are dead + if (m_pInstance->GetData(TYPE_RING_OF_LAW) == SPECIAL && m_uiPhase == PHASE_MOBS) + { + m_uiPhase = PHASE_GLADIATORS; + SummonRingMob(NPC_THELDREN, POS_NORTH); + for (uint8 i = 0; i < MAX_THELDREN_ADDS; ++i) + SummonRingMob(m_uiGladiatorId[i], POS_NORTH); + } + else + { + m_uiPhase = PHASE_BOSS; + SummonRingMob(aRingBoss[urand(0, 5)], POS_NORTH); + } + m_uiEventTimer = 0; + break; + case 10: + // Boss dead + m_lSummonedGUIDList.clear(); + m_pInstance->DoUseDoorOrButton(GO_ARENA_2); + m_pInstance->DoUseDoorOrButton(GO_ARENA_3); + m_pInstance->DoUseDoorOrButton(GO_ARENA_4); + SetEscortPaused(false); + m_uiEventTimer = 0; + break; + } + ++m_uiEventPhase; + } + else + m_uiEventTimer -= uiDiff; + } + } +}; + +CreatureAI* GetAI_npc_grimstone(Creature* pCreature) +{ + return new npc_grimstoneAI(pCreature); +} + +bool EffectDummyCreature_spell_banner_of_provocation(Unit* /*pCaster*/, uint32 uiSpellId, SpellEffectIndex uiEffIndex, Creature* pCreatureTarget, ObjectGuid /*originalCasterGuid*/) +{ + if (uiSpellId == SPELL_SUMMON_THELRIN_DND && uiEffIndex != EFFECT_INDEX_0) + { + instance_blackrock_depths* pInstance = (instance_blackrock_depths*)pCreatureTarget->GetInstanceData(); + if (pInstance && pInstance->GetData(TYPE_RING_OF_LAW) != DONE && pInstance->GetData(TYPE_RING_OF_LAW) != SPECIAL) + pInstance->SetData(TYPE_RING_OF_LAW, pInstance->GetData(TYPE_RING_OF_LAW) == IN_PROGRESS ? uint32(SPECIAL) : uint32(DATA_BANNER_BEFORE_EVENT)); + + return true; + } + return false; +} + +/*###### +## npc_kharan_mighthammer +######*/ +enum +{ + QUEST_WHAT_IS_GOING_ON = 4001, + QUEST_KHARANS_TALE = 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?" +#define GOSSIP_ITEM_KHARAN_6 "Continue..." +#define GOSSIP_ITEM_KHARAN_7 "So you suspect that someone on the inside was involved? That they were tipped off?" +#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->GetObjectGuid()); + + if (pPlayer->GetQuestStatus(QUEST_WHAT_IS_GOING_ON) == 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(QUEST_KHARANS_TALE) == 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->GetObjectGuid()); + else + pPlayer->SEND_GOSSIP_MENU(2474, pCreature->GetObjectGuid()); + + return true; +} + +bool GossipSelect_npc_kharan_mighthammer(Player* pPlayer, Creature* pCreature, uint32 /*uiSender*/, uint32 uiAction) +{ + switch (uiAction) + { + case GOSSIP_ACTION_INFO_DEF+1: + pPlayer->ADD_GOSSIP_ITEM(GOSSIP_ICON_CHAT, GOSSIP_ITEM_KHARAN_3, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 2); + pPlayer->SEND_GOSSIP_MENU(2475, pCreature->GetObjectGuid()); + break; + case GOSSIP_ACTION_INFO_DEF+2: + 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->GetObjectGuid()); + 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->GetObjectGuid()); + break; + case GOSSIP_ACTION_INFO_DEF+4: + pPlayer->ADD_GOSSIP_ITEM(GOSSIP_ICON_CHAT, GOSSIP_ITEM_KHARAN_6, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 5); + pPlayer->SEND_GOSSIP_MENU(2478, pCreature->GetObjectGuid()); + break; + case GOSSIP_ACTION_INFO_DEF+5: + pPlayer->ADD_GOSSIP_ITEM(GOSSIP_ICON_CHAT, GOSSIP_ITEM_KHARAN_7, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 6); + pPlayer->SEND_GOSSIP_MENU(2479, pCreature->GetObjectGuid()); + break; + case GOSSIP_ACTION_INFO_DEF+6: + pPlayer->ADD_GOSSIP_ITEM(GOSSIP_ICON_CHAT, GOSSIP_ITEM_KHARAN_8, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 7); + pPlayer->SEND_GOSSIP_MENU(2480, pCreature->GetObjectGuid()); + break; + case GOSSIP_ACTION_INFO_DEF+7: + pPlayer->ADD_GOSSIP_ITEM(GOSSIP_ICON_CHAT, GOSSIP_ITEM_KHARAN_9, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 8); + pPlayer->SEND_GOSSIP_MENU(2481, pCreature->GetObjectGuid()); + break; + case GOSSIP_ACTION_INFO_DEF+8: + pPlayer->ADD_GOSSIP_ITEM(GOSSIP_ICON_CHAT, GOSSIP_ITEM_KHARAN_10, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 9); + pPlayer->SEND_GOSSIP_MENU(2482, pCreature->GetObjectGuid()); + break; + case GOSSIP_ACTION_INFO_DEF+9: + pPlayer->CLOSE_GOSSIP_MENU(); + if (pPlayer->GetTeam() == HORDE) + pPlayer->AreaExploredOrEventHappens(QUEST_WHAT_IS_GOING_ON); + else + pPlayer->AreaExploredOrEventHappens(QUEST_KHARANS_TALE); + break; + } + return true; +} + +/*###### +## npc_rocknot +######*/ + +enum +{ + SAY_GOT_BEER = -1230000, + + SPELL_DRUNKEN_RAGE = 14872, + + QUEST_ALE = 4295 +}; + +struct npc_rocknotAI : public npc_escortAI +{ + npc_rocknotAI(Creature* pCreature) : npc_escortAI(pCreature) + { + m_pInstance = (ScriptedInstance*)pCreature->GetInstanceData(); + Reset(); + } + + ScriptedInstance* m_pInstance; + + uint32 m_uiBreakKegTimer; + uint32 m_uiBreakDoorTimer; + + void Reset() override + { + if (HasEscortState(STATE_ESCORT_ESCORTING)) + return; + + m_uiBreakKegTimer = 0; + m_uiBreakDoorTimer = 0; + } + + void DoGo(uint32 id, uint32 state) + { + if (GameObject* pGo = m_pInstance->GetSingleGameObjectFromStorage(id)) + pGo->SetGoState(GOState(state)); + } + + void WaypointReached(uint32 uiPointId) override + { + if (!m_pInstance) + return; + + switch (uiPointId) + { + case 1: + m_creature->HandleEmote(EMOTE_ONESHOT_KICK); + break; + case 2: + m_creature->HandleEmote(EMOTE_ONESHOT_ATTACKUNARMED); + break; + case 3: + m_creature->HandleEmote(EMOTE_ONESHOT_ATTACKUNARMED); + break; + case 4: + m_creature->HandleEmote(EMOTE_ONESHOT_KICK); + break; + case 5: + m_creature->HandleEmote(EMOTE_ONESHOT_KICK); + m_uiBreakKegTimer = 2000; + break; + } + } + + void UpdateEscortAI(const uint32 uiDiff) override + { + if (!m_pInstance) + return; + + if (m_uiBreakKegTimer) + { + if (m_uiBreakKegTimer <= uiDiff) + { + DoGo(GO_BAR_KEG_SHOT, 0); + m_uiBreakKegTimer = 0; + m_uiBreakDoorTimer = 1000; + } + else + m_uiBreakKegTimer -= uiDiff; + } + + if (m_uiBreakDoorTimer) + { + if (m_uiBreakDoorTimer <= uiDiff) + { + DoGo(GO_BAR_DOOR, 2); + DoGo(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 (Creature* pTmp = m_pInstance->GetSingleCreatureFromStorage(NPC_PHALANX)) + pTmp->SetFactionTemporary(14, TEMPFACTION_NONE); + + // for later, this event(s) has alot more to it. + // optionally, DONE can trigger bar to go hostile. + m_pInstance->SetData(TYPE_BAR, DONE); + + m_uiBreakDoorTimer = 0; + } + else + m_uiBreakDoorTimer -= uiDiff; + } + } +}; + +CreatureAI* GetAI_npc_rocknot(Creature* pCreature) +{ + return new npc_rocknotAI(pCreature); +} + +bool QuestRewarded_npc_rocknot(Player* /*pPlayer*/, Creature* pCreature, Quest const* pQuest) +{ + ScriptedInstance* pInstance = (ScriptedInstance*)pCreature->GetInstanceData(); + + if (!pInstance) + return true; + + if (pInstance->GetData(TYPE_BAR) == DONE || pInstance->GetData(TYPE_BAR) == SPECIAL) + return true; + + if (pQuest->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) + { + DoScriptText(SAY_GOT_BEER, pCreature); + pCreature->CastSpell(pCreature, SPELL_DRUNKEN_RAGE, false); + + if (npc_rocknotAI* pEscortAI = dynamic_cast(pCreature->AI())) + pEscortAI->Start(false, NULL, NULL, true); + } + } + + return true; +} + +/*###### +## npc_marshal_windsor +######*/ + +enum +{ + // Windsor texts + SAY_WINDSOR_AGGRO1 = -1230011, + SAY_WINDSOR_AGGRO2 = -1230012, + SAY_WINDSOR_AGGRO3 = -1230013, + SAY_WINDSOR_START = -1230014, + SAY_WINDSOR_CELL_DUGHAL_1 = -1230015, + SAY_WINDSOR_CELL_DUGHAL_3 = -1230016, + SAY_WINDSOR_EQUIPMENT_1 = -1230017, + SAY_WINDSOR_EQUIPMENT_2 = -1230018, + SAY_WINDSOR_EQUIPMENT_3 = -1230019, + SAY_WINDSOR_EQUIPMENT_4 = -1230020, + SAY_WINDSOR_CELL_JAZ_1 = -1230021, + SAY_WINDSOR_CELL_JAZ_2 = -1230022, + SAY_WINDSOR_CELL_SHILL_1 = -1230023, + SAY_WINDSOR_CELL_SHILL_2 = -1230024, + SAY_WINDSOR_CELL_SHILL_3 = -1230025, + SAY_WINDSOR_CELL_CREST_1 = -1230026, + SAY_WINDSOR_CELL_CREST_2 = -1230027, + SAY_WINDSOR_CELL_TOBIAS_1 = -1230028, + SAY_WINDSOR_CELL_TOBIAS_2 = -1230029, + SAY_WINDSOR_FREE_1 = -1230030, + SAY_WINDSOR_FREE_2 = -1230031, + + // Additional gossips + SAY_DUGHAL_FREE = -1230010, + GOSSIP_ID_DUGHAL = -3230000, + GOSSIP_TEXT_ID_DUGHAL = 2846, + + SAY_TOBIAS_FREE_1 = -1230032, + SAY_TOBIAS_FREE_2 = -1230033, + GOSSIP_ID_TOBIAS = -3230001, + GOSSIP_TEXT_ID_TOBIAS = 2847, + + NPC_REGINALD_WINDSOR = 9682, + + QUEST_JAIL_BREAK = 4322 +}; + +struct npc_marshal_windsorAI : public npc_escortAI +{ + npc_marshal_windsorAI(Creature* m_creature) : npc_escortAI(m_creature) + { + m_pInstance = (instance_blackrock_depths*)m_creature->GetInstanceData(); + Reset(); + } + + instance_blackrock_depths* m_pInstance; + + uint8 m_uiEventPhase; + + void Reset() override + { + if (!HasEscortState(STATE_ESCORT_ESCORTING)) + m_uiEventPhase = 0; + } + + void Aggro(Unit* pWho) override + { + switch (urand(0, 2)) + { + case 0: DoScriptText(SAY_WINDSOR_AGGRO1, m_creature, pWho); break; + case 1: DoScriptText(SAY_WINDSOR_AGGRO2, m_creature); break; + case 2: DoScriptText(SAY_WINDSOR_AGGRO3, m_creature, pWho); break; + } + } + + void WaypointReached(uint32 uiPointId) override + { + switch (uiPointId) + { + case 1: + if (m_pInstance) + m_pInstance->SetData(TYPE_QUEST_JAIL_BREAK, IN_PROGRESS); + + DoScriptText(SAY_WINDSOR_START, m_creature); + break; + case 7: + if (Player* pPlayer = GetPlayerForEscort()) + DoScriptText(SAY_WINDSOR_CELL_DUGHAL_1, m_creature, pPlayer); + if (m_pInstance) + { + if (Creature* pDughal = m_pInstance->GetSingleCreatureFromStorage(NPC_DUGHAL)) + { + pDughal->SetFlag(UNIT_NPC_FLAGS, UNIT_NPC_FLAG_GOSSIP); + m_creature->SetFacingToObject(pDughal); + } + } + ++m_uiEventPhase; + SetEscortPaused(true); + break; + case 9: + if (Player* pPlayer = GetPlayerForEscort()) + DoScriptText(SAY_WINDSOR_CELL_DUGHAL_3, m_creature, pPlayer); + break; + case 14: + if (Player* pPlayer = GetPlayerForEscort()) + DoScriptText(SAY_WINDSOR_EQUIPMENT_1, m_creature, pPlayer); + break; + case 15: + m_creature->HandleEmoteCommand(EMOTE_ONESHOT_USESTANDING); + break; + case 16: + if (m_pInstance) + m_pInstance->DoUseDoorOrButton(GO_JAIL_DOOR_SUPPLY); + break; + case 18: + DoScriptText(SAY_WINDSOR_EQUIPMENT_2, m_creature); + break; + case 19: + m_creature->HandleEmoteCommand(EMOTE_ONESHOT_USESTANDING); + break; + case 20: + if (m_pInstance) + m_pInstance->DoUseDoorOrButton(GO_JAIL_SUPPLY_CRATE); + break; + case 21: + m_creature->UpdateEntry(NPC_REGINALD_WINDSOR); + break; + case 22: + if (Player* pPlayer = GetPlayerForEscort()) + { + DoScriptText(SAY_WINDSOR_EQUIPMENT_3, m_creature, pPlayer); + m_creature->SetFacingToObject(pPlayer); + } + break; + case 23: + DoScriptText(SAY_WINDSOR_EQUIPMENT_4, m_creature); + if (Player* pPlayer = GetPlayerForEscort()) + m_creature->SetFacingToObject(pPlayer); + break; + case 30: + if (m_pInstance) + { + if (Creature* pJaz = m_pInstance->GetSingleCreatureFromStorage(NPC_JAZ)) + m_creature->SetFacingToObject(pJaz); + } + DoScriptText(SAY_WINDSOR_CELL_JAZ_1, m_creature); + ++m_uiEventPhase; + SetEscortPaused(true); + break; + case 32: + DoScriptText(SAY_WINDSOR_CELL_JAZ_2, m_creature); + break; + case 35: + if (m_pInstance) + { + if (Creature* pShill = m_pInstance->GetSingleCreatureFromStorage(NPC_SHILL)) + m_creature->SetFacingToObject(pShill); + } + DoScriptText(SAY_WINDSOR_CELL_SHILL_1, m_creature); + ++m_uiEventPhase; + SetEscortPaused(true); + break; + case 37: + DoScriptText(SAY_WINDSOR_CELL_SHILL_2, m_creature); + break; + case 38: + DoScriptText(SAY_WINDSOR_CELL_SHILL_3, m_creature); + break; + case 45: + if (m_pInstance) + { + if (Creature* pCrest = m_pInstance->GetSingleCreatureFromStorage(NPC_CREST)) + m_creature->SetFacingToObject(pCrest); + } + DoScriptText(SAY_WINDSOR_CELL_CREST_1, m_creature); + ++m_uiEventPhase; + SetEscortPaused(true); + break; + case 47: + DoScriptText(SAY_WINDSOR_CELL_CREST_2, m_creature); + break; + case 49: + DoScriptText(SAY_WINDSOR_CELL_TOBIAS_1, m_creature); + if (m_pInstance) + { + if (Creature* pTobias = m_pInstance->GetSingleCreatureFromStorage(NPC_TOBIAS)) + { + pTobias->SetFlag(UNIT_NPC_FLAGS, UNIT_NPC_FLAG_GOSSIP); + m_creature->SetFacingToObject(pTobias); + } + } + ++m_uiEventPhase; + SetEscortPaused(true); + break; + case 51: + if (Player* pPlayer = GetPlayerForEscort()) + DoScriptText(SAY_WINDSOR_CELL_TOBIAS_2, m_creature, pPlayer); + break; + case 57: + DoScriptText(SAY_WINDSOR_FREE_1, m_creature); + if (Player* pPlayer = GetPlayerForEscort()) + m_creature->SetFacingToObject(pPlayer); + break; + case 58: + DoScriptText(SAY_WINDSOR_FREE_2, m_creature); + if (m_pInstance) + m_pInstance->SetData(TYPE_QUEST_JAIL_BREAK, DONE); + + if (Player* pPlayer = GetPlayerForEscort()) + pPlayer->GroupEventHappens(QUEST_JAIL_BREAK, m_creature); + break; + } + } + + void UpdateEscortAI(const uint32 /*uiDiff*/) override + { + // Handle escort resume events + if (m_pInstance && m_pInstance->GetData(TYPE_QUEST_JAIL_BREAK) == SPECIAL) + { + switch (m_uiEventPhase) + { + case 1: // Dughal + case 3: // Ograbisi + case 4: // Crest + case 5: // Shill + case 6: // Tobias + SetEscortPaused(false); + break; + case 2: // Jaz + ++m_uiEventPhase; + break; + } + + m_pInstance->SetData(TYPE_QUEST_JAIL_BREAK, IN_PROGRESS); + } + + if (!m_creature->SelectHostileTarget() || !m_creature->getVictim()) + return; + + DoMeleeAttackIfReady(); + } +}; + +CreatureAI* GetAI_npc_marshal_windsor(Creature* pCreature) +{ + return new npc_marshal_windsorAI(pCreature); +} + +bool QuestAccept_npc_marshal_windsor(Player* pPlayer, Creature* pCreature, const Quest* pQuest) +{ + if (pQuest->GetQuestId() == QUEST_JAIL_BREAK) + { + pCreature->SetFactionTemporary(FACTION_ESCORT_A_NEUTRAL_ACTIVE, TEMPFACTION_RESTORE_RESPAWN); + + if (npc_marshal_windsorAI* pEscortAI = dynamic_cast(pCreature->AI())) + pEscortAI->Start(false, pPlayer, pQuest); + + return true; + } + + return false; +} + +/*###### +## npc_dughal_stormwing +######*/ + +bool GossipHello_npc_dughal_stormwing(Player* pPlayer, Creature* pCreature) +{ + if (pPlayer->GetQuestStatus(QUEST_JAIL_BREAK) == QUEST_STATUS_INCOMPLETE) + pPlayer->ADD_GOSSIP_ITEM_ID(GOSSIP_ICON_CHAT, GOSSIP_ID_DUGHAL, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 1); + + pPlayer->SEND_GOSSIP_MENU(GOSSIP_TEXT_ID_DUGHAL, pCreature->GetObjectGuid()); + return true; +} + +bool GossipSelect_npc_dughal_stormwing(Player* pPlayer, Creature* pCreature, uint32 /*uiSender*/, uint32 uiAction) +{ + if (uiAction == GOSSIP_ACTION_INFO_DEF + 1) + { + // Set instance data in order to allow the quest to continue + if (instance_blackrock_depths* pInstance = (instance_blackrock_depths*)pCreature->GetInstanceData()) + pInstance->SetData(TYPE_QUEST_JAIL_BREAK, SPECIAL); + + DoScriptText(SAY_DUGHAL_FREE, pCreature, pPlayer); + pCreature->RemoveFlag(UNIT_NPC_FLAGS, UNIT_NPC_FLAG_GOSSIP); + + pCreature->SetWalk(false); + pCreature->GetMotionMaster()->MoveWaypoint(); + + pPlayer->CLOSE_GOSSIP_MENU(); + } + + return true; +} + +/*###### +## npc_tobias_seecher +######*/ + +bool GossipHello_npc_tobias_seecher(Player* pPlayer, Creature* pCreature) +{ + if (pPlayer->GetQuestStatus(QUEST_JAIL_BREAK) == QUEST_STATUS_INCOMPLETE) + pPlayer->ADD_GOSSIP_ITEM_ID(GOSSIP_ICON_CHAT, GOSSIP_ID_TOBIAS, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 1); + + pPlayer->SEND_GOSSIP_MENU(GOSSIP_TEXT_ID_TOBIAS, pCreature->GetObjectGuid()); + + return true; +} + +bool GossipSelect_npc_tobias_seecher(Player* pPlayer, Creature* pCreature, uint32 /*uiSender*/, uint32 uiAction) +{ + if (uiAction == GOSSIP_ACTION_INFO_DEF + 1) + { + // Set instance data in order to allow the quest to continue + if (instance_blackrock_depths* pInstance = (instance_blackrock_depths*)pCreature->GetInstanceData()) + pInstance->SetData(TYPE_QUEST_JAIL_BREAK, SPECIAL); + + DoScriptText(urand(0, 1) ? SAY_TOBIAS_FREE_1 : SAY_TOBIAS_FREE_2, pCreature); + pCreature->RemoveFlag(UNIT_NPC_FLAGS, UNIT_NPC_FLAG_GOSSIP); + + pCreature->SetWalk(false); + pCreature->GetMotionMaster()->MoveWaypoint(); + + pPlayer->CLOSE_GOSSIP_MENU(); + } + + return true; +} + +/*###### +## boss_doomrel +######*/ + +enum +{ + SAY_DOOMREL_START_EVENT = -1230003, + GOSSIP_ITEM_CHALLENGE = -3230002, + GOSSIP_TEXT_ID_CHALLENGE = 2601, +}; + +bool GossipHello_boss_doomrel(Player* pPlayer, Creature* pCreature) +{ + if (ScriptedInstance* pInstance = (ScriptedInstance*)pCreature->GetInstanceData()) + { + if (pInstance->GetData(TYPE_TOMB_OF_SEVEN) == NOT_STARTED || pInstance->GetData(TYPE_TOMB_OF_SEVEN) == FAIL) + pPlayer->ADD_GOSSIP_ITEM_ID(GOSSIP_ICON_CHAT, GOSSIP_ITEM_CHALLENGE, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 1); + } + + pPlayer->SEND_GOSSIP_MENU(GOSSIP_TEXT_ID_CHALLENGE, pCreature->GetObjectGuid()); + return true; +} + +bool GossipSelect_boss_doomrel(Player* pPlayer, Creature* pCreature, uint32 /*uiSender*/, uint32 uiAction) +{ + switch (uiAction) + { + case GOSSIP_ACTION_INFO_DEF+1: + pPlayer->CLOSE_GOSSIP_MENU(); + DoScriptText(SAY_DOOMREL_START_EVENT, pCreature); + // start event + if (ScriptedInstance* pInstance = (ScriptedInstance*)pCreature->GetInstanceData()) + pInstance->SetData(TYPE_TOMB_OF_SEVEN, IN_PROGRESS); + + break; + } + return true; +} + +void AddSC_blackrock_depths() +{ + Script* pNewScript; + + pNewScript = new Script; + pNewScript->Name = "go_shadowforge_brazier"; + pNewScript->pGOUse = &GOUse_go_shadowforge_brazier; + pNewScript->RegisterSelf(); + + pNewScript = new Script; + pNewScript->Name = "go_relic_coffer_door"; + pNewScript->pGOUse = &GOUse_go_relic_coffer_door; + pNewScript->RegisterSelf(); + + pNewScript = new Script; + pNewScript->Name = "at_ring_of_law"; + pNewScript->pAreaTrigger = &AreaTrigger_at_ring_of_law; + pNewScript->RegisterSelf(); + + pNewScript = new Script; + pNewScript->Name = "npc_grimstone"; + pNewScript->GetAI = &GetAI_npc_grimstone; + pNewScript->RegisterSelf(); + + pNewScript = new Script; + pNewScript->Name = "npc_theldren_trigger"; + pNewScript->pEffectDummyNPC = &EffectDummyCreature_spell_banner_of_provocation; + pNewScript->RegisterSelf(); + + pNewScript = new Script; + pNewScript->Name = "npc_kharan_mighthammer"; + pNewScript->pGossipHello = &GossipHello_npc_kharan_mighthammer; + pNewScript->pGossipSelect = &GossipSelect_npc_kharan_mighthammer; + pNewScript->RegisterSelf(); + + pNewScript = new Script; + pNewScript->Name = "npc_rocknot"; + pNewScript->GetAI = &GetAI_npc_rocknot; + pNewScript->pQuestRewardedNPC = &QuestRewarded_npc_rocknot; + pNewScript->RegisterSelf(); + + pNewScript = new Script; + pNewScript->Name = "npc_marshal_windsor"; + pNewScript->GetAI = &GetAI_npc_marshal_windsor; + pNewScript->pQuestAcceptNPC = &QuestAccept_npc_marshal_windsor; + pNewScript->RegisterSelf(); + + pNewScript = new Script; + pNewScript->Name = "npc_tobias_seecher"; + pNewScript->pGossipHello = &GossipHello_npc_tobias_seecher; + pNewScript->pGossipSelect = &GossipSelect_npc_tobias_seecher; + pNewScript->RegisterSelf(); + + pNewScript = new Script; + pNewScript->Name = "npc_dughal_stormwing"; + pNewScript->pGossipHello = &GossipHello_npc_dughal_stormwing; + pNewScript->pGossipSelect = &GossipSelect_npc_dughal_stormwing; + pNewScript->RegisterSelf(); + + pNewScript = new Script; + pNewScript->Name = "boss_doomrel"; + pNewScript->pGossipHello = &GossipHello_boss_doomrel; + pNewScript->pGossipSelect = &GossipSelect_boss_doomrel; + pNewScript->RegisterSelf(); +} diff --git a/src/modules/SD2/scripts/eastern_kingdoms/blackrock_mountain/blackrock_depths/blackrock_depths.h b/src/modules/SD2/scripts/eastern_kingdoms/blackrock_mountain/blackrock_depths/blackrock_depths.h new file mode 100644 index 000000000..a7f1a4b24 --- /dev/null +++ b/src/modules/SD2/scripts/eastern_kingdoms/blackrock_mountain/blackrock_depths/blackrock_depths.h @@ -0,0 +1,164 @@ +/* Copyright (C) 2006 - 2013 ScriptDev2 + * 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 + +enum +{ + MAX_ENCOUNTER = 7, + MAX_RELIC_DOORS = 12, + MAX_DWARFS = 7, + + TYPE_RING_OF_LAW = 1, + TYPE_VAULT = 2, + TYPE_BAR = 3, + TYPE_TOMB_OF_SEVEN = 4, + TYPE_LYCEUM = 5, + TYPE_IRON_HALL = 6, + TYPE_QUEST_JAIL_BREAK = 7, + + NPC_EMPEROR = 9019, + NPC_PRINCESS = 8929, + NPC_PHALANX = 9502, + NPC_HATEREL = 9034, + NPC_ANGERREL = 9035, + NPC_VILEREL = 9036, + NPC_GLOOMREL = 9037, + NPC_SEETHREL = 9038, + NPC_DOOMREL = 9039, + NPC_DOPEREL = 9040, + NPC_MAGMUS = 9938, + NPC_WATCHER_DOOMGRIP = 9476, + NPC_WARBRINGER_CONST = 8905, // Four of them in Relict Vault are related to Doomgrip summon event + + // Jail Break event related + NPC_OGRABISI = 9677, + NPC_SHILL = 9678, + NPC_CREST = 9680, + NPC_JAZ = 9681, + NPC_TOBIAS = 9679, + NPC_DUGHAL = 9022, + + GO_ARENA_1 = 161525, + GO_ARENA_2 = 161522, + GO_ARENA_3 = 161524, + GO_ARENA_4 = 161523, + + GO_SHADOW_LOCK = 161460, + GO_SHADOW_MECHANISM = 161461, + GO_SHADOW_GIANT_DOOR = 157923, + GO_SHADOW_DUMMY = 161516, + GO_BAR_KEG_SHOT = 170607, + GO_BAR_KEG_TRAP = 171941, + GO_BAR_DOOR = 170571, + GO_TOMB_ENTER = 170576, + GO_TOMB_EXIT = 170577, + GO_LYCEUM = 170558, + GO_GOLEM_ROOM_N = 170573, + GO_GOLEM_ROOM_S = 170574, + GO_THRONE_ROOM = 170575, + + GO_SPECTRAL_CHALICE = 164869, + GO_CHEST_SEVEN = 169243, + GO_ARENA_SPOILS = 181074, + GO_SECRET_DOOR = 174553, + + // Jail break event related + GO_JAIL_DOOR_SUPPLY = 170561, + GO_JAIL_SUPPLY_CRATE = 166872, + + SPELL_STONED = 10255, // Aura of Warbringer Constructs in Relict Vault + + FACTION_DWARF_HOSTILE = 754, // Hostile faction for the Tomb of the Seven dwarfs +}; + +enum ArenaNPCs +{ + // Gladiators + NPC_LEFTY = 16049, + NPC_ROTFANG = 16050, + NPC_SNOKH = 16051, + NPC_MALGEN = 16052, + NPC_KORV = 16053, + NPC_REZZNIK = 16054, + NPC_VAJASHNI = 16055, + NPC_VOLIDA = 16058, + NPC_THELDREN = 16059, + // Ring mobs + NPC_WORM = 8925, + NPC_STINGER = 8926, + NPC_SCREECHER = 8927, + NPC_THUNDERSNOUT = 8928, + NPC_CREEPER = 8933, + NPC_BEETLE = 8932, + // Ring bosses + NPC_GOROSH = 9027, + NPC_GRIZZLE = 9028, + NPC_EVISCERATOR = 9029, + NPC_OKTHOR = 9030, + NPC_ANUBSHIAH = 9031, + NPC_HEDRUM = 9032 +}; + +static const uint32 aArenaNPCs[] = +{ + // Gladiators + NPC_LEFTY, NPC_ROTFANG, NPC_SNOKH, NPC_MALGEN, NPC_KORV, NPC_REZZNIK, NPC_VAJASHNI, NPC_VOLIDA, NPC_THELDREN, + // Ring mobs + NPC_WORM, NPC_STINGER, NPC_SCREECHER, NPC_THUNDERSNOUT, NPC_CREEPER, NPC_BEETLE, + // Ring bosses + NPC_GOROSH, NPC_GRIZZLE, NPC_EVISCERATOR, NPC_OKTHOR, NPC_ANUBSHIAH, NPC_HEDRUM +}; + +// Used to summon Watcher Doomgrip +static const float aVaultPositions[4] = {821.905f, -338.382f, -50.134f, 3.78736f}; + +// Tomb of the Seven dwarfs +static const uint32 aTombDwarfes[MAX_DWARFS] = {NPC_ANGERREL, NPC_SEETHREL, NPC_DOPEREL, NPC_GLOOMREL, NPC_VILEREL, NPC_HATEREL, NPC_DOOMREL}; + +class instance_blackrock_depths : public ScriptedInstance +{ + public: + instance_blackrock_depths(Map* pMap); + ~instance_blackrock_depths() {} + + void Initialize() override; + + void OnCreatureCreate(Creature* pCreature) override; + void OnCreatureEnterCombat(Creature* pCreature) override; + void OnCreatureDeath(Creature* pCreature) override; + void OnCreatureEvade(Creature* pCreature); + void OnObjectCreate(GameObject* pGo) override; + + void SetData(uint32 uiType, uint32 uiData) override; + uint32 GetData(uint32 uiType) const override; + + const char* Save() const override { return m_strInstData.c_str(); } + void Load(const char* chrIn) override; + + void Update(uint32 uiDiff) override; + + // Arena Event + void SetArenaCenterCoords(float fX, float fY, float fZ) { m_fArenaCenterX = fX; m_fArenaCenterY = fY; m_fArenaCenterZ = fZ; } + void GetArenaCenterCoords(float& fX, float& fY, float& fZ) { fX = m_fArenaCenterX; fY = m_fArenaCenterY; fZ = m_fArenaCenterZ; } + + private: + void DoCallNextDwarf(); + + uint32 m_auiEncounter[MAX_ENCOUNTER]; + std::string m_strInstData; + + uint32 m_uiBarAleCount; + uint8 m_uiCofferDoorsOpened; + + uint8 m_uiDwarfRound; + uint32 m_uiDwarfFightTimer; + + float m_fArenaCenterX, m_fArenaCenterY, m_fArenaCenterZ; + + GuidSet m_sVaultNpcGuids; +}; + +#endif diff --git a/src/modules/SD2/scripts/eastern_kingdoms/blackrock_mountain/blackrock_depths/boss_ambassador_flamelash.cpp b/src/modules/SD2/scripts/eastern_kingdoms/blackrock_mountain/blackrock_depths/boss_ambassador_flamelash.cpp new file mode 100644 index 000000000..9e99674d4 --- /dev/null +++ b/src/modules/SD2/scripts/eastern_kingdoms/blackrock_mountain/blackrock_depths/boss_ambassador_flamelash.cpp @@ -0,0 +1,122 @@ +/* Copyright (C) 2006 - 2013 ScriptDev2 + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with 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: 80 +SDComment: Texts missing, Add handling rather guesswork, Add spell Burning Spirit likely won't work +SDCategory: Blackrock Depths +EndScriptData */ + +#include "precompiled.h" + +enum +{ + SPELL_FIREBLAST = 15573, + SPELL_BURNING_SPIRIT = 13489, + SPELL_BURNING_SPIRIT_BUFF = 14744, + + NPC_BURNING_SPIRIT = 9178, +}; + +struct boss_ambassador_flamelashAI : public ScriptedAI +{ + boss_ambassador_flamelashAI(Creature* pCreature) : ScriptedAI(pCreature) {Reset();} + + uint32 m_uiSpiritTimer; + int Rand; + int RandX; + int RandY; + + void Reset() override + { + m_uiSpiritTimer = 12000; + } + + void SummonSpirits() + { + float fX, fY, fZ; + m_creature->GetRandomPoint(m_creature->GetPositionX(), m_creature->GetPositionY(), m_creature->GetPositionZ(), 30.0f, fX, fY, fZ); + m_creature->SummonCreature(NPC_BURNING_SPIRIT, fX, fY, fZ, m_creature->GetAngle(fX, fY) + M_PI_F, TEMPSUMMON_TIMED_OOC_OR_DEAD_DESPAWN, 60000); + } + + void Aggro(Unit* /*pWho*/) override + { + DoCastSpellIfCan(m_creature, SPELL_FIREBLAST); + } + + void JustSummoned(Creature* pSummoned) override + { + pSummoned->GetMotionMaster()->MovePoint(1, m_creature->GetPositionX(), m_creature->GetPositionY(), m_creature->GetPositionZ()); + } + + void SummonedMovementInform(Creature* pSummoned, uint32 /*uiMotionType*/, uint32 uiPointId) override + { + if (uiPointId != 1) + return; + + pSummoned->CastSpell(m_creature, SPELL_BURNING_SPIRIT, true); + } + + void UpdateAI(const uint32 uiDiff) override + { + // Return since we have no target + if (!m_creature->SelectHostileTarget() || !m_creature->getVictim()) + return; + + // m_uiSpiritTimer + if (m_uiSpiritTimer < uiDiff) + { + SummonSpirits(); + SummonSpirits(); + SummonSpirits(); + SummonSpirits(); + + m_uiSpiritTimer = 20000; + } + else + m_uiSpiritTimer -= uiDiff; + + DoMeleeAttackIfReady(); + } +}; + +bool EffectDummyCreature_spell_boss_ambassador_flamelash(Unit* /*pCaster*/, uint32 uiSpellId, SpellEffectIndex uiEffIndex, Creature* pCreatureTarget, ObjectGuid /*originalCasterGuid*/) +{ + if (uiSpellId == SPELL_BURNING_SPIRIT && uiEffIndex == EFFECT_INDEX_1) + { + pCreatureTarget->CastSpell(pCreatureTarget, SPELL_BURNING_SPIRIT_BUFF, true); + return true; + } + + return false; +} + +CreatureAI* GetAI_boss_ambassador_flamelash(Creature* pCreature) +{ + return new boss_ambassador_flamelashAI(pCreature); +} + +void AddSC_boss_ambassador_flamelash() +{ + Script* pNewScript; + + pNewScript = new Script; + pNewScript->Name = "boss_ambassador_flamelash"; + pNewScript->GetAI = &GetAI_boss_ambassador_flamelash; + pNewScript->pEffectDummyNPC = &EffectDummyCreature_spell_boss_ambassador_flamelash; + pNewScript->RegisterSelf(); +} diff --git a/src/modules/SD2/scripts/eastern_kingdoms/blackrock_mountain/blackrock_depths/boss_coren_direbrew.cpp b/src/modules/SD2/scripts/eastern_kingdoms/blackrock_mountain/blackrock_depths/boss_coren_direbrew.cpp new file mode 100644 index 000000000..ee42c52ee --- /dev/null +++ b/src/modules/SD2/scripts/eastern_kingdoms/blackrock_mountain/blackrock_depths/boss_coren_direbrew.cpp @@ -0,0 +1,181 @@ +/* Copyright (C) 2006 - 2013 ScriptDev2 + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +/* ScriptData +SDName: boss_coren_direbrew +SD%Complete: 75 +SDComment: Some parts are not complete - requires additional research. Brewmaidens scripts handled in eventAI. +SDCategory: Blackrock Depths +EndScriptData */ + +#include "precompiled.h" + +enum +{ + SAY_AGGRO = -1230034, + + // spells + SPELL_DIREBREW_DISARM = 47310, + SPELL_SUMMON_DIREBREW_MINION = 47375, + SPELL_DIREBREW_CHARGE = 47718, + SPELL_SUMMON_MOLE_MACHINE = 47691, // triggers 47690 + + // summoned auras + SPELL_PORT_TO_COREN = 52850, + + // other summoned spells - currently not used in script + // SPELL_CHUCK_MUG = 50276, + // SPELL_BARRELED_AURA = 50278, // used by Ursula + // SPELL_HAS_BREW = 47331, // triggers 47344 - aura which asks for the second brew on item expire + // SPELL_SEND_FIRST_MUG = 47333, // triggers 47345 + // SPELL_SEND_SECOND_MUG = 47339, // triggers 47340 - spell triggered by 47344 + // SPELL_BREWMAIDEN_DESPAWN_AURA = 48186, // purpose unk + + // npcs + NPC_DIREBREW_MINION = 26776, + NPC_ILSA_DIREBREW = 26764, + NPC_URSULA_DIREBREW = 26822, + + // other + FACTION_HOSTILE = 736, + + QUEST_INSULT_COREN = 12062, + + MAX_DIREBREW_MINIONS = 3, +}; + +struct boss_coren_direbrewAI : public ScriptedAI +{ + boss_coren_direbrewAI(Creature* pCreature) : ScriptedAI(pCreature) { Reset(); } + + uint32 m_uiDisarmTimer; + uint32 m_uiChargeTimer; + uint32 m_uiSummonTimer; + uint8 m_uiPhase; + + void Reset() override + { + m_uiDisarmTimer = 10000; + m_uiChargeTimer = 5000; + m_uiSummonTimer = 15000; + m_uiPhase = 0; + } + + void Aggro(Unit* /*pWho*/) override + { + // Spawn 3 minions on aggro + for (uint8 i = 0; i < MAX_DIREBREW_MINIONS; ++i) + DoCastSpellIfCan(m_creature, SPELL_SUMMON_DIREBREW_MINION, CAST_TRIGGERED); + } + + void JustSummoned(Creature* pSummoned) override + { + switch (pSummoned->GetEntry()) + { + case NPC_ILSA_DIREBREW: + case NPC_URSULA_DIREBREW: + pSummoned->CastSpell(m_creature, SPELL_PORT_TO_COREN, true); + break; + } + + if (m_creature->getVictim()) + pSummoned->AI()->AttackStart(m_creature->getVictim()); + } + + void UpdateAI(const uint32 uiDiff) override + { + if (!m_creature->SelectHostileTarget() || !m_creature->getVictim()) + return; + + // Spawn Ilsa + if (m_creature->GetHealthPercent() < 66.0f && m_uiPhase == 0) + { + float fX, fY, fZ; + m_creature->GetRandomPoint(m_creature->GetPositionX(), m_creature->GetPositionY(), m_creature->GetPositionZ(), 10, fX, fY, fZ); + m_creature->SummonCreature(NPC_ILSA_DIREBREW, fX, fY, fZ, 0, TEMPSUMMON_DEAD_DESPAWN, 0); + m_uiPhase = 1; + } + + // Spawn Ursula + if (m_creature->GetHealthPercent() < 33.0f && m_uiPhase == 1) + { + float fX, fY, fZ; + m_creature->GetRandomPoint(m_creature->GetPositionX(), m_creature->GetPositionY(), m_creature->GetPositionZ(), 10, fX, fY, fZ); + m_creature->SummonCreature(NPC_URSULA_DIREBREW, fX, fY, fZ, 0, TEMPSUMMON_DEAD_DESPAWN, 0); + m_uiPhase = 2; + } + + if (m_uiDisarmTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature, SPELL_DIREBREW_DISARM) == CAST_OK) + m_uiDisarmTimer = 15000; + } + else + m_uiDisarmTimer -= uiDiff; + + if (m_uiChargeTimer < uiDiff) + { + if (Unit* pTarget = m_creature->SelectAttackingTarget(ATTACKING_TARGET_RANDOM, 0, SPELL_DIREBREW_CHARGE, SELECT_FLAG_NOT_IN_MELEE_RANGE)) + { + if (DoCastSpellIfCan(pTarget, SPELL_DIREBREW_CHARGE) == CAST_OK) + m_uiChargeTimer = urand(5000, 10000); + } + } + else + m_uiChargeTimer -= uiDiff; + + if (m_uiSummonTimer < uiDiff) + { + for (uint8 i = 0; i < MAX_DIREBREW_MINIONS; ++i) + DoCastSpellIfCan(m_creature, SPELL_SUMMON_DIREBREW_MINION, CAST_TRIGGERED); + + m_uiSummonTimer = 15000; + } + else + m_uiSummonTimer -= uiDiff; + + DoMeleeAttackIfReady(); + } +}; + +CreatureAI* GetAI_boss_coren_direbrew(Creature* pCreature) +{ + return new boss_coren_direbrewAI(pCreature); +} + +bool QuestRewarded_npc_coren_direbrew(Player* pPlayer, Creature* pCreature, Quest const* pQuest) +{ + if (pQuest->GetQuestId() == QUEST_INSULT_COREN) + { + DoScriptText(SAY_AGGRO, pCreature, pPlayer); + + pCreature->SetFactionTemporary(FACTION_HOSTILE, TEMPFACTION_RESTORE_REACH_HOME | TEMPFACTION_RESTORE_RESPAWN); + pCreature->AI()->AttackStart(pPlayer); + } + + return true; +} + +void AddSC_boss_coren_direbrew() +{ + Script* pNewScript; + + pNewScript = new Script; + pNewScript->Name = "boss_coren_direbrew"; + pNewScript->GetAI = &GetAI_boss_coren_direbrew; + pNewScript->pQuestRewardedNPC = &QuestRewarded_npc_coren_direbrew; + pNewScript->RegisterSelf(); +} diff --git a/src/modules/SD2/scripts/eastern_kingdoms/blackrock_mountain/blackrock_depths/boss_emperor_dagran_thaurissan.cpp b/src/modules/SD2/scripts/eastern_kingdoms/blackrock_mountain/blackrock_depths/boss_emperor_dagran_thaurissan.cpp new file mode 100644 index 000000000..49f73c034 --- /dev/null +++ b/src/modules/SD2/scripts/eastern_kingdoms/blackrock_mountain/blackrock_depths/boss_emperor_dagran_thaurissan.cpp @@ -0,0 +1,260 @@ +/* Copyright (C) 2006 - 2013 ScriptDev2 + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with 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: 90 +SDComment: With script for Moria +SDCategory: Blackrock Depths +EndScriptData */ + +#include "precompiled.h" +#include "blackrock_depths.h" + +enum eEmperor +{ + FACTION_NEUTRAL = 734, + SAY_AGGRO = -1230001, + SAY_SLAY = -1230002, + + SPELL_HANDOFTHAURISSAN = 17492, + SPELL_AVATAROFFLAME = 15636 +}; + +struct boss_emperor_dagran_thaurissanAI : public ScriptedAI +{ + boss_emperor_dagran_thaurissanAI(Creature* pCreature) : ScriptedAI(pCreature) + { + m_pInstance = (ScriptedInstance*)pCreature->GetInstanceData(); + Reset(); + } + + ScriptedInstance* m_pInstance; + + uint32 m_uiHandOfThaurissan_Timer; + uint32 m_uiAvatarOfFlame_Timer; + // uint32 m_uiCounter; + + void Reset() override + { + m_uiHandOfThaurissan_Timer = 4000; + m_uiAvatarOfFlame_Timer = 25000; + // m_uiCounter = 0; + } + + void Aggro(Unit* /*pWho*/) override + { + DoScriptText(SAY_AGGRO, m_creature); + m_creature->CallForHelp(VISIBLE_RANGE); + } + + void JustDied(Unit* /*pVictim*/) override + { + if (!m_pInstance) + return; + + if (Creature* pPrincess = m_pInstance->GetSingleCreatureFromStorage(NPC_PRINCESS)) + { + if (pPrincess->IsAlive()) + { + pPrincess->SetFactionTemporary(FACTION_NEUTRAL, TEMPFACTION_NONE); + pPrincess->AI()->EnterEvadeMode(); + } + } + } + + void KilledUnit(Unit* /*pVictim*/) override + { + DoScriptText(SAY_SLAY, m_creature); + } + + void UpdateAI(const uint32 uiDiff) override + { + if (!m_creature->SelectHostileTarget() || !m_creature->getVictim()) + return; + + if (m_uiHandOfThaurissan_Timer < uiDiff) + { + if (Unit* pTarget = m_creature->SelectAttackingTarget(ATTACKING_TARGET_RANDOM, 0)) + DoCastSpellIfCan(pTarget, SPELL_HANDOFTHAURISSAN); + + // 3 Hands of Thaurissan will be casted + // if (m_uiCounter < 3) + //{ + // m_uiHandOfThaurissan_Timer = 1000; + // ++m_uiCounter; + //} + // else + //{ + m_uiHandOfThaurissan_Timer = 5000; + // m_uiCounter = 0; + //} + } + else + m_uiHandOfThaurissan_Timer -= uiDiff; + + // AvatarOfFlame_Timer + if (m_uiAvatarOfFlame_Timer < uiDiff) + { + DoCastSpellIfCan(m_creature->getVictim(), SPELL_AVATAROFFLAME); + m_uiAvatarOfFlame_Timer = 18000; + } + else + m_uiAvatarOfFlame_Timer -= uiDiff; + + DoMeleeAttackIfReady(); + } +}; + +CreatureAI* GetAI_boss_emperor_dagran_thaurissan(Creature* pCreature) +{ + return new boss_emperor_dagran_thaurissanAI(pCreature); +} + +/*###### +## boss_moira_bronzebeard +######*/ + +enum ePrincess +{ + SPELL_HEAL = 15586, + SPELL_RENEW = 10929, + SPELL_SHIELD = 10901, + SPELL_MINDBLAST = 15587, + SPELL_SHADOWWORDPAIN = 15654, + SPELL_SMITE = 10934, + SPELL_SHADOW_BOLT = 15537, + SPELL_OPEN_PORTAL = 13912 +}; + +struct boss_moira_bronzebeardAI : public ScriptedAI +{ + boss_moira_bronzebeardAI(Creature* pCreature) : ScriptedAI(pCreature) + { + m_pInstance = (ScriptedInstance*)pCreature->GetInstanceData(); + Reset(); + } + + ScriptedInstance* m_pInstance; + + uint32 m_uiHeal_Timer; + uint32 m_uiMindBlast_Timer; + uint32 m_uiShadowWordPain_Timer; + uint32 m_uiSmite_Timer; + + void Reset() override + { + m_uiHeal_Timer = 12000; // These times are probably wrong + m_uiMindBlast_Timer = 16000; + m_uiShadowWordPain_Timer = 2000; + m_uiSmite_Timer = 8000; + } + + void AttackStart(Unit* pWho) override + { + if (m_creature->Attack(pWho, false)) + { + m_creature->AddThreat(pWho); + m_creature->SetInCombatWith(pWho); + pWho->SetInCombatWith(m_creature); + + m_creature->GetMotionMaster()->MoveChase(pWho, 25.0f); + } + } + + void JustReachedHome() override + { + if (m_pInstance) + { + if (Creature* pEmperor = m_pInstance->GetSingleCreatureFromStorage(NPC_EMPEROR)) + { + // if evade, then check if he is alive. If not, start make portal + if (!pEmperor->IsAlive()) + m_creature->CastSpell(m_creature, SPELL_OPEN_PORTAL, false); + } + } + } + + void UpdateAI(const uint32 uiDiff) override + { + // Return since we have no target + if (!m_creature->SelectHostileTarget() || !m_creature->getVictim()) + return; + + // MindBlast_Timer + if (m_uiMindBlast_Timer < uiDiff) + { + DoCastSpellIfCan(m_creature->getVictim(), SPELL_MINDBLAST); + m_uiMindBlast_Timer = 14000; + } + else + m_uiMindBlast_Timer -= uiDiff; + + // ShadowWordPain_Timer + if (m_uiShadowWordPain_Timer < uiDiff) + { + DoCastSpellIfCan(m_creature->getVictim(), SPELL_SHADOWWORDPAIN); + m_uiShadowWordPain_Timer = 18000; + } + else + m_uiShadowWordPain_Timer -= uiDiff; + + // Smite_Timer + if (m_uiSmite_Timer < uiDiff) + { + DoCastSpellIfCan(m_creature->getVictim(), SPELL_SMITE); + m_uiSmite_Timer = 10000; + } + else + m_uiSmite_Timer -= uiDiff; + + // Heal_Timer + if (m_uiHeal_Timer < uiDiff) + { + if (Creature* pEmperor = m_pInstance->GetSingleCreatureFromStorage(NPC_EMPEROR)) + { + if (pEmperor->IsAlive() && pEmperor->GetHealthPercent() != 100.0f) + DoCastSpellIfCan(pEmperor, SPELL_HEAL); + } + + m_uiHeal_Timer = 10000; + } + else + m_uiHeal_Timer -= uiDiff; + + // No meele? + } +}; + +CreatureAI* GetAI_boss_moira_bronzebeard(Creature* pCreature) +{ + return new boss_moira_bronzebeardAI(pCreature); +} + +void AddSC_boss_draganthaurissan() +{ + Script* pNewScript; + + pNewScript = new Script; + pNewScript->Name = "boss_emperor_dagran_thaurissan"; + pNewScript->GetAI = &GetAI_boss_emperor_dagran_thaurissan; + pNewScript->RegisterSelf(); + + pNewScript = new Script; + pNewScript->Name = "boss_moira_bronzebeard"; + pNewScript->GetAI = &GetAI_boss_moira_bronzebeard; + pNewScript->RegisterSelf(); +} diff --git a/src/modules/SD2/scripts/eastern_kingdoms/blackrock_mountain/blackrock_depths/boss_general_angerforge.cpp b/src/modules/SD2/scripts/eastern_kingdoms/blackrock_mountain/blackrock_depths/boss_general_angerforge.cpp new file mode 100644 index 000000000..b6f6e3f45 --- /dev/null +++ b/src/modules/SD2/scripts/eastern_kingdoms/blackrock_mountain/blackrock_depths/boss_general_angerforge.cpp @@ -0,0 +1,142 @@ +/* Copyright (C) 2006 - 2013 ScriptDev2 + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with 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" + +enum +{ + SPELL_MIGHTYBLOW = 14099, + SPELL_HAMSTRING = 9080, + SPELL_CLEAVE = 20691, + + NPC_ANVILRAGE_RESERVIST = 8901, + NPC_ANVILRAGE_MEDIC = 8894, +}; + +struct boss_general_angerforgeAI : public ScriptedAI +{ + boss_general_angerforgeAI(Creature* pCreature) : ScriptedAI(pCreature) { Reset(); } + + uint32 m_uiMightyBlowTimer; + uint32 m_uiHamStringTimer; + uint32 m_uiCleaveTimer; + uint32 m_uiAddsTimer; + bool m_bSummonedMedics; + + void Reset() override + { + m_uiMightyBlowTimer = 8000; + m_uiHamStringTimer = 12000; + m_uiCleaveTimer = 16000; + m_uiAddsTimer = 0; + m_bSummonedMedics = false; + } + + void SummonAdd(uint32 uiEntry) + { + float fX, fY, fZ; + m_creature->GetRandomPoint(m_creature->GetPositionX(), m_creature->GetPositionY(), m_creature->GetPositionZ(), 20.0f, fX, fY, fZ); + m_creature->SummonCreature(uiEntry, fX, fY, fZ, 0.0f, TEMPSUMMON_TIMED_OOC_OR_DEAD_DESPAWN, 60000); + } + + void JustSummoned(Creature* pSummoned) override + { + if (Unit* pTarget = m_creature->SelectAttackingTarget(ATTACKING_TARGET_RANDOM, 0)) + pSummoned->AI()->AttackStart(pTarget); + } + + void UpdateAI(const uint32 uiDiff) override + { + // Return since we have no target + if (!m_creature->SelectHostileTarget() || !m_creature->getVictim()) + return; + + // MightyBlow_Timer + if (m_uiMightyBlowTimer < uiDiff) + { + DoCastSpellIfCan(m_creature->getVictim(), SPELL_MIGHTYBLOW); + m_uiMightyBlowTimer = 18000; + } + else + m_uiMightyBlowTimer -= uiDiff; + + // HamString_Timer + if (m_uiHamStringTimer < uiDiff) + { + DoCastSpellIfCan(m_creature->getVictim(), SPELL_HAMSTRING); + m_uiHamStringTimer = 15000; + } + else + m_uiHamStringTimer -= uiDiff; + + // Cleave_Timer + if (m_uiCleaveTimer < uiDiff) + { + DoCastSpellIfCan(m_creature->getVictim(), SPELL_CLEAVE); + m_uiCleaveTimer = 9000; + } + else + m_uiCleaveTimer -= uiDiff; + + // Adds_Timer + if (m_creature->GetHealthPercent() < 21.0f) + { + if (m_uiAddsTimer < uiDiff) + { + // summon 3 Adds every 25s + SummonAdd(NPC_ANVILRAGE_RESERVIST); + SummonAdd(NPC_ANVILRAGE_RESERVIST); + SummonAdd(NPC_ANVILRAGE_RESERVIST); + + m_uiAddsTimer = 25000; + } + else + m_uiAddsTimer -= uiDiff; + } + + // Summon Medics + if (!m_bSummonedMedics && m_creature->GetHealthPercent() < 21.0f) + { + SummonAdd(NPC_ANVILRAGE_MEDIC); + SummonAdd(NPC_ANVILRAGE_MEDIC); + m_bSummonedMedics = true; + } + + DoMeleeAttackIfReady(); + } +}; + +CreatureAI* GetAI_boss_general_angerforge(Creature* pCreature) +{ + return new boss_general_angerforgeAI(pCreature); +} + +void AddSC_boss_general_angerforge() +{ + Script* pNewScript; + + pNewScript = new Script; + pNewScript->Name = "boss_general_angerforge"; + pNewScript->GetAI = &GetAI_boss_general_angerforge; + pNewScript->RegisterSelf(); +} diff --git a/src/modules/SD2/scripts/eastern_kingdoms/blackrock_mountain/blackrock_depths/boss_high_interrogator_gerstahn.cpp b/src/modules/SD2/scripts/eastern_kingdoms/blackrock_mountain/blackrock_depths/boss_high_interrogator_gerstahn.cpp new file mode 100644 index 000000000..971409c1a --- /dev/null +++ b/src/modules/SD2/scripts/eastern_kingdoms/blackrock_mountain/blackrock_depths/boss_high_interrogator_gerstahn.cpp @@ -0,0 +1,114 @@ +/* Copyright (C) 2006 - 2013 ScriptDev2 + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with 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" + +enum +{ + SPELL_SHADOWWORDPAIN = 14032, + SPELL_MANABURN = 14033, + SPELL_PSYCHICSCREAM = 13704, + SPELL_SHADOWSHIELD = 12040 +}; + +struct boss_high_interrogator_gerstahnAI : public ScriptedAI +{ + boss_high_interrogator_gerstahnAI(Creature* pCreature) : ScriptedAI(pCreature) {Reset();} + + uint32 m_uiShadowWordPainTimer; + uint32 m_uiManaBurnTimer; + uint32 m_uiPsychicScreamTimer; + uint32 m_uiShadowShieldTimer; + + void Reset() override + { + m_uiShadowWordPainTimer = 4000; + m_uiManaBurnTimer = 14000; + m_uiPsychicScreamTimer = 32000; + m_uiShadowShieldTimer = 8000; + } + + void UpdateAI(const uint32 uiDiff) override + { + // Return since we have no target + if (!m_creature->SelectHostileTarget() || !m_creature->getVictim()) + return; + + // ShadowWordPain_Timer + if (m_uiShadowWordPainTimer < uiDiff) + { + if (Unit* pTarget = m_creature->SelectAttackingTarget(ATTACKING_TARGET_RANDOM, 0)) + DoCastSpellIfCan(pTarget, SPELL_SHADOWWORDPAIN); + + m_uiShadowWordPainTimer = 7000; + } + else + m_uiShadowWordPainTimer -= uiDiff; + + // ManaBurn_Timer + if (m_uiManaBurnTimer < uiDiff) + { + if (Unit* pTarget = m_creature->SelectAttackingTarget(ATTACKING_TARGET_RANDOM, 0, SPELL_MANABURN, SELECT_FLAG_POWER_MANA)) + DoCastSpellIfCan(pTarget, SPELL_MANABURN); + + m_uiManaBurnTimer = 10000; + } + else + m_uiManaBurnTimer -= uiDiff; + + // PsychicScream_Timer + if (m_uiPsychicScreamTimer < uiDiff) + { + DoCastSpellIfCan(m_creature, SPELL_PSYCHICSCREAM); + m_uiPsychicScreamTimer = 30000; + } + else + m_uiPsychicScreamTimer -= uiDiff; + + // ShadowShield_Timer + if (m_uiShadowShieldTimer < uiDiff) + { + DoCastSpellIfCan(m_creature, SPELL_SHADOWSHIELD); + m_uiShadowShieldTimer = 25000; + } + else + m_uiShadowShieldTimer -= uiDiff; + + DoMeleeAttackIfReady(); + } +}; + +CreatureAI* GetAI_boss_high_interrogator_gerstahn(Creature* pCreature) +{ + return new boss_high_interrogator_gerstahnAI(pCreature); +} + +void AddSC_boss_high_interrogator_gerstahn() +{ + Script* pNewScript; + + pNewScript = new Script; + pNewScript->Name = "boss_high_interrogator_gerstahn"; + pNewScript->GetAI = &GetAI_boss_high_interrogator_gerstahn; + pNewScript->RegisterSelf(); +} diff --git a/src/modules/SD2/scripts/eastern_kingdoms/blackrock_mountain/blackrock_depths/instance_blackrock_depths.cpp b/src/modules/SD2/scripts/eastern_kingdoms/blackrock_mountain/blackrock_depths/instance_blackrock_depths.cpp new file mode 100644 index 000000000..2e9c565ba --- /dev/null +++ b/src/modules/SD2/scripts/eastern_kingdoms/blackrock_mountain/blackrock_depths/instance_blackrock_depths.cpp @@ -0,0 +1,417 @@ +/* Copyright (C) 2006 - 2013 ScriptDev2 + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along 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: 80 +SDComment: +SDCategory: Blackrock Depths +EndScriptData */ + +#include "precompiled.h" +#include "blackrock_depths.h" + +instance_blackrock_depths::instance_blackrock_depths(Map* pMap) : ScriptedInstance(pMap), + m_uiBarAleCount(0), + m_uiCofferDoorsOpened(0), + m_uiDwarfRound(0), + m_uiDwarfFightTimer(0), + + m_fArenaCenterX(0.0f), + m_fArenaCenterY(0.0f), + m_fArenaCenterZ(0.0f) +{ + Initialize(); +} + +void instance_blackrock_depths::Initialize() +{ + memset(&m_auiEncounter, 0, sizeof(m_auiEncounter)); +} + +void instance_blackrock_depths::OnCreatureCreate(Creature* pCreature) +{ + switch (pCreature->GetEntry()) + { + case NPC_EMPEROR: + case NPC_PRINCESS: + case NPC_PHALANX: + case NPC_HATEREL: + case NPC_ANGERREL: + case NPC_VILEREL: + case NPC_GLOOMREL: + case NPC_SEETHREL: + case NPC_DOOMREL: + case NPC_DOPEREL: + case NPC_SHILL: + case NPC_CREST: + case NPC_JAZ: + case NPC_TOBIAS: + case NPC_DUGHAL: + m_mNpcEntryGuidStore[pCreature->GetEntry()] = pCreature->GetObjectGuid(); + break; + + case NPC_WARBRINGER_CONST: + // Golems not in the Relict Vault? + if (std::abs(pCreature->GetPositionZ() - aVaultPositions[2]) > 1.0f || !pCreature->IsWithinDist2d(aVaultPositions[0], aVaultPositions[1], 20.0f)) + break; + // Golems in Relict Vault need to have a stoned aura, set manually to prevent reapply when reached home + pCreature->CastSpell(pCreature, SPELL_STONED, true); + // Store the Relict Vault Golems into m_sVaultNpcGuids + case NPC_WATCHER_DOOMGRIP: + m_sVaultNpcGuids.insert(pCreature->GetObjectGuid()); + break; + } +} + +void instance_blackrock_depths::OnObjectCreate(GameObject* pGo) +{ + switch (pGo->GetEntry()) + { + case GO_ARENA_1: + case GO_ARENA_2: + case GO_ARENA_3: + case GO_ARENA_4: + case GO_SHADOW_LOCK: + case GO_SHADOW_MECHANISM: + case GO_SHADOW_GIANT_DOOR: + case GO_SHADOW_DUMMY: + case GO_BAR_KEG_SHOT: + case GO_BAR_KEG_TRAP: + case GO_BAR_DOOR: + case GO_TOMB_ENTER: + case GO_TOMB_EXIT: + case GO_LYCEUM: + case GO_GOLEM_ROOM_N: + case GO_GOLEM_ROOM_S: + case GO_THRONE_ROOM: + case GO_SPECTRAL_CHALICE: + case GO_CHEST_SEVEN: + case GO_ARENA_SPOILS: + case GO_SECRET_DOOR: + case GO_JAIL_DOOR_SUPPLY: + case GO_JAIL_SUPPLY_CRATE: + break; + + default: + return; + } + m_mGoEntryGuidStore[pGo->GetEntry()] = pGo->GetObjectGuid(); +} + +void instance_blackrock_depths::SetData(uint32 uiType, uint32 uiData) +{ + switch (uiType) + { + case TYPE_RING_OF_LAW: + // If finished the arena event after theldren fight + if (uiData == DONE && m_auiEncounter[0] == SPECIAL) + DoRespawnGameObject(GO_ARENA_SPOILS, HOUR); + m_auiEncounter[0] = uiData; + break; + case TYPE_VAULT: + if (uiData == SPECIAL) + { + ++m_uiCofferDoorsOpened; + + if (m_uiCofferDoorsOpened == MAX_RELIC_DOORS) + { + SetData(TYPE_VAULT, IN_PROGRESS); + + Creature* pConstruct = NULL; + + // Activate vault constructs + for (GuidSet::const_iterator itr = m_sVaultNpcGuids.begin(); itr != m_sVaultNpcGuids.end(); ++itr) + { + pConstruct = instance->GetCreature(*itr); + if (pConstruct) + pConstruct->RemoveAurasDueToSpell(SPELL_STONED); + } + + if (!pConstruct) + return; + + // Summon doomgrip + pConstruct->SummonCreature(NPC_WATCHER_DOOMGRIP, aVaultPositions[0], aVaultPositions[1], aVaultPositions[2], aVaultPositions[3], TEMPSUMMON_DEAD_DESPAWN, 0); + } + // No need to store in this case + return; + } + if (uiData == DONE) + DoUseDoorOrButton(GO_SECRET_DOOR); + m_auiEncounter[1] = uiData; + break; + case TYPE_BAR: + if (uiData == SPECIAL) + ++m_uiBarAleCount; + else + m_auiEncounter[2] = uiData; + break; + case TYPE_TOMB_OF_SEVEN: + // Don't set the same data twice + if (uiData == m_auiEncounter[3]) + break; + // Combat door + DoUseDoorOrButton(GO_TOMB_ENTER); + // Start the event + if (uiData == IN_PROGRESS) + DoCallNextDwarf(); + if (uiData == FAIL) + { + // Reset dwarfes + for (uint8 i = 0; i < MAX_DWARFS; ++i) + { + if (Creature* pDwarf = GetSingleCreatureFromStorage(aTombDwarfes[i])) + { + if (!pDwarf->IsAlive()) + pDwarf->Respawn(); + } + } + + m_uiDwarfRound = 0; + m_uiDwarfFightTimer = 0; + } + if (uiData == DONE) + { + DoRespawnGameObject(GO_CHEST_SEVEN, HOUR); + DoUseDoorOrButton(GO_TOMB_EXIT); + } + m_auiEncounter[3] = uiData; + break; + case TYPE_LYCEUM: + if (uiData == DONE) + { + DoUseDoorOrButton(GO_GOLEM_ROOM_N); + DoUseDoorOrButton(GO_GOLEM_ROOM_S); + } + m_auiEncounter[4] = uiData; + break; + case TYPE_IRON_HALL: + switch (uiData) + { + case IN_PROGRESS: + DoUseDoorOrButton(GO_GOLEM_ROOM_N); + DoUseDoorOrButton(GO_GOLEM_ROOM_S); + break; + case FAIL: + DoUseDoorOrButton(GO_GOLEM_ROOM_N); + DoUseDoorOrButton(GO_GOLEM_ROOM_S); + break; + case DONE: + DoUseDoorOrButton(GO_GOLEM_ROOM_N); + DoUseDoorOrButton(GO_GOLEM_ROOM_S); + DoUseDoorOrButton(GO_THRONE_ROOM); + break; + } + m_auiEncounter[5] = uiData; + break; + case TYPE_QUEST_JAIL_BREAK: + m_auiEncounter[6] = uiData; + return; + } + + 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_strInstData = saveStream.str(); + + SaveToDB(); + OUT_SAVE_INST_DATA_COMPLETE; + } +} + +uint32 instance_blackrock_depths::GetData(uint32 uiType) const +{ + switch (uiType) + { + case TYPE_RING_OF_LAW: + return m_auiEncounter[0]; + case TYPE_VAULT: + return m_auiEncounter[1]; + case TYPE_BAR: + if (m_auiEncounter[2] == IN_PROGRESS && m_uiBarAleCount == 3) + return SPECIAL; + else + return m_auiEncounter[2]; + case TYPE_TOMB_OF_SEVEN: + return m_auiEncounter[3]; + case TYPE_LYCEUM: + return m_auiEncounter[4]; + case TYPE_IRON_HALL: + return m_auiEncounter[5]; + case TYPE_QUEST_JAIL_BREAK: + return m_auiEncounter[6]; + default: + return 0; + } +} + +void instance_blackrock_depths::Load(const char* chrIn) +{ + if (!chrIn) + { + 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]; + + for (uint8 i = 0; i < MAX_ENCOUNTER; ++i) + if (m_auiEncounter[i] == IN_PROGRESS) + m_auiEncounter[i] = NOT_STARTED; + + OUT_LOAD_INST_DATA_COMPLETE; +} + +void instance_blackrock_depths::OnCreatureEnterCombat(Creature* pCreature) +{ + if (pCreature->GetEntry() == NPC_MAGMUS) + SetData(TYPE_IRON_HALL, IN_PROGRESS); +} + +void instance_blackrock_depths::OnCreatureEvade(Creature* pCreature) +{ + if (GetData(TYPE_RING_OF_LAW) == IN_PROGRESS || GetData(TYPE_RING_OF_LAW) == SPECIAL) + { + for (uint8 i = 0; i < countof(aArenaNPCs); ++i) + { + if (pCreature->GetEntry() == aArenaNPCs[i]) + { + SetData(TYPE_RING_OF_LAW, FAIL); + return; + } + } + } + + switch (pCreature->GetEntry()) + { + // Handle Tomb of the Seven reset in case of wipe + case NPC_HATEREL: + case NPC_ANGERREL: + case NPC_VILEREL: + case NPC_GLOOMREL: + case NPC_SEETHREL: + case NPC_DOPEREL: + case NPC_DOOMREL: + SetData(TYPE_TOMB_OF_SEVEN, FAIL); + break; + case NPC_MAGMUS: + SetData(TYPE_IRON_HALL, FAIL); + break; + } +} + +void instance_blackrock_depths::OnCreatureDeath(Creature* pCreature) +{ + switch (pCreature->GetEntry()) + { + case NPC_WARBRINGER_CONST: + case NPC_WATCHER_DOOMGRIP: + if (GetData(TYPE_VAULT) == IN_PROGRESS) + { + m_sVaultNpcGuids.erase(pCreature->GetObjectGuid()); + + // If all event npcs dead then set event to done + if (m_sVaultNpcGuids.empty()) + SetData(TYPE_VAULT, DONE); + } + break; + case NPC_OGRABISI: + case NPC_SHILL: + case NPC_CREST: + case NPC_JAZ: + if (GetData(TYPE_QUEST_JAIL_BREAK) == IN_PROGRESS) + SetData(TYPE_QUEST_JAIL_BREAK, SPECIAL); + break; + // Handle Tomb of the Seven dwarf death event + case NPC_HATEREL: + case NPC_ANGERREL: + case NPC_VILEREL: + case NPC_GLOOMREL: + case NPC_SEETHREL: + case NPC_DOPEREL: + // Only handle the event when event is in progress + if (GetData(TYPE_TOMB_OF_SEVEN) != IN_PROGRESS) + return; + // Call the next dwarf only if it's the last one which joined the fight + if (pCreature->GetEntry() == aTombDwarfes[m_uiDwarfRound - 1]) + DoCallNextDwarf(); + break; + case NPC_DOOMREL: + SetData(TYPE_TOMB_OF_SEVEN, DONE); + break; + case NPC_MAGMUS: + SetData(TYPE_IRON_HALL, DONE); + break; + } +} + +void instance_blackrock_depths::DoCallNextDwarf() +{ + if (Creature* pDwarf = GetSingleCreatureFromStorage(aTombDwarfes[m_uiDwarfRound])) + { + if (Player* pPlayer = GetPlayerInMap()) + { + pDwarf->SetFactionTemporary(FACTION_DWARF_HOSTILE, TEMPFACTION_RESTORE_RESPAWN | TEMPFACTION_RESTORE_REACH_HOME); + pDwarf->AI()->AttackStart(pPlayer); + } + } + m_uiDwarfFightTimer = 30000; + ++m_uiDwarfRound; +} + +void instance_blackrock_depths::Update(uint32 uiDiff) +{ + if (m_uiDwarfFightTimer) + { + if (m_uiDwarfFightTimer <= uiDiff) + { + if (m_uiDwarfRound < MAX_DWARFS) + { + DoCallNextDwarf(); + m_uiDwarfFightTimer = 30000; + } + else + m_uiDwarfFightTimer = 0; + } + else + m_uiDwarfFightTimer -= uiDiff; + } +} + +InstanceData* GetInstanceData_instance_blackrock_depths(Map* pMap) +{ + return new instance_blackrock_depths(pMap); +} + +void AddSC_instance_blackrock_depths() +{ + Script* pNewScript; + + pNewScript = new Script; + pNewScript->Name = "instance_blackrock_depths"; + pNewScript->GetInstanceData = &GetInstanceData_instance_blackrock_depths; + pNewScript->RegisterSelf(); +} diff --git a/src/modules/SD2/scripts/eastern_kingdoms/blackrock_mountain/blackrock_spire/blackrock_spire.h b/src/modules/SD2/scripts/eastern_kingdoms/blackrock_mountain/blackrock_spire/blackrock_spire.h new file mode 100644 index 000000000..943b29465 --- /dev/null +++ b/src/modules/SD2/scripts/eastern_kingdoms/blackrock_mountain/blackrock_spire/blackrock_spire.h @@ -0,0 +1,149 @@ +/* Copyright (C) 2006 - 2013 ScriptDev2 + * This program is free software licensed under GPL version 2 + * Please see the included DOCS/LICENSE.TXT for more information */ + +#ifndef DEF_BLACKROCK_SPIRE_H +#define DEF_BLACKROCK_SPIRE_H + +enum +{ + MAX_ENCOUNTER = 6, + MAX_ROOMS = 7, + + TYPE_ROOM_EVENT = 0, + TYPE_EMBERSEER = 1, + TYPE_FLAMEWREATH = 2, // Only summon once per instance + TYPE_STADIUM = 3, + TYPE_DRAKKISATH = 4, + TYPE_VALTHALAK = 5, // Only summon once per instance + + NPC_SCARSHIELD_INFILTRATOR = 10299, + NPC_BLACKHAND_SUMMONER = 9818, + NPC_BLACKHAND_VETERAN = 9819, + NPC_PYROGUARD_EMBERSEER = 9816, + NPC_SOLAKAR_FLAMEWREATH = 10264, + NPC_BLACKHAND_INCARCERATOR = 10316, + NPC_LORD_VICTOR_NEFARIUS = 10162, + NPC_REND_BLACKHAND = 10429, + NPC_GYTH = 10339, + NPC_DRAKKISATH = 10363, + NPC_CHROMATIC_WHELP = 10442, // related to Gyth arena event + NPC_CHROMATIC_DRAGON = 10447, + NPC_BLACKHAND_HANDLER = 10742, + + // Doors + GO_EMBERSEER_IN = 175244, + GO_DOORS = 175705, + GO_EMBERSEER_OUT = 175153, + GO_FATHER_FLAME = 175245, + GO_GYTH_ENTRY_DOOR = 164726, + GO_GYTH_COMBAT_DOOR = 175185, + GO_GYTH_EXIT_DOOR = 175186, + GO_DRAKKISATH_DOOR_1 = 175946, + GO_DRAKKISATH_DOOR_2 = 175947, + + GO_ROOM_7_RUNE = 175194, + GO_ROOM_3_RUNE = 175195, + GO_ROOM_6_RUNE = 175196, + GO_ROOM_1_RUNE = 175197, + GO_ROOM_5_RUNE = 175198, + GO_ROOM_2_RUNE = 175199, + GO_ROOM_4_RUNE = 175200, + + GO_ROOKERY_EGG = 175124, + + GO_EMBERSEER_RUNE_1 = 175266, + GO_EMBERSEER_RUNE_2 = 175267, + GO_EMBERSEER_RUNE_3 = 175268, + GO_EMBERSEER_RUNE_4 = 175269, + GO_EMBERSEER_RUNE_5 = 175270, + GO_EMBERSEER_RUNE_6 = 175271, + GO_EMBERSEER_RUNE_7 = 175272, + + MAX_STADIUM_WAVES = 7, + MAX_STADIUM_MOBS_PER_WAVE = 5, + + FACTION_BLACK_DRAGON = 103 +}; + +struct SpawnLocation +{ + float m_fX, m_fY, m_fZ, m_fO; +}; + +static const SpawnLocation aStadiumLocs[7] = +{ + {210.00f, -420.30f, 110.94f, 3.14f}, // dragons summon location + {210.14f, -397.54f, 111.1f}, // Gyth summon location + {163.62f, -420.33f, 110.47f}, // center of the stadium location (for movement) + {164.63f, -444.04f, 121.97f, 3.22f}, // Lord Nefarius summon position + {161.01f, -443.73f, 121.97f, 6.26f}, // Rend summon position + {164.64f, -443.30f, 121.97f, 1.61f}, // Nefarius move position + {165.74f, -466.46f, 116.80f}, // Rend move position +}; + +// Stadium event description +static const uint32 aStadiumEventNpcs[MAX_STADIUM_WAVES][5] = +{ + {NPC_CHROMATIC_WHELP, NPC_CHROMATIC_WHELP, NPC_CHROMATIC_WHELP, NPC_CHROMATIC_DRAGON, 0}, + {NPC_CHROMATIC_WHELP, NPC_CHROMATIC_WHELP, NPC_CHROMATIC_WHELP, NPC_CHROMATIC_DRAGON, 0}, + {NPC_CHROMATIC_WHELP, NPC_CHROMATIC_WHELP, NPC_CHROMATIC_DRAGON, NPC_BLACKHAND_HANDLER, 0}, + {NPC_CHROMATIC_WHELP, NPC_CHROMATIC_WHELP, NPC_CHROMATIC_DRAGON, NPC_BLACKHAND_HANDLER, 0}, + {NPC_CHROMATIC_WHELP, NPC_CHROMATIC_WHELP, NPC_CHROMATIC_WHELP, NPC_CHROMATIC_DRAGON, NPC_BLACKHAND_HANDLER}, + {NPC_CHROMATIC_WHELP, NPC_CHROMATIC_WHELP, NPC_CHROMATIC_DRAGON, NPC_CHROMATIC_DRAGON, NPC_BLACKHAND_HANDLER}, + {NPC_CHROMATIC_WHELP, NPC_CHROMATIC_WHELP, NPC_CHROMATIC_DRAGON, NPC_CHROMATIC_DRAGON, NPC_BLACKHAND_HANDLER}, +}; + +class instance_blackrock_spire : public ScriptedInstance, private DialogueHelper +{ + public: + instance_blackrock_spire(Map* pMap); + ~instance_blackrock_spire() {} + + void Initialize() override; + + void OnObjectCreate(GameObject* pGo) override; + void OnCreatureCreate(Creature* pCreature) override; + + void OnCreatureDeath(Creature* pCreature) override; + void OnCreatureEvade(Creature* pCreature); + void OnCreatureEnterCombat(Creature* pCreature) override; + + void SetData(uint32 uiType, uint32 uiData) override; + uint32 GetData(uint32 uiType) const override; + + const char* Save() const override { return m_strInstData.c_str(); } + void Load(const char* chrIn) override; + + void DoUseEmberseerRunes(bool bReset = false); + void DoProcessEmberseerEvent(); + + void DoSortRoomEventMobs(); + void GetIncarceratorGUIDList(GuidList& lList) { lList = m_lIncarceratorGUIDList; } + + void StartflamewreathEventIfCan(); + + void Update(uint32 uiDiff) override; + + private: + void JustDidDialogueStep(int32 iEntry) override; + void DoSendNextStadiumWave(); + void DoSendNextFlamewreathWave(); + + uint32 m_auiEncounter[MAX_ENCOUNTER]; + std::string m_strInstData; + + uint32 m_uiFlamewreathEventTimer; + uint32 m_uiFlamewreathWaveCount; + uint32 m_uiStadiumEventTimer; + uint8 m_uiStadiumWaves; + uint8 m_uiStadiumMobsAlive; + + ObjectGuid m_aRoomRuneGuid[MAX_ROOMS]; + GuidList m_alRoomEventMobGUIDSorted[MAX_ROOMS]; + GuidList m_lRoomEventMobGUIDList; + GuidList m_lIncarceratorGUIDList; + GuidList m_lEmberseerRunesGUIDList; +}; + +#endif diff --git a/src/modules/SD2/scripts/eastern_kingdoms/blackrock_mountain/blackrock_spire/boss_gyth.cpp b/src/modules/SD2/scripts/eastern_kingdoms/blackrock_mountain/blackrock_spire/boss_gyth.cpp new file mode 100644 index 000000000..dfae0443e --- /dev/null +++ b/src/modules/SD2/scripts/eastern_kingdoms/blackrock_mountain/blackrock_spire/boss_gyth.cpp @@ -0,0 +1,148 @@ +/* Copyright (C) 2006 - 2013 ScriptDev2 + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with 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: Timers may need adjustments +SDCategory: Blackrock Spire +EndScriptData */ + +#include "precompiled.h" +#include "blackrock_spire.h" + +enum +{ + SAY_NEFARIUS_BUFF_GYTH = -1229017, + EMOTE_KNOCKED_OFF = -1229019, + + SPELL_CHROMATIC_CHAOS = 16337, // casted by Nefarius at 50% + SPELL_REND_MOUNTS = 16167, + SPELL_SUMMON_REND = 16328, + SPELL_CORROSIVE_ACID = 16359, + SPELL_FREEZE = 16350, + SPELL_FLAME_BREATH = 16390, + SPELL_KNOCK_AWAY = 10101, +}; + +struct boss_gythAI : public ScriptedAI +{ + boss_gythAI(Creature* pCreature) : ScriptedAI(pCreature) + { + m_pInstance = (instance_blackrock_spire*) pCreature->GetInstanceData(); + Reset(); + } + + instance_blackrock_spire* m_pInstance; + + uint32 uiCorrosiveAcidTimer; + uint32 uiFreezeTimer; + uint32 uiFlamebreathTimer; + + bool m_bSummonedRend; + bool m_bHasChromaticChaos; + + void Reset() override + { + uiCorrosiveAcidTimer = 8000; + uiFreezeTimer = 11000; + uiFlamebreathTimer = 4000; + m_bSummonedRend = false; + m_bHasChromaticChaos = false; + + DoCastSpellIfCan(m_creature, SPELL_REND_MOUNTS); + } + + void JustSummoned(Creature* pSummoned) override + { + DoScriptText(EMOTE_KNOCKED_OFF, pSummoned); + } + + void UpdateAI(const uint32 uiDiff) override + { + // Return since we have no target + if (!m_creature->SelectHostileTarget() || !m_creature->getVictim()) + return; + + // Chromatic Chaos at 50% + if (!m_bHasChromaticChaos && m_creature->GetHealthPercent() < 50.0f) + { + if (m_pInstance) + { + if (Creature* pNefarius = m_pInstance->GetSingleCreatureFromStorage(NPC_LORD_VICTOR_NEFARIUS)) + { + pNefarius->CastSpell(m_creature, SPELL_CHROMATIC_CHAOS, true); + DoScriptText(SAY_NEFARIUS_BUFF_GYTH, pNefarius); + m_bHasChromaticChaos = true; + } + } + } + + // CorrosiveAcid_Timer + if (uiCorrosiveAcidTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature, SPELL_CORROSIVE_ACID) == CAST_OK) + uiCorrosiveAcidTimer = 7000; + } + else + uiCorrosiveAcidTimer -= uiDiff; + + // Freeze_Timer + if (uiFreezeTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature, SPELL_FREEZE) == CAST_OK) + uiFreezeTimer = 16000; + } + else + uiFreezeTimer -= uiDiff; + + // Flamebreath_Timer + if (uiFlamebreathTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature, SPELL_FLAME_BREATH) == CAST_OK) + uiFlamebreathTimer = 10500; + } + else + uiFlamebreathTimer -= uiDiff; + + // Summon Rend + if (!m_bSummonedRend && m_creature->GetHealthPercent() < 11.0f) + { + if (DoCastSpellIfCan(m_creature, SPELL_SUMMON_REND) == CAST_OK) + { + m_creature->RemoveAurasDueToSpell(SPELL_REND_MOUNTS); + m_bSummonedRend = true; + } + } + + DoMeleeAttackIfReady(); + } +}; + +CreatureAI* GetAI_boss_gyth(Creature* pCreature) +{ + return new boss_gythAI(pCreature); +} + +void AddSC_boss_gyth() +{ + Script* pNewScript; + + pNewScript = new Script; + pNewScript->Name = "boss_gyth"; + pNewScript->GetAI = &GetAI_boss_gyth; + pNewScript->RegisterSelf(); +} diff --git a/src/modules/SD2/scripts/eastern_kingdoms/blackrock_mountain/blackrock_spire/boss_overlord_wyrmthalak.cpp b/src/modules/SD2/scripts/eastern_kingdoms/blackrock_mountain/blackrock_spire/boss_overlord_wyrmthalak.cpp new file mode 100644 index 000000000..7502aa68d --- /dev/null +++ b/src/modules/SD2/scripts/eastern_kingdoms/blackrock_mountain/blackrock_spire/boss_overlord_wyrmthalak.cpp @@ -0,0 +1,142 @@ +/* Copyright (C) 2006 - 2013 ScriptDev2 + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with 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" + +enum +{ + SPELL_BLASTWAVE = 11130, + SPELL_SHOUT = 23511, + SPELL_CLEAVE = 20691, + SPELL_KNOCKAWAY = 20686, + + NPC_SPIRESTONE_WARLORD = 9216, + NPC_SMOLDERTHORN_BERSERKER = 9268 +}; + +const float afLocations[2][4] = +{ + { -39.355381f, -513.456482f, 88.472046f, 4.679872f}, + { -49.875881f, -511.896942f, 88.195160f, 4.613114f} +}; + +struct boss_overlordwyrmthalakAI : public ScriptedAI +{ + boss_overlordwyrmthalakAI(Creature* pCreature) : ScriptedAI(pCreature) {Reset();} + + uint32 m_uiBlastWaveTimer; + uint32 m_uiShoutTimer; + uint32 m_uiCleaveTimer; + uint32 m_uiKnockawayTimer; + bool m_bSummoned; + + void Reset() override + { + m_uiBlastWaveTimer = 20000; + m_uiShoutTimer = 2000; + m_uiCleaveTimer = 6000; + m_uiKnockawayTimer = 12000; + m_bSummoned = false; + } + + void JustSummoned(Creature* pSummoned) override + { + if (pSummoned->GetEntry() != NPC_SPIRESTONE_WARLORD && pSummoned->GetEntry() != NPC_SMOLDERTHORN_BERSERKER) + return; + + if (m_creature->getVictim()) + { + Unit* pTarget = m_creature->SelectAttackingTarget(ATTACKING_TARGET_RANDOM, 0); + pSummoned->AI()->AttackStart(pTarget ? pTarget : m_creature->getVictim()); + } + } + + void UpdateAI(const uint32 uiDiff) override + { + // Return since we have no target + if (!m_creature->SelectHostileTarget() || !m_creature->getVictim()) + return; + + // BlastWave + if (m_uiBlastWaveTimer < uiDiff) + { + DoCastSpellIfCan(m_creature, SPELL_BLASTWAVE); + m_uiBlastWaveTimer = 20000; + } + else + m_uiBlastWaveTimer -= uiDiff; + + // Shout + if (m_uiShoutTimer < uiDiff) + { + DoCastSpellIfCan(m_creature, SPELL_SHOUT); + m_uiShoutTimer = 10000; + } + else + m_uiShoutTimer -= uiDiff; + + // Cleave + if (m_uiCleaveTimer < uiDiff) + { + DoCastSpellIfCan(m_creature->getVictim(), SPELL_CLEAVE); + m_uiCleaveTimer = 7000; + } + else + m_uiCleaveTimer -= uiDiff; + + // Knockaway + if (m_uiKnockawayTimer < uiDiff) + { + DoCastSpellIfCan(m_creature, SPELL_KNOCKAWAY); + m_uiKnockawayTimer = 14000; + } + else + m_uiKnockawayTimer -= uiDiff; + + // Summon two Beserks + if (!m_bSummoned && m_creature->GetHealthPercent() < 51.0f) + { + m_creature->SummonCreature(NPC_SPIRESTONE_WARLORD, afLocations[0][0], afLocations[0][1], afLocations[0][2], afLocations[0][3], TEMPSUMMON_TIMED_DESPAWN, 300000); + m_creature->SummonCreature(NPC_SMOLDERTHORN_BERSERKER, afLocations[1][0], afLocations[1][1], afLocations[1][2], afLocations[1][3], TEMPSUMMON_TIMED_DESPAWN, 300000); + + m_bSummoned = true; + } + + DoMeleeAttackIfReady(); + } +}; + +CreatureAI* GetAI_boss_overlordwyrmthalak(Creature* pCreature) +{ + return new boss_overlordwyrmthalakAI(pCreature); +} + +void AddSC_boss_overlordwyrmthalak() +{ + Script* pNewScript; + + pNewScript = new Script; + pNewScript->Name = "boss_overlord_wyrmthalak"; + pNewScript->GetAI = &GetAI_boss_overlordwyrmthalak; + pNewScript->RegisterSelf(); +} diff --git a/src/modules/SD2/scripts/eastern_kingdoms/blackrock_mountain/blackrock_spire/boss_pyroguard_emberseer.cpp b/src/modules/SD2/scripts/eastern_kingdoms/blackrock_mountain/blackrock_spire/boss_pyroguard_emberseer.cpp new file mode 100644 index 000000000..b862cd08d --- /dev/null +++ b/src/modules/SD2/scripts/eastern_kingdoms/blackrock_mountain/blackrock_spire/boss_pyroguard_emberseer.cpp @@ -0,0 +1,213 @@ +/* Copyright (C) 2006 - 2013 ScriptDev2 + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with 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: 90 +SDComment: Dummy spells used during the transformation may need further research +SDCategory: Blackrock Spire +EndScriptData */ + +#include "precompiled.h" +#include "blackrock_spire.h" + +enum +{ + // Intro emote/say + EMOTE_NEAR = -1229001, + EMOTE_FULL = -1229002, + SAY_FREE = -1229003, + + MAX_GROWING_STACKS = 20, + + // Intro spells + SPELL_ENCAGE_EMBERSEER = 15281, // cast by Blackhand Incarcerator + + SPELL_FIRE_SHIELD = 13376, // not sure what's the purpose of this + SPELL_DESPAWN_EMBERSEER = 16078, // not sure what's the purpose of this + SPELL_FREEZE_ANIM = 16245, // not sure what's the purpose of this + SPELL_FULL_STRENGHT = 16047, + SPELL_GROWING = 16049, // stacking aura + SPELL_BONUS_DAMAGE = 16534, // triggered on full grow + SPELL_TRANSFORM = 16052, + + // Combat spells + SPELL_FIRENOVA = 23462, + SPELL_FLAMEBUFFET = 23341, + SPELL_PYROBLAST = 20228 // guesswork, but best fitting in spells-area, was 17274 (has mana cost) +}; + +struct boss_pyroguard_emberseerAI : public ScriptedAI +{ + boss_pyroguard_emberseerAI(Creature* pCreature) : ScriptedAI(pCreature) + { + m_pInstance = (instance_blackrock_spire*) pCreature->GetInstanceData(); + Reset(); + } + + instance_blackrock_spire* m_pInstance; + + uint32 m_uiEncageTimer; + uint32 m_uiFireNovaTimer; + uint32 m_uiFlameBuffetTimer; + uint32 m_uiPyroBlastTimer; + uint8 m_uiGrowingStacks; + + void Reset() override + { + m_uiEncageTimer = 10000; + m_uiFireNovaTimer = 6000; + m_uiFlameBuffetTimer = 3000; + m_uiPyroBlastTimer = 14000; + m_uiGrowingStacks = 0; + + m_creature->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE); + } + + void JustDied(Unit* /*pKiller*/) override + { + if (m_pInstance) + m_pInstance->SetData(TYPE_EMBERSEER, DONE); + } + + void JustReachedHome() override + { + if (m_pInstance) + m_pInstance->SetData(TYPE_EMBERSEER, FAIL); + } + + // Wrapper to handle the transformation + void DoHandleEmberseerGrowing() + { + ++m_uiGrowingStacks; + + if (m_uiGrowingStacks == MAX_GROWING_STACKS * 0.5f) + DoScriptText(EMOTE_NEAR, m_creature); + else if (m_uiGrowingStacks == MAX_GROWING_STACKS) + { + DoScriptText(EMOTE_FULL, m_creature); + DoScriptText(SAY_FREE, m_creature); + + // Note: the spell order needs further research + DoCastSpellIfCan(m_creature, SPELL_FULL_STRENGHT, CAST_TRIGGERED); + DoCastSpellIfCan(m_creature, SPELL_BONUS_DAMAGE, CAST_TRIGGERED); + DoCastSpellIfCan(m_creature, SPELL_TRANSFORM, CAST_TRIGGERED); + + // activate all runes + if (m_pInstance) + { + m_pInstance->DoUseEmberseerRunes(); + // Redundant check: if for some reason the event isn't set in progress until this point - avoid using the altar again when the boss is fully grown + m_pInstance->SetData(TYPE_EMBERSEER, IN_PROGRESS); + } + + m_creature->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE); + } + } + + void UpdateAI(const uint32 uiDiff) override + { + // Cast Encage spell on OOC timer + if (m_uiEncageTimer) + { + if (m_uiEncageTimer <= uiDiff) + { + if (!m_pInstance) + { + script_error_log("Instance Blackrock Spire: ERROR Failed to load instance data for this instace."); + return; + } + + GuidList m_lIncarceratorsGuid; + m_pInstance->GetIncarceratorGUIDList(m_lIncarceratorsGuid); + + for (GuidList::const_iterator itr = m_lIncarceratorsGuid.begin(); itr != m_lIncarceratorsGuid.end(); ++itr) + { + if (Creature* pIncarcerator = m_creature->GetMap()->GetCreature(*itr)) + pIncarcerator->CastSpell(m_creature, SPELL_ENCAGE_EMBERSEER, false); + } + + m_uiEncageTimer = 0; + } + else + m_uiEncageTimer -= uiDiff; + } + + // Return since we have no target + if (!m_creature->SelectHostileTarget() || !m_creature->getVictim()) + return; + + // FireNova Timer + if (m_uiFireNovaTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature, SPELL_FIRENOVA) == CAST_OK) + m_uiFireNovaTimer = 6000; + } + else + m_uiFireNovaTimer -= uiDiff; + + // FlameBuffet Timer + if (m_uiFlameBuffetTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature, SPELL_FLAMEBUFFET) == CAST_OK) + m_uiFlameBuffetTimer = 14000; + } + else + m_uiFlameBuffetTimer -= uiDiff; + + // PyroBlast Timer + if (m_uiPyroBlastTimer < uiDiff) + { + if (Unit* pTarget = m_creature->SelectAttackingTarget(ATTACKING_TARGET_RANDOM, 0)) + { + if (DoCastSpellIfCan(pTarget, SPELL_PYROBLAST) == CAST_OK) + m_uiPyroBlastTimer = 15000; + } + } + else + m_uiPyroBlastTimer -= uiDiff; + + DoMeleeAttackIfReady(); + } +}; + +CreatureAI* GetAI_boss_pyroguard_emberseer(Creature* pCreature) +{ + return new boss_pyroguard_emberseerAI(pCreature); +} + +bool EffectDummyCreature_pyroguard_emberseer(Unit* /*pCaster*/, uint32 uiSpellId, SpellEffectIndex uiEffIndex, Creature* pCreatureTarget, ObjectGuid /*originalCasterGuid*/) +{ + // always check spellid and effectindex + if (uiSpellId == SPELL_GROWING && uiEffIndex == EFFECT_INDEX_0) + { + if (boss_pyroguard_emberseerAI* pEmberseerAI = dynamic_cast(pCreatureTarget->AI())) + pEmberseerAI->DoHandleEmberseerGrowing(); + } + + return false; +} + +void AddSC_boss_pyroguard_emberseer() +{ + Script* pNewScript; + + pNewScript = new Script; + pNewScript->Name = "boss_pyroguard_emberseer"; + pNewScript->GetAI = &GetAI_boss_pyroguard_emberseer; + pNewScript->pEffectDummyNPC = &EffectDummyCreature_pyroguard_emberseer; + pNewScript->RegisterSelf(); +} diff --git a/src/modules/SD2/scripts/eastern_kingdoms/blackrock_mountain/blackrock_spire/instance_blackrock_spire.cpp b/src/modules/SD2/scripts/eastern_kingdoms/blackrock_mountain/blackrock_spire/instance_blackrock_spire.cpp new file mode 100644 index 000000000..358a92df1 --- /dev/null +++ b/src/modules/SD2/scripts/eastern_kingdoms/blackrock_mountain/blackrock_spire/instance_blackrock_spire.cpp @@ -0,0 +1,715 @@ +/* Copyright (C) 2006 - 2013 ScriptDev2 + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along 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_spire +SD%Complete: 75 +SDComment: The Stadium event is missing some yells. Seal of Ascension related event NYI +SDCategory: Blackrock Spire +EndScriptData */ + +#include "precompiled.h" +#include "blackrock_spire.h" + +enum +{ + AREATRIGGER_ENTER_UBRS = 2046, + AREATRIGGER_STADIUM = 2026, + + // Arena event dialogue - handled by instance + SAY_NEFARIUS_INTRO_1 = -1229004, + SAY_NEFARIUS_INTRO_2 = -1229005, + SAY_NEFARIUS_ATTACK_1 = -1229006, + SAY_REND_JOIN = -1229007, + SAY_NEFARIUS_ATTACK_2 = -1229008, + SAY_NEFARIUS_ATTACK_3 = -1229009, + SAY_NEFARIUS_ATTACK_4 = -1229010, + SAY_REND_LOSE_1 = -1229011, + SAY_REND_LOSE_2 = -1229012, + SAY_NEFARIUS_LOSE_3 = -1229013, + SAY_NEFARIUS_LOSE_4 = -1229014, + SAY_REND_ATTACK = -1229015, + SAY_NEFARIUS_WARCHIEF = -1229016, + SAY_NEFARIUS_VICTORY = -1229018, + + // Emberseer event + EMOTE_BEGIN = -1229000, + SPELL_EMBERSEER_GROWING = 16048, + + // Solakar Flamewreath Event + SAY_ROOKERY_EVENT_START = -1229020, + NPC_ROOKERY_GUARDIAN = 10258, + NPC_ROOKERY_HATCHER = 10683, +}; + +/* Areatrigger +1470 Instance Entry +1628 LBRS, between Spiders and Ogres +1946 LBRS, ubrs pre-quest giver (1) +1986 LBRS, ubrs pre-quest giver (2) +1987 LBRS, ubrs pre-quest giver (3) +2026 UBRS, stadium event trigger +2046 UBRS, way to upper +2066 UBRS, The Beast - Exit (to the dark chamber) +2067 UBRS, The Beast - Entry +2068 LBRS, fall out of map +3726 UBRS, entrance to BWL +*/ + +static const DialogueEntry aStadiumDialogue[] = +{ + {NPC_LORD_VICTOR_NEFARIUS, 0, 1000}, + {SAY_NEFARIUS_INTRO_1, NPC_LORD_VICTOR_NEFARIUS, 7000}, + {SAY_NEFARIUS_INTRO_2, NPC_LORD_VICTOR_NEFARIUS, 5000}, + {NPC_BLACKHAND_HANDLER, 0, 0}, + {SAY_NEFARIUS_LOSE_4, NPC_LORD_VICTOR_NEFARIUS, 3000}, + {SAY_REND_ATTACK, NPC_REND_BLACKHAND, 2000}, + {SAY_NEFARIUS_WARCHIEF, NPC_LORD_VICTOR_NEFARIUS, 0}, + {SAY_NEFARIUS_VICTORY, NPC_LORD_VICTOR_NEFARIUS, 5000}, + {NPC_REND_BLACKHAND, 0, 0}, + {0, 0, 0}, +}; + +static const float rookeryEventSpawnPos[3] = {43.7685f, -259.82f, 91.6483f}; + +instance_blackrock_spire::instance_blackrock_spire(Map* pMap) : ScriptedInstance(pMap), DialogueHelper(aStadiumDialogue), + m_uiFlamewreathEventTimer(0), + m_uiFlamewreathWaveCount(0), + m_uiStadiumEventTimer(0), + m_uiStadiumWaves(0), + m_uiStadiumMobsAlive(0) +{ + Initialize(); +} + +void instance_blackrock_spire::Initialize() +{ + memset(&m_auiEncounter, 0, sizeof(m_auiEncounter)); + memset(&m_aRoomRuneGuid, 0, sizeof(m_aRoomRuneGuid)); + InitializeDialogueHelper(this); +} + +void instance_blackrock_spire::OnObjectCreate(GameObject* pGo) +{ + switch (pGo->GetEntry()) + { + case GO_EMBERSEER_IN: + if (GetData(TYPE_ROOM_EVENT) == DONE) + pGo->SetGoState(GO_STATE_ACTIVE); + break; + case GO_DOORS: + break; + case GO_EMBERSEER_OUT: + if (GetData(TYPE_EMBERSEER) == DONE) + pGo->SetGoState(GO_STATE_ACTIVE); + break; + case GO_FATHER_FLAME: + case GO_GYTH_ENTRY_DOOR: + case GO_GYTH_COMBAT_DOOR: + case GO_DRAKKISATH_DOOR_1: + case GO_DRAKKISATH_DOOR_2: + break; + case GO_GYTH_EXIT_DOOR: + if (GetData(TYPE_STADIUM) == DONE) + pGo->SetGoState(GO_STATE_ACTIVE); + break; + + case GO_ROOM_1_RUNE: m_aRoomRuneGuid[0] = pGo->GetObjectGuid(); return; + case GO_ROOM_2_RUNE: m_aRoomRuneGuid[1] = pGo->GetObjectGuid(); return; + case GO_ROOM_3_RUNE: m_aRoomRuneGuid[2] = pGo->GetObjectGuid(); return; + case GO_ROOM_4_RUNE: m_aRoomRuneGuid[3] = pGo->GetObjectGuid(); return; + case GO_ROOM_5_RUNE: m_aRoomRuneGuid[4] = pGo->GetObjectGuid(); return; + case GO_ROOM_6_RUNE: m_aRoomRuneGuid[5] = pGo->GetObjectGuid(); return; + case GO_ROOM_7_RUNE: m_aRoomRuneGuid[6] = pGo->GetObjectGuid(); return; + + case GO_EMBERSEER_RUNE_1: + case GO_EMBERSEER_RUNE_2: + case GO_EMBERSEER_RUNE_3: + case GO_EMBERSEER_RUNE_4: + case GO_EMBERSEER_RUNE_5: + case GO_EMBERSEER_RUNE_6: + case GO_EMBERSEER_RUNE_7: + m_lEmberseerRunesGUIDList.push_back(pGo->GetObjectGuid()); + return; + + default: + return; + } + m_mGoEntryGuidStore[pGo->GetEntry()] = pGo->GetObjectGuid(); +} + +void instance_blackrock_spire::OnCreatureCreate(Creature* pCreature) +{ + switch (pCreature->GetEntry()) + { + case NPC_PYROGUARD_EMBERSEER: + case NPC_SOLAKAR_FLAMEWREATH: + case NPC_LORD_VICTOR_NEFARIUS: + case NPC_GYTH: + case NPC_REND_BLACKHAND: + case NPC_SCARSHIELD_INFILTRATOR: + m_mNpcEntryGuidStore[pCreature->GetEntry()] = pCreature->GetObjectGuid(); + break; + + case NPC_BLACKHAND_SUMMONER: + case NPC_BLACKHAND_VETERAN: m_lRoomEventMobGUIDList.push_back(pCreature->GetObjectGuid()); break; + case NPC_BLACKHAND_INCARCERATOR: m_lIncarceratorGUIDList.push_back(pCreature->GetObjectGuid()); break; + } +} + +void instance_blackrock_spire::SetData(uint32 uiType, uint32 uiData) +{ + switch (uiType) + { + case TYPE_ROOM_EVENT: + if (uiData == DONE) + DoUseDoorOrButton(GO_EMBERSEER_IN); + m_auiEncounter[uiType] = uiData; + break; + case TYPE_EMBERSEER: + // Don't set the same data twice + if (m_auiEncounter[uiType] == uiData) + break; + // Combat door + DoUseDoorOrButton(GO_DOORS); + // Respawn all incarcerators and reset the runes on FAIL + if (uiData == FAIL) + { + for (GuidList::const_iterator itr = m_lIncarceratorGUIDList.begin(); itr != m_lIncarceratorGUIDList.end(); ++itr) + { + if (Creature* pIncarcerator = instance->GetCreature(*itr)) + { + if (!pIncarcerator->IsAlive()) + pIncarcerator->Respawn(); + pIncarcerator->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_PASSIVE); + } + } + + DoUseEmberseerRunes(true); + } + else if (uiData == DONE) + { + DoUseEmberseerRunes(); + DoUseDoorOrButton(GO_EMBERSEER_OUT); + } + m_auiEncounter[uiType] = uiData; + break; + case TYPE_FLAMEWREATH: + if (uiData == FAIL) + { + m_uiFlamewreathEventTimer = 0; + m_uiFlamewreathWaveCount = 0; + } + m_auiEncounter[uiType] = uiData; + break; + case TYPE_STADIUM: + // Don't set the same data twice + if (m_auiEncounter[uiType] == uiData) + break; + // Combat door + DoUseDoorOrButton(GO_GYTH_ENTRY_DOOR); + // Start event + if (uiData == IN_PROGRESS) + StartNextDialogueText(SAY_NEFARIUS_INTRO_1); + else if (uiData == DONE) + DoUseDoorOrButton(GO_GYTH_EXIT_DOOR); + else if (uiData == FAIL) + { + // Despawn Nefarius and Rend on fail (the others are despawned OnCreatureEvade()) + if (Creature* pNefarius = GetSingleCreatureFromStorage(NPC_LORD_VICTOR_NEFARIUS)) + pNefarius->ForcedDespawn(); + if (Creature* pRend = GetSingleCreatureFromStorage(NPC_REND_BLACKHAND)) + pRend->ForcedDespawn(); + if (Creature* pGyth = GetSingleCreatureFromStorage(NPC_GYTH)) + pGyth->ForcedDespawn(); + + m_uiStadiumEventTimer = 0; + m_uiStadiumMobsAlive = 0; + m_uiStadiumWaves = 0; + } + m_auiEncounter[uiType] = uiData; + break; + case TYPE_DRAKKISATH: + case TYPE_VALTHALAK: + m_auiEncounter[uiType] = uiData; + 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_strInstData = saveStream.str(); + + SaveToDB(); + OUT_SAVE_INST_DATA_COMPLETE; + } +} + +void instance_blackrock_spire::Load(const char* chrIn) +{ + if (!chrIn) + { + 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]; + + for (uint8 i = 0; i < MAX_ENCOUNTER; ++i) + { + if (m_auiEncounter[i] == IN_PROGRESS) + m_auiEncounter[i] = NOT_STARTED; + } + + OUT_LOAD_INST_DATA_COMPLETE; +} + +uint32 instance_blackrock_spire::GetData(uint32 uiType) const +{ + if (uiType < MAX_ENCOUNTER) + return m_auiEncounter[uiType]; + + return 0; +} + +void instance_blackrock_spire::DoSortRoomEventMobs() +{ + if (GetData(TYPE_ROOM_EVENT) != NOT_STARTED) + return; + + for (uint8 i = 0; i < MAX_ROOMS; ++i) + { + if (GameObject* pRune = instance->GetGameObject(m_aRoomRuneGuid[i])) + { + for (GuidList::const_iterator itr = m_lRoomEventMobGUIDList.begin(); itr != m_lRoomEventMobGUIDList.end(); ++itr) + { + Creature* pCreature = instance->GetCreature(*itr); + if (pCreature && pCreature->IsAlive() && pCreature->GetDistance(pRune) < 10.0f) + m_alRoomEventMobGUIDSorted[i].push_back(*itr); + } + } + } + + SetData(TYPE_ROOM_EVENT, IN_PROGRESS); +} + +void instance_blackrock_spire::OnCreatureDeath(Creature* pCreature) +{ + switch (pCreature->GetEntry()) + { + case NPC_BLACKHAND_SUMMONER: + case NPC_BLACKHAND_VETERAN: + // Handle Runes + if (m_auiEncounter[TYPE_ROOM_EVENT] == IN_PROGRESS) + { + uint8 uiNotEmptyRoomsCount = 0; + for (uint8 i = 0; i < MAX_ROOMS; ++i) + { + if (m_aRoomRuneGuid[i]) // This check is used, to ensure which runes still need processing + { + m_alRoomEventMobGUIDSorted[i].remove(pCreature->GetObjectGuid()); + if (m_alRoomEventMobGUIDSorted[i].empty()) + { + DoUseDoorOrButton(m_aRoomRuneGuid[i]); + m_aRoomRuneGuid[i].Clear(); + } + else + ++uiNotEmptyRoomsCount; // found an not empty room + } + } + if (!uiNotEmptyRoomsCount) + SetData(TYPE_ROOM_EVENT, DONE); + } + break; + case NPC_SOLAKAR_FLAMEWREATH: + SetData(TYPE_FLAMEWREATH, DONE); + break; + case NPC_DRAKKISATH: + SetData(TYPE_DRAKKISATH, DONE); + DoUseDoorOrButton(GO_DRAKKISATH_DOOR_1); + DoUseDoorOrButton(GO_DRAKKISATH_DOOR_2); + break; + case NPC_CHROMATIC_WHELP: + case NPC_CHROMATIC_DRAGON: + case NPC_BLACKHAND_HANDLER: + // check if it's summoned - some npcs with the same entry are already spawned in the instance + if (!pCreature->IsTemporarySummon()) + break; + --m_uiStadiumMobsAlive; + if (m_uiStadiumMobsAlive == 0) + DoSendNextStadiumWave(); + break; + case NPC_GYTH: + case NPC_REND_BLACKHAND: + --m_uiStadiumMobsAlive; + if (m_uiStadiumMobsAlive == 0) + StartNextDialogueText(SAY_NEFARIUS_VICTORY); + break; + } +} + +void instance_blackrock_spire::OnCreatureEvade(Creature* pCreature) +{ + switch (pCreature->GetEntry()) + { + // Emberseer should evade if the incarcerators evade + case NPC_BLACKHAND_INCARCERATOR: + if (Creature* pEmberseer = GetSingleCreatureFromStorage(NPC_PYROGUARD_EMBERSEER)) + pEmberseer->AI()->EnterEvadeMode(); + break; + case NPC_SOLAKAR_FLAMEWREATH: + case NPC_ROOKERY_GUARDIAN: + case NPC_ROOKERY_HATCHER: + SetData(TYPE_FLAMEWREATH, FAIL); + break; + case NPC_CHROMATIC_WHELP: + case NPC_CHROMATIC_DRAGON: + case NPC_BLACKHAND_HANDLER: + case NPC_GYTH: + case NPC_REND_BLACKHAND: + // check if it's summoned - some npcs with the same entry are already spawned in the instance + if (!pCreature->IsTemporarySummon()) + break; + SetData(TYPE_STADIUM, FAIL); + pCreature->ForcedDespawn(); + break; + } +} + +void instance_blackrock_spire::OnCreatureEnterCombat(Creature* pCreature) +{ + switch (pCreature->GetEntry()) + { + // Once one of the Incarcerators gets Aggro, the door should close + case NPC_BLACKHAND_INCARCERATOR: + SetData(TYPE_EMBERSEER, IN_PROGRESS); + break; + } +} + +void instance_blackrock_spire::DoProcessEmberseerEvent() +{ + if (GetData(TYPE_EMBERSEER) == DONE || GetData(TYPE_EMBERSEER) == IN_PROGRESS) + return; + + if (m_lIncarceratorGUIDList.empty()) + { + script_error_log("Npc %u couldn't be found. Please check your DB content!", NPC_BLACKHAND_INCARCERATOR); + return; + } + + // start to grow + if (Creature* pEmberseer = GetSingleCreatureFromStorage(NPC_PYROGUARD_EMBERSEER)) + { + // If already casting, return + if (pEmberseer->HasAura(SPELL_EMBERSEER_GROWING)) + return; + + DoScriptText(EMOTE_BEGIN, pEmberseer); + pEmberseer->CastSpell(pEmberseer, SPELL_EMBERSEER_GROWING, true); + } + + // remove the incarcerators flags and stop casting + for (GuidList::const_iterator itr = m_lIncarceratorGUIDList.begin(); itr != m_lIncarceratorGUIDList.end(); ++itr) + { + if (Creature* pCreature = instance->GetCreature(*itr)) + { + if (pCreature->IsAlive()) + { + pCreature->InterruptNonMeleeSpells(false); + pCreature->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_PASSIVE); + } + } + } +} + +void instance_blackrock_spire::DoUseEmberseerRunes(bool bReset) +{ + if (m_lEmberseerRunesGUIDList.empty()) + return; + + for (GuidList::const_iterator itr = m_lEmberseerRunesGUIDList.begin(); itr != m_lEmberseerRunesGUIDList.end(); ++itr) + { + if (bReset) + { + if (GameObject* pRune = instance->GetGameObject(*itr)) + pRune->ResetDoorOrButton(); + } + else + DoUseDoorOrButton(*itr); + } +} + +void instance_blackrock_spire::JustDidDialogueStep(int32 iEntry) +{ + switch (iEntry) + { + case NPC_BLACKHAND_HANDLER: + m_uiStadiumEventTimer = 1000; + // Move the two near the balcony + if (Creature* pRend = GetSingleCreatureFromStorage(NPC_REND_BLACKHAND)) + pRend->SetFacingTo(aStadiumLocs[5].m_fO); + if (Creature* pNefarius = GetSingleCreatureFromStorage(NPC_LORD_VICTOR_NEFARIUS)) + pNefarius->GetMotionMaster()->MovePoint(0, aStadiumLocs[5].m_fX, aStadiumLocs[5].m_fY, aStadiumLocs[5].m_fZ); + break; + case SAY_NEFARIUS_WARCHIEF: + // Prepare for Gyth - note: Nefarius should be moving around the balcony + if (Creature* pRend = GetSingleCreatureFromStorage(NPC_REND_BLACKHAND)) + { + pRend->ForcedDespawn(5000); + pRend->SetWalk(false); + pRend->GetMotionMaster()->MovePoint(0, aStadiumLocs[6].m_fX, aStadiumLocs[6].m_fY, aStadiumLocs[6].m_fZ); + } + m_uiStadiumEventTimer = 30000; + break; + case SAY_NEFARIUS_VICTORY: + SetData(TYPE_STADIUM, DONE); + break; + case NPC_REND_BLACKHAND: + // Despawn Nefarius + if (Creature* pNefarius = GetSingleCreatureFromStorage(NPC_LORD_VICTOR_NEFARIUS)) + { + pNefarius->ForcedDespawn(5000); + pNefarius->GetMotionMaster()->MovePoint(0, aStadiumLocs[6].m_fX, aStadiumLocs[6].m_fY, aStadiumLocs[6].m_fZ); + } + break; + } +} + +void instance_blackrock_spire::DoSendNextStadiumWave() +{ + if (m_uiStadiumWaves < MAX_STADIUM_WAVES) + { + // Send current wave mobs + if (Creature* pNefarius = GetSingleCreatureFromStorage(NPC_LORD_VICTOR_NEFARIUS)) + { + float fX, fY, fZ; + for (uint8 i = 0; i < MAX_STADIUM_MOBS_PER_WAVE; ++i) + { + if (aStadiumEventNpcs[m_uiStadiumWaves][i] == 0) + continue; + + pNefarius->GetRandomPoint(aStadiumLocs[0].m_fX, aStadiumLocs[0].m_fY, aStadiumLocs[0].m_fZ, 7.0f, fX, fY, fZ); + fX = std::min(aStadiumLocs[0].m_fX, fX); // Halfcircle - suits better the rectangular form + if (Creature* pTemp = pNefarius->SummonCreature(aStadiumEventNpcs[m_uiStadiumWaves][i], fX, fY, fZ, 0.0f, TEMPSUMMON_DEAD_DESPAWN, 0)) + { + // Get some point in the center of the stadium + pTemp->GetRandomPoint(aStadiumLocs[2].m_fX, aStadiumLocs[2].m_fY, aStadiumLocs[2].m_fZ, 5.0f, fX, fY, fZ); + fX = std::min(aStadiumLocs[2].m_fX, fX);// Halfcircle - suits better the rectangular form + + pTemp->GetMotionMaster()->MovePoint(0, fX, fY, fZ); + ++m_uiStadiumMobsAlive; + } + } + } + + DoUseDoorOrButton(GO_GYTH_COMBAT_DOOR); + } + // All waves are cleared - start Gyth intro + else if (m_uiStadiumWaves == MAX_STADIUM_WAVES) + StartNextDialogueText(SAY_NEFARIUS_LOSE_4); + else + { + // Send Gyth + if (Creature* pNefarius = GetSingleCreatureFromStorage(NPC_LORD_VICTOR_NEFARIUS)) + { + if (Creature* pTemp = pNefarius->SummonCreature(NPC_GYTH, aStadiumLocs[1].m_fX, aStadiumLocs[1].m_fY, aStadiumLocs[1].m_fZ, 0.0f, TEMPSUMMON_DEAD_DESPAWN, 0)) + pTemp->GetMotionMaster()->MovePoint(0, aStadiumLocs[2].m_fX, aStadiumLocs[2].m_fY, aStadiumLocs[2].m_fZ); + } + + // Set this to 2, because Rend will be summoned later during the fight + m_uiStadiumMobsAlive = 2; + + DoUseDoorOrButton(GO_GYTH_COMBAT_DOOR); + } + + ++m_uiStadiumWaves; + + // Stop the timer when all the waves have been sent + if (m_uiStadiumWaves >= MAX_STADIUM_WAVES) + m_uiStadiumEventTimer = 0; + else + m_uiStadiumEventTimer = 60000; +} + +void instance_blackrock_spire::DoSendNextFlamewreathWave() +{ + GameObject* pSummoner = GetSingleGameObjectFromStorage(GO_FATHER_FLAME); + if (!pSummoner) + return; + + // TODO - The npcs would move nicer if they had DB waypoints, so i suggest to change their default movement to DB waypoints, and random movement when they reached their goal + + if (m_uiFlamewreathWaveCount < 6) // Send two adds (6 waves, then boss) + { + Creature* pSummoned = NULL; + for (uint8 i = 0; i < 2; ++i) + { + float fX, fY, fZ; + pSummoner->GetRandomPoint(rookeryEventSpawnPos[0], rookeryEventSpawnPos[1], rookeryEventSpawnPos[2], 2.5f, fX, fY, fZ); + // Summon Rookery Hatchers in first wave, else random + if (pSummoned = pSummoner->SummonCreature(urand(0, 1) && m_uiFlamewreathWaveCount ? NPC_ROOKERY_GUARDIAN : NPC_ROOKERY_HATCHER, + fX, fY, fZ, 0.0, TEMPSUMMON_TIMED_OOC_OR_DEAD_DESPAWN, 300000)) + { + pSummoner->GetContactPoint(pSummoned, fX, fY, fZ); + pSummoned->GetMotionMaster()->MovePoint(1, fX, fY, pSummoner->GetPositionZ()); + } + } + if (pSummoned && m_uiFlamewreathWaveCount == 0) + DoScriptText(SAY_ROOKERY_EVENT_START, pSummoned); + + if (m_uiFlamewreathWaveCount < 4) + m_uiFlamewreathEventTimer = 30000; + else if (m_uiFlamewreathWaveCount < 6) + m_uiFlamewreathEventTimer = 40000; + else + m_uiFlamewreathEventTimer = 10000; + + ++m_uiFlamewreathWaveCount; + } + else // Send Flamewreath + { + if (Creature* pSolakar = pSummoner->SummonCreature(NPC_SOLAKAR_FLAMEWREATH, rookeryEventSpawnPos[0], rookeryEventSpawnPos[1], rookeryEventSpawnPos[2], 0.0f, TEMPSUMMON_TIMED_OOC_OR_DEAD_DESPAWN, HOUR * IN_MILLISECONDS)) + pSolakar->GetMotionMaster()->MovePoint(1, pSummoner->GetPositionX(), pSummoner->GetPositionY(), pSummoner->GetPositionZ()); + SetData(TYPE_FLAMEWREATH, SPECIAL); + m_uiFlamewreathEventTimer = 0; + } +} + +void instance_blackrock_spire::Update(uint32 uiDiff) +{ + DialogueUpdate(uiDiff); + + if (m_uiStadiumEventTimer) + { + if (m_uiStadiumEventTimer <= uiDiff) + DoSendNextStadiumWave(); + else + m_uiStadiumEventTimer -= uiDiff; + } + + if (m_uiFlamewreathEventTimer) + { + if (m_uiFlamewreathEventTimer <= uiDiff) + DoSendNextFlamewreathWave(); + else + m_uiFlamewreathEventTimer -= uiDiff; + } +} + +void instance_blackrock_spire::StartflamewreathEventIfCan() +{ + // Already done or currently in progress - or endboss done + if (m_auiEncounter[TYPE_FLAMEWREATH] == DONE || m_auiEncounter[TYPE_FLAMEWREATH] == IN_PROGRESS || m_auiEncounter[TYPE_DRAKKISATH] == DONE) + return; + + // Boss still around + if (GetSingleCreatureFromStorage(NPC_SOLAKAR_FLAMEWREATH, true)) + return; + + // Start summoning of mobs + m_uiFlamewreathEventTimer = 1; + m_uiFlamewreathWaveCount = 0; +} + +InstanceData* GetInstanceData_instance_blackrock_spire(Map* pMap) +{ + return new instance_blackrock_spire(pMap); +} + +bool AreaTrigger_at_blackrock_spire(Player* pPlayer, AreaTriggerEntry const* pAt) +{ + if (!pPlayer->IsAlive() || pPlayer->isGameMaster()) + return false; + + switch (pAt->id) + { + case AREATRIGGER_ENTER_UBRS: + if (instance_blackrock_spire* pInstance = (instance_blackrock_spire*) pPlayer->GetInstanceData()) + pInstance->DoSortRoomEventMobs(); + break; + case AREATRIGGER_STADIUM: + if (instance_blackrock_spire* pInstance = (instance_blackrock_spire*) pPlayer->GetInstanceData()) + { + if (pInstance->GetData(TYPE_STADIUM) == IN_PROGRESS || pInstance->GetData(TYPE_STADIUM) == DONE) + return false; + + // Summon Nefarius and Rend for the dialogue event + // Note: Nefarius and Rend need to be hostile and not attackable + if (Creature* pNefarius = pPlayer->SummonCreature(NPC_LORD_VICTOR_NEFARIUS, aStadiumLocs[3].m_fX, aStadiumLocs[3].m_fY, aStadiumLocs[3].m_fZ, aStadiumLocs[3].m_fO, TEMPSUMMON_CORPSE_DESPAWN, 0)) + pNefarius->SetFactionTemporary(FACTION_BLACK_DRAGON, TEMPFACTION_NONE); + if (Creature* pRend = pPlayer->SummonCreature(NPC_REND_BLACKHAND, aStadiumLocs[4].m_fX, aStadiumLocs[4].m_fY, aStadiumLocs[4].m_fZ, aStadiumLocs[4].m_fO, TEMPSUMMON_CORPSE_DESPAWN, 0)) + pRend->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_OOC_NOT_ATTACKABLE); + + pInstance->SetData(TYPE_STADIUM, IN_PROGRESS); + } + break; + } + return false; +} + +bool ProcessEventId_event_spell_altar_emberseer(uint32 /*uiEventId*/, Object* pSource, Object* /*pTarget*/, bool bIsStart) +{ + if (bIsStart && pSource->GetTypeId() == TYPEID_PLAYER) + { + if (instance_blackrock_spire* pInstance = (instance_blackrock_spire*)((Player*)pSource)->GetInstanceData()) + { + pInstance->DoProcessEmberseerEvent(); + return true; + } + } + return false; +} + +bool GOUse_go_father_flame(Player* /*pPlayer*/, GameObject* pGo) +{ + if (instance_blackrock_spire* pInstance = (instance_blackrock_spire*)pGo->GetInstanceData()) + pInstance->StartflamewreathEventIfCan(); + + return true; +} + +void AddSC_instance_blackrock_spire() +{ + Script* pNewScript; + + pNewScript = new Script; + pNewScript->Name = "instance_blackrock_spire"; + pNewScript->GetInstanceData = &GetInstanceData_instance_blackrock_spire; + pNewScript->RegisterSelf(); + + pNewScript = new Script; + pNewScript->Name = "at_blackrock_spire"; + pNewScript->pAreaTrigger = &AreaTrigger_at_blackrock_spire; + pNewScript->RegisterSelf(); + + pNewScript = new Script; + pNewScript->Name = "event_spell_altar_emberseer"; + pNewScript->pProcessEventId = &ProcessEventId_event_spell_altar_emberseer; + pNewScript->RegisterSelf(); + + pNewScript = new Script; + pNewScript->Name = "go_father_flame"; + pNewScript->pGOUse = &GOUse_go_father_flame; + pNewScript->RegisterSelf(); +} diff --git a/src/modules/SD2/scripts/eastern_kingdoms/blackrock_mountain/blackwing_descent/blackwing_descent.h b/src/modules/SD2/scripts/eastern_kingdoms/blackrock_mountain/blackwing_descent/blackwing_descent.h new file mode 100644 index 000000000..6b5f9ef47 --- /dev/null +++ b/src/modules/SD2/scripts/eastern_kingdoms/blackrock_mountain/blackwing_descent/blackwing_descent.h @@ -0,0 +1,3 @@ +/* Copyright (C) 2006 - 2013 ScriptDev2 + * This program is free software licensed under GPL version 2 + * Please see the included DOCS/LICENSE.TXT for more information */ diff --git a/src/modules/SD2/scripts/eastern_kingdoms/blackrock_mountain/blackwing_descent/boss_atramedes.cpp b/src/modules/SD2/scripts/eastern_kingdoms/blackrock_mountain/blackwing_descent/boss_atramedes.cpp new file mode 100644 index 000000000..b1de0cf8a --- /dev/null +++ b/src/modules/SD2/scripts/eastern_kingdoms/blackrock_mountain/blackwing_descent/boss_atramedes.cpp @@ -0,0 +1,28 @@ +/* Copyright (C) 2006 - 2013 ScriptDev2 + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +/* ScriptData +SDName: boss_atramedes +SD%Complete: 0 +SDComment: Placeholder +SDCategory: Blackwing Descent +EndScriptData */ + +#include "precompiled.h" + +void AddSC_boss_adramedes() +{ +} diff --git a/src/modules/SD2/scripts/eastern_kingdoms/blackrock_mountain/blackwing_descent/boss_chimaeron.cpp b/src/modules/SD2/scripts/eastern_kingdoms/blackrock_mountain/blackwing_descent/boss_chimaeron.cpp new file mode 100644 index 000000000..205994313 --- /dev/null +++ b/src/modules/SD2/scripts/eastern_kingdoms/blackrock_mountain/blackwing_descent/boss_chimaeron.cpp @@ -0,0 +1,28 @@ +/* Copyright (C) 2006 - 2013 ScriptDev2 + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +/* ScriptData +SDName: boss_chimaeron +SD%Complete: 0 +SDComment: Placeholder +SDCategory: Blackwing Descent +EndScriptData */ + +#include "precompiled.h" + +void AddSC_boss_chimaeron() +{ +} diff --git a/src/modules/SD2/scripts/eastern_kingdoms/blackrock_mountain/blackwing_descent/boss_magmaw.cpp b/src/modules/SD2/scripts/eastern_kingdoms/blackrock_mountain/blackwing_descent/boss_magmaw.cpp new file mode 100644 index 000000000..501adc8dd --- /dev/null +++ b/src/modules/SD2/scripts/eastern_kingdoms/blackrock_mountain/blackwing_descent/boss_magmaw.cpp @@ -0,0 +1,28 @@ +/* Copyright (C) 2006 - 2013 ScriptDev2 + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +/* ScriptData +SDName: boss_magmaw +SD%Complete: 0 +SDComment: Placeholder +SDCategory: Blackwing Descent +EndScriptData */ + +#include "precompiled.h" + +void AddSC_boss_magmaw() +{ +} diff --git a/src/modules/SD2/scripts/eastern_kingdoms/blackrock_mountain/blackwing_descent/boss_maloriak.cpp b/src/modules/SD2/scripts/eastern_kingdoms/blackrock_mountain/blackwing_descent/boss_maloriak.cpp new file mode 100644 index 000000000..2aa5afce6 --- /dev/null +++ b/src/modules/SD2/scripts/eastern_kingdoms/blackrock_mountain/blackwing_descent/boss_maloriak.cpp @@ -0,0 +1,28 @@ +/* Copyright (C) 2006 - 2013 ScriptDev2 + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +/* ScriptData +SDName: boss_maloriak +SD%Complete: 0 +SDComment: Placeholder +SDCategory: Blackwing Descent +EndScriptData */ + +#include "precompiled.h" + +void AddSC_boss_maloriak() +{ +} diff --git a/src/modules/SD2/scripts/eastern_kingdoms/blackrock_mountain/blackwing_descent/boss_nefarian_descent.cpp b/src/modules/SD2/scripts/eastern_kingdoms/blackrock_mountain/blackwing_descent/boss_nefarian_descent.cpp new file mode 100644 index 000000000..050d6b7f9 --- /dev/null +++ b/src/modules/SD2/scripts/eastern_kingdoms/blackrock_mountain/blackwing_descent/boss_nefarian_descent.cpp @@ -0,0 +1,28 @@ +/* Copyright (C) 2006 - 2013 ScriptDev2 + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with 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_descent +SD%Complete: 0 +SDComment: Placeholder +SDCategory: Blackwing Descent +EndScriptData */ + +#include "precompiled.h" + +void AddSC_boss_nefarian_descent() +{ +} diff --git a/src/modules/SD2/scripts/eastern_kingdoms/blackrock_mountain/blackwing_descent/instance_blackwing_descent.cpp b/src/modules/SD2/scripts/eastern_kingdoms/blackrock_mountain/blackwing_descent/instance_blackwing_descent.cpp new file mode 100644 index 000000000..cfac7421b --- /dev/null +++ b/src/modules/SD2/scripts/eastern_kingdoms/blackrock_mountain/blackwing_descent/instance_blackwing_descent.cpp @@ -0,0 +1,28 @@ +/* Copyright (C) 2006 - 2013 ScriptDev2 + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along 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_descent +SD%Complete: 0 +SDComment: Placeholder +SDCategory: Blackwing Descent +EndScriptData */ + +#include "precompiled.h" + +void AddSC_instance_blackwing_descent() +{ +} diff --git a/src/modules/SD2/scripts/eastern_kingdoms/blackrock_mountain/blackwing_descent/omnotron_defense.cpp b/src/modules/SD2/scripts/eastern_kingdoms/blackrock_mountain/blackwing_descent/omnotron_defense.cpp new file mode 100644 index 000000000..1a901ce74 --- /dev/null +++ b/src/modules/SD2/scripts/eastern_kingdoms/blackrock_mountain/blackwing_descent/omnotron_defense.cpp @@ -0,0 +1,28 @@ +/* Copyright (C) 2006 - 2013 ScriptDev2 + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +/* ScriptData +SDName: omnotron_defense +SD%Complete: 0 +SDComment: Placeholder +SDCategory: Blackwing Descent +EndScriptData */ + +#include "precompiled.h" + +void AddSC_omnotron_defense() +{ +} diff --git a/src/modules/SD2/scripts/eastern_kingdoms/blackrock_mountain/blackwing_lair/blackwing_lair.h b/src/modules/SD2/scripts/eastern_kingdoms/blackrock_mountain/blackwing_lair/blackwing_lair.h new file mode 100644 index 000000000..5ecd443fb --- /dev/null +++ b/src/modules/SD2/scripts/eastern_kingdoms/blackrock_mountain/blackwing_lair/blackwing_lair.h @@ -0,0 +1,106 @@ +/* Copyright (C) 2006 - 2013 ScriptDev2 + * This program is free software licensed under GPL version 2 + * Please see the included DOCS/LICENSE.TXT for more information */ + +#ifndef DEF_BLACKWING_LAIR +#define DEF_BLACKWING_LAIR + +enum +{ + MAX_ENCOUNTER = 8, + + TYPE_RAZORGORE = 0, + TYPE_VAELASTRASZ = 1, + TYPE_LASHLAYER = 2, + TYPE_FIREMAW = 3, + TYPE_EBONROC = 4, + TYPE_FLAMEGOR = 5, + TYPE_CHROMAGGUS = 6, + TYPE_NEFARIAN = 7, + + DATA_DRAGON_EGG = 1, // track the used eggs + + NPC_RAZORGORE = 12435, + NPC_VAELASTRASZ = 13020, + NPC_LASHLAYER = 12017, + NPC_FIREMAW = 11983, + NPC_EBONROC = 14601, + NPC_FLAMEGOR = 11981, + NPC_CHROMAGGUS = 14020, + NPC_NEFARIAN = 11583, + NPC_LORD_VICTOR_NEFARIUS = 10162, + NPC_BLACKWING_TECHNICIAN = 13996, // Flees at Vael intro event + + // Razorgore event related + NPC_GRETHOK_CONTROLLER = 12557, + NPC_BLACKWING_ORB_TRIGGER = 14449, + NPC_NEFARIANS_TROOPS = 14459, + NPC_MONSTER_GENERATOR = 12434, + NPC_BLACKWING_LEGIONNAIRE = 12416, // one spawn per turn + NPC_BLACKWING_MAGE = 12420, // one spawn per turn + NPC_DRAGONSPAWN = 12422, // two spawns per turn + + GO_DOOR_RAZORGORE_ENTER = 176964, + GO_DOOR_RAZORGORE_EXIT = 176965, + GO_DOOR_NEFARIAN = 176966, + // GO_DOOR_CHROMAGGUS_ENTER = 179115, + // GO_DOOR_CHROMAGGUS_SIDE = 179116, + GO_DOOR_CHROMAGGUS_EXIT = 179117, + GO_DOOR_VAELASTRASZ = 179364, + GO_DOOR_LASHLAYER = 179365, + GO_ORB_OF_DOMINATION = 177808, // trigger 19832 on Razorgore + GO_BLACK_DRAGON_EGG = 177807, + GO_DRAKONID_BONES = 179804, + + EMOTE_ORB_SHUT_OFF = -1469035, + EMOTE_TROOPS_FLEE = -1469033, // emote by Nefarian's Troops npc + + MAX_EGGS_DEFENDERS = 4, +}; + +// Coords used to spawn Nefarius at the throne +static const float aNefariusSpawnLoc[4] = { -7466.16f, -1040.80f, 412.053f, 2.14675f}; + +static const uint32 aRazorgoreSpawns[MAX_EGGS_DEFENDERS] = {NPC_BLACKWING_LEGIONNAIRE, NPC_BLACKWING_MAGE, NPC_DRAGONSPAWN, NPC_DRAGONSPAWN}; + +class instance_blackwing_lair : public ScriptedInstance +{ + public: + instance_blackwing_lair(Map* pMap); + ~instance_blackwing_lair() {} + + void Initialize() override; + bool IsEncounterInProgress() const override; + + void OnCreatureCreate(Creature* pCreature) override; + void OnObjectCreate(GameObject* pGo) override; + + void OnCreatureEnterCombat(Creature* pCreature) override; + void OnCreatureDeath(Creature* pCreature) override; + + void SetData(uint32 uiType, uint32 uiData) override; + uint32 GetData(uint32 uiType) const override; + + void SetData64(uint32 uiData, uint64 uiGuid) override; + + const char* Save() const override { return m_strInstData.c_str(); } + void Load(const char* chrIn) override; + + void Update(uint32 uiDiff) override; + + protected: + std::string m_strInstData; + uint32 m_auiEncounter[MAX_ENCOUNTER]; + + uint32 m_uiResetTimer; + uint32 m_uiDefenseTimer; + + GuidList m_lTechnicianGuids; + GuidList m_lDragonEggsGuids; + GuidList m_lDrakonidBonesGuids; + GuidList m_lDefendersGuids; + GuidList m_lUsedEggsGuids; + GuidVector m_vGeneratorGuids; +}; + +#endif diff --git a/src/modules/SD2/scripts/eastern_kingdoms/blackrock_mountain/blackwing_lair/boss_broodlord_lashlayer.cpp b/src/modules/SD2/scripts/eastern_kingdoms/blackrock_mountain/blackwing_lair/boss_broodlord_lashlayer.cpp new file mode 100644 index 000000000..ef1ca2d71 --- /dev/null +++ b/src/modules/SD2/scripts/eastern_kingdoms/blackrock_mountain/blackwing_lair/boss_broodlord_lashlayer.cpp @@ -0,0 +1,144 @@ +/* Copyright (C) 2006 - 2013 ScriptDev2 + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with 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" +#include "blackwing_lair.h" + +enum +{ + SAY_AGGRO = -1469000, + SAY_LEASH = -1469001, + + SPELL_CLEAVE = 26350, + SPELL_BLAST_WAVE = 23331, + SPELL_MORTAL_STRIKE = 24573, + SPELL_KNOCK_AWAY = 25778 +}; + +struct boss_broodlordAI : public ScriptedAI +{ + boss_broodlordAI(Creature* pCreature) : ScriptedAI(pCreature) + { + m_pInstance = (ScriptedInstance*)pCreature->GetInstanceData(); + Reset(); + } + + ScriptedInstance* m_pInstance; + + uint32 m_uiCleaveTimer; + uint32 m_uiBlastWaveTimer; + uint32 m_uiMortalStrikeTimer; + uint32 m_uiKnockAwayTimer; + + void Reset() override + { + m_uiCleaveTimer = 8000; // These times are probably wrong + m_uiBlastWaveTimer = 12000; + m_uiMortalStrikeTimer = 20000; + m_uiKnockAwayTimer = 30000; + } + + void Aggro(Unit* /*pWho*/) override + { + if (m_pInstance) + m_pInstance->SetData(TYPE_LASHLAYER, IN_PROGRESS); + + DoScriptText(SAY_AGGRO, m_creature); + } + + void JustDied(Unit* /*pKiller*/) override + { + if (m_pInstance) + m_pInstance->SetData(TYPE_LASHLAYER, DONE); + } + + void JustReachedHome() override + { + if (m_pInstance) + m_pInstance->SetData(TYPE_LASHLAYER, FAIL); + } + + void UpdateAI(const uint32 uiDiff) override + { + if (!m_creature->SelectHostileTarget() || !m_creature->getVictim()) + return; + + // Cleave Timer + if (m_uiCleaveTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature->getVictim(), SPELL_CLEAVE) == CAST_OK) + m_uiCleaveTimer = 7000; + } + else + m_uiCleaveTimer -= uiDiff; + + // Blast Wave + if (m_uiBlastWaveTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature, SPELL_BLAST_WAVE) == CAST_OK) + m_uiBlastWaveTimer = urand(8000, 16000); + } + else + m_uiBlastWaveTimer -= uiDiff; + + // Mortal Strike Timer + if (m_uiMortalStrikeTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature->getVictim(), SPELL_MORTAL_STRIKE) == CAST_OK) + m_uiMortalStrikeTimer = urand(25000, 35000); + } + else + m_uiMortalStrikeTimer -= uiDiff; + + if (m_uiKnockAwayTimer < uiDiff) + { + DoCastSpellIfCan(m_creature->getVictim(), SPELL_KNOCK_AWAY); + // Drop 50% aggro - TODO should be scriptedEffect? + if (m_creature->GetThreatManager().getThreat(m_creature->getVictim())) + m_creature->GetThreatManager().modifyThreatPercent(m_creature->getVictim(), -50); + + m_uiKnockAwayTimer = urand(15000, 30000); + } + else + m_uiKnockAwayTimer -= uiDiff; + + DoMeleeAttackIfReady(); + + if (EnterEvadeIfOutOfCombatArea(uiDiff)) + DoScriptText(SAY_LEASH, m_creature); + } +}; +CreatureAI* GetAI_boss_broodlord(Creature* pCreature) +{ + return new boss_broodlordAI(pCreature); +} + +void AddSC_boss_broodlord() +{ + Script* pNewScript; + + pNewScript = new Script; + pNewScript->Name = "boss_broodlord"; + pNewScript->GetAI = &GetAI_boss_broodlord; + pNewScript->RegisterSelf(); +} diff --git a/src/modules/SD2/scripts/eastern_kingdoms/blackrock_mountain/blackwing_lair/boss_chromaggus.cpp b/src/modules/SD2/scripts/eastern_kingdoms/blackrock_mountain/blackwing_lair/boss_chromaggus.cpp new file mode 100644 index 000000000..61b416981 --- /dev/null +++ b/src/modules/SD2/scripts/eastern_kingdoms/blackrock_mountain/blackwing_lair/boss_chromaggus.cpp @@ -0,0 +1,258 @@ +/* Copyright (C) 2006 - 2013 ScriptDev2 + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with 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" +#include "blackwing_lair.h" + +enum +{ + EMOTE_GENERIC_FRENZY_KILL = -1000001, + 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% + SPELL_FIRE_VULNERABILITY = 22277, + SPELL_FROST_VULNERABILITY = 22278, + SPELL_SHADOW_VULNERABILITY = 22279, + SPELL_NATURE_VULNERABILITY = 22280, + SPELL_ARCANE_VULNERABILITY = 22281, + + MAX_BREATHS = 5, + SPELL_INCINERATE = 23308, // Incinerate 23308,23309 + SPELL_TIME_LAPSE = 23310, // Time lapse 23310, 23311(old threat mod that was removed in 2.01) + SPELL_CORROSIVE_ACID = 23313, // Corrosive Acid 23313, 23314 + SPELL_IGNITE_FLESH = 23315, // Ignite Flesh 23315,23316 + SPELL_FROST_BURN = 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 + SPELL_BROODAF_BLUE = 23153, // Blue affliction 23153 + SPELL_BROODAF_BLACK = 23154, // Black affliction 23154 + SPELL_BROODAF_RED = 23155, // Red affliction 23155 (23168 on death) + SPELL_BROODAF_BRONZE = 23170, // Bronze Affliction 23170 + SPELL_BROODAF_GREEN = 23169, // Brood Affliction Green 23169 + + SPELL_CHROMATIC_MUT_1 = 23174, // Spell cast on player if they get all 5 debuffs + + SPELL_FRENZY = 28371, // The frenzy spell may be wrong + SPELL_ENRAGE = 28747 +}; + +static const uint32 aPossibleBreaths[MAX_BREATHS] = {SPELL_INCINERATE, SPELL_TIME_LAPSE, SPELL_CORROSIVE_ACID, SPELL_IGNITE_FLESH, SPELL_FROST_BURN}; + +struct boss_chromaggusAI : public ScriptedAI +{ + boss_chromaggusAI(Creature* pCreature) : ScriptedAI(pCreature) + { + // Select the 2 different breaths that we are going to use until despawned + // 5 possiblities for the first breath, 4 for the second, 20 total possiblites + + // select two different numbers between 0..MAX_BREATHS-1 + uint8 uiPos1 = urand(0, MAX_BREATHS - 1); + uint8 uiPos2 = (uiPos1 + urand(1, MAX_BREATHS - 1)) % MAX_BREATHS; + + m_uiBreathOneSpell = aPossibleBreaths[uiPos1]; + m_uiBreathTwoSpell = aPossibleBreaths[uiPos2]; + + m_pInstance = (ScriptedInstance*)pCreature->GetInstanceData(); + Reset(); + } + + ScriptedInstance* m_pInstance; + + uint32 m_uiBreathOneSpell; + uint32 m_uiBreathTwoSpell; + uint32 m_uiCurrentVulnerabilitySpell; + + uint32 m_uiShimmerTimer; + uint32 m_uiBreathOneTimer; + uint32 m_uiBreathTwoTimer; + uint32 m_uiAfflictionTimer; + uint32 m_uiFrenzyTimer; + bool m_bEnraged; + + void Reset() override + { + m_uiCurrentVulnerabilitySpell = 0; // We use this to store our last vulnerability spell so we can remove it later + + m_uiShimmerTimer = 0; // Time till we change vurlnerabilites + m_uiBreathOneTimer = 30000; // First breath is 30 seconds + m_uiBreathTwoTimer = 60000; // Second is 1 minute so that we can alternate + m_uiAfflictionTimer = 10000; // This is special - 5 seconds means that we cast this on 1 pPlayer every 5 sconds + m_uiFrenzyTimer = 15000; + + m_bEnraged = false; + } + + void Aggro(Unit* /*pWho*/) override + { + if (m_pInstance) + m_pInstance->SetData(TYPE_CHROMAGGUS, IN_PROGRESS); + } + + void JustDied(Unit* /*pKiller*/) override + { + if (m_pInstance) + m_pInstance->SetData(TYPE_CHROMAGGUS, DONE); + } + + void JustReachedHome() override + { + if (m_pInstance) + m_pInstance->SetData(TYPE_CHROMAGGUS, FAIL); + } + + void UpdateAI(const uint32 uiDiff) override + { + if (!m_creature->SelectHostileTarget() || !m_creature->getVictim()) + return; + + // Shimmer Timer Timer + if (m_uiShimmerTimer < uiDiff) + { + // Remove old vulnerability spell + if (m_uiCurrentVulnerabilitySpell) + m_creature->RemoveAurasDueToSpell(m_uiCurrentVulnerabilitySpell); + + // Cast new random vurlnabilty on self + uint32 aSpellId[] = {SPELL_FIRE_VULNERABILITY, SPELL_FROST_VULNERABILITY, SPELL_SHADOW_VULNERABILITY, SPELL_NATURE_VULNERABILITY, SPELL_ARCANE_VULNERABILITY}; + uint32 uiSpell = aSpellId[urand(0, 4)]; + + if (DoCastSpellIfCan(m_creature, uiSpell) == CAST_OK) + { + m_uiCurrentVulnerabilitySpell = uiSpell; + + DoScriptText(EMOTE_SHIMMER, m_creature); + m_uiShimmerTimer = 45000; + } + } + else + m_uiShimmerTimer -= uiDiff; + + // Breath One Timer + if (m_uiBreathOneTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature, m_uiBreathOneSpell) == CAST_OK) + m_uiBreathOneTimer = 60000; + } + else + m_uiBreathOneTimer -= uiDiff; + + // Breath Two Timer + if (m_uiBreathTwoTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature, m_uiBreathTwoSpell) == CAST_OK) + m_uiBreathTwoTimer = 60000; + } + else + m_uiBreathTwoTimer -= uiDiff; + + // Affliction Timer + if (m_uiAfflictionTimer < uiDiff) + { + uint32 m_uiSpellAfflict = 0; + + switch (urand(0, 4)) + { + case 0: m_uiSpellAfflict = SPELL_BROODAF_BLUE; break; + case 1: m_uiSpellAfflict = SPELL_BROODAF_BLACK; break; + case 2: m_uiSpellAfflict = SPELL_BROODAF_RED; break; + case 3: m_uiSpellAfflict = SPELL_BROODAF_BRONZE; break; + case 4: m_uiSpellAfflict = SPELL_BROODAF_GREEN; break; + } + + GuidVector vGuids; + m_creature->FillGuidsListFromThreatList(vGuids); + for (GuidVector::const_iterator i = vGuids.begin(); i != vGuids.end(); ++i) + { + Unit* pUnit = m_creature->GetMap()->GetUnit(*i); + + if (pUnit) + { + // Cast affliction + DoCastSpellIfCan(pUnit, m_uiSpellAfflict, CAST_TRIGGERED); + + // Chromatic mutation if target is effected by all afflictions + if (pUnit->HasAura(SPELL_BROODAF_BLUE, EFFECT_INDEX_0) + && pUnit->HasAura(SPELL_BROODAF_BLACK, EFFECT_INDEX_0) + && pUnit->HasAura(SPELL_BROODAF_RED, EFFECT_INDEX_0) + && pUnit->HasAura(SPELL_BROODAF_BRONZE, EFFECT_INDEX_0) + && pUnit->HasAura(SPELL_BROODAF_GREEN, EFFECT_INDEX_0)) + { + // target->RemoveAllAuras(); + // DoCastSpellIfCan(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) + m_creature->CastSpell(pUnit, 5, false); + } + } + } + + m_uiAfflictionTimer = 10000; + } + else + m_uiAfflictionTimer -= uiDiff; + + // Frenzy Timer + if (m_uiFrenzyTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature, SPELL_FRENZY) == CAST_OK) + { + DoScriptText(EMOTE_GENERIC_FRENZY_KILL, m_creature); + m_uiFrenzyTimer = urand(10000, 15000); + } + } + else + m_uiFrenzyTimer -= uiDiff; + + // Enrage if not already enraged and below 20% + if (!m_bEnraged && m_creature->GetHealthPercent() < 20.0f) + { + DoCastSpellIfCan(m_creature, SPELL_ENRAGE); + m_bEnraged = true; + } + + DoMeleeAttackIfReady(); + } +}; + +CreatureAI* GetAI_boss_chromaggus(Creature* pCreature) +{ + return new boss_chromaggusAI(pCreature); +} + +void AddSC_boss_chromaggus() +{ + Script* pNewScript; + + pNewScript = new Script; + pNewScript->Name = "boss_chromaggus"; + pNewScript->GetAI = &GetAI_boss_chromaggus; + pNewScript->RegisterSelf(); +} diff --git a/src/modules/SD2/scripts/eastern_kingdoms/blackrock_mountain/blackwing_lair/boss_ebonroc.cpp b/src/modules/SD2/scripts/eastern_kingdoms/blackrock_mountain/blackwing_lair/boss_ebonroc.cpp new file mode 100644 index 000000000..f6aa5ef44 --- /dev/null +++ b/src/modules/SD2/scripts/eastern_kingdoms/blackrock_mountain/blackwing_lair/boss_ebonroc.cpp @@ -0,0 +1,134 @@ +/* Copyright (C) 2006 - 2013 ScriptDev2 + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with 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: 100 +SDComment: None +SDCategory: Blackwing Lair +EndScriptData */ + +#include "precompiled.h" +#include "blackwing_lair.h" + +enum +{ + SPELL_SHADOW_FLAME = 22539, + SPELL_WING_BUFFET = 18500, + SPELL_SHADOW_OF_EBONROC = 23340, + SPELL_THRASH = 3391 +}; + +struct boss_ebonrocAI : public ScriptedAI +{ + boss_ebonrocAI(Creature* pCreature) : ScriptedAI(pCreature) + { + m_pInstance = (ScriptedInstance*)pCreature->GetInstanceData(); + Reset(); + } + + ScriptedInstance* m_pInstance; + + uint32 m_uiShadowFlameTimer; + uint32 m_uiWingBuffetTimer; + uint32 m_uiShadowOfEbonrocTimer; + uint32 m_uiTrashTimer; + + void Reset() override + { + m_uiShadowFlameTimer = 15000; // These times are probably wrong + m_uiWingBuffetTimer = 30000; + m_uiShadowOfEbonrocTimer = 45000; + m_uiTrashTimer = 25000; + } + + void Aggro(Unit* /*pWho*/) override + { + if (m_pInstance) + m_pInstance->SetData(TYPE_EBONROC, IN_PROGRESS); + } + + void JustDied(Unit* /*pKiller*/) override + { + if (m_pInstance) + m_pInstance->SetData(TYPE_EBONROC, DONE); + } + + void JustReachedHome() override + { + if (m_pInstance) + m_pInstance->SetData(TYPE_EBONROC, FAIL); + } + + void UpdateAI(const uint32 uiDiff) override + { + if (!m_creature->SelectHostileTarget() || !m_creature->getVictim()) + return; + + // Shadow Flame Timer + if (m_uiShadowFlameTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature, SPELL_SHADOW_FLAME) == CAST_OK) + m_uiShadowFlameTimer = urand(12000, 15000); + } + else + m_uiShadowFlameTimer -= uiDiff; + + // Wing Buffet Timer + if (m_uiWingBuffetTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature, SPELL_WING_BUFFET) == CAST_OK) + m_uiWingBuffetTimer = 25000; + } + else + m_uiWingBuffetTimer -= uiDiff; + + // Shadow of Ebonroc Timer + if (m_uiShadowOfEbonrocTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature->getVictim(), SPELL_SHADOW_OF_EBONROC) == CAST_OK) + m_uiShadowOfEbonrocTimer = urand(25000, 35000); + } + else + m_uiShadowOfEbonrocTimer -= uiDiff; + + // Thrash Timer + if (m_uiTrashTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature, SPELL_THRASH) == CAST_OK) + m_uiTrashTimer = 20000; + } + else + m_uiTrashTimer -= uiDiff; + + DoMeleeAttackIfReady(); + } +}; + +CreatureAI* GetAI_boss_ebonroc(Creature* pCreature) +{ + return new boss_ebonrocAI(pCreature); +} + +void AddSC_boss_ebonroc() +{ + Script* pNewScript; + + pNewScript = new Script; + pNewScript->Name = "boss_ebonroc"; + pNewScript->GetAI = &GetAI_boss_ebonroc; + pNewScript->RegisterSelf(); +} diff --git a/src/modules/SD2/scripts/eastern_kingdoms/blackrock_mountain/blackwing_lair/boss_firemaw.cpp b/src/modules/SD2/scripts/eastern_kingdoms/blackrock_mountain/blackwing_lair/boss_firemaw.cpp new file mode 100644 index 000000000..86f2bc403 --- /dev/null +++ b/src/modules/SD2/scripts/eastern_kingdoms/blackrock_mountain/blackwing_lair/boss_firemaw.cpp @@ -0,0 +1,138 @@ +/* Copyright (C) 2006 - 2013 ScriptDev2 + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with 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: None +SDCategory: Blackwing Lair +EndScriptData */ + +#include "precompiled.h" +#include "blackwing_lair.h" + +enum +{ + SPELL_SHADOW_FLAME = 22539, + SPELL_WING_BUFFET = 23339, + SPELL_FLAME_BUFFET = 23341, + SPELL_THRASH = 3391 +}; + +struct boss_firemawAI : public ScriptedAI +{ + boss_firemawAI(Creature* pCreature) : ScriptedAI(pCreature) + { + m_pInstance = (ScriptedInstance*)pCreature->GetInstanceData(); + Reset(); + } + + ScriptedInstance* m_pInstance; + + uint32 m_uiShadowFlameTimer; + uint32 m_uiWingBuffetTimer; + uint32 m_uiFlameBuffetTimer; + uint32 m_uiTrashTimer; + + void Reset() override + { + m_uiShadowFlameTimer = 30000; // These times are probably wrong + m_uiWingBuffetTimer = 24000; + m_uiFlameBuffetTimer = 5000; + m_uiTrashTimer = 25000; + } + + void Aggro(Unit* /*pWho*/) override + { + if (m_pInstance) + m_pInstance->SetData(TYPE_FIREMAW, IN_PROGRESS); + } + + void JustDied(Unit* /*pKiller*/) override + { + if (m_pInstance) + m_pInstance->SetData(TYPE_FIREMAW, DONE); + } + + void JustReachedHome() override + { + if (m_pInstance) + m_pInstance->SetData(TYPE_FIREMAW, FAIL); + } + + void UpdateAI(const uint32 uiDiff) override + { + if (!m_creature->SelectHostileTarget() || !m_creature->getVictim()) + return; + + // Shadow Flame Timer + if (m_uiShadowFlameTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature, SPELL_SHADOW_FLAME) == CAST_OK) + m_uiShadowFlameTimer = urand(15000, 18000); + } + else + m_uiShadowFlameTimer -= uiDiff; + + // Wing Buffet Timer + if (m_uiWingBuffetTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature, SPELL_WING_BUFFET) == CAST_OK) + { + if (m_creature->GetThreatManager().getThreat(m_creature->getVictim())) + m_creature->GetThreatManager().modifyThreatPercent(m_creature->getVictim(), -75); + + m_uiWingBuffetTimer = 25000; + } + } + else + m_uiWingBuffetTimer -= uiDiff; + + // Flame Buffet Timer + if (m_uiFlameBuffetTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature, SPELL_FLAME_BUFFET) == CAST_OK) + m_uiFlameBuffetTimer = 5000; + } + else + m_uiFlameBuffetTimer -= uiDiff; + + // Thrash Timer + if (m_uiTrashTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature, SPELL_THRASH) == CAST_OK) + m_uiTrashTimer = 20000; + } + else + m_uiTrashTimer -= uiDiff; + + DoMeleeAttackIfReady(); + } +}; +CreatureAI* GetAI_boss_firemaw(Creature* pCreature) +{ + return new boss_firemawAI(pCreature); +} + +void AddSC_boss_firemaw() +{ + Script* pNewScript; + + pNewScript = new Script; + pNewScript->Name = "boss_firemaw"; + pNewScript->GetAI = &GetAI_boss_firemaw; + pNewScript->RegisterSelf(); +} diff --git a/src/modules/SD2/scripts/eastern_kingdoms/blackrock_mountain/blackwing_lair/boss_flamegor.cpp b/src/modules/SD2/scripts/eastern_kingdoms/blackrock_mountain/blackwing_lair/boss_flamegor.cpp new file mode 100644 index 000000000..cc6aecc0f --- /dev/null +++ b/src/modules/SD2/scripts/eastern_kingdoms/blackrock_mountain/blackwing_lair/boss_flamegor.cpp @@ -0,0 +1,144 @@ +/* Copyright (C) 2006 - 2013 ScriptDev2 + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with 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: None +SDCategory: Blackwing Lair +EndScriptData */ + +#include "precompiled.h" +#include "blackwing_lair.h" + +enum +{ + EMOTE_GENERIC_FRENZY = -1000002, + + SPELL_SHADOW_FLAME = 22539, + SPELL_WING_BUFFET = 23339, + SPELL_FRENZY = 23342, // This spell periodically triggers fire nova + SPELL_THRASH = 3391 +}; + +struct boss_flamegorAI : public ScriptedAI +{ + boss_flamegorAI(Creature* pCreature) : ScriptedAI(pCreature) + { + m_pInstance = (ScriptedInstance*)pCreature->GetInstanceData(); + Reset(); + } + + ScriptedInstance* m_pInstance; + + uint32 m_uiShadowFlameTimer; + uint32 m_uiWingBuffetTimer; + uint32 m_uiFrenzyTimer; + uint32 m_uiTrashTimer; + + void Reset() override + { + m_uiShadowFlameTimer = 21000; // These times are probably wrong + m_uiWingBuffetTimer = 35000; + m_uiFrenzyTimer = 10000; + m_uiTrashTimer = 25000; + } + + void Aggro(Unit* /*pWho*/) override + { + if (m_pInstance) + m_pInstance->SetData(TYPE_FLAMEGOR, IN_PROGRESS); + } + + void JustDied(Unit* /*pKiller*/) override + { + if (m_pInstance) + m_pInstance->SetData(TYPE_FLAMEGOR, DONE); + } + + void JustReachedHome() override + { + if (m_pInstance) + m_pInstance->SetData(TYPE_FLAMEGOR, FAIL); + } + + void UpdateAI(const uint32 uiDiff) override + { + if (!m_creature->SelectHostileTarget() || !m_creature->getVictim()) + return; + + // Shadow Flame Timer + if (m_uiShadowFlameTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature, SPELL_SHADOW_FLAME) == CAST_OK) + m_uiShadowFlameTimer = urand(15000, 22000); + } + else + m_uiShadowFlameTimer -= uiDiff; + + // Wing Buffet Timer + if (m_uiWingBuffetTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature, SPELL_WING_BUFFET) == CAST_OK) + { + if (m_creature->GetThreatManager().getThreat(m_creature->getVictim())) + m_creature->GetThreatManager().modifyThreatPercent(m_creature->getVictim(), -75); + + m_uiWingBuffetTimer = 25000; + } + } + else + m_uiWingBuffetTimer -= uiDiff; + + // Frenzy Timer + if (m_uiFrenzyTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature, SPELL_FRENZY) == CAST_OK) + { + DoScriptText(EMOTE_GENERIC_FRENZY, m_creature); + m_uiFrenzyTimer = urand(8000, 10000); + } + } + else + m_uiFrenzyTimer -= uiDiff; + + // Thrash Timer + if (m_uiTrashTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature, SPELL_THRASH) == CAST_OK) + m_uiTrashTimer = 20000; + } + else + m_uiTrashTimer -= uiDiff; + + DoMeleeAttackIfReady(); + } +}; + +CreatureAI* GetAI_boss_flamegor(Creature* pCreature) +{ + return new boss_flamegorAI(pCreature); +} + +void AddSC_boss_flamegor() +{ + Script* pNewScript; + + pNewScript = new Script; + pNewScript->Name = "boss_flamegor"; + pNewScript->GetAI = &GetAI_boss_flamegor; + pNewScript->RegisterSelf(); +} diff --git a/src/modules/SD2/scripts/eastern_kingdoms/blackrock_mountain/blackwing_lair/boss_nefarian.cpp b/src/modules/SD2/scripts/eastern_kingdoms/blackrock_mountain/blackwing_lair/boss_nefarian.cpp new file mode 100644 index 000000000..7a37cdb00 --- /dev/null +++ b/src/modules/SD2/scripts/eastern_kingdoms/blackrock_mountain/blackwing_lair/boss_nefarian.cpp @@ -0,0 +1,281 @@ +/* Copyright (C) 2006 - 2013 ScriptDev2 + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with 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" +#include "blackwing_lair.h" +#include "TemporarySummon.h" + +enum +{ + SAY_XHEALTH = -1469008, // at 5% hp + SAY_AGGRO = -1469009, + SAY_RAISE_SKELETONS = -1469010, + SAY_SLAY = -1469011, + SAY_DEATH = -1469012, + + SAY_MAGE = -1469013, + SAY_WARRIOR = -1469014, + SAY_DRUID = -1469015, + SAY_PRIEST = -1469016, + SAY_PALADIN = -1469017, + SAY_SHAMAN = -1469018, + SAY_WARLOCK = -1469019, + SAY_HUNTER = -1469020, + SAY_ROGUE = -1469021, + SAY_DEATH_KNIGHT = -1469031, // spell unk for the moment + + SPELL_SHADOWFLAME_INITIAL = 22992, // old spell id 22972 -> wrong + SPELL_SHADOWFLAME = 22539, + SPELL_BELLOWING_ROAR = 22686, + SPELL_VEIL_OF_SHADOW = 22687, // old spell id 7068 -> wrong + SPELL_CLEAVE = 20691, + SPELL_TAIL_LASH = 23364, + // SPELL_BONE_CONTRUST = 23363, // 23362, 23361 Missing from DBC! + + SPELL_MAGE = 23410, // wild magic + SPELL_WARRIOR = 23397, // beserk + SPELL_DRUID = 23398, // cat form + SPELL_PRIEST = 23401, // corrupted healing + SPELL_PALADIN = 23418, // syphon blessing + SPELL_SHAMAN = 23425, // totems + SPELL_WARLOCK = 23427, // infernals -> should trigger 23426 + SPELL_HUNTER = 23436, // bow broke + SPELL_ROGUE = 23414, // Paralise +}; + +struct boss_nefarianAI : public ScriptedAI +{ + boss_nefarianAI(Creature* pCreature) : ScriptedAI(pCreature) + { + m_pInstance = (ScriptedInstance*)pCreature->GetInstanceData(); + Reset(); + } + + ScriptedInstance* m_pInstance; + + uint32 m_uiShadowFlameTimer; + uint32 m_uiBellowingRoarTimer; + uint32 m_uiVeilOfShadowTimer; + uint32 m_uiCleaveTimer; + uint32 m_uiTailLashTimer; + uint32 m_uiClassCallTimer; + bool m_bPhase3; + bool m_bHasEndYell; + + void Reset() override + { + m_uiShadowFlameTimer = 12000; // These times are probably wrong + m_uiBellowingRoarTimer = 30000; + m_uiVeilOfShadowTimer = 15000; + m_uiCleaveTimer = 7000; + m_uiTailLashTimer = 10000; + m_uiClassCallTimer = 35000; // 35-40 seconds + m_bPhase3 = false; + m_bHasEndYell = false; + } + + void KilledUnit(Unit* pVictim) override + { + if (urand(0, 4)) + return; + + DoScriptText(SAY_SLAY, m_creature, pVictim); + } + + void JustDied(Unit* /*pKiller*/) override + { + DoScriptText(SAY_DEATH, m_creature); + + if (m_pInstance) + m_pInstance->SetData(TYPE_NEFARIAN, DONE); + } + + void JustReachedHome() override + { + if (m_pInstance) + { + m_pInstance->SetData(TYPE_NEFARIAN, FAIL); + + // Cleanup encounter + if (m_creature->IsTemporarySummon()) + { + TemporarySummon* pTemporary = (TemporarySummon*)m_creature; + + if (Creature* pNefarius = m_creature->GetMap()->GetCreature(pTemporary->GetSummonerGuid())) + pNefarius->AI()->EnterEvadeMode(); + } + + m_creature->ForcedDespawn(); + } + } + + void Aggro(Unit* /*pWho*/) override + { + DoScriptText(SAY_AGGRO, m_creature); + + // Remove flying in case Nefarian aggroes before his combat point was reached + if (m_creature->IsLevitating()) + { + m_creature->SetByteValue(UNIT_FIELD_BYTES_1, 3, 0); + m_creature->SetLevitate(false); + } + + DoCastSpellIfCan(m_creature, SPELL_SHADOWFLAME_INITIAL); + } + + void UpdateAI(const uint32 uiDiff) override + { + if (!m_creature->SelectHostileTarget() || !m_creature->getVictim()) + return; + + // ShadowFlame_Timer + if (m_uiShadowFlameTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature, SPELL_SHADOWFLAME) == CAST_OK) + m_uiShadowFlameTimer = 12000; + } + else + m_uiShadowFlameTimer -= uiDiff; + + // BellowingRoar_Timer + if (m_uiBellowingRoarTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature, SPELL_BELLOWING_ROAR) == CAST_OK) + m_uiBellowingRoarTimer = 30000; + } + else + m_uiBellowingRoarTimer -= uiDiff; + + // VeilOfShadow_Timer + if (m_uiVeilOfShadowTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature->getVictim(), SPELL_VEIL_OF_SHADOW) == CAST_OK) + m_uiVeilOfShadowTimer = 15000; + } + else + m_uiVeilOfShadowTimer -= uiDiff; + + // Cleave_Timer + if (m_uiCleaveTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature->getVictim(), SPELL_CLEAVE) == CAST_OK) + m_uiCleaveTimer = 7000; + } + else + m_uiCleaveTimer -= uiDiff; + + // TailLash_Timer + if (m_uiTailLashTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature, SPELL_TAIL_LASH) == CAST_OK) + m_uiTailLashTimer = 10000; + } + else + m_uiTailLashTimer -= uiDiff; + + // ClassCall_Timer + if (m_uiClassCallTimer < uiDiff) + { + // 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 (urand(0, 8)) + { + case 0: + DoScriptText(SAY_MAGE, m_creature); + DoCastSpellIfCan(m_creature, SPELL_MAGE); + break; + case 1: + DoScriptText(SAY_WARRIOR, m_creature); + DoCastSpellIfCan(m_creature, SPELL_WARRIOR); + break; + case 2: + DoScriptText(SAY_DRUID, m_creature); + DoCastSpellIfCan(m_creature, SPELL_DRUID); + break; + case 3: + DoScriptText(SAY_PRIEST, m_creature); + DoCastSpellIfCan(m_creature, SPELL_PRIEST); + break; + case 4: + DoScriptText(SAY_PALADIN, m_creature); + DoCastSpellIfCan(m_creature, SPELL_PALADIN); + break; + case 5: + DoScriptText(SAY_SHAMAN, m_creature); + DoCastSpellIfCan(m_creature, SPELL_SHAMAN); + break; + case 6: + DoScriptText(SAY_WARLOCK, m_creature); + DoCastSpellIfCan(m_creature, SPELL_WARLOCK); + break; + case 7: + DoScriptText(SAY_HUNTER, m_creature); + DoCastSpellIfCan(m_creature, SPELL_HUNTER); + break; + case 8: + DoScriptText(SAY_ROGUE, m_creature); + DoCastSpellIfCan(m_creature, SPELL_ROGUE); + break; + } + + m_uiClassCallTimer = urand(35000, 40000); + } + else + m_uiClassCallTimer -= uiDiff; + + // Phase3 begins when we are below X health + if (!m_bPhase3 && m_creature->GetHealthPercent() < 20.0f) + { + if (m_pInstance) + m_pInstance->SetData(TYPE_NEFARIAN, SPECIAL); + m_bPhase3 = true; + DoScriptText(SAY_RAISE_SKELETONS, m_creature); + } + + // 5% hp yell + if (!m_bHasEndYell && m_creature->GetHealthPercent() < 5.0f) + { + m_bHasEndYell = true; + DoScriptText(SAY_XHEALTH, m_creature); + } + + DoMeleeAttackIfReady(); + } +}; + +CreatureAI* GetAI_boss_nefarian(Creature* pCreature) +{ + return new boss_nefarianAI(pCreature); +} + +void AddSC_boss_nefarian() +{ + Script* pNewScript; + + pNewScript = new Script; + pNewScript->Name = "boss_nefarian"; + pNewScript->GetAI = &GetAI_boss_nefarian; + pNewScript->RegisterSelf(); +} diff --git a/src/modules/SD2/scripts/eastern_kingdoms/blackrock_mountain/blackwing_lair/boss_razorgore.cpp b/src/modules/SD2/scripts/eastern_kingdoms/blackrock_mountain/blackwing_lair/boss_razorgore.cpp new file mode 100644 index 000000000..5b67610b8 --- /dev/null +++ b/src/modules/SD2/scripts/eastern_kingdoms/blackrock_mountain/blackwing_lair/boss_razorgore.cpp @@ -0,0 +1,248 @@ +/* Copyright (C) 2006 - 2013 ScriptDev2 + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with 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: 95 +SDComment: Timers may be improved. +SDCategory: Blackwing Lair +EndScriptData */ + +#include "precompiled.h" +#include "blackwing_lair.h" + +enum +{ + SAY_EGGS_BROKEN_1 = -1469022, + SAY_EGGS_BROKEN_2 = -1469023, + SAY_EGGS_BROKEN_3 = -1469024, + SAY_DEATH = -1469025, + + SPELL_POSSESS = 23014, // visual effect and increase the damage taken + SPELL_DESTROY_EGG = 19873, + SPELL_EXPLODE_ORB = 20037, // used if attacked without destroying the eggs - triggers 20038 + + SPELL_CLEAVE = 19632, + SPELL_WARSTOMP = 24375, + SPELL_FIREBALL_VOLLEY = 22425, + SPELL_CONFLAGRATION = 23023, +}; + +struct boss_razorgoreAI : public ScriptedAI +{ + boss_razorgoreAI(Creature* pCreature) : ScriptedAI(pCreature) + { + m_pInstance = (instance_blackwing_lair*)pCreature->GetInstanceData(); + Reset(); + } + + instance_blackwing_lair* m_pInstance; + + uint32 m_uiIntroVisualTimer; + uint32 m_uiCleaveTimer; + uint32 m_uiWarStompTimer; + uint32 m_uiFireballVolleyTimer; + uint32 m_uiConflagrationTimer; + + bool m_bEggsExploded; + + void Reset() override + { + m_uiIntroVisualTimer = 5000; + m_bEggsExploded = false; + + m_uiCleaveTimer = urand(4000, 8000); + m_uiWarStompTimer = 30000; + m_uiConflagrationTimer = urand(10000, 15000); + m_uiFireballVolleyTimer = urand(15000, 20000); + } + + void JustDied(Unit* /*pKiller*/) override + { + if (m_pInstance) + { + // Don't set instance data unless all eggs are destroyed + if (m_pInstance->GetData(TYPE_RAZORGORE) != SPECIAL) + return; + + m_pInstance->SetData(TYPE_RAZORGORE, DONE); + } + + DoScriptText(SAY_DEATH, m_creature); + } + + void DamageTaken(Unit* /*pDoneBy*/, uint32& uiDamage) override + { + if (uiDamage < m_creature->GetHealth()) + return; + + if (!m_pInstance) + return; + + // Don't allow any accident + if (m_bEggsExploded) + { + uiDamage = 0; + return; + } + + // Boss explodes everything and resets - this happens if not all eggs are destroyed + if (m_pInstance->GetData(TYPE_RAZORGORE) == IN_PROGRESS) + { + uiDamage = 0; + m_bEggsExploded = true; + m_pInstance->SetData(TYPE_RAZORGORE, FAIL); + DoCastSpellIfCan(m_creature, SPELL_EXPLODE_ORB, CAST_TRIGGERED); + m_creature->ForcedDespawn(); + } + } + + void JustReachedHome() override + { + if (m_pInstance) + m_pInstance->SetData(TYPE_RAZORGORE, FAIL); + } + + void JustSummoned(Creature* pSummoned) override + { + // Defenders should attack the players and the boss + pSummoned->SetInCombatWithZone(); + pSummoned->AI()->AttackStart(m_creature); + } + + void UpdateAI(const uint32 uiDiff) override + { + if (!m_creature->SelectHostileTarget() || !m_creature->getVictim()) + { + // Set visual only on OOC timer + if (m_uiIntroVisualTimer) + { + if (m_uiIntroVisualTimer <= uiDiff) + { + if (!m_pInstance) + { + script_error_log("Instance Blackwing Lair: ERROR Failed to load instance data for this instace."); + return; + } + + if (Creature* pOrbTrigger = m_pInstance->GetSingleCreatureFromStorage(NPC_BLACKWING_ORB_TRIGGER)) + pOrbTrigger->CastSpell(m_creature, SPELL_POSSESS, false); + m_uiIntroVisualTimer = 0; + } + else + m_uiIntroVisualTimer -= uiDiff; + } + + return; + } + + // Cleave + if (m_uiCleaveTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature->getVictim(), SPELL_CLEAVE) == CAST_OK) + m_uiCleaveTimer = urand(4000, 8000); + } + else + m_uiCleaveTimer -= uiDiff; + + // War Stomp + if (m_uiWarStompTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature, SPELL_WARSTOMP) == CAST_OK) + m_uiWarStompTimer = 30000; + } + else + m_uiWarStompTimer -= uiDiff; + + // Fireball Volley + if (m_uiFireballVolleyTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature, SPELL_FIREBALL_VOLLEY) == CAST_OK) + m_uiFireballVolleyTimer = urand(15000, 20000); + } + else + m_uiFireballVolleyTimer -= uiDiff; + + // Conflagration + if (m_uiConflagrationTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature, SPELL_CONFLAGRATION) == CAST_OK) + m_uiConflagrationTimer = urand(15000, 25000); + } + else + m_uiConflagrationTimer -= uiDiff; + + /* This is obsolete code, not working anymore, keep as reference, should be handled in core though + * // Aura Check. If the gamer is affected by confliguration we attack a random gamer. + * if (m_creature->getVictim()->HasAura(SPELL_CONFLAGRATION, EFFECT_INDEX_0)) + * { + * if (Unit* pTarget = m_creature->SelectAttackingTarget(ATTACKING_TARGET_RANDOM, 1)) + * m_creature->TauntApply(pTarget); + * } + */ + + DoMeleeAttackIfReady(); + } +}; + +CreatureAI* GetAI_boss_razorgore(Creature* pCreature) +{ + return new boss_razorgoreAI(pCreature); +} + +bool EffectDummyGameObj_go_black_dragon_egg(Unit* pCaster, uint32 uiSpellId, SpellEffectIndex uiEffIndex, GameObject* pGOTarget, ObjectGuid /*originalCasterGuid*/) +{ + if (uiSpellId == SPELL_DESTROY_EGG && uiEffIndex == EFFECT_INDEX_1) + { + if (!pGOTarget->isSpawned()) + return true; + + if (ScriptedInstance* pInstance = (ScriptedInstance*)pGOTarget->GetInstanceData()) + { + if (urand(0, 1)) + { + switch (urand(0, 2)) + { + case 0: DoScriptText(SAY_EGGS_BROKEN_1, pCaster); break; + case 1: DoScriptText(SAY_EGGS_BROKEN_2, pCaster); break; + case 2: DoScriptText(SAY_EGGS_BROKEN_3, pCaster); break; + } + } + + // Store the eggs which are destroyed, in order to count them for the second phase + pInstance->SetData64(DATA_DRAGON_EGG, pGOTarget->GetObjectGuid()); + } + + return true; + } + + return false; +} + +void AddSC_boss_razorgore() +{ + Script* pNewScript; + + pNewScript = new Script; + pNewScript->Name = "boss_razorgore"; + pNewScript->GetAI = &GetAI_boss_razorgore; + pNewScript->RegisterSelf(); + + pNewScript = new Script; + pNewScript->Name = "go_black_dragon_egg"; + pNewScript->pEffectDummyGO = &EffectDummyGameObj_go_black_dragon_egg; + pNewScript->RegisterSelf(); +} diff --git a/src/modules/SD2/scripts/eastern_kingdoms/blackrock_mountain/blackwing_lair/boss_vaelastrasz.cpp b/src/modules/SD2/scripts/eastern_kingdoms/blackrock_mountain/blackwing_lair/boss_vaelastrasz.cpp new file mode 100644 index 000000000..8a588e259 --- /dev/null +++ b/src/modules/SD2/scripts/eastern_kingdoms/blackrock_mountain/blackwing_lair/boss_vaelastrasz.cpp @@ -0,0 +1,390 @@ +/* Copyright (C) 2006 - 2013 ScriptDev2 + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with 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" +#include "blackwing_lair.h" + +enum +{ + SAY_LINE_1 = -1469026, + SAY_LINE_2 = -1469027, + SAY_LINE_3 = -1469028, + SAY_HALFLIFE = -1469029, + SAY_KILLTARGET = -1469030, + SAY_NEFARIUS_CORRUPT_1 = -1469006, // When he corrupts Vaelastrasz + SAY_NEFARIUS_CORRUPT_2 = -1469032, + SAY_TECHNICIAN_RUN = -1469034, + + SPELL_ESSENCE_OF_THE_RED = 23513, + SPELL_FLAME_BREATH = 23461, + SPELL_FIRE_NOVA = 23462, + SPELL_TAIL_SWEEP = 15847, + SPELL_BURNING_ADRENALINE = 23620, + SPELL_CLEAVE = 20684, // Chain cleave is most likely named something different and contains a dummy effect + + SPELL_NEFARIUS_CORRUPTION = 23642, + + GOSSIP_ITEM_VAEL_1 = -3469003, + GOSSIP_ITEM_VAEL_2 = -3469004, + // Vael Gossip texts might be 7156 and 7256; At the moment are missing from DB + // For the moment add the default values + GOSSIP_TEXT_VAEL_1 = 384, + GOSSIP_TEXT_VAEL_2 = 384, + + FACTION_HOSTILE = 14, + + AREATRIGGER_VAEL_INTRO = 3626, +}; + +struct boss_vaelastraszAI : public ScriptedAI +{ + boss_vaelastraszAI(Creature* pCreature) : ScriptedAI(pCreature) + { + m_pInstance = (ScriptedInstance*)pCreature->GetInstanceData(); + Reset(); + + // Set stand state to dead before the intro event + m_creature->SetStandState(UNIT_STAND_STATE_DEAD); + } + + ScriptedInstance* m_pInstance; + + ObjectGuid m_nefariusGuid; + uint32 m_uiIntroTimer; + uint8 m_uiIntroPhase; + + ObjectGuid m_playerGuid; + uint32 m_uiSpeechTimer; + uint8 m_uiSpeechNum; + + uint32 m_uiCleaveTimer; + uint32 m_uiFlameBreathTimer; + uint32 m_uiFireNovaTimer; + uint32 m_uiBurningAdrenalineCasterTimer; + uint32 m_uiBurningAdrenalineTankTimer; + uint32 m_uiTailSweepTimer; + bool m_bHasYelled; + + void Reset() override + { + m_playerGuid.Clear(); + + m_uiIntroTimer = 0; + m_uiIntroPhase = 0; + m_uiSpeechTimer = 0; + m_uiSpeechNum = 0; + m_uiCleaveTimer = 8000; // These times are probably wrong + m_uiFlameBreathTimer = 11000; + m_uiBurningAdrenalineCasterTimer = 15000; + m_uiBurningAdrenalineTankTimer = 45000; + m_uiFireNovaTimer = 5000; + m_uiTailSweepTimer = 20000; + m_bHasYelled = false; + + // Creature should have only 1/3 of hp + m_creature->SetHealth(uint32(m_creature->GetMaxHealth()*.3)); + } + + void BeginIntro() + { + // Start Intro delayed + m_uiIntroTimer = 1000; + + if (m_pInstance) + m_pInstance->SetData(TYPE_VAELASTRASZ, SPECIAL); + } + + void BeginSpeech(Player* pTarget) + { + // Stand up and begin speach + m_playerGuid = pTarget->GetObjectGuid(); + + // 10 seconds + DoScriptText(SAY_LINE_1, m_creature); + + // Make boss stand + m_creature->SetStandState(UNIT_STAND_STATE_STAND); + + m_uiSpeechTimer = 10000; + m_uiSpeechNum = 0; + } + + void KilledUnit(Unit* pVictim) override + { + if (urand(0, 4)) + return; + + DoScriptText(SAY_KILLTARGET, m_creature, pVictim); + } + + void Aggro(Unit* /*pWho*/) override + { + if (m_pInstance) + m_pInstance->SetData(TYPE_VAELASTRASZ, IN_PROGRESS); + + // Buff players on aggro + DoCastSpellIfCan(m_creature, SPELL_ESSENCE_OF_THE_RED); + } + + void JustDied(Unit* /*pKiller*/) override + { + if (m_pInstance) + m_pInstance->SetData(TYPE_VAELASTRASZ, DONE); + } + + void JustReachedHome() override + { + if (m_pInstance) + m_pInstance->SetData(TYPE_VAELASTRASZ, FAIL); + } + + void JustSummoned(Creature* pSummoned) override + { + if (pSummoned->GetEntry() == NPC_LORD_VICTOR_NEFARIUS) + { + // Set not selectable, so players won't interact with it + pSummoned->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE); + m_nefariusGuid = pSummoned->GetObjectGuid(); + } + } + + void UpdateAI(const uint32 uiDiff) override + { + if (m_uiIntroTimer) + { + if (m_uiIntroTimer <= uiDiff) + { + switch (m_uiIntroPhase) + { + case 0: + m_creature->SummonCreature(NPC_LORD_VICTOR_NEFARIUS, aNefariusSpawnLoc[0], aNefariusSpawnLoc[1], aNefariusSpawnLoc[2], aNefariusSpawnLoc[3], TEMPSUMMON_TIMED_DESPAWN, 25000); + m_uiIntroTimer = 1000; + break; + case 1: + if (Creature* pNefarius = m_creature->GetMap()->GetCreature(m_nefariusGuid)) + { + pNefarius->CastSpell(m_creature, SPELL_NEFARIUS_CORRUPTION, true); + DoScriptText(SAY_NEFARIUS_CORRUPT_1, pNefarius); + } + m_uiIntroTimer = 16000; + break; + case 2: + if (Creature* pNefarius = m_creature->GetMap()->GetCreature(m_nefariusGuid)) + DoScriptText(SAY_NEFARIUS_CORRUPT_2, pNefarius); + + // Set npc flags now + m_creature->SetFlag(UNIT_NPC_FLAGS, UNIT_NPC_FLAG_GOSSIP); + m_creature->SetFlag(UNIT_NPC_FLAGS, UNIT_NPC_FLAG_QUESTGIVER); + m_uiIntroTimer = 0; + break; + } + ++m_uiIntroPhase; + } + else + m_uiIntroTimer -= uiDiff; + } + + // Speech + if (m_uiSpeechTimer) + { + if (m_uiSpeechTimer <= uiDiff) + { + switch (m_uiSpeechNum) + { + case 0: + // 16 seconds till next line + DoScriptText(SAY_LINE_2, m_creature); + m_uiSpeechTimer = 16000; + ++m_uiSpeechNum; + break; + case 1: + // This one is actually 16 seconds but we only go to 10 seconds because he starts attacking after he says "I must fight this!" + DoScriptText(SAY_LINE_3, m_creature); + m_uiSpeechTimer = 10000; + ++m_uiSpeechNum; + break; + case 2: + m_creature->SetFactionTemporary(FACTION_HOSTILE, TEMPFACTION_RESTORE_RESPAWN); + + if (m_playerGuid) + { + if (Player* pPlayer = m_creature->GetMap()->GetPlayer(m_playerGuid)) + AttackStart(pPlayer); + } + m_uiSpeechTimer = 0; + break; + } + } + else + m_uiSpeechTimer -= uiDiff; + } + + // Return since we have no target + if (!m_creature->SelectHostileTarget() || !m_creature->getVictim()) + return; + + // Yell if hp lower than 15% + if (m_creature->GetHealthPercent() < 15.0f && !m_bHasYelled) + { + DoScriptText(SAY_HALFLIFE, m_creature); + m_bHasYelled = true; + } + + // Cleave Timer + if (m_uiCleaveTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature->getVictim(), SPELL_CLEAVE) == CAST_OK) + m_uiCleaveTimer = 15000; + } + else + m_uiCleaveTimer -= uiDiff; + + // Flame Breath Timer + if (m_uiFlameBreathTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature->getVictim(), SPELL_FLAME_BREATH) == CAST_OK) + m_uiFlameBreathTimer = urand(4000, 8000); + } + else + m_uiFlameBreathTimer -= uiDiff; + + // Burning Adrenaline Caster Timer + if (m_uiBurningAdrenalineCasterTimer < uiDiff) + { + if (Unit* pTarget = m_creature->SelectAttackingTarget(ATTACKING_TARGET_RANDOM, 0, SPELL_BURNING_ADRENALINE, SELECT_FLAG_PLAYER | SELECT_FLAG_POWER_MANA)) + { + pTarget->CastSpell(pTarget, SPELL_BURNING_ADRENALINE, true, NULL, NULL, m_creature->GetObjectGuid()); + m_uiBurningAdrenalineCasterTimer = 15000; + } + } + else + m_uiBurningAdrenalineCasterTimer -= uiDiff; + + // Burning Adrenaline Tank Timer + if (m_uiBurningAdrenalineTankTimer < uiDiff) + { + // 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_BURNING_ADRENALINE, true, NULL, NULL, m_creature->GetObjectGuid()); + + m_uiBurningAdrenalineTankTimer = 45000; + } + else + m_uiBurningAdrenalineTankTimer -= uiDiff; + + // Fire Nova Timer + if (m_uiFireNovaTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature, SPELL_FIRE_NOVA) == CAST_OK) + m_uiFireNovaTimer = 5000; + } + else + m_uiFireNovaTimer -= uiDiff; + + // Tail Sweep Timer + if (m_uiTailSweepTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature, SPELL_TAIL_SWEEP) == CAST_OK) + m_uiTailSweepTimer = 20000; + } + else + m_uiTailSweepTimer -= uiDiff; + + DoMeleeAttackIfReady(); + } +}; + +bool GossipSelect_boss_vaelastrasz(Player* pPlayer, Creature* pCreature, uint32 /*uiSender*/, uint32 uiAction) +{ + switch (uiAction) + { + case GOSSIP_ACTION_INFO_DEF + 1: + pPlayer->ADD_GOSSIP_ITEM_ID(GOSSIP_ICON_CHAT, GOSSIP_ITEM_VAEL_2, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 2); + pPlayer->SEND_GOSSIP_MENU(GOSSIP_TEXT_VAEL_2, pCreature->GetObjectGuid()); + break; + case GOSSIP_ACTION_INFO_DEF + 2: + pPlayer->CLOSE_GOSSIP_MENU(); + if (boss_vaelastraszAI* pVaelAI = dynamic_cast(pCreature->AI())) + pVaelAI->BeginSpeech(pPlayer); + break; + } + + return true; +} + +bool GossipHello_boss_vaelastrasz(Player* pPlayer, Creature* pCreature) +{ + if (pCreature->IsQuestGiver()) + pPlayer->PrepareQuestMenu(pCreature->GetObjectGuid()); + + pPlayer->ADD_GOSSIP_ITEM_ID(GOSSIP_ICON_CHAT, GOSSIP_ITEM_VAEL_1, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 1); + pPlayer->SEND_GOSSIP_MENU(GOSSIP_TEXT_VAEL_1, pCreature->GetObjectGuid()); + + return true; +} + +CreatureAI* GetAI_boss_vaelastrasz(Creature* pCreature) +{ + return new boss_vaelastraszAI(pCreature); +} + +bool AreaTrigger_at_vaelastrasz(Player* pPlayer, AreaTriggerEntry const* pAt) +{ + if (pAt->id == AREATRIGGER_VAEL_INTRO) + { + if (pPlayer->isGameMaster() || pPlayer->IsDead()) + return false; + + if (instance_blackwing_lair* pInstance = (instance_blackwing_lair*)pPlayer->GetInstanceData()) + { + // Handle intro event + if (pInstance->GetData(TYPE_VAELASTRASZ) == NOT_STARTED) + { + if (Creature* pVaelastrasz = pInstance->GetSingleCreatureFromStorage(NPC_VAELASTRASZ)) + if (boss_vaelastraszAI* pVaelAI = dynamic_cast(pVaelastrasz->AI())) + pVaelAI->BeginIntro(); + } + + // ToDo: make goblins flee + } + } + + return false; +} + +void AddSC_boss_vaelastrasz() +{ + Script* pNewScript; + + pNewScript = new Script; + pNewScript->Name = "boss_vaelastrasz"; + pNewScript->GetAI = &GetAI_boss_vaelastrasz; + pNewScript->pGossipHello = &GossipHello_boss_vaelastrasz; + pNewScript->pGossipSelect = &GossipSelect_boss_vaelastrasz; + pNewScript->RegisterSelf(); + + pNewScript = new Script; + pNewScript->Name = "at_vaelastrasz"; + pNewScript->pAreaTrigger = &AreaTrigger_at_vaelastrasz; + pNewScript->RegisterSelf(); +} diff --git a/src/modules/SD2/scripts/eastern_kingdoms/blackrock_mountain/blackwing_lair/boss_victor_nefarius.cpp b/src/modules/SD2/scripts/eastern_kingdoms/blackrock_mountain/blackwing_lair/boss_victor_nefarius.cpp new file mode 100644 index 000000000..6e0370989 --- /dev/null +++ b/src/modules/SD2/scripts/eastern_kingdoms/blackrock_mountain/blackwing_lair/boss_victor_nefarius.cpp @@ -0,0 +1,418 @@ +/* Copyright (C) 2006 - 2013 ScriptDev2 + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with 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: 90 +SDComment: Small adjustments needed; Timers +SDCategory: Blackwing Lair +EndScriptData */ + +#include "precompiled.h" +#include "blackwing_lair.h" + +enum +{ + SAY_GAMESBEGIN_1 = -1469004, + SAY_GAMESBEGIN_2 = -1469005, + SAY_NEFARIAN_INTRO = -1469007, + + GOSSIP_ITEM_NEFARIUS_1 = -3469000, + GOSSIP_ITEM_NEFARIUS_2 = -3469001, + GOSSIP_ITEM_NEFARIUS_3 = -3469002, + GOSSIP_TEXT_NEFARIUS_1 = 7134, + GOSSIP_TEXT_NEFARIUS_2 = 7198, + GOSSIP_TEXT_NEFARIUS_3 = 7199, + + MAX_DRAKES = 5, + MAX_DRAKE_SUMMONS = 42, + NPC_BRONZE_DRAKANOID = 14263, + NPC_BLUE_DRAKANOID = 14261, + NPC_RED_DRAKANOID = 14264, + NPC_GREEN_DRAKANOID = 14262, + NPC_BLACK_DRAKANOID = 14265, + NPC_CHROMATIC_DRAKANOID = 14302, + + SPELL_NEFARIUS_BARRIER = 22663, // immunity in phase 1 + SPELL_SHADOWBLINK_INTRO = 22664, + SPELL_SHADOWBOLT_VOLLEY = 22665, + SPELL_SILENCE = 22666, + SPELL_SHADOW_COMMAND = 22667, + SPELL_SHADOWBOLT = 22677, + SPELL_FEAR = 22678, + SPELL_SHADOWBLINK = 22681, // triggers a random from spells (22668 - 22676) + + SPELL_SUMMON_DRAKONID_BONES = 23363, + + MAP_ID_BWL = 469, + + FACTION_BLACK_DRAGON = 103 +}; + +static const DialogueEntry aIntroDialogue[] = +{ + {SAY_GAMESBEGIN_1, NPC_LORD_VICTOR_NEFARIUS, 4000}, + {SAY_GAMESBEGIN_2, NPC_LORD_VICTOR_NEFARIUS, 5000}, + {SPELL_SHADOWBLINK, 0, 0}, + {0, 0, 0}, +}; + +struct SpawnLocation +{ + float m_fX, m_fY, m_fZ; +}; + +static const SpawnLocation aNefarianLocs[4] = +{ + { -7599.32f, -1191.72f, 475.545f}, // opening where red/blue/black darknid spawner appear (ori 3.05433) + { -7526.27f, -1135.04f, 473.445f}, // same as above, closest to door (ori 5.75959) + { -7498.177f, -1273.277f, 481.649f}, // nefarian spawn location (ori 1.798) + { -7502.002f, -1256.503f, 476.758f}, // nefarian fly to this position +}; + +static const uint32 aPossibleDrake[MAX_DRAKES] = {NPC_BRONZE_DRAKANOID, NPC_BLUE_DRAKANOID, NPC_RED_DRAKANOID, NPC_GREEN_DRAKANOID, NPC_BLACK_DRAKANOID}; + +// This script is complicated +// Instead of morphing Victor Nefarius we will have him control phase 1 +// And then have him spawn "Nefarian" for phase 2 +// When phase 2 starts Victor Nefarius will go invisible and stop attacking +// If Nefarian reched home because nef killed the players then nef will trigger this guy to EnterEvadeMode +// and allow players to start the event over +// If nefarian dies then he will kill himself then he will be despawned in Nefarian script +// To prevent players from doing the event twice + +// Dev note: Lord Victor Nefarius should despawn completely, then ~5 seconds later Nefarian should appear. + +struct boss_victor_nefariusAI : public ScriptedAI, private DialogueHelper +{ + boss_victor_nefariusAI(Creature* pCreature) : ScriptedAI(pCreature), + DialogueHelper(aIntroDialogue) + { + // Select the 2 different drakes that we are going to use until despawned + // 5 possiblities for the first drake, 4 for the second, 20 total possiblites + + // select two different numbers between 0..MAX_DRAKES-1 + uint8 uiPos1 = urand(0, MAX_DRAKES - 1); + uint8 uiPos2 = (uiPos1 + urand(1, MAX_DRAKES - 1)) % MAX_DRAKES; + + m_uiDrakeTypeOne = aPossibleDrake[uiPos1]; + m_uiDrakeTypeTwo = aPossibleDrake[uiPos2]; + + m_pInstance = (ScriptedInstance*)pCreature->GetInstanceData(); + InitializeDialogueHelper(m_pInstance); + Reset(); + } + + ScriptedInstance* m_pInstance; + + uint32 m_uiSpawnedAdds; + uint32 m_uiAddSpawnTimer; + uint32 m_uiShadowBoltTimer; + uint32 m_uiFearTimer; + uint32 m_uiDrakeTypeOne; + uint32 m_uiDrakeTypeTwo; + uint32 m_uiShadowboltVolleyTimer; + uint32 m_uiSilenceTimer; + uint32 m_uiShadowCommandTimer; + uint32 m_uiShadowBlinkTimer; + + void Reset() override + { + // Check the map id because the same creature entry is involved in other scripted event in other instance + if (m_creature->GetMapId() != MAP_ID_BWL) + return; + + m_uiSpawnedAdds = 0; + m_uiAddSpawnTimer = 10000; + m_uiShadowBoltTimer = 3000; + m_uiFearTimer = 8000; + m_uiShadowboltVolleyTimer = 13000; + m_uiSilenceTimer = 23000; + m_uiShadowCommandTimer = 30000; + m_uiShadowBlinkTimer = 40000; + + // set gossip flag to begin the event + m_creature->SetFlag(UNIT_NPC_FLAGS, UNIT_NPC_FLAG_GOSSIP); + + // Make visible if needed + if (m_creature->GetVisibility() != VISIBILITY_ON) + m_creature->SetVisibility(VISIBILITY_ON); + } + + void Aggro(Unit* /*pWho*/) override + { + if (m_pInstance) + m_pInstance->SetData(TYPE_NEFARIAN, IN_PROGRESS); + } + + void JustReachedHome() override + { + if (m_pInstance) + m_pInstance->SetData(TYPE_NEFARIAN, FAIL); + } + + void AttackStart(Unit* pWho) override + { + if (m_creature->Attack(pWho, false)) + { + m_creature->AddThreat(pWho); + m_creature->SetInCombatWith(pWho); + pWho->SetInCombatWith(m_creature); + + // Only range attack - ToDo: research the distance + m_creature->GetMotionMaster()->MoveChase(pWho, 30.0f); + } + } + + void JustSummoned(Creature* pSummoned) override + { + if (m_creature->GetMapId() != MAP_ID_BWL) + return; + + if (pSummoned->GetEntry() == NPC_NEFARIAN) + { + pSummoned->SetWalk(false); + + // see boss_onyxia (also note the removal of this in boss_nefarian) + pSummoned->SetByteValue(UNIT_FIELD_BYTES_1, 3, UNIT_BYTE1_FLAG_ALWAYS_STAND | UNIT_BYTE1_FLAG_UNK_2); + pSummoned->SetLevitate(true); + + // Let Nefarian fly towards combat area + pSummoned->GetMotionMaster()->MovePoint(1, aNefarianLocs[3].m_fX, aNefarianLocs[3].m_fY, aNefarianLocs[3].m_fZ); + DoScriptText(SAY_NEFARIAN_INTRO, pSummoned); + } + else + { + ++m_uiSpawnedAdds; + + if (Unit* pTarget = m_creature->SelectAttackingTarget(ATTACKING_TARGET_RANDOM, 0)) + pSummoned->AI()->AttackStart(pTarget); + } + } + + void SummonedMovementInform(Creature* pSummoned, uint32 uiMotionType, uint32 uiPointId) override + { + if (m_creature->GetMapId() != MAP_ID_BWL) + return; + + // If Nefarian has reached combat area, let him attack + if (pSummoned->GetEntry() == NPC_NEFARIAN && uiMotionType == POINT_MOTION_TYPE && uiPointId == 1) + { + if (Unit* pTarget = m_creature->SelectAttackingTarget(ATTACKING_TARGET_RANDOM, 0)) + pSummoned->AI()->AttackStart(pTarget); + } + } + + void SummonedCreatureJustDied(Creature* pSummoned) override + { + if (m_creature->GetMapId() != MAP_ID_BWL) + return; + + // Despawn self when Nefarian is killed + if (pSummoned->GetEntry() == NPC_NEFARIAN) + m_creature->ForcedDespawn(); + else + pSummoned->CastSpell(pSummoned, SPELL_SUMMON_DRAKONID_BONES, true); + } + + void JustDidDialogueStep(int32 iEntry) override + { + // Start combat after the dialogue is finished + if (iEntry == SPELL_SHADOWBLINK) + { + m_creature->SetStandState(UNIT_STAND_STATE_STAND); + m_creature->SetFactionTemporary(FACTION_BLACK_DRAGON, TEMPFACTION_RESTORE_REACH_HOME); + DoCastSpellIfCan(m_creature, SPELL_NEFARIUS_BARRIER, CAST_TRIGGERED); + DoCastSpellIfCan(m_creature, SPELL_SHADOWBLINK_INTRO, CAST_TRIGGERED); + m_creature->SetInCombatWithZone(); + } + } + + void DoStartIntro() + { + StartNextDialogueText(SAY_GAMESBEGIN_1); + } + + void UpdateAI(const uint32 uiDiff) override + { + if (m_creature->GetMapId() != MAP_ID_BWL) + return; + + DialogueUpdate(uiDiff); + + if (!m_creature->SelectHostileTarget() || !m_creature->getVictim()) + return; + + // Only do this if we haven't spawned nef yet + if (m_uiSpawnedAdds < MAX_DRAKE_SUMMONS) + { + // Shadowbolt Timer + if (m_uiShadowBoltTimer < uiDiff) + { + if (Unit* pTarget = m_creature->SelectAttackingTarget(ATTACKING_TARGET_RANDOM, 0)) + { + if (DoCastSpellIfCan(pTarget, SPELL_SHADOWBOLT) == CAST_OK) + m_uiShadowBoltTimer = urand(2000, 4000); + } + } + else + m_uiShadowBoltTimer -= uiDiff; + + // Fear Timer + if (m_uiFearTimer < uiDiff) + { + if (Unit* pTarget = m_creature->SelectAttackingTarget(ATTACKING_TARGET_RANDOM, 1)) + { + if (DoCastSpellIfCan(pTarget, SPELL_FEAR) == CAST_OK) + m_uiFearTimer = urand(10000, 20000); + } + } + else + m_uiFearTimer -= uiDiff; + + // Shadowbolt Volley + if (m_uiShadowboltVolleyTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature, SPELL_SHADOWBOLT_VOLLEY) == CAST_OK) + m_uiShadowboltVolleyTimer = urand(19000, 28000); + } + else + m_uiShadowboltVolleyTimer -= uiDiff; + + // Silence + if (m_uiSilenceTimer < uiDiff) + { + if (Unit* pTarget = m_creature->SelectAttackingTarget(ATTACKING_TARGET_RANDOM, 0)) + { + if (DoCastSpellIfCan(pTarget, SPELL_SILENCE) == CAST_OK) + m_uiSilenceTimer = urand(14000, 23000); + } + } + else + m_uiSilenceTimer -= uiDiff; + + // Shadow Command + if (m_uiShadowCommandTimer < uiDiff) + { + if (Unit* pTarget = m_creature->SelectAttackingTarget(ATTACKING_TARGET_RANDOM, 1)) + { + if (DoCastSpellIfCan(pTarget, SPELL_SHADOW_COMMAND) == CAST_OK) + m_uiShadowCommandTimer = urand(24000, 30000); + } + } + else + m_uiShadowCommandTimer -= uiDiff; + + // ShadowBlink + if (m_uiShadowBlinkTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature, SPELL_SHADOWBLINK) == CAST_OK) + m_uiShadowBlinkTimer = urand(30000, 40000); + } + else + m_uiShadowBlinkTimer -= uiDiff; + + // Add spawning mechanism + if (m_uiAddSpawnTimer < uiDiff) + { + // Spawn 2 random types of creatures at the 2 locations + uint32 uiCreatureId = 0; + + // 1 in 3 chance it will be a chromatic + uiCreatureId = urand(0, 2) ? m_uiDrakeTypeOne : uint32(NPC_CHROMATIC_DRAKANOID); + m_creature->SummonCreature(uiCreatureId, aNefarianLocs[0].m_fX, aNefarianLocs[0].m_fY, aNefarianLocs[0].m_fZ, 5.000f, TEMPSUMMON_TIMED_OOC_OR_CORPSE_DESPAWN, 30000); + + // 1 in 3 chance it will be a chromatic + uiCreatureId = urand(0, 2) ? m_uiDrakeTypeTwo : uint32(NPC_CHROMATIC_DRAKANOID); + m_creature->SummonCreature(uiCreatureId, aNefarianLocs[1].m_fX, aNefarianLocs[1].m_fY, aNefarianLocs[1].m_fZ, 5.000, TEMPSUMMON_TIMED_OOC_OR_CORPSE_DESPAWN, 30000); + + // Begin phase 2 by spawning Nefarian + if (m_uiSpawnedAdds >= MAX_DRAKE_SUMMONS) + { + // Inturrupt any spell casting + m_creature->InterruptNonMeleeSpells(false); + + // Make super invis + if (m_creature->GetVisibility() != VISIBILITY_OFF) + m_creature->SetVisibility(VISIBILITY_OFF); + + // Spawn Nefarian + // Summon as active, to be able to work proper! + m_creature->SummonCreature(NPC_NEFARIAN, aNefarianLocs[2].m_fX, aNefarianLocs[2].m_fY, aNefarianLocs[2].m_fZ, 0, TEMPSUMMON_DEAD_DESPAWN, 0, true); + } + + m_uiAddSpawnTimer = 4000; + } + else + m_uiAddSpawnTimer -= uiDiff; + } + } +}; + +CreatureAI* GetAI_boss_victor_nefarius(Creature* pCreature) +{ + return new boss_victor_nefariusAI(pCreature); +} + +bool GossipHello_boss_victor_nefarius(Player* pPlayer, Creature* pCreature) +{ + if (pCreature->GetMapId() != MAP_ID_BWL) + return true; + + pPlayer->ADD_GOSSIP_ITEM_ID(GOSSIP_ICON_CHAT, GOSSIP_ITEM_NEFARIUS_1 , GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 1); + pPlayer->SEND_GOSSIP_MENU(GOSSIP_TEXT_NEFARIUS_1, pCreature->GetObjectGuid()); + return true; +} + +bool GossipSelect_boss_victor_nefarius(Player* pPlayer, Creature* pCreature, uint32 /*uiSender*/, uint32 uiAction) +{ + if (pCreature->GetMapId() != MAP_ID_BWL) + return true; + + switch (uiAction) + { + case GOSSIP_ACTION_INFO_DEF+1: + pCreature->HandleEmote(EMOTE_ONESHOT_TALK); + pPlayer->ADD_GOSSIP_ITEM_ID(GOSSIP_ICON_CHAT, GOSSIP_ITEM_NEFARIUS_2, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 2); + pPlayer->SEND_GOSSIP_MENU(GOSSIP_TEXT_NEFARIUS_2, pCreature->GetObjectGuid()); + break; + case GOSSIP_ACTION_INFO_DEF+2: + pCreature->HandleEmote(EMOTE_ONESHOT_TALK); + pPlayer->ADD_GOSSIP_ITEM_ID(GOSSIP_ICON_CHAT, GOSSIP_ITEM_NEFARIUS_3, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 3); + pPlayer->SEND_GOSSIP_MENU(GOSSIP_TEXT_NEFARIUS_3, pCreature->GetObjectGuid()); + break; + case GOSSIP_ACTION_INFO_DEF+3: + pCreature->RemoveFlag(UNIT_NPC_FLAGS, UNIT_NPC_FLAG_GOSSIP); + pPlayer->CLOSE_GOSSIP_MENU(); + // Start the intro event + if (boss_victor_nefariusAI* pBossAI = dynamic_cast(pCreature->AI())) + pBossAI->DoStartIntro(); + break; + } + return true; +} + +void AddSC_boss_victor_nefarius() +{ + Script* pNewScript; + + pNewScript = new Script; + pNewScript->Name = "boss_victor_nefarius"; + pNewScript->GetAI = &GetAI_boss_victor_nefarius; + pNewScript->pGossipHello = &GossipHello_boss_victor_nefarius; + pNewScript->pGossipSelect = &GossipSelect_boss_victor_nefarius; + pNewScript->RegisterSelf(); +} diff --git a/src/modules/SD2/scripts/eastern_kingdoms/blackrock_mountain/blackwing_lair/instance_blackwing_lair.cpp b/src/modules/SD2/scripts/eastern_kingdoms/blackrock_mountain/blackwing_lair/instance_blackwing_lair.cpp new file mode 100644 index 000000000..07044b062 --- /dev/null +++ b/src/modules/SD2/scripts/eastern_kingdoms/blackrock_mountain/blackwing_lair/instance_blackwing_lair.cpp @@ -0,0 +1,380 @@ +/* Copyright (C) 2006 - 2013 ScriptDev2 + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along 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: 90 +SDComment: +SDCategory: Blackwing Lair +EndScriptData */ + +#include "precompiled.h" +#include "blackwing_lair.h" + +instance_blackwing_lair::instance_blackwing_lair(Map* pMap) : ScriptedInstance(pMap), + m_uiResetTimer(0), + m_uiDefenseTimer(0) +{ + Initialize(); +} + +void instance_blackwing_lair::Initialize() +{ + memset(&m_auiEncounter, 0, sizeof(m_auiEncounter)); +} + +bool instance_blackwing_lair::IsEncounterInProgress() const +{ + for (uint8 i = 0; i < MAX_ENCOUNTER; ++i) + { + if (m_auiEncounter[i] == IN_PROGRESS) + return true; + } + return false; +} + +void instance_blackwing_lair::OnCreatureCreate(Creature* pCreature) +{ + switch (pCreature->GetEntry()) + { + case NPC_BLACKWING_TECHNICIAN: + // Sort creatures so we can get only the ones near Vaelastrasz + if (pCreature->IsWithinDist2d(aNefariusSpawnLoc[0], aNefariusSpawnLoc[1], 50.0f)) + m_lTechnicianGuids.push_back(pCreature->GetObjectGuid()); + break; + case NPC_MONSTER_GENERATOR: + m_vGeneratorGuids.push_back(pCreature->GetObjectGuid()); + break; + case NPC_BLACKWING_LEGIONNAIRE: + case NPC_BLACKWING_MAGE: + case NPC_DRAGONSPAWN: + m_lDefendersGuids.push_back(pCreature->GetObjectGuid()); + break; + case NPC_RAZORGORE: + case NPC_NEFARIANS_TROOPS: + case NPC_BLACKWING_ORB_TRIGGER: + case NPC_VAELASTRASZ: + case NPC_LORD_VICTOR_NEFARIUS: + m_mNpcEntryGuidStore[pCreature->GetEntry()] = pCreature->GetObjectGuid(); + break; + } +} + +void instance_blackwing_lair::OnObjectCreate(GameObject* pGo) +{ + switch (pGo->GetEntry()) + { + case GO_DOOR_RAZORGORE_ENTER: + case GO_ORB_OF_DOMINATION: + case GO_DOOR_NEFARIAN: + break; + case GO_DOOR_RAZORGORE_EXIT: + if (m_auiEncounter[TYPE_RAZORGORE] == DONE) + pGo->SetGoState(GO_STATE_ACTIVE); + break; + case GO_DOOR_CHROMAGGUS_EXIT: + if (m_auiEncounter[TYPE_CHROMAGGUS] == DONE) + pGo->SetGoState(GO_STATE_ACTIVE); + break; + case GO_DOOR_VAELASTRASZ: + if (m_auiEncounter[TYPE_VAELASTRASZ] == DONE) + pGo->SetGoState(GO_STATE_ACTIVE); + break; + case GO_DOOR_LASHLAYER: + if (m_auiEncounter[TYPE_LASHLAYER] == DONE) + pGo->SetGoState(GO_STATE_ACTIVE); + break; + case GO_BLACK_DRAGON_EGG: + m_lDragonEggsGuids.push_back(pGo->GetObjectGuid()); + return; + case GO_DRAKONID_BONES: + m_lDrakonidBonesGuids.push_back(pGo->GetObjectGuid()); + return; + + default: + return; + } + m_mGoEntryGuidStore[pGo->GetEntry()] = pGo->GetObjectGuid(); +} + +void instance_blackwing_lair::SetData(uint32 uiType, uint32 uiData) +{ + switch (uiType) + { + case TYPE_RAZORGORE: + m_auiEncounter[uiType] = uiData; + if (uiData != SPECIAL) + DoUseDoorOrButton(GO_DOOR_RAZORGORE_ENTER); + if (uiData == DONE) + DoUseDoorOrButton(GO_DOOR_RAZORGORE_EXIT); + else if (uiData == FAIL) + { + m_uiResetTimer = 30000; + + // Reset the Orb of Domination and the eggs + DoToggleGameObjectFlags(GO_ORB_OF_DOMINATION, GO_FLAG_NO_INTERACT, true); + + // Reset defenders + for (GuidList::const_iterator itr = m_lDefendersGuids.begin(); itr != m_lDefendersGuids.end(); ++itr) + { + if (Creature* pDefender = instance->GetCreature(*itr)) + pDefender->ForcedDespawn(); + } + + m_lUsedEggsGuids.clear(); + m_lDefendersGuids.clear(); + } + break; + case TYPE_VAELASTRASZ: + m_auiEncounter[uiType] = uiData; + // Prevent the players from running back to the first room; use if the encounter is not special + if (uiData != SPECIAL) + DoUseDoorOrButton(GO_DOOR_RAZORGORE_EXIT); + if (uiData == DONE) + DoUseDoorOrButton(GO_DOOR_VAELASTRASZ); + break; + case TYPE_LASHLAYER: + m_auiEncounter[uiType] = uiData; + if (uiData == DONE) + DoUseDoorOrButton(GO_DOOR_LASHLAYER); + break; + case TYPE_FIREMAW: + case TYPE_EBONROC: + case TYPE_FLAMEGOR: + m_auiEncounter[uiType] = uiData; + break; + case TYPE_CHROMAGGUS: + m_auiEncounter[uiType] = uiData; + if (uiData == DONE) + DoUseDoorOrButton(GO_DOOR_CHROMAGGUS_EXIT); + break; + case TYPE_NEFARIAN: + // Don't store the same thing twice + if (m_auiEncounter[uiType] == uiData) + break; + if (uiData == SPECIAL) + { + // handle missing spell 23362 + Creature* pNefarius = GetSingleCreatureFromStorage(NPC_LORD_VICTOR_NEFARIUS); + if (!pNefarius) + break; + + for (GuidList::const_iterator itr = m_lDrakonidBonesGuids.begin(); itr != m_lDrakonidBonesGuids.end(); ++itr) + { + // The Go script will handle the missing spell 23361 + if (GameObject* pGo = instance->GetGameObject(*itr)) + pGo->Use(pNefarius); + } + // Don't store special data + break; + } + m_auiEncounter[uiType] = uiData; + DoUseDoorOrButton(GO_DOOR_NEFARIAN); + // Cleanup the drakonid bones + if (uiData == FAIL) + { + for (GuidList::const_iterator itr = m_lDrakonidBonesGuids.begin(); itr != m_lDrakonidBonesGuids.end(); ++itr) + { + if (GameObject* pGo = instance->GetGameObject(*itr)) + pGo->SetLootState(GO_JUST_DEACTIVATED); + } + + m_lDrakonidBonesGuids.clear(); + } + 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_strInstData = saveStream.str(); + + SaveToDB(); + OUT_SAVE_INST_DATA_COMPLETE; + } +} + +void instance_blackwing_lair::Load(const char* chrIn) +{ + if (!chrIn) + { + 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]; + + for (uint8 i = 0; i < MAX_ENCOUNTER; ++i) + { + if (m_auiEncounter[i] == IN_PROGRESS) + m_auiEncounter[i] = NOT_STARTED; + } + + OUT_LOAD_INST_DATA_COMPLETE; +} + +uint32 instance_blackwing_lair::GetData(uint32 uiType) const +{ + if (uiType < MAX_ENCOUNTER) + return m_auiEncounter[uiType]; + + return 0; +} + +void instance_blackwing_lair::SetData64(uint32 uiData, uint64 uiGuid) +{ + if (uiData == DATA_DRAGON_EGG) + { + if (GameObject* pEgg = instance->GetGameObject(ObjectGuid(uiGuid))) + m_lUsedEggsGuids.push_back(pEgg->GetObjectGuid()); + + // If all eggs are destroyed, then allow Razorgore to be attacked + if (m_lUsedEggsGuids.size() == m_lDragonEggsGuids.size()) + { + SetData(TYPE_RAZORGORE, SPECIAL); + DoToggleGameObjectFlags(GO_ORB_OF_DOMINATION, GO_FLAG_NO_INTERACT, true); + + // Emote for the start of the second phase + if (Creature* pTrigger = GetSingleCreatureFromStorage(NPC_NEFARIANS_TROOPS)) + { + DoScriptText(EMOTE_ORB_SHUT_OFF, pTrigger); + DoScriptText(EMOTE_TROOPS_FLEE, pTrigger); + } + + // Break mind control and set max health + if (Creature* pRazorgore = GetSingleCreatureFromStorage(NPC_RAZORGORE)) + { + pRazorgore->RemoveAllAuras(); + pRazorgore->SetHealth(pRazorgore->GetMaxHealth()); + } + + // All defenders evade and despawn + for (GuidList::const_iterator itr = m_lDefendersGuids.begin(); itr != m_lDefendersGuids.end(); ++itr) + { + if (Creature* pDefender = instance->GetCreature(*itr)) + { + pDefender->AI()->EnterEvadeMode(); + pDefender->ForcedDespawn(10000); + } + } + } + } +} + +void instance_blackwing_lair::OnCreatureEnterCombat(Creature* pCreature) +{ + if (pCreature->GetEntry() == NPC_GRETHOK_CONTROLLER) + { + SetData(TYPE_RAZORGORE, IN_PROGRESS); + m_uiDefenseTimer = 40000; + } +} + +void instance_blackwing_lair::OnCreatureDeath(Creature* pCreature) +{ + if (pCreature->GetEntry() == NPC_GRETHOK_CONTROLLER) + { + // Allow orb to be used + DoToggleGameObjectFlags(GO_ORB_OF_DOMINATION, GO_FLAG_NO_INTERACT, false); + + if (Creature* pOrbTrigger = GetSingleCreatureFromStorage(NPC_BLACKWING_ORB_TRIGGER)) + pOrbTrigger->InterruptNonMeleeSpells(false); + } +} + +void instance_blackwing_lair::Update(uint32 uiDiff) +{ + // Reset Razorgore in case of wipe + if (m_uiResetTimer) + { + if (m_uiResetTimer <= uiDiff) + { + // Respawn Razorgore + if (Creature* pRazorgore = GetSingleCreatureFromStorage(NPC_RAZORGORE)) + { + if (!pRazorgore->IsAlive()) + pRazorgore->Respawn(); + } + + // Respawn the Dragon Eggs + for (GuidList::const_iterator itr = m_lDragonEggsGuids.begin(); itr != m_lDragonEggsGuids.end(); ++itr) + { + if (GameObject* pEgg = instance->GetGameObject(*itr)) + { + if (!pEgg->isSpawned()) + pEgg->Respawn(); + } + } + + m_uiResetTimer = 0; + } + else + m_uiResetTimer -= uiDiff; + } + + if (GetData(TYPE_RAZORGORE) != IN_PROGRESS) + return; + + if (m_uiDefenseTimer < uiDiff) + { + // Allow Razorgore to spawn the defenders + Creature* pRazorgore = GetSingleCreatureFromStorage(NPC_RAZORGORE); + if (!pRazorgore) + return; + + // Randomize generators + std::random_shuffle(m_vGeneratorGuids.begin(), m_vGeneratorGuids.end()); + + // Spawn the defenders + for (uint8 i = 0; i < MAX_EGGS_DEFENDERS; ++i) + { + Creature* pGenerator = instance->GetCreature(m_vGeneratorGuids[i]); + if (!pGenerator) + return; + + pRazorgore->SummonCreature(aRazorgoreSpawns[i], pGenerator->GetPositionX(), pGenerator->GetPositionY(), pGenerator->GetPositionZ(), pGenerator->GetOrientation(), TEMPSUMMON_DEAD_DESPAWN, 0); + } + + m_uiDefenseTimer = 20000; + } + else + m_uiDefenseTimer -= uiDiff; +} + +InstanceData* GetInstanceData_instance_blackwing_lair(Map* pMap) +{ + return new instance_blackwing_lair(pMap); +} + +void AddSC_instance_blackwing_lair() +{ + Script* pNewScript; + + pNewScript = new Script; + pNewScript->Name = "instance_blackwing_lair"; + pNewScript->GetInstanceData = &GetInstanceData_instance_blackwing_lair; + pNewScript->RegisterSelf(); +} diff --git a/src/modules/SD2/scripts/eastern_kingdoms/blackrock_mountain/molten_core/boss_baron_geddon.cpp b/src/modules/SD2/scripts/eastern_kingdoms/blackrock_mountain/molten_core/boss_baron_geddon.cpp new file mode 100644 index 000000000..807345d14 --- /dev/null +++ b/src/modules/SD2/scripts/eastern_kingdoms/blackrock_mountain/molten_core/boss_baron_geddon.cpp @@ -0,0 +1,144 @@ +/* Copyright (C) 2006 - 2013 ScriptDev2 + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with 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: Armaggedon is not working properly (core issue) +SDCategory: Molten Core +EndScriptData */ + +#include "precompiled.h" +#include "molten_core.h" + +enum +{ + EMOTE_SERVICE = -1409000, + + SPELL_INFERNO = 19695, + SPELL_IGNITE_MANA = 19659, + SPELL_LIVING_BOMB = 20475, + SPELL_ARMAGEDDON = 20478 +}; + +struct boss_baron_geddonAI : public ScriptedAI +{ + boss_baron_geddonAI(Creature* pCreature) : ScriptedAI(pCreature) + { + m_pInstance = (ScriptedInstance*)pCreature->GetInstanceData(); + Reset(); + } + + ScriptedInstance* m_pInstance; + + bool m_bIsArmageddon; + uint32 m_uiInfernoTimer; + uint32 m_uiIgniteManaTimer; + uint32 m_uiLivingBombTimer; + + void Reset() override + { + m_bIsArmageddon = false; + m_uiInfernoTimer = 45000; + m_uiIgniteManaTimer = 30000; + m_uiLivingBombTimer = 35000; + } + + void Aggro(Unit* /*pWho*/) override + { + if (m_pInstance) + m_pInstance->SetData(TYPE_GEDDON, IN_PROGRESS); + } + + void JustDied(Unit* /*pKiller*/) override + { + if (m_pInstance) + m_pInstance->SetData(TYPE_GEDDON, DONE); + } + + void JustReachedHome() override + { + if (m_pInstance) + m_pInstance->SetData(TYPE_GEDDON, NOT_STARTED); + } + + void UpdateAI(const uint32 uiDiff) override + { + if (!m_creature->SelectHostileTarget() || !m_creature->getVictim()) + return; + + if (m_bIsArmageddon) // Do nothing untill armageddon triggers + return; + + // If we are <2% hp cast Armageddom + if (m_creature->GetHealthPercent() <= 2.0f && !m_bIsArmageddon) + { + if (DoCastSpellIfCan(m_creature, SPELL_ARMAGEDDON, CAST_INTERRUPT_PREVIOUS) == CAST_OK) + { + DoScriptText(EMOTE_SERVICE, m_creature); + m_bIsArmageddon = true; + return; + } + } + + // Inferno_Timer + if (m_uiInfernoTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature, SPELL_INFERNO) == CAST_OK) + m_uiInfernoTimer = 45000; + } + else + m_uiInfernoTimer -= uiDiff; + + // Ignite Mana Timer + if (m_uiIgniteManaTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature, SPELL_IGNITE_MANA) == CAST_OK) + m_uiIgniteManaTimer = 30000; + } + else + m_uiIgniteManaTimer -= uiDiff; + + // Living Bomb Timer + if (m_uiLivingBombTimer < uiDiff) + { + if (Unit* pTarget = m_creature->SelectAttackingTarget(ATTACKING_TARGET_RANDOM, 0)) + { + if (DoCastSpellIfCan(pTarget, SPELL_LIVING_BOMB) == CAST_OK) + m_uiLivingBombTimer = 35000; + } + } + else + m_uiLivingBombTimer -= uiDiff; + + DoMeleeAttackIfReady(); + } +}; + +CreatureAI* GetAI_boss_baron_geddon(Creature* pCreature) +{ + return new boss_baron_geddonAI(pCreature); +} + +void AddSC_boss_baron_geddon() +{ + Script* pNewScript; + + pNewScript = new Script; + pNewScript->Name = "boss_baron_geddon"; + pNewScript->GetAI = &GetAI_boss_baron_geddon; + pNewScript->RegisterSelf(); +} diff --git a/src/modules/SD2/scripts/eastern_kingdoms/blackrock_mountain/molten_core/boss_garr.cpp b/src/modules/SD2/scripts/eastern_kingdoms/blackrock_mountain/molten_core/boss_garr.cpp new file mode 100644 index 000000000..81114f403 --- /dev/null +++ b/src/modules/SD2/scripts/eastern_kingdoms/blackrock_mountain/molten_core/boss_garr.cpp @@ -0,0 +1,187 @@ +/* Copyright (C) 2006 - 2013 ScriptDev2 + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with 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: Garr's enrage is missing +SDCategory: Molten Core +EndScriptData */ + +#include "precompiled.h" +#include "molten_core.h" + +enum +{ + // Garr spells + SPELL_ANTIMAGICPULSE = 19492, + SPELL_MAGMASHACKLES = 19496, + SPELL_ENRAGE = 19516, // TODO Stacking enrage (stacks to 10 times) + + // Add spells + SPELL_ERUPTION = 19497, + SPELL_MASSIVE_ERUPTION = 20483, // TODO possible on death + SPELL_IMMOLATE = 20294, + SPELL_SEPARATION_ANXIETY = 23492, // Used if separated too far from Garr, 21095 use unknown. +}; + +struct boss_garrAI : public ScriptedAI +{ + boss_garrAI(Creature* pCreature) : ScriptedAI(pCreature) + { + m_pInstance = (ScriptedInstance*)pCreature->GetInstanceData(); + Reset(); + } + + ScriptedInstance* m_pInstance; + + uint32 m_uiAntiMagicPulseTimer; + uint32 m_uiMagmaShacklesTimer; + + void Reset() override + { + m_uiAntiMagicPulseTimer = 25000; + m_uiMagmaShacklesTimer = 15000; + } + + void Aggro(Unit* /*pWho*/) override + { + if (m_pInstance) + m_pInstance->SetData(TYPE_GARR, IN_PROGRESS); + } + + void JustDied(Unit* /*pKiller*/) override + { + if (m_pInstance) + m_pInstance->SetData(TYPE_GARR, DONE); + } + + void JustReachedHome() override + { + if (m_pInstance) + m_pInstance->SetData(TYPE_GARR, FAIL); + } + + void UpdateAI(const uint32 uiDiff) override + { + if (!m_creature->SelectHostileTarget() || !m_creature->getVictim()) + return; + + // AntiMagicPulse_Timer + if (m_uiAntiMagicPulseTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature, SPELL_ANTIMAGICPULSE) == CAST_OK) + m_uiAntiMagicPulseTimer = urand(10000, 15000); + } + else + m_uiAntiMagicPulseTimer -= uiDiff; + + // MagmaShackles_Timer + if (m_uiMagmaShacklesTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature, SPELL_MAGMASHACKLES) == CAST_OK) + m_uiMagmaShacklesTimer = urand(8000, 12000); + } + else + m_uiMagmaShacklesTimer -= uiDiff; + + DoMeleeAttackIfReady(); + } +}; + +struct mob_fireswornAI : public ScriptedAI +{ + mob_fireswornAI(Creature* pCreature) : ScriptedAI(pCreature) + { + m_pInstance = (ScriptedInstance*)pCreature->GetInstanceData(); + Reset(); + } + + ScriptedInstance* m_pInstance; + + uint32 m_uiImmolateTimer; + uint32 m_uiSeparationCheckTimer; + + void Reset() override + { + m_uiImmolateTimer = urand(4000, 8000); // These times are probably wrong + m_uiSeparationCheckTimer = 5000; + } + + void UpdateAI(const uint32 uiDiff) override + { + if (!m_creature->SelectHostileTarget() || !m_creature->getVictim()) + return; + + // Immolate_Timer + if (m_uiImmolateTimer < uiDiff) + { + if (Unit* pTarget = m_creature->SelectAttackingTarget(ATTACKING_TARGET_RANDOM, 0)) + { + if (DoCastSpellIfCan(pTarget, SPELL_IMMOLATE) == CAST_OK) + m_uiImmolateTimer = urand(5000, 10000); + } + } + else m_uiImmolateTimer -= uiDiff; + + if (m_uiSeparationCheckTimer < uiDiff) + { + // Distance guesswork, but should be ok + Creature* pGarr = m_pInstance->GetSingleCreatureFromStorage(NPC_GARR); + if (pGarr && pGarr->IsAlive() && !m_creature->IsWithinDist2d(pGarr->GetPositionX(), pGarr->GetPositionY(), 50.0f)) + DoCastSpellIfCan(m_creature, SPELL_SEPARATION_ANXIETY, CAST_TRIGGERED); + + m_uiSeparationCheckTimer = 5000; + } + else + m_uiSeparationCheckTimer -= uiDiff; + + // Cast Erruption and let them die + if (m_creature->GetHealthPercent() <= 10.0f) + { + DoCastSpellIfCan(m_creature->getVictim(), SPELL_ERUPTION); + m_creature->SetDeathState(JUST_DIED); + m_creature->RemoveCorpse(); + } + + DoMeleeAttackIfReady(); + } +}; + +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* pNewScript; + + pNewScript = new Script; + pNewScript->Name = "boss_garr"; + pNewScript->GetAI = &GetAI_boss_garr; + pNewScript->RegisterSelf(); + + pNewScript = new Script; + pNewScript->Name = "mob_firesworn"; + pNewScript->GetAI = &GetAI_mob_firesworn; + pNewScript->RegisterSelf(); +} diff --git a/src/modules/SD2/scripts/eastern_kingdoms/blackrock_mountain/molten_core/boss_gehennas.cpp b/src/modules/SD2/scripts/eastern_kingdoms/blackrock_mountain/molten_core/boss_gehennas.cpp new file mode 100644 index 000000000..047a9e349 --- /dev/null +++ b/src/modules/SD2/scripts/eastern_kingdoms/blackrock_mountain/molten_core/boss_gehennas.cpp @@ -0,0 +1,130 @@ +/* Copyright (C) 2006 - 2013 ScriptDev2 + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with 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: +SDCategory: Molten Core +EndScriptData */ + +#include "precompiled.h" +#include "molten_core.h" + +enum +{ + SPELL_SHADOW_BOLT = 19728, // 19729 exists too, but can be reflected + SPELL_RAIN_OF_FIRE = 19717, + SPELL_GEHENNAS_CURSE = 19716 +}; + +struct boss_gehennasAI : public ScriptedAI +{ + boss_gehennasAI(Creature* pCreature) : ScriptedAI(pCreature) + { + m_pInstance = (ScriptedInstance*)pCreature->GetInstanceData(); + Reset(); + } + + ScriptedInstance* m_pInstance; + + uint32 m_uiShadowBoltTimer; + uint32 m_uiRainOfFireTimer; + uint32 m_uiGehennasCurseTimer; + + void Reset() override + { + m_uiShadowBoltTimer = 6000; + m_uiRainOfFireTimer = 10000; + m_uiGehennasCurseTimer = 12000; + } + + void Aggro(Unit* /*pwho*/) override + { + if (m_pInstance) + m_pInstance->SetData(TYPE_GEHENNAS, IN_PROGRESS); + } + + void JustDied(Unit* /*pKiller*/) override + { + if (m_pInstance) + m_pInstance->SetData(TYPE_GEHENNAS, DONE); + } + + void JustReachedHome() override + { + if (m_pInstance) + m_pInstance->SetData(TYPE_GEHENNAS, FAIL); + } + + void UpdateAI(const uint32 uiDiff) override + { + if (!m_creature->SelectHostileTarget() || !m_creature->getVictim()) + return; + + // ShadowBolt Timer + if (m_uiShadowBoltTimer < uiDiff) + { + if (Unit* pTarget = m_creature->SelectAttackingTarget(ATTACKING_TARGET_RANDOM, 1)) + { + if (DoCastSpellIfCan(pTarget, SPELL_SHADOW_BOLT) == CAST_OK) + m_uiShadowBoltTimer = 7000; + } + else // In case someone attempts soloing, we don't need to scan for targets every tick + m_uiShadowBoltTimer = 7000; + } + else + m_uiShadowBoltTimer -= uiDiff; + + // Rain of Fire Timer + if (m_uiRainOfFireTimer < uiDiff) + { + if (Unit* pTarget = m_creature->SelectAttackingTarget(ATTACKING_TARGET_RANDOM, 0)) + { + if (DoCastSpellIfCan(pTarget, SPELL_RAIN_OF_FIRE) == CAST_OK) + m_uiRainOfFireTimer = urand(4000, 12000); + } + } + else + m_uiRainOfFireTimer -= uiDiff; + + // GehennasCurse Timer + if (m_uiGehennasCurseTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature, SPELL_GEHENNAS_CURSE) == CAST_OK) + m_uiGehennasCurseTimer = 30000; + } + else + m_uiGehennasCurseTimer -= uiDiff; + + DoMeleeAttackIfReady(); + } +}; + +CreatureAI* GetAI_boss_gehennas(Creature* pCreature) +{ + return new boss_gehennasAI(pCreature); +} + +void AddSC_boss_gehennas() +{ + Script* pNewScript; + + pNewScript = new Script; + pNewScript->Name = "boss_gehennas"; + pNewScript->GetAI = &GetAI_boss_gehennas; + pNewScript->RegisterSelf(); +} diff --git a/src/modules/SD2/scripts/eastern_kingdoms/blackrock_mountain/molten_core/boss_golemagg.cpp b/src/modules/SD2/scripts/eastern_kingdoms/blackrock_mountain/molten_core/boss_golemagg.cpp new file mode 100644 index 000000000..94086fa0e --- /dev/null +++ b/src/modules/SD2/scripts/eastern_kingdoms/blackrock_mountain/molten_core/boss_golemagg.cpp @@ -0,0 +1,202 @@ +/* Copyright (C) 2006 - 2013 ScriptDev2 + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with 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: 80 +SDComment: Rager need to be tied to boss (Despawn on boss-death) +SDCategory: Molten Core +EndScriptData */ + +#include "precompiled.h" +#include "molten_core.h" + +enum +{ + SPELL_MAGMA_SPLASH = 13879, + SPELL_PYROBLAST = 20228, + SPELL_EARTHQUAKE = 19798, + SPELL_ENRAGE = 19953, + SPELL_GOLEMAGG_TRUST = 20553, + + // Core Rager + EMOTE_LOW_HP = -1409002, + SPELL_MANGLE = 19820 +}; + +struct boss_golemaggAI : public ScriptedAI +{ + boss_golemaggAI(Creature* pCreature) : ScriptedAI(pCreature) + { + m_pInstance = (ScriptedInstance*)pCreature->GetInstanceData(); + Reset(); + } + + ScriptedInstance* m_pInstance; + + uint32 m_uiPyroblastTimer; + uint32 m_uiEarthquakeTimer; + uint32 m_uiBuffTimer; + bool m_bEnraged; + + void Reset() override + { + m_uiPyroblastTimer = 7 * IN_MILLISECONDS; + m_uiEarthquakeTimer = 3 * IN_MILLISECONDS; + m_uiBuffTimer = 1.5 * IN_MILLISECONDS; + m_bEnraged = false; + + m_creature->CastSpell(m_creature, SPELL_MAGMA_SPLASH, true); + } + + void Aggro(Unit* /*pWho*/) override + { + if (m_pInstance) + m_pInstance->SetData(TYPE_GOLEMAGG, IN_PROGRESS); + } + + void JustDied(Unit* /*pKiller*/) override + { + if (m_pInstance) + m_pInstance->SetData(TYPE_GOLEMAGG, DONE); + } + + void JustReachedHome() override + { + if (m_pInstance) + m_pInstance->SetData(TYPE_GOLEMAGG, FAIL); + } + + void UpdateAI(const uint32 uiDiff) override + { + if (!m_creature->SelectHostileTarget() || !m_creature->getVictim()) + return; + + // Pyroblast + if (m_uiPyroblastTimer < uiDiff) + { + if (Unit* pTarget = m_creature->SelectAttackingTarget(ATTACKING_TARGET_RANDOM, 0)) + { + if (DoCastSpellIfCan(pTarget, SPELL_PYROBLAST) == CAST_OK) + m_uiPyroblastTimer = 7 * IN_MILLISECONDS; + } + } + else + m_uiPyroblastTimer -= uiDiff; + + // Enrage + if (!m_bEnraged && m_creature->GetHealthPercent() < 10.0f) + { + if (DoCastSpellIfCan(m_creature, SPELL_ENRAGE) == CAST_OK) + m_bEnraged = true; + } + + // Earthquake + if (m_bEnraged) + { + if (m_uiEarthquakeTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature, SPELL_EARTHQUAKE) == CAST_OK) + m_uiEarthquakeTimer = 3 * IN_MILLISECONDS; + } + else + m_uiEarthquakeTimer -= uiDiff; + } + + // Golemagg's Trust + if (m_uiBuffTimer < uiDiff) + { + DoCastSpellIfCan(m_creature, SPELL_GOLEMAGG_TRUST); + m_uiBuffTimer = 1.5 * IN_MILLISECONDS; + } + else + m_uiBuffTimer -= uiDiff; + + DoMeleeAttackIfReady(); + } +}; + +struct mob_core_ragerAI : public ScriptedAI +{ + mob_core_ragerAI(Creature* pCreature) : ScriptedAI(pCreature) + { + m_pInstance = (ScriptedInstance*)pCreature->GetInstanceData(); + Reset(); + } + + ScriptedInstance* m_pInstance; + uint32 m_uiMangleTimer; + + void Reset() override + { + m_uiMangleTimer = 7 * IN_MILLISECONDS; // These times are probably wrong + } + + void DamageTaken(Unit* /*pDoneBy*/, uint32& uiDamage) override + { + if (m_creature->GetHealthPercent() < 50.0f) + { + if (m_pInstance && m_pInstance->GetData(TYPE_GOLEMAGG) != DONE) + { + DoScriptText(EMOTE_LOW_HP, m_creature); + m_creature->SetHealth(m_creature->GetMaxHealth()); + uiDamage = 0; + } + } + } + + void UpdateAI(const uint32 uiDiff) override + { + if (!m_creature->SelectHostileTarget() || !m_creature->getVictim()) + return; + + // Mangle + if (m_uiMangleTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature->getVictim(), SPELL_MANGLE) == CAST_OK) + m_uiMangleTimer = 10 * IN_MILLISECONDS; + } + 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* pNewScript; + + pNewScript = new Script; + pNewScript->Name = "boss_golemagg"; + pNewScript->GetAI = &GetAI_boss_golemagg; + pNewScript->RegisterSelf(); + + pNewScript = new Script; + pNewScript->Name = "mob_core_rager"; + pNewScript->GetAI = &GetAI_mob_core_rager; + pNewScript->RegisterSelf(); +} diff --git a/src/modules/SD2/scripts/eastern_kingdoms/blackrock_mountain/molten_core/boss_lucifron.cpp b/src/modules/SD2/scripts/eastern_kingdoms/blackrock_mountain/molten_core/boss_lucifron.cpp new file mode 100644 index 000000000..0ac7d8965 --- /dev/null +++ b/src/modules/SD2/scripts/eastern_kingdoms/blackrock_mountain/molten_core/boss_lucifron.cpp @@ -0,0 +1,125 @@ +/* Copyright (C) 2006 - 2013 ScriptDev2 + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with 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" +#include "molten_core.h" + +enum +{ + SPELL_IMPENDINGDOOM = 19702, + SPELL_LUCIFRONCURSE = 19703, + SPELL_SHADOWSHOCK = 19460 +}; + +struct boss_lucifronAI : public ScriptedAI +{ + boss_lucifronAI(Creature* pCreature) : ScriptedAI(pCreature) + { + m_pInstance = (ScriptedInstance*)pCreature->GetInstanceData(); + Reset(); + } + + ScriptedInstance* m_pInstance; + + uint32 m_uiImpendingDoomTimer; + uint32 m_uiLucifronCurseTimer; + uint32 m_uiShadowShockTimer; + + void Reset() override + { + m_uiImpendingDoomTimer = 10000; + m_uiLucifronCurseTimer = 20000; + m_uiShadowShockTimer = 6000; + } + + void Aggro(Unit* /*pWho*/) override + { + if (m_pInstance) + m_pInstance->SetData(TYPE_LUCIFRON, IN_PROGRESS); + } + + void JustDied(Unit* /*pKiller*/) override + { + if (m_pInstance) + m_pInstance->SetData(TYPE_LUCIFRON, DONE); + } + + void JustReachedHome() override + { + if (m_pInstance) + m_pInstance->SetData(TYPE_LUCIFRON, FAIL); + } + + void UpdateAI(const uint32 uiDiff) override + { + if (!m_creature->SelectHostileTarget() || !m_creature->getVictim()) + return; + + // Impending doom timer + if (m_uiImpendingDoomTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature, SPELL_IMPENDINGDOOM) == CAST_OK) + m_uiImpendingDoomTimer = 20000; + } + else + m_uiImpendingDoomTimer -= uiDiff; + + // Lucifron's curse timer + if (m_uiLucifronCurseTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature, SPELL_LUCIFRONCURSE) == CAST_OK) + m_uiLucifronCurseTimer = 20000; + } + else + m_uiLucifronCurseTimer -= uiDiff; + + // Shadowshock + if (m_uiShadowShockTimer < uiDiff) + { + if (Unit* pTarget = m_creature->SelectAttackingTarget(ATTACKING_TARGET_RANDOM, 0)) + { + if (DoCastSpellIfCan(pTarget, SPELL_SHADOWSHOCK) == CAST_OK) + m_uiShadowShockTimer = 6000; + } + } + else + m_uiShadowShockTimer -= uiDiff; + + DoMeleeAttackIfReady(); + } +}; + +CreatureAI* GetAI_boss_lucifron(Creature* pCreature) +{ + return new boss_lucifronAI(pCreature); +} + +void AddSC_boss_lucifron() +{ + Script* pNewScript; + + pNewScript = new Script; + pNewScript->Name = "boss_lucifron"; + pNewScript->GetAI = &GetAI_boss_lucifron; + pNewScript->RegisterSelf(); +} diff --git a/src/modules/SD2/scripts/eastern_kingdoms/blackrock_mountain/molten_core/boss_magmadar.cpp b/src/modules/SD2/scripts/eastern_kingdoms/blackrock_mountain/molten_core/boss_magmadar.cpp new file mode 100644 index 000000000..753bb519b --- /dev/null +++ b/src/modules/SD2/scripts/eastern_kingdoms/blackrock_mountain/molten_core/boss_magmadar.cpp @@ -0,0 +1,134 @@ +/* Copyright (C) 2006 - 2013 ScriptDev2 + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with 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: Lavabomb needs still core support +SDCategory: Molten Core +EndScriptData */ + +#include "precompiled.h" +#include "molten_core.h" + +enum +{ + EMOTE_GENERIC_FRENZY_KILL = -1000001, + + SPELL_FRENZY = 19451, + SPELL_MAGMASPIT = 19449, // This is actually a buff he gives himself + SPELL_PANIC = 19408, + SPELL_LAVABOMB = 19411, // This calls a dummy server side effect that isn't implemented yet + SPELL_LAVABOMB_ALT = 19428 +}; + +struct boss_magmadarAI : public ScriptedAI +{ + boss_magmadarAI(Creature* pCreature) : ScriptedAI(pCreature) + { + m_pInstance = (ScriptedInstance*)pCreature->GetInstanceData(); + Reset(); + } + + ScriptedInstance* m_pInstance; + + uint32 m_uiFrenzyTimer; + uint32 m_uiPanicTimer; + uint32 m_uiLavabombTimer; + + void Reset() override + { + m_uiFrenzyTimer = 30000; + m_uiPanicTimer = 7000; + m_uiLavabombTimer = 12000; + } + + void Aggro(Unit* /*pWho*/) override + { + DoCastSpellIfCan(m_creature, SPELL_MAGMASPIT, true); + + if (m_pInstance) + m_pInstance->SetData(TYPE_MAGMADAR, IN_PROGRESS); + } + + void JustDied(Unit* /*pKiller*/) override + { + if (m_pInstance) + m_pInstance->SetData(TYPE_MAGMADAR, DONE); + } + + void JustReachedHome() override + { + if (m_pInstance) + m_pInstance->SetData(TYPE_MAGMADAR, NOT_STARTED); + } + + void UpdateAI(const uint32 uiDiff) override + { + if (!m_creature->SelectHostileTarget() || !m_creature->getVictim()) + return; + + // Frenzy_Timer + if (m_uiFrenzyTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature, SPELL_FRENZY) == CAST_OK) + { + DoScriptText(EMOTE_GENERIC_FRENZY_KILL, m_creature); + m_uiFrenzyTimer = 15000; + } + } + else + m_uiFrenzyTimer -= uiDiff; + + // Panic_Timer + if (m_uiPanicTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature, SPELL_PANIC) == CAST_OK) + m_uiPanicTimer = 30000; + } + else + m_uiPanicTimer -= uiDiff; + + // Lavabomb_Timer + if (m_uiLavabombTimer < uiDiff) + { + if (Unit* pTarget = m_creature->SelectAttackingTarget(ATTACKING_TARGET_RANDOM, 0)) + { + if (DoCastSpellIfCan(pTarget, SPELL_LAVABOMB) == CAST_OK) + m_uiLavabombTimer = 12000; + } + } + else + m_uiLavabombTimer -= uiDiff; + + DoMeleeAttackIfReady(); + } +}; + +CreatureAI* GetAI_boss_magmadar(Creature* pCreature) +{ + return new boss_magmadarAI(pCreature); +} + +void AddSC_boss_magmadar() +{ + Script* pNewScript; + + pNewScript = new Script; + pNewScript->Name = "boss_magmadar"; + pNewScript->GetAI = &GetAI_boss_magmadar; + pNewScript->RegisterSelf(); +} diff --git a/src/modules/SD2/scripts/eastern_kingdoms/blackrock_mountain/molten_core/boss_majordomo_executus.cpp b/src/modules/SD2/scripts/eastern_kingdoms/blackrock_mountain/molten_core/boss_majordomo_executus.cpp new file mode 100644 index 000000000..e10755143 --- /dev/null +++ b/src/modules/SD2/scripts/eastern_kingdoms/blackrock_mountain/molten_core/boss_majordomo_executus.cpp @@ -0,0 +1,478 @@ +/* Copyright (C) 2006 - 2013 ScriptDev2 + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with 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: 95 +SDComment: Minor weaknesses +SDCategory: Molten Core +EndScriptData */ + +#include "precompiled.h" +#include "molten_core.h" +#include "TemporarySummon.h" + +enum +{ + SAY_AGGRO = -1409003, + SAY_SLAY = -1409005, + SAY_SPECIAL = -1409006, // Use unknown + SAY_LAST_ADD = -1409019, // When only one add remaining + SAY_DEFEAT_1 = -1409007, + SAY_DEFEAT_2 = -1409020, + SAY_DEFEAT_3 = -1409021, + + SAY_SUMMON_0 = -1409023, + SAY_SUMMON_1 = -1409024, + SAY_SUMMON_MAJ = -1409008, + SAY_ARRIVAL1_RAG = -1409009, + SAY_ARRIVAL2_MAJ = -1409010, + SAY_ARRIVAL3_RAG = -1409011, + SAY_ARRIVAL4_MAJ = -1409022, + + GOSSIP_ITEM_SUMMON_1 = -3409000, + GOSSIP_ITEM_SUMMON_2 = -3409001, + GOSSIP_ITEM_SUMMON_3 = -3409002, + + TEXT_ID_SUMMON_1 = 4995, + TEXT_ID_SUMMON_2 = 5011, + TEXT_ID_SUMMON_3 = 5012, + + SPELL_MAGIC_REFLECTION = 20619, + SPELL_DAMAGE_REFLECTION = 21075, + SPELL_BLASTWAVE = 20229, + SPELL_AEGIS = 20620, + SPELL_TELEPORT = 20618, + + SPELL_TELEPORT_SELF = 19484, + SPELL_SUMMON_RAGNAROS = 19774, + SPELL_ELEMENTAL_FIRE = 19773, + SPELL_RAGNA_EMERGE = 20568, +}; + +struct boss_majordomoAI : public ScriptedAI +{ + boss_majordomoAI(Creature* pCreature) : ScriptedAI(pCreature) + { + m_pInstance = (instance_molten_core*)pCreature->GetInstanceData(); + m_bHasEncounterFinished = false; + Reset(); + } + + instance_molten_core* m_pInstance; + + uint32 m_uiMagicReflectionTimer; + uint32 m_uiDamageReflectionTimer; + uint32 m_uiBlastwaveTimer; + uint32 m_uiTeleportTimer; + uint32 m_uiAegisTimer; + uint32 m_uiSpeechTimer; + + ObjectGuid m_ragnarosGuid; + bool m_bHasEncounterFinished; + uint8 m_uiAddsKilled; + uint8 m_uiSpeech; + GuidList m_luiMajordomoAddsGUIDs; + + void Reset() override + { + m_uiMagicReflectionTimer = 30000; // Damage reflection first so we alternate + m_uiDamageReflectionTimer = 15000; + m_uiBlastwaveTimer = 10000; + m_uiTeleportTimer = 20000; + m_uiAegisTimer = 5000; + m_uiSpeechTimer = 1000; + + m_uiAddsKilled = 0; + m_uiSpeech = 0; + } + + void KilledUnit(Unit* /*pVictim*/) override + { + if (urand(0, 4)) + return; + + DoScriptText(SAY_SLAY, m_creature); + } + + void Aggro(Unit* pWho) override + { + if (pWho->GetTypeId() == TYPEID_UNIT && pWho->GetEntry() == NPC_RAGNAROS) + return; + + DoScriptText(SAY_AGGRO, m_creature); + + if (m_pInstance) + m_pInstance->SetData(TYPE_MAJORDOMO, IN_PROGRESS); + } + + void JustReachedHome() override + { + if (!m_bHasEncounterFinished) // Normal reached home, FAIL + { + if (m_pInstance) + m_pInstance->SetData(TYPE_MAJORDOMO, FAIL); + } + else // Finished the encounter, DONE + { + // Exit combat + m_creature->RemoveAllAurasOnEvade(); + m_creature->DeleteThreatList(); + m_creature->CombatStop(true); + m_creature->SetLootRecipient(NULL); + + // Set friendly + m_creature->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_OOC_NOT_ATTACKABLE); + m_creature->SetFactionTemporary(FACTION_MAJORDOMO_FRIENDLY, TEMPFACTION_RESTORE_RESPAWN); + + // Reset orientation + m_creature->SetFacingTo(m_aMajordomoLocations[0].m_fO); + + // Start his speech + m_uiSpeechTimer = 1; // At next tick + m_uiSpeech = 1; + + m_pInstance->SetData(TYPE_MAJORDOMO, DONE); + } + } + + void StartSummonEvent(Player* pPlayer) + { + m_creature->RemoveFlag(UNIT_NPC_FLAGS, UNIT_NPC_FLAG_GOSSIP); + + // Prevent possible exploits with double summoning + if (m_creature->GetMap()->GetCreature(m_ragnarosGuid)) + return; + + DoScriptText(SAY_SUMMON_0, m_creature, pPlayer); + + m_uiSpeechTimer = 5000; + m_uiSpeech = 10; + } + + void JustRespawned() override + { + // Encounter finished, need special treatment + if (m_bHasEncounterFinished) + { + // This needs to be set to be able to resummon Ragnaros + m_creature->SetFlag(UNIT_NPC_FLAGS, UNIT_NPC_FLAG_GOSSIP); + + // Relocate here + debug_log("SD2: boss_majordomo_executus: Relocate to Ragnaros' Lair on respawn"); + m_creature->GetMap()->CreatureRelocation(m_creature, m_aMajordomoLocations[1].m_fX, m_aMajordomoLocations[1].m_fY, m_aMajordomoLocations[1].m_fZ, m_aMajordomoLocations[1].m_fO); + m_creature->SetActiveObjectState(false); + } + } + + void JustSummoned(Creature* pSummoned) override + { + if (pSummoned->GetEntry() == NPC_FLAMEWAKER_HEALER || pSummoned->GetEntry() == NPC_FLAMEWAKER_ELITE) + { + m_luiMajordomoAddsGUIDs.push_back(pSummoned->GetObjectGuid()); + pSummoned->SetRespawnDelay(2 * HOUR); + } + else if (pSummoned->GetEntry() == NPC_RAGNAROS) + { + m_ragnarosGuid = pSummoned->GetObjectGuid(); + pSummoned->CastSpell(pSummoned, SPELL_RAGNA_EMERGE, false); + } + } + + void JustDied(Unit* pKiller) override + { + if (pKiller->GetTypeId() == TYPEID_UNIT && pKiller->GetEntry() == NPC_RAGNAROS) + DoScriptText(SAY_ARRIVAL4_MAJ, m_creature); + } + + void CorpseRemoved(uint32& uiRespawnDelay) override + { + uiRespawnDelay = urand(2 * HOUR, 3 * HOUR); + + if (m_bHasEncounterFinished) + { + // Needed for proper respawn handling + debug_log("SD2: boss_majordomo_executus: Set active"); + m_creature->SetActiveObjectState(true); + } + } + + void SummonedCreatureJustDied(Creature* pSummoned) override + { + if (pSummoned->GetEntry() == NPC_FLAMEWAKER_HEALER || pSummoned->GetEntry() == NPC_FLAMEWAKER_ELITE) + { + m_uiAddsKilled += 1; + + // Yell if only one Add alive + if (m_uiAddsKilled == m_luiMajordomoAddsGUIDs.size() - 1) + DoScriptText(SAY_LAST_ADD, m_creature); + + // All adds are killed, retreat + else if (m_uiAddsKilled == m_luiMajordomoAddsGUIDs.size()) + { + m_bHasEncounterFinished = true; + m_creature->GetMotionMaster()->MoveTargetedHome(); + } + } + } + + // Unsummon Majordomo adds + void UnsummonMajordomoAdds() + { + for (GuidList::const_iterator itr = m_luiMajordomoAddsGUIDs.begin(); itr != m_luiMajordomoAddsGUIDs.end(); ++itr) + { + if (Creature* pAdd = m_creature->GetMap()->GetCreature(*itr)) + if (pAdd->IsTemporarySummon()) + ((TemporarySummon*)pAdd)->UnSummon(); + } + + m_luiMajordomoAddsGUIDs.clear(); + } + + void DamageTaken(Unit* /*pDealer*/, uint32& uiDamage) override + { + if (uiDamage > m_creature->GetHealth()) + { + uiDamage = 0; + DoCastSpellIfCan(m_creature, SPELL_AEGIS, CAST_TRIGGERED); + } + } + + void UpdateAI(const uint32 uiDiff) override + { + // Handling of his combat-end speech and Ragnaros summoning + if (m_uiSpeech) + { + if (m_uiSpeechTimer < uiDiff) + { + switch (m_uiSpeech) + { + // Majordomo retreat event + case 1: + DoScriptText(SAY_DEFEAT_1, m_creature); + m_uiSpeechTimer = 7500; + ++m_uiSpeech; + break; + case 2: + DoScriptText(SAY_DEFEAT_2, m_creature); + m_uiSpeechTimer = 8000; + ++m_uiSpeech; + break; + case 3: + DoScriptText(SAY_DEFEAT_3, m_creature); + m_uiSpeechTimer = 21500; + ++m_uiSpeech; + break; + case 4: + DoCastSpellIfCan(m_creature, SPELL_TELEPORT_SELF); + // TODO - when should they be unsummoned? + // TODO - also unclear how this should be handled, as of range issues + m_uiSpeechTimer = 900; + ++m_uiSpeech; + break; + case 5: + // Majordomo is away now, remove his adds + UnsummonMajordomoAdds(); + m_uiSpeech = 0; + break; + + // Ragnaros Summon Event + case 10: + DoScriptText(SAY_SUMMON_1, m_creature); + ++m_uiSpeech; + m_uiSpeechTimer = 1000; + break; + case 11: + DoCastSpellIfCan(m_creature, SPELL_SUMMON_RAGNAROS); + // TODO - Move along, this expects to be handled with mmaps + m_creature->GetMotionMaster()->MovePoint(1, 831.079590f, -816.023193f, -229.023270f); + ++m_uiSpeech; + m_uiSpeechTimer = 7000; + break; + case 12: + // Reset orientation + if (GameObject* pLavaSteam = m_pInstance->GetSingleGameObjectFromStorage(GO_LAVA_STEAM)) + m_creature->SetFacingToObject(pLavaSteam); + m_uiSpeechTimer = 4500; + ++m_uiSpeech; + break; + case 13: + DoScriptText(SAY_SUMMON_MAJ, m_creature); + ++m_uiSpeech; + m_uiSpeechTimer = 8000; + break; + case 14: + // Summon Ragnaros + if (m_pInstance) + if (GameObject* pGo = m_pInstance->GetSingleGameObjectFromStorage(GO_LAVA_STEAM)) + m_creature->SummonCreature(NPC_RAGNAROS, pGo->GetPositionX(), pGo->GetPositionY(), pGo->GetPositionZ(), fmod(m_creature->GetOrientation() + M_PI, 2 * M_PI), TEMPSUMMON_TIMED_OOC_OR_DEAD_DESPAWN, 2 * HOUR * IN_MILLISECONDS); + ++m_uiSpeech; + m_uiSpeechTimer = 8700; + break; + case 15: + if (Creature* pRagnaros = m_creature->GetMap()->GetCreature(m_ragnarosGuid)) + DoScriptText(SAY_ARRIVAL1_RAG, pRagnaros); + ++m_uiSpeech; + m_uiSpeechTimer = 11700; + break; + case 16: + DoScriptText(SAY_ARRIVAL2_MAJ, m_creature); + ++m_uiSpeech; + m_uiSpeechTimer = 8700; + break; + case 17: + if (Creature* pRagnaros = m_creature->GetMap()->GetCreature(m_ragnarosGuid)) + DoScriptText(SAY_ARRIVAL3_RAG, pRagnaros); + ++m_uiSpeech; + m_uiSpeechTimer = 16500; + break; + case 18: + if (Creature* pRagnaros = m_creature->GetMap()->GetCreature(m_ragnarosGuid)) + pRagnaros->CastSpell(m_creature, SPELL_ELEMENTAL_FIRE, false); + // Rest of summoning speech is handled by Ragnaros, as Majordomo will be dead + m_uiSpeech = 0; + break; + } + } + else + m_uiSpeechTimer -= uiDiff; + } + + // When encounter finished, no need to do anything anymore (important for moving home after victory) + if (m_bHasEncounterFinished) + return; + + if (!m_creature->SelectHostileTarget() || !m_creature->getVictim()) + return; + + // Cast Ageis to heal self + if (m_uiAegisTimer <= uiDiff) + m_uiAegisTimer = 0; + else + m_uiAegisTimer -= uiDiff; + + if (m_creature->GetHealthPercent() < 90.0f && !m_uiAegisTimer) + { + DoCastSpellIfCan(m_creature, SPELL_AEGIS); + m_uiAegisTimer = 10000; + } + + // Magic Reflection Timer + if (m_uiMagicReflectionTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature, SPELL_MAGIC_REFLECTION) == CAST_OK) + m_uiMagicReflectionTimer = 30000; + } + else + m_uiMagicReflectionTimer -= uiDiff; + + // Damage Reflection Timer + if (m_uiDamageReflectionTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature, SPELL_DAMAGE_REFLECTION) == CAST_OK) + m_uiDamageReflectionTimer = 30000; + } + else + m_uiDamageReflectionTimer -= uiDiff; + + // Teleports the target to the heated rock in the center of the area + if (m_uiTeleportTimer < uiDiff) + { + if (Unit* pTarget = m_creature->SelectAttackingTarget(ATTACKING_TARGET_RANDOM, 1)) + { + if (DoCastSpellIfCan(pTarget, SPELL_TELEPORT) == CAST_OK) + m_uiTeleportTimer = 20000; + } + } + else + m_uiTeleportTimer -= uiDiff; + + // Blastwave Timer + if (m_uiBlastwaveTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature, SPELL_BLASTWAVE) == CAST_OK) + m_uiBlastwaveTimer = 10000; + } + else + m_uiBlastwaveTimer -= uiDiff; + + DoMeleeAttackIfReady(); + } +}; + +CreatureAI* GetAI_boss_majordomo(Creature* pCreature) +{ + return new boss_majordomoAI(pCreature); +} + +bool GossipHello_boss_majordomo(Player* pPlayer, Creature* pCreature) +{ + if (instance_molten_core* pInstance = (instance_molten_core*)pCreature->GetInstanceData()) + { + if (pInstance->GetData(TYPE_RAGNAROS) == NOT_STARTED || pInstance->GetData(TYPE_RAGNAROS) == FAIL) + { + pPlayer->ADD_GOSSIP_ITEM_ID(GOSSIP_ICON_CHAT, GOSSIP_ITEM_SUMMON_1, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 1); + pPlayer->SEND_GOSSIP_MENU(TEXT_ID_SUMMON_1, pCreature->GetObjectGuid()); + } + } + return true; +} + +bool GossipSelect_boss_majordomo(Player* pPlayer, Creature* pCreature, uint32 /*sender*/, uint32 uiAction) +{ + switch (uiAction) + { + case GOSSIP_ACTION_INFO_DEF + 1: + pPlayer->ADD_GOSSIP_ITEM_ID(GOSSIP_ICON_CHAT, GOSSIP_ITEM_SUMMON_2, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 2); + pPlayer->SEND_GOSSIP_MENU(TEXT_ID_SUMMON_2, pCreature->GetObjectGuid()); + break; + case GOSSIP_ACTION_INFO_DEF + 2: + pPlayer->ADD_GOSSIP_ITEM_ID(GOSSIP_ICON_CHAT, GOSSIP_ITEM_SUMMON_3, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 3); + pPlayer->SEND_GOSSIP_MENU(TEXT_ID_SUMMON_3, pCreature->GetObjectGuid()); + break; + case GOSSIP_ACTION_INFO_DEF + 3: + pPlayer->CLOSE_GOSSIP_MENU(); + if (boss_majordomoAI* pMajoAI = dynamic_cast(pCreature->AI())) + pMajoAI->StartSummonEvent(pPlayer); + break; + } + + return true; +} + +bool EffectDummyCreature_spell_boss_majordomo(Unit* /*pCaster*/, uint32 uiSpellId, SpellEffectIndex uiEffIndex, Creature* pCreatureTarget, ObjectGuid /*originalCasterGuid*/) +{ + if (uiSpellId != SPELL_TELEPORT_SELF || uiEffIndex != EFFECT_INDEX_0) + return false; + + pCreatureTarget->SetFlag(UNIT_NPC_FLAGS, UNIT_NPC_FLAG_GOSSIP); + pCreatureTarget->NearTeleportTo(m_aMajordomoLocations[1].m_fX, m_aMajordomoLocations[1].m_fY, m_aMajordomoLocations[1].m_fZ, m_aMajordomoLocations[1].m_fO, true); + // TODO - some visibility update? + return true; +} + +void AddSC_boss_majordomo() +{ + Script* pNewScript; + + pNewScript = new Script; + pNewScript->Name = "boss_majordomo"; + pNewScript->pEffectDummyNPC = &EffectDummyCreature_spell_boss_majordomo; + pNewScript->pGossipHello = &GossipHello_boss_majordomo; + pNewScript->pGossipSelect = &GossipSelect_boss_majordomo; + pNewScript->GetAI = &GetAI_boss_majordomo; + pNewScript->RegisterSelf(); +} diff --git a/src/modules/SD2/scripts/eastern_kingdoms/blackrock_mountain/molten_core/boss_ragnaros.cpp b/src/modules/SD2/scripts/eastern_kingdoms/blackrock_mountain/molten_core/boss_ragnaros.cpp new file mode 100644 index 000000000..6978bc902 --- /dev/null +++ b/src/modules/SD2/scripts/eastern_kingdoms/blackrock_mountain/molten_core/boss_ragnaros.cpp @@ -0,0 +1,343 @@ +/* Copyright (C) 2006 - 2013 ScriptDev2 + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with 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: 70 +SDComment: Melee/ Range Combat behavior is not correct(any enemy in melee range, not only getVictim), Some abilities are missing +SDCategory: Molten Core +EndScriptData */ + +#include "precompiled.h" +#include "molten_core.h" + +/* There have been quite some bugs about his spells, keep this as reference untill all finished + * Missing features (based on wowwiki) + * Lava Burst - this spell is handled by Go 178088 which is summoned by spells 21886, 21900 - 21907 + */ + +enum +{ + SAY_ARRIVAL5_RAG = -1409012, + SAY_REINFORCEMENTS_1 = -1409013, + SAY_REINFORCEMENTS_2 = -1409014, + SAY_HAMMER = -1409015, + SAY_WRATH = -1409016, + SAY_KILL = -1409017, + SAY_MAGMABURST = -1409018, + + SPELL_WRATH_OF_RAGNAROS = 20566, + SPELL_ELEMENTAL_FIRE = 20564, + SPELL_MAGMA_BLAST = 20565, // Ranged attack if nobody is in melee range + SPELL_MELT_WEAPON = 21387, + SPELL_RAGNA_SUBMERGE = 21107, // Stealth aura + SPELL_RAGNA_EMERGE = 20568, // Emerge from lava + SPELL_ELEMENTAL_FIRE_KILL = 19773, + SPELL_MIGHT_OF_RAGNAROS = 21154, + SPELL_INTENSE_HEAT = 21155, + + MAX_ADDS_IN_SUBMERGE = 8, + NPC_SON_OF_FLAME = 12143, + NPC_FLAME_OF_RAGNAROS = 13148, +}; + +struct boss_ragnarosAI : public Scripted_NoMovementAI +{ + boss_ragnarosAI(Creature* pCreature) : Scripted_NoMovementAI(pCreature) + { + m_pInstance = (instance_molten_core*)pCreature->GetInstanceData(); + m_uiEnterCombatTimer = 0; + m_bHasAggroYelled = false; + Reset(); + } + + instance_molten_core* m_pInstance; + + uint32 m_uiEnterCombatTimer; + uint32 m_uiWrathOfRagnarosTimer; + uint32 m_uiHammerTimer; + uint32 m_uiMagmaBlastTimer; + uint32 m_uiElementalFireTimer; + uint32 m_uiSubmergeTimer; + uint32 m_uiAttackTimer; + uint32 m_uiAddCount; + + bool m_bHasAggroYelled; + bool m_bHasYelledMagmaBurst; + bool m_bHasSubmergedOnce; + bool m_bIsSubmerged; + + void Reset() override + { + m_uiWrathOfRagnarosTimer = 30000; // TODO Research more, according to wowwiki 25s, but timers up to 34s confirmed + m_uiHammerTimer = 11000; // TODO wowwiki states 20-30s timer, but ~11s confirmed + m_uiMagmaBlastTimer = 2000; + m_uiElementalFireTimer = 3000; + m_uiSubmergeTimer = 3 * MINUTE * IN_MILLISECONDS; + m_uiAttackTimer = 90 * IN_MILLISECONDS; + m_uiAddCount = 0; + + m_bHasYelledMagmaBurst = false; + m_bHasSubmergedOnce = false; + m_bIsSubmerged = false; + } + + void KilledUnit(Unit* pVictim) override + { + if (pVictim->GetTypeId() != TYPEID_PLAYER) + return; + + if (urand(0, 3)) + return; + + DoScriptText(SAY_KILL, m_creature); + } + + void JustDied(Unit* /*pKiller*/) override + { + if (m_pInstance) + m_pInstance->SetData(TYPE_RAGNAROS, DONE); + } + + void Aggro(Unit* pWho) override + { + if (pWho->GetTypeId() == TYPEID_UNIT && pWho->GetEntry() == NPC_MAJORDOMO) + return; + + DoCastSpellIfCan(m_creature, SPELL_MELT_WEAPON); + + if (m_pInstance) + m_pInstance->SetData(TYPE_RAGNAROS, IN_PROGRESS); + } + + void EnterEvadeMode() override + { + if (m_pInstance) + m_pInstance->SetData(TYPE_RAGNAROS, FAIL); + + // Reset flag if had been submerged + if (m_creature->HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE)) + m_creature->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE); + + ScriptedAI::EnterEvadeMode(); + } + + void SummonedCreatureJustDied(Creature* pSummmoned) override + { + // If all Sons of Flame are dead, trigger emerge + if (pSummmoned->GetEntry() == NPC_SON_OF_FLAME) + { + m_uiAddCount--; + + // If last add killed then emerge soonish + if (m_uiAddCount == 0) + m_uiAttackTimer = std::min(m_uiAttackTimer, (uint32)1000); + } + } + + void JustSummoned(Creature* pSummoned) override + { + if (pSummoned->GetEntry() == NPC_SON_OF_FLAME) + { + if (Unit* pTarget = m_creature->SelectAttackingTarget(ATTACKING_TARGET_RANDOM, 0)) + pSummoned->AI()->AttackStart(pTarget); + + ++m_uiAddCount; + } + else if (pSummoned->GetEntry() == NPC_FLAME_OF_RAGNAROS) + pSummoned->CastSpell(pSummoned, SPELL_INTENSE_HEAT, true, NULL, NULL, m_creature->GetObjectGuid()); + } + + void SpellHitTarget(Unit* pTarget, const SpellEntry* pSpell) override + { + // As Majordomo is now killed, the last timer (until attacking) must be handled with ragnaros script + if (pSpell->Id == SPELL_ELEMENTAL_FIRE_KILL && pTarget->GetTypeId() == TYPEID_UNIT && pTarget->GetEntry() == NPC_MAJORDOMO) + m_uiEnterCombatTimer = 10000; + } + + void UpdateAI(const uint32 uiDiff) override + { + if (m_uiEnterCombatTimer) + { + if (m_uiEnterCombatTimer <= uiDiff) + { + if (!m_bHasAggroYelled) + { + m_uiEnterCombatTimer = 3000; + m_bHasAggroYelled = true; + DoScriptText(SAY_ARRIVAL5_RAG, m_creature); + } + else + { + m_uiEnterCombatTimer = 0; + // If we don't remove this passive flag, he will be unattackable after evading, this way he will enter combat + m_creature->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_PASSIVE); + if (m_pInstance) + { + if (Player* pPlayer = m_pInstance->GetPlayerInMap(true, false)) + { + m_creature->AI()->AttackStart(pPlayer); + return; + } + } + } + } + else + m_uiEnterCombatTimer -= uiDiff; + } + // Return since we have no target + if (!m_creature->SelectHostileTarget() || !m_creature->getVictim()) + return; + + if (m_bIsSubmerged) + { + // Timer to check when Ragnaros should emerge (is set to soonish, when last add is killed) + if (m_uiAttackTimer < uiDiff) + { + // Become emerged again + DoCastSpellIfCan(m_creature, SPELL_RAGNA_EMERGE); + m_creature->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE); + m_uiSubmergeTimer = 3 * MINUTE * IN_MILLISECONDS; + m_uiMagmaBlastTimer = 3000; // Delay the magma blast after emerge + m_bIsSubmerged = false; + } + else + m_uiAttackTimer -= uiDiff; + + // Do nothing while submerged + return; + } + + // Wrath Of Ragnaros Timer + if (m_uiWrathOfRagnarosTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature, SPELL_WRATH_OF_RAGNAROS) == CAST_OK) + { + DoScriptText(SAY_WRATH, m_creature); + m_uiWrathOfRagnarosTimer = 30000; + } + } + else + m_uiWrathOfRagnarosTimer -= uiDiff; + + // Elemental Fire Timer + if (m_uiElementalFireTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature->getVictim(), SPELL_ELEMENTAL_FIRE) == CAST_OK) + m_uiElementalFireTimer = urand(10000, 14000); + } + else + m_uiElementalFireTimer -= uiDiff; + + // Hammer of Ragnaros + if (m_uiHammerTimer < uiDiff) + { + if (Unit* pTarget = m_creature->SelectAttackingTarget(ATTACKING_TARGET_RANDOM, 0, SPELL_MIGHT_OF_RAGNAROS, SELECT_FLAG_POWER_MANA)) + { + if (DoCastSpellIfCan(pTarget, SPELL_MIGHT_OF_RAGNAROS) == CAST_OK) + { + DoScriptText(SAY_HAMMER, m_creature); + m_uiHammerTimer = 11000; + } + } + else + m_uiHammerTimer = 11000; + } + else + m_uiHammerTimer -= uiDiff; + + // Submerge Timer + if (m_uiSubmergeTimer < uiDiff) + { + // Submerge and attack again after 90 secs + DoCastSpellIfCan(m_creature, SPELL_RAGNA_SUBMERGE, CAST_INTERRUPT_PREVIOUS); + m_creature->HandleEmote(EMOTE_ONESHOT_SUBMERGE); + m_bIsSubmerged = true; + m_uiAttackTimer = 90 * IN_MILLISECONDS; + + m_creature->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE); + + // Say dependend if first time or not + DoScriptText(!m_bHasSubmergedOnce ? SAY_REINFORCEMENTS_1 : SAY_REINFORCEMENTS_2, m_creature); + m_bHasSubmergedOnce = true; + + // Summon 8 elementals at random points around the boss + float fX, fY, fZ; + for (uint8 i = 0; i < MAX_ADDS_IN_SUBMERGE; ++i) + { + m_creature->GetRandomPoint(m_creature->GetPositionX(), m_creature->GetPositionY(), m_creature->GetPositionZ(), 30.0f, fX, fY, fZ); + m_creature->SummonCreature(NPC_SON_OF_FLAME, fX, fY, fZ, 0.0f, TEMPSUMMON_TIMED_OOC_DESPAWN, 1000); + } + + return; + } + else + m_uiSubmergeTimer -= uiDiff; + + // TODO this actually should select _any_ enemy in melee range, not only the tank + // Range check for melee target, if nobody is found in range, then cast magma blast on random + // If we are within range melee the target + if (m_creature->IsNonMeleeSpellCasted(false) || !m_creature->getVictim()) + return; + + if (m_creature->CanReachWithMeleeAttack(m_creature->getVictim())) + { + // Make sure our attack is ready + if (m_creature->isAttackReady()) + { + m_creature->AttackerStateUpdate(m_creature->getVictim()); + m_creature->resetAttackTimer(); + m_bHasYelledMagmaBurst = false; + } + } + else + { + // Magma Burst Timer + if (m_uiMagmaBlastTimer < uiDiff) + { + if (Unit* pTarget = m_creature->SelectAttackingTarget(ATTACKING_TARGET_RANDOM, 0)) + { + if (DoCastSpellIfCan(pTarget, SPELL_MAGMA_BLAST) == CAST_OK) + { + if (!m_bHasYelledMagmaBurst) + { + DoScriptText(SAY_MAGMABURST, m_creature); + m_bHasYelledMagmaBurst = true; + } + m_uiMagmaBlastTimer = 1000; // Spamm this! + } + } + } + else + m_uiMagmaBlastTimer -= uiDiff; + } + } +}; + +CreatureAI* GetAI_boss_ragnaros(Creature* pCreature) +{ + return new boss_ragnarosAI(pCreature); +} + +void AddSC_boss_ragnaros() +{ + Script* pNewScript; + + pNewScript = new Script; + pNewScript->Name = "boss_ragnaros"; + pNewScript->GetAI = &GetAI_boss_ragnaros; + pNewScript->RegisterSelf(); +} diff --git a/src/modules/SD2/scripts/eastern_kingdoms/blackrock_mountain/molten_core/boss_shazzrah.cpp b/src/modules/SD2/scripts/eastern_kingdoms/blackrock_mountain/molten_core/boss_shazzrah.cpp new file mode 100644 index 000000000..b51fd4f0f --- /dev/null +++ b/src/modules/SD2/scripts/eastern_kingdoms/blackrock_mountain/molten_core/boss_shazzrah.cpp @@ -0,0 +1,156 @@ +/* Copyright (C) 2006 - 2013 ScriptDev2 + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with 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 (need core support, remove hack here when implemented) +SDCategory: Molten Core +EndScriptData */ + +#include "precompiled.h" +#include "molten_core.h" + +enum +{ + SPELL_ARCANE_EXPLOSION = 19712, + SPELL_SHAZZRAH_CURSE = 19713, + SPELL_MAGIC_GROUNDING = 19714, + SPELL_COUNTERSPELL = 19715, + SPELL_GATE_OF_SHAZZRAH = 23138 // effect spell: 23139 +}; + +struct boss_shazzrahAI : public ScriptedAI +{ + boss_shazzrahAI(Creature* pCreature) : ScriptedAI(pCreature) + { + m_pInstance = (ScriptedInstance*)pCreature->GetInstanceData(); + Reset(); + } + + ScriptedInstance* m_pInstance; + + uint32 m_uiArcaneExplosionTimer; + uint32 m_uiShazzrahCurseTimer; + uint32 m_uiMagicGroundingTimer; + uint32 m_uiCounterspellTimer; + uint32 m_uiBlinkTimer; + + void Reset() override + { + m_uiArcaneExplosionTimer = 6000; + m_uiShazzrahCurseTimer = 10000; + m_uiMagicGroundingTimer = 24000; + m_uiCounterspellTimer = 15000; + m_uiBlinkTimer = 30000; + } + + void Aggro(Unit* /*pWho*/) override + { + if (m_pInstance) + m_pInstance->SetData(TYPE_SHAZZRAH, IN_PROGRESS); + } + + void JustDied(Unit* /*pKiller*/) override + { + if (m_pInstance) + m_pInstance->SetData(TYPE_SHAZZRAH, DONE); + } + + void JustReachedHome() override + { + if (m_pInstance) + m_pInstance->SetData(TYPE_SHAZZRAH, NOT_STARTED); + } + + void UpdateAI(const uint32 uiDiff) override + { + if (!m_creature->SelectHostileTarget() || !m_creature->getVictim()) + return; + + // Arcane Explosion Timer + if (m_uiArcaneExplosionTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature, SPELL_ARCANE_EXPLOSION) == CAST_OK) + m_uiArcaneExplosionTimer = urand(5000, 9000); + } + else + m_uiArcaneExplosionTimer -= uiDiff; + + // Shazzrah Curse Timer + if (m_uiShazzrahCurseTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature, SPELL_SHAZZRAH_CURSE) == CAST_OK) + m_uiShazzrahCurseTimer = 20000; + } + else + m_uiShazzrahCurseTimer -= uiDiff; + + // Magic Grounding Timer + if (m_uiMagicGroundingTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature, SPELL_MAGIC_GROUNDING) == CAST_OK) + m_uiMagicGroundingTimer = 35000; + } + else + m_uiMagicGroundingTimer -= uiDiff; + + // Counterspell Timer + if (m_uiCounterspellTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature, SPELL_COUNTERSPELL) == CAST_OK) + m_uiCounterspellTimer = urand(16000, 20000); + } + else + m_uiCounterspellTimer -= uiDiff; + + // Blink Timer + if (m_uiBlinkTimer < uiDiff) + { + // Teleporting him to a random gamer and casting Arcane Explosion after that. + if (DoCastSpellIfCan(m_creature, SPELL_GATE_OF_SHAZZRAH) == CAST_OK) + { + // manual, until added effect of dummy properly -- TODO REMOVE HACK + if (Unit* pTarget = m_creature->SelectAttackingTarget(ATTACKING_TARGET_RANDOM, 0)) + m_creature->NearTeleportTo(pTarget->GetPositionX(), pTarget->GetPositionY(), pTarget->GetPositionZ(), m_creature->GetOrientation()); + DoResetThreat(); + + DoCastSpellIfCan(m_creature, SPELL_ARCANE_EXPLOSION, CAST_TRIGGERED); + + m_uiBlinkTimer = 45000; + } + } + else + m_uiBlinkTimer -= uiDiff; + + DoMeleeAttackIfReady(); + } +}; + +CreatureAI* GetAI_boss_shazzrah(Creature* pCreature) +{ + return new boss_shazzrahAI(pCreature); +} + +void AddSC_boss_shazzrah() +{ + Script* pNewScript; + + pNewScript = new Script; + pNewScript->Name = "boss_shazzrah"; + pNewScript->GetAI = &GetAI_boss_shazzrah; + pNewScript->RegisterSelf(); +} diff --git a/src/modules/SD2/scripts/eastern_kingdoms/blackrock_mountain/molten_core/boss_sulfuron_harbinger.cpp b/src/modules/SD2/scripts/eastern_kingdoms/blackrock_mountain/molten_core/boss_sulfuron_harbinger.cpp new file mode 100644 index 000000000..b0bc14271 --- /dev/null +++ b/src/modules/SD2/scripts/eastern_kingdoms/blackrock_mountain/molten_core/boss_sulfuron_harbinger.cpp @@ -0,0 +1,242 @@ +/* Copyright (C) 2006 - 2013 ScriptDev2 + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with 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: Spells Dark strike and Flamespear need confirmation +SDCategory: Molten Core +EndScriptData */ + +#include "precompiled.h" +#include "molten_core.h" + +enum +{ + SPELL_DARK_STRIKE = 19777, // Wowhead Linked to add - need confirmation! + SPELL_DEMORALIZING_SHOUT = 19778, + SPELL_INSPIRE = 19779, + SPELL_HAND_OF_RAGNAROS = 19780, + SPELL_FLAMESPEAR = 19781, + + // Adds Spells + SPELL_HEAL = 19775, + SPELL_SHADOWWORD_PAIN = 19776, + SPELL_IMMOLATE = 20294 +}; + +struct boss_sulfuronAI : public ScriptedAI +{ + boss_sulfuronAI(Creature* pCreature) : ScriptedAI(pCreature) + { + m_pInstance = (ScriptedInstance*)pCreature->GetInstanceData(); + Reset(); + } + + ScriptedInstance* m_pInstance; + + uint32 m_uiDarkstrikeTimer; + uint32 m_uiDemoralizingShoutTimer; + uint32 m_uiInspireTimer; + uint32 m_uiKnockdownTimer; + uint32 m_uiFlamespearTimer; + + void Reset() override + { + m_uiDarkstrikeTimer = 10000; + m_uiDemoralizingShoutTimer = 15000; + m_uiInspireTimer = 3000; + m_uiKnockdownTimer = 6000; + m_uiFlamespearTimer = 2000; + } + + void Aggro(Unit* /*pWho*/) override + { + if (m_pInstance) + m_pInstance->SetData(TYPE_SULFURON, IN_PROGRESS); + } + + void JustDied(Unit* /*pKiller*/) override + { + if (m_pInstance) + m_pInstance->SetData(TYPE_SULFURON, DONE); + } + + void JustReachedHome() override + { + if (m_pInstance) + m_pInstance->SetData(TYPE_SULFURON, FAIL); + } + + void UpdateAI(const uint32 uiDiff) override + { + if (!m_creature->SelectHostileTarget() || !m_creature->getVictim()) + return; + + // Demoralizing Shout Timer + if (m_uiDemoralizingShoutTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature, SPELL_DEMORALIZING_SHOUT) == CAST_OK) + m_uiDemoralizingShoutTimer = urand(15000, 20000); + } + else + m_uiDemoralizingShoutTimer -= uiDiff; + + // Inspire Timer + if (m_uiInspireTimer < uiDiff) + { + Creature* pTarget = NULL; + std::list pList = DoFindFriendlyMissingBuff(45.0f, SPELL_INSPIRE); + if (!pList.empty()) + { + std::list::iterator i = pList.begin(); + advance(i, (rand() % pList.size())); + pTarget = (*i); + } + + if (!pTarget) + pTarget = m_creature; + + if (DoCastSpellIfCan(pTarget, SPELL_INSPIRE) == CAST_OK) + m_uiInspireTimer = 10000; + } + else + m_uiInspireTimer -= uiDiff; + + // Hand of Ragnaros Timer + if (m_uiKnockdownTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature, SPELL_HAND_OF_RAGNAROS) == CAST_OK) + m_uiKnockdownTimer = urand(12000, 15000); + } + else + m_uiKnockdownTimer -= uiDiff; + + // Flamespear Timer + if (m_uiFlamespearTimer < uiDiff) + { + if (Unit* pTarget = m_creature->SelectAttackingTarget(ATTACKING_TARGET_RANDOM, 0)) + { + if (DoCastSpellIfCan(pTarget, SPELL_FLAMESPEAR) == CAST_OK) + m_uiFlamespearTimer = urand(12000, 16000); + } + } + else + m_uiFlamespearTimer -= uiDiff; + + // Dark Strike Timer + if (m_uiDarkstrikeTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature->getVictim(), SPELL_DARK_STRIKE) == CAST_OK) + m_uiDarkstrikeTimer = urand(15000, 18000); + } + else + m_uiDarkstrikeTimer -= uiDiff; + + DoMeleeAttackIfReady(); + } +}; + +struct mob_flamewaker_priestAI : public ScriptedAI +{ + mob_flamewaker_priestAI(Creature* pCreature) : ScriptedAI(pCreature) + { + m_pInstance = (ScriptedInstance*)pCreature->GetInstanceData(); + Reset(); + } + + uint32 m_uiHealTimer; + uint32 m_uiShadowWordPainTimer; + uint32 m_uiImmolateTimer; + + ScriptedInstance* m_pInstance; + + void Reset() override + { + m_uiHealTimer = urand(15000, 30000); + m_uiShadowWordPainTimer = 2000; + m_uiImmolateTimer = 8000; + } + + void UpdateAI(const uint32 uiDiff) override + { + if (!m_creature->SelectHostileTarget() || !m_creature->getVictim()) + return; + + // Casting Heal to Sulfuron or other Guards. + if (m_uiHealTimer < uiDiff) + { + if (Unit* pUnit = DoSelectLowestHpFriendly(60.0f, 1)) + { + if (DoCastSpellIfCan(pUnit, SPELL_HEAL) == CAST_OK) + m_uiHealTimer = urand(15000, 20000); + } + } + else + m_uiHealTimer -= uiDiff; + + // ShadowWord Pain Timer + if (m_uiShadowWordPainTimer < uiDiff) + { + if (Unit* pTarget = m_creature->SelectAttackingTarget(ATTACKING_TARGET_RANDOM, 0)) + { + if (DoCastSpellIfCan(pTarget, SPELL_SHADOWWORD_PAIN) == CAST_OK) + m_uiShadowWordPainTimer = urand(18000, 26000); + } + } + else + m_uiShadowWordPainTimer -= uiDiff; + + // Immolate Timer + if (m_uiImmolateTimer < uiDiff) + { + if (Unit* pTarget = m_creature->SelectAttackingTarget(ATTACKING_TARGET_RANDOM, 0)) + { + if (DoCastSpellIfCan(pTarget, SPELL_IMMOLATE) == CAST_OK) + m_uiImmolateTimer = urand(15000, 25000); + } + } + else + m_uiImmolateTimer -= uiDiff; + + 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* pNewScript; + + pNewScript = new Script; + pNewScript->Name = "boss_sulfuron"; + pNewScript->GetAI = &GetAI_boss_sulfuron; + pNewScript->RegisterSelf(); + + pNewScript = new Script; + pNewScript->Name = "mob_flamewaker_priest"; + pNewScript->GetAI = &GetAI_mob_flamewaker_priest; + pNewScript->RegisterSelf(); +} diff --git a/src/modules/SD2/scripts/eastern_kingdoms/blackrock_mountain/molten_core/instance_molten_core.cpp b/src/modules/SD2/scripts/eastern_kingdoms/blackrock_mountain/molten_core/instance_molten_core.cpp new file mode 100644 index 000000000..09d786c78 --- /dev/null +++ b/src/modules/SD2/scripts/eastern_kingdoms/blackrock_mountain/molten_core/instance_molten_core.cpp @@ -0,0 +1,265 @@ +/* Copyright (C) 2006 - 2013 ScriptDev2 + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along 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: 25 +SDComment: Majordomos and Ragnaros Event missing +SDCategory: Molten Core +EndScriptData */ + +#include "precompiled.h" +#include "molten_core.h" + +static sSpawnLocation m_aBosspawnLocs[MAX_MAJORDOMO_ADDS] = +{ + {NPC_FLAMEWAKER_ELITE, 737.945f, -1156.48f, -118.945f, 4.46804f}, + {NPC_FLAMEWAKER_ELITE, 752.520f, -1191.02f, -118.218f, 2.49582f}, + {NPC_FLAMEWAKER_ELITE, 752.953f, -1163.94f, -118.869f, 3.70010f}, + {NPC_FLAMEWAKER_ELITE, 738.814f, -1197.40f, -118.018f, 1.83260f}, + {NPC_FLAMEWAKER_HEALER, 746.939f, -1194.87f, -118.016f, 2.21657f}, + {NPC_FLAMEWAKER_HEALER, 747.132f, -1158.87f, -118.897f, 4.03171f}, + {NPC_FLAMEWAKER_HEALER, 757.116f, -1170.12f, -118.793f, 3.40339f}, + {NPC_FLAMEWAKER_HEALER, 755.910f, -1184.46f, -118.449f, 2.80998f} +}; + +instance_molten_core::instance_molten_core(Map* pMap) : ScriptedInstance(pMap) +{ + Initialize(); +} + +void instance_molten_core::Initialize() +{ + memset(&m_auiEncounter, 0, sizeof(m_auiEncounter)); +} + +bool instance_molten_core::IsEncounterInProgress() const +{ + for (uint8 i = 0; i < MAX_ENCOUNTER; ++i) + { + if (m_auiEncounter[i] == IN_PROGRESS) + return true; + } + + return false; +} + +void instance_molten_core::OnPlayerEnter(Player* /*pPlayer*/) +{ + // Summon Majordomo if can + DoSpawnMajordomoIfCan(true); +} + +void instance_molten_core::OnCreatureCreate(Creature* pCreature) +{ + switch (pCreature->GetEntry()) + { + // Bosses + case NPC_GARR: + case NPC_SULFURON: + case NPC_MAJORDOMO: + m_mNpcEntryGuidStore[pCreature->GetEntry()] = pCreature->GetObjectGuid(); + break; + } +} + +void instance_molten_core::OnObjectCreate(GameObject* pGo) +{ + switch (pGo->GetEntry()) + { + // Runes + case GO_RUNE_KRESS: + case GO_RUNE_MOHN: + case GO_RUNE_BLAZ: + case GO_RUNE_MAZJ: + case GO_RUNE_ZETH: + case GO_RUNE_THERI: + case GO_RUNE_KORO: + + // Majordomo event chest + case GO_CACHE_OF_THE_FIRE_LORD: + // Ragnaros GOs + case GO_LAVA_STEAM: + case GO_LAVA_SPLASH: + m_mGoEntryGuidStore[pGo->GetEntry()] = pGo->GetObjectGuid(); + break; + } +} + +void instance_molten_core::SetData(uint32 uiType, uint32 uiData) +{ + switch (uiType) + { + case TYPE_LUCIFRON: + m_auiEncounter[uiType] = uiData; + break; + case TYPE_MAGMADAR: + m_auiEncounter[uiType] = uiData; + if (uiData == DONE) + DoUseDoorOrButton(GO_RUNE_KRESS); + break; + case TYPE_GEHENNAS: + m_auiEncounter[uiType] = uiData; + if (uiData == DONE) + DoUseDoorOrButton(GO_RUNE_MOHN); + break; + case TYPE_GARR: + m_auiEncounter[uiType] = uiData; + if (uiData == DONE) + DoUseDoorOrButton(GO_RUNE_BLAZ); + break; + case TYPE_SHAZZRAH: + m_auiEncounter[uiType] = uiData; + if (uiData == DONE) + DoUseDoorOrButton(GO_RUNE_MAZJ); + break; + case TYPE_GEDDON: + m_auiEncounter[uiType] = uiData; + if (uiData == DONE) + DoUseDoorOrButton(GO_RUNE_ZETH); + break; + case TYPE_GOLEMAGG: + m_auiEncounter[uiType] = uiData; + if (uiData == DONE) + DoUseDoorOrButton(GO_RUNE_THERI); + break; + case TYPE_SULFURON: + m_auiEncounter[uiType] = uiData; + if (uiData == DONE) + DoUseDoorOrButton(GO_RUNE_KORO); + break; + case TYPE_MAJORDOMO: + m_auiEncounter[uiType] = uiData; + if (uiData == DONE) + DoRespawnGameObject(GO_CACHE_OF_THE_FIRE_LORD, HOUR); + break; + case TYPE_RAGNAROS: + m_auiEncounter[uiType] = uiData; + break; + } + + // Check if Majordomo can be summoned + if (uiData == DONE) + DoSpawnMajordomoIfCan(false); + + 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_strInstData = saveStream.str(); + + SaveToDB(); + OUT_SAVE_INST_DATA_COMPLETE; + } +} + +uint32 instance_molten_core::GetData(uint32 uiType) const +{ + if (uiType < MAX_ENCOUNTER) + return m_auiEncounter[uiType]; + + return 0; +} + +// Handle Majordomo summon here +void instance_molten_core::DoSpawnMajordomoIfCan(bool bByPlayerEnter) +{ + // If both Majordomo and Ragnaros events are finished, return + if (m_auiEncounter[TYPE_MAJORDOMO] == DONE && m_auiEncounter[TYPE_RAGNAROS] == DONE) + return; + + // If already spawned return + if (GetSingleCreatureFromStorage(NPC_MAJORDOMO, true)) + return; + + // Check if all rune bosses are done + for (uint8 i = TYPE_MAGMADAR; i < TYPE_MAJORDOMO; ++i) + { + if (m_auiEncounter[i] != DONE) + return; + } + + Player* pPlayer = GetPlayerInMap(); + if (!pPlayer) + return; + + // Summon Majordomo + // If Majordomo encounter isn't done, summon at encounter place, else near Ragnaros + uint8 uiSummonPos = m_auiEncounter[TYPE_MAJORDOMO] == DONE ? 1 : 0; + if (Creature* pMajordomo = pPlayer->SummonCreature(m_aMajordomoLocations[uiSummonPos].m_uiEntry, m_aMajordomoLocations[uiSummonPos].m_fX, m_aMajordomoLocations[uiSummonPos].m_fY, m_aMajordomoLocations[uiSummonPos].m_fZ, m_aMajordomoLocations[uiSummonPos].m_fO, TEMPSUMMON_MANUAL_DESPAWN, 2 * HOUR * IN_MILLISECONDS)) + { + if (uiSummonPos) // Majordomo encounter already done, set faction + { + pMajordomo->SetFactionTemporary(FACTION_MAJORDOMO_FRIENDLY, TEMPFACTION_RESTORE_RESPAWN); + pMajordomo->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_OOC_NOT_ATTACKABLE); + pMajordomo->SetFlag(UNIT_NPC_FLAGS, UNIT_NPC_FLAG_GOSSIP); + } + else // Else yell and summon adds + { + if (!bByPlayerEnter) + DoScriptText(SAY_MAJORDOMO_SPAWN, pMajordomo); + + for (uint8 i = 0; i < MAX_MAJORDOMO_ADDS; ++i) + pMajordomo->SummonCreature(m_aBosspawnLocs[i].m_uiEntry, m_aBosspawnLocs[i].m_fX, m_aBosspawnLocs[i].m_fY, m_aBosspawnLocs[i].m_fZ, m_aBosspawnLocs[i].m_fO, TEMPSUMMON_MANUAL_DESPAWN, DAY * IN_MILLISECONDS); + } + } +} + +void instance_molten_core::Load(const char* chrIn) +{ + if (!chrIn) + { + 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]; + + 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* GetInstance_instance_molten_core(Map* pMap) +{ + return new instance_molten_core(pMap); +} + +void AddSC_instance_molten_core() +{ + Script* pNewScript; + + pNewScript = new Script; + pNewScript->Name = "instance_molten_core"; + pNewScript->GetInstanceData = &GetInstance_instance_molten_core; + pNewScript->RegisterSelf(); +} diff --git a/src/modules/SD2/scripts/eastern_kingdoms/blackrock_mountain/molten_core/molten_core.cpp b/src/modules/SD2/scripts/eastern_kingdoms/blackrock_mountain/molten_core/molten_core.cpp new file mode 100644 index 000000000..293595284 --- /dev/null +++ b/src/modules/SD2/scripts/eastern_kingdoms/blackrock_mountain/molten_core/molten_core.cpp @@ -0,0 +1,31 @@ +/* Copyright (C) 2006 - 2013 ScriptDev2 + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along 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: +SDComment: +SDCategory: Molten Core +EndScriptData */ + +/* ContentData +EndContentData */ + +#include "precompiled.h" + +void AddSC_molten_core() +{ +} diff --git a/src/modules/SD2/scripts/eastern_kingdoms/blackrock_mountain/molten_core/molten_core.h b/src/modules/SD2/scripts/eastern_kingdoms/blackrock_mountain/molten_core/molten_core.h new file mode 100644 index 000000000..ebf267eb9 --- /dev/null +++ b/src/modules/SD2/scripts/eastern_kingdoms/blackrock_mountain/molten_core/molten_core.h @@ -0,0 +1,98 @@ +/* Copyright (C) 2006 - 2013 ScriptDev2 + * This program is free software licensed under GPL version 2 + * Please see the included DOCS/LICENSE.TXT for more information */ + +#ifndef DEF_MOLTEN_CORE_H +#define DEF_MOLTEN_CORE_H + +enum +{ + MAX_ENCOUNTER = 10, + + TYPE_LUCIFRON = 0, + TYPE_MAGMADAR = 1, + TYPE_GEHENNAS = 2, + TYPE_GARR = 3, + TYPE_SHAZZRAH = 4, + TYPE_GEDDON = 5, + TYPE_GOLEMAGG = 6, + TYPE_SULFURON = 7, + TYPE_MAJORDOMO = 8, + TYPE_RAGNAROS = 9, + + NPC_LUCIFRON = 12118, + NPC_MAGMADAR = 11982, + NPC_GEHENNAS = 12259, + NPC_GARR = 12057, + NPC_SHAZZRAH = 12264, + NPC_GEDDON = 12056, + NPC_GOLEMAGG = 11988, + NPC_SULFURON = 12098, + NPC_MAJORDOMO = 12018, + NPC_RAGNAROS = 11502, + + // Adds + // Used for respawn in case of wipe + NPC_FLAMEWAKER_PROTECTOR = 12119, // Lucifron + NPC_FLAMEWAKER = 11661, // Gehennas + NPC_FIRESWORN = 12099, // Garr + NPC_CORE_RAGER = 11672, // Golemagg + NPC_FLAMEWAKER_PRIEST = 11662, // Sulfuron + NPC_FLAMEWAKER_HEALER = 11663, // Majordomo + NPC_FLAMEWAKER_ELITE = 11664, // Majordomo + + GO_LAVA_STEAM = 178107, + GO_LAVA_SPLASH = 178108, + GO_CACHE_OF_THE_FIRE_LORD = 179703, + GO_RUNE_KRESS = 176956, // Magmadar + GO_RUNE_MOHN = 176957, // Gehennas + GO_RUNE_BLAZ = 176955, // Garr + GO_RUNE_MAZJ = 176953, // Shazzah + GO_RUNE_ZETH = 176952, // Geddon + GO_RUNE_THERI = 176954, // Golemagg + GO_RUNE_KORO = 176951, // Sulfuron + + MAX_MAJORDOMO_ADDS = 8, + FACTION_MAJORDOMO_FRIENDLY = 1080, + SAY_MAJORDOMO_SPAWN = -1409004, +}; + +struct sSpawnLocation +{ + uint32 m_uiEntry; + float m_fX, m_fY, m_fZ, m_fO; +}; + +static sSpawnLocation m_aMajordomoLocations[2] = +{ + {NPC_MAJORDOMO, 758.089f, -1176.71f, -118.640f, 3.12414f}, // Summon fight position + {NPC_MAJORDOMO, 847.103f, -816.153f, -229.775f, 4.344f} // Summon and teleport location (near Ragnaros) +}; + +class instance_molten_core : public ScriptedInstance +{ + public: + instance_molten_core(Map* pMap); + ~instance_molten_core() {} + + void Initialize() override; + bool IsEncounterInProgress() const override; + + void OnCreatureCreate(Creature* pCreature) override; + void OnObjectCreate(GameObject* pGo) override; + void OnPlayerEnter(Player* pPlayer) override; + + void SetData(uint32 uiType, uint32 uiData) override; + uint32 GetData(uint32 uiType) const override; + + const char* Save() const override { return m_strInstData.c_str(); } + void Load(const char* chrIn) override; + + protected: + void DoSpawnMajordomoIfCan(bool bByPlayerEnter); + + std::string m_strInstData; + uint32 m_auiEncounter[MAX_ENCOUNTER]; +}; + +#endif diff --git a/src/modules/SD2/scripts/eastern_kingdoms/blasted_lands.cpp b/src/modules/SD2/scripts/eastern_kingdoms/blasted_lands.cpp new file mode 100644 index 000000000..154423f3e --- /dev/null +++ b/src/modules/SD2/scripts/eastern_kingdoms/blasted_lands.cpp @@ -0,0 +1,144 @@ +/** + * ScriptDev2 is an extension for mangos providing enhanced features for + * area triggers, creatures, game objects, instances, items, and spells beyond + * the default database scripting in mangos. + * + * Copyright (C) 2006-2013 ScriptDev2 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * World of Warcraft, and all World of Warcraft or Warcraft art, images, + * and lore are copyrighted by Blizzard Entertainment, Inc. + */ + +/** + * ScriptData + * SDName: Blasted_Lands + * SD%Complete: 90 + * SDComment: Quest support: 2784, 2801. Missing some texts for Fallen Hero. Teleporter to Rise of the Defiler missing group support. + * SDCategory: Blasted Lands + * EndScriptData + */ + +/** + * ContentData + * npc_fallen_hero_of_horde + * EndContentData + */ + +#include "precompiled.h" + +/*###### +## npc_fallen_hero_of_horde +######*/ + +#define GOSSIP_ITEM_FALLEN "Continue..." + +#define GOSSIP_ITEM_FALLEN1 "What could be worse than death?" +#define GOSSIP_ITEM_FALLEN2 "Subordinates?" +#define GOSSIP_ITEM_FALLEN3 "What are the stones of binding?" +#define GOSSIP_ITEM_FALLEN4 "You can count on me, Hero" +#define GOSSIP_ITEM_FALLEN5 "I shall" + +bool GossipHello_npc_fallen_hero_of_horde(Player* pPlayer, Creature* pCreature) +{ + if (pCreature->IsQuestGiver()) + { + pPlayer->PrepareQuestMenu(pCreature->GetObjectGuid()); + } + + if (pPlayer->GetQuestStatus(2784) == QUEST_STATUS_INCOMPLETE) + { + pPlayer->ADD_GOSSIP_ITEM(GOSSIP_ICON_CHAT, "Why are you here?", 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, "Continue story...", 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, "Why are you here?", GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 1); + } + + pPlayer->SEND_GOSSIP_MENU(pPlayer->GetGossipTextId(pCreature), pCreature->GetObjectGuid()); + + return true; +} + +bool GossipSelect_npc_fallen_hero_of_horde(Player* pPlayer, Creature* pCreature, uint32 /*uiSender*/, uint32 uiAction) +{ + switch (uiAction) + { + case GOSSIP_ACTION_INFO_DEF+1: + pPlayer->ADD_GOSSIP_ITEM(GOSSIP_ICON_CHAT, GOSSIP_ITEM_FALLEN, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 11); + pPlayer->SEND_GOSSIP_MENU(1392, pCreature->GetObjectGuid()); + break; + case GOSSIP_ACTION_INFO_DEF+11: + pPlayer->SEND_GOSSIP_MENU(1411, pCreature->GetObjectGuid()); + if (pPlayer->GetQuestStatus(2784) == QUEST_STATUS_INCOMPLETE) + { + pPlayer->AreaExploredOrEventHappens(2784); + } + if (pPlayer->GetTeam() == ALLIANCE) + { + pPlayer->ADD_GOSSIP_ITEM(GOSSIP_ICON_CHAT, GOSSIP_ITEM_FALLEN, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 2); + pPlayer->SEND_GOSSIP_MENU(1411, pCreature->GetObjectGuid()); + } + 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->GetObjectGuid()); + break; + case GOSSIP_ACTION_INFO_DEF+21: + pPlayer->ADD_GOSSIP_ITEM(GOSSIP_ICON_CHAT, GOSSIP_ITEM_FALLEN1, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 22); + pPlayer->SEND_GOSSIP_MENU(1452, pCreature->GetObjectGuid()); + break; + case GOSSIP_ACTION_INFO_DEF+22: + pPlayer->ADD_GOSSIP_ITEM(GOSSIP_ICON_CHAT, GOSSIP_ITEM_FALLEN2, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 23); + pPlayer->SEND_GOSSIP_MENU(1453, pCreature->GetObjectGuid()); + break; + case GOSSIP_ACTION_INFO_DEF+23: + pPlayer->ADD_GOSSIP_ITEM(GOSSIP_ICON_CHAT, GOSSIP_ITEM_FALLEN3, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 24); + pPlayer->SEND_GOSSIP_MENU(1454, pCreature->GetObjectGuid()); + break; + case GOSSIP_ACTION_INFO_DEF+24: + pPlayer->ADD_GOSSIP_ITEM(GOSSIP_ICON_CHAT, GOSSIP_ITEM_FALLEN4, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 25); + pPlayer->SEND_GOSSIP_MENU(1455, pCreature->GetObjectGuid()); + break; + case GOSSIP_ACTION_INFO_DEF+25: + pPlayer->ADD_GOSSIP_ITEM(GOSSIP_ICON_CHAT, GOSSIP_ITEM_FALLEN5, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 26); + pPlayer->SEND_GOSSIP_MENU(1456, pCreature->GetObjectGuid()); + break; + case GOSSIP_ACTION_INFO_DEF+26: + pPlayer->CLOSE_GOSSIP_MENU(); + pPlayer->AreaExploredOrEventHappens(2801); + break; + } + return true; +} + +void AddSC_blasted_lands() +{ + Script* pNewScript; + + pNewScript = new Script; + pNewScript->Name = "npc_fallen_hero_of_horde"; + pNewScript->pGossipHello = &GossipHello_npc_fallen_hero_of_horde; + pNewScript->pGossipSelect = &GossipSelect_npc_fallen_hero_of_horde; + pNewScript->RegisterSelf(); +} diff --git a/src/modules/SD2/scripts/eastern_kingdoms/burning_steppes.cpp b/src/modules/SD2/scripts/eastern_kingdoms/burning_steppes.cpp new file mode 100644 index 000000000..3f82bb078 --- /dev/null +++ b/src/modules/SD2/scripts/eastern_kingdoms/burning_steppes.cpp @@ -0,0 +1,513 @@ +/** + * ScriptDev2 is an extension for mangos providing enhanced features for + * area triggers, creatures, game objects, instances, items, and spells beyond + * the default database scripting in mangos. + * + * Copyright (C) 2006-2013 ScriptDev2 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * World of Warcraft, and all World of Warcraft or Warcraft art, images, + * and lore are copyrighted by Blizzard Entertainment, Inc. + */ + +/** + * ScriptData + * SDName: Burning_Steppes + * SD%Complete: 100 + * SDComment: Quest support: 4121, 4122, 4224, 4866. + * SDCategory: Burning Steppes + * EndScriptData + */ + +/** + * ContentData + * npc_ragged_john + * npc_grark_lorkrub + * EndContentData + */ + +#include "precompiled.h" +#include "escort_ai.h" + +/*###### +## npc_ragged_john +######*/ + +struct npc_ragged_johnAI : public ScriptedAI +{ + npc_ragged_johnAI(Creature* pCreature) : ScriptedAI(pCreature) { Reset(); } + + void Reset() override {} + + void MoveInLineOfSight(Unit* who) override + { + if (who->HasAura(16468, EFFECT_INDEX_0)) + { + if (who->GetTypeId() == TYPEID_PLAYER && m_creature->IsWithinDistInMap(who, 15) && who->isInAccessablePlaceFor(m_creature)) + { + DoCastSpellIfCan(who, 16472); + ((Player*)who)->AreaExploredOrEventHappens(4866); + } + } + + if (!m_creature->getVictim() && who->IsTargetableForAttack() && (m_creature->IsHostileTo(who)) && who->isInAccessablePlaceFor(m_creature)) + { + if (!m_creature->CanFly() && m_creature->GetDistanceZ(who) > CREATURE_Z_ATTACK_RANGE) + { + return; + } + + float attackRadius = m_creature->GetAttackDistance(who); + if (m_creature->IsWithinDistInMap(who, attackRadius) && m_creature->IsWithinLOSInMap(who)) + { + who->RemoveSpellsCausingAura(SPELL_AURA_MOD_STEALTH); + AttackStart(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->GetObjectGuid()); + } + + if (pPlayer->GetQuestStatus(4224) == QUEST_STATUS_INCOMPLETE) + { + pPlayer->ADD_GOSSIP_ITEM(GOSSIP_ICON_CHAT, "Official business, John. I need some information about Marshal Windsor. Tell me about he last time you saw him.", GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF); + } + + pPlayer->SEND_GOSSIP_MENU(2713, pCreature->GetObjectGuid()); + return true; +} + +bool GossipSelect_npc_ragged_john(Player* pPlayer, Creature* pCreature, uint32 /*uiSender*/, uint32 uiAction) +{ + switch (uiAction) + { + case GOSSIP_ACTION_INFO_DEF: + pPlayer->ADD_GOSSIP_ITEM(GOSSIP_ICON_CHAT, "So what did you do?", GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 1); + pPlayer->SEND_GOSSIP_MENU(2714, pCreature->GetObjectGuid()); + break; + case GOSSIP_ACTION_INFO_DEF+1: + pPlayer->ADD_GOSSIP_ITEM(GOSSIP_ICON_CHAT, "Start making sense, dwarf. I don't want to have anything to do with your cracker, your pappy, or any sort of 'discreditin'.", GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 2); + pPlayer->SEND_GOSSIP_MENU(2715, pCreature->GetObjectGuid()); + break; + case GOSSIP_ACTION_INFO_DEF+2: + pPlayer->ADD_GOSSIP_ITEM(GOSSIP_ICON_CHAT, "Ironfoe?", GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 3); + pPlayer->SEND_GOSSIP_MENU(2716, pCreature->GetObjectGuid()); + break; + case GOSSIP_ACTION_INFO_DEF+3: + pPlayer->ADD_GOSSIP_ITEM(GOSSIP_ICON_CHAT, "Interesting... continue, John.", GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 4); + pPlayer->SEND_GOSSIP_MENU(2717, pCreature->GetObjectGuid()); + break; + case GOSSIP_ACTION_INFO_DEF+4: + pPlayer->ADD_GOSSIP_ITEM(GOSSIP_ICON_CHAT, "So that's how Windsor died...", GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 5); + pPlayer->SEND_GOSSIP_MENU(2718, pCreature->GetObjectGuid()); + break; + case GOSSIP_ACTION_INFO_DEF+5: + pPlayer->ADD_GOSSIP_ITEM(GOSSIP_ICON_CHAT, "So how did he die?", GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 6); + pPlayer->SEND_GOSSIP_MENU(2719, pCreature->GetObjectGuid()); + break; + case GOSSIP_ACTION_INFO_DEF+6: + pPlayer->ADD_GOSSIP_ITEM(GOSSIP_ICON_CHAT, "Ok, so where the hell is he? Wait a minute! Are you drunk?", GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 7); + pPlayer->SEND_GOSSIP_MENU(2720, pCreature->GetObjectGuid()); + break; + case GOSSIP_ACTION_INFO_DEF+7: + pPlayer->ADD_GOSSIP_ITEM(GOSSIP_ICON_CHAT, "WHY is he in Blackrock Depths?", GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 8); + pPlayer->SEND_GOSSIP_MENU(2721, pCreature->GetObjectGuid()); + break; + case GOSSIP_ACTION_INFO_DEF+8: + pPlayer->ADD_GOSSIP_ITEM(GOSSIP_ICON_CHAT, "300? So the Dark Irons killed him and dragged him into the Depths?", GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 9); + pPlayer->SEND_GOSSIP_MENU(2722, pCreature->GetObjectGuid()); + break; + case GOSSIP_ACTION_INFO_DEF+9: + pPlayer->ADD_GOSSIP_ITEM(GOSSIP_ICON_CHAT, "Ahh... Ironfoe.", GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 10); + pPlayer->SEND_GOSSIP_MENU(2723, pCreature->GetObjectGuid()); + break; + case GOSSIP_ACTION_INFO_DEF+10: + pPlayer->ADD_GOSSIP_ITEM(GOSSIP_ICON_CHAT, "Thanks, Ragged John. Your story was very uplifting and informative.", GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 11); + pPlayer->SEND_GOSSIP_MENU(2725, pCreature->GetObjectGuid()); + break; + case GOSSIP_ACTION_INFO_DEF+11: + pPlayer->CLOSE_GOSSIP_MENU(); + pPlayer->AreaExploredOrEventHappens(4224); + break; + } + return true; +} + +/*###### +## npc_grark_lorkrub +######*/ + +enum +{ + SAY_START = -1000873, + SAY_PAY = -1000874, + SAY_FIRST_AMBUSH_START = -1000875, + SAY_FIRST_AMBUSH_END = -1000876, + SAY_SEC_AMBUSH_START = -1000877, + SAY_SEC_AMBUSH_END = -1000878, + SAY_THIRD_AMBUSH_START = -1000879, + SAY_THIRD_AMBUSH_END = -1000880, + EMOTE_LAUGH = -1000881, + SAY_LAST_STAND = -1000882, + SAY_LEXLORT_1 = -1000883, + SAY_LEXLORT_2 = -1000884, + EMOTE_RAISE_AXE = -1000885, + EMOTE_LOWER_HAND = -1000886, + SAY_LEXLORT_3 = -1000887, + SAY_LEXLORT_4 = -1000888, + + EMOTE_SUBMIT = -1000889, + SAY_AGGRO = -1000890, + + SPELL_CAPTURE_GRARK = 14250, + + NPC_BLACKROCK_AMBUSHER = 9522, + NPC_BLACKROCK_RAIDER = 9605, + NPC_FLAMESCALE_DRAGONSPAWN = 7042, + NPC_SEARSCALE_DRAKE = 7046, + + NPC_GRARK_LORKRUB = 9520, + NPC_HIGH_EXECUTIONER_NUZARK = 9538, + NPC_SHADOW_OF_LEXLORT = 9539, + + FACTION_FRIENDLY = 35, + + QUEST_ID_PRECARIOUS_PREDICAMENT = 4121 +}; + +static const DialogueEntry aOutroDialogue[] = +{ + {SAY_LAST_STAND, NPC_GRARK_LORKRUB, 5000}, + {SAY_LEXLORT_1, NPC_SHADOW_OF_LEXLORT, 3000}, + {SAY_LEXLORT_2, NPC_SHADOW_OF_LEXLORT, 5000}, + {EMOTE_RAISE_AXE, NPC_HIGH_EXECUTIONER_NUZARK, 4000}, + {EMOTE_LOWER_HAND, NPC_SHADOW_OF_LEXLORT, 3000}, + {SAY_LEXLORT_3, NPC_SHADOW_OF_LEXLORT, 3000}, + {NPC_GRARK_LORKRUB, 0, 5000}, + {SAY_LEXLORT_4, NPC_SHADOW_OF_LEXLORT, 0}, + {0, 0, 0}, +}; + +struct npc_grark_lorkrubAI : public npc_escortAI, private DialogueHelper +{ + npc_grark_lorkrubAI(Creature* pCreature) : npc_escortAI(pCreature), + DialogueHelper(aOutroDialogue) + { + Reset(); + } + + ObjectGuid m_nuzarkGuid; + ObjectGuid m_lexlortGuid; + + GuidList m_lSearscaleGuidList; + + uint8 m_uiKilledCreatures; + bool m_bIsFirstSearScale; + + void Reset() override + { + if (!HasEscortState(STATE_ESCORT_ESCORTING)) + { + m_uiKilledCreatures = 0; + m_bIsFirstSearScale = true; + + m_lSearscaleGuidList.clear(); + + m_creature->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE); + } + } + + void Aggro(Unit* /*pWho*/) override + { + if (!HasEscortState(STATE_ESCORT_ESCORTING)) + { + DoScriptText(SAY_AGGRO, m_creature); + } + } + + void MoveInLineOfSight(Unit* pWho) override + { + // No combat during escort + if (HasEscortState(STATE_ESCORT_ESCORTING)) + { + return; + } + + npc_escortAI::MoveInLineOfSight(pWho); + } + + void WaypointReached(uint32 uiPointId) override + { + switch (uiPointId) + { + case 1: + DoScriptText(SAY_START, m_creature); + break; + case 7: + DoScriptText(SAY_PAY, m_creature); + break; + case 12: + DoScriptText(SAY_FIRST_AMBUSH_START, m_creature); + SetEscortPaused(true); + + m_creature->SummonCreature(NPC_BLACKROCK_AMBUSHER, -7844.3f, -1521.6f, 139.2f, 0.0f, TEMPSUMMON_TIMED_OOC_DESPAWN, 20000); + m_creature->SummonCreature(NPC_BLACKROCK_AMBUSHER, -7860.4f, -1507.8f, 141.0f, 6.0f, TEMPSUMMON_TIMED_OOC_DESPAWN, 20000); + m_creature->SummonCreature(NPC_BLACKROCK_RAIDER, -7845.6f, -1508.1f, 138.8f, 6.1f, TEMPSUMMON_TIMED_OOC_DESPAWN, 20000); + m_creature->SummonCreature(NPC_BLACKROCK_RAIDER, -7859.8f, -1521.8f, 139.2f, 6.2f, TEMPSUMMON_TIMED_OOC_DESPAWN, 20000); + break; + case 24: + DoScriptText(SAY_SEC_AMBUSH_START, m_creature); + SetEscortPaused(true); + + m_creature->SummonCreature(NPC_BLACKROCK_AMBUSHER, -8035.3f, -1222.2f, 135.5f, 5.1f, TEMPSUMMON_TIMED_OOC_DESPAWN, 20000); + m_creature->SummonCreature(NPC_FLAMESCALE_DRAGONSPAWN, -8037.5f, -1216.9f, 135.8f, 5.1f, TEMPSUMMON_TIMED_OOC_DESPAWN, 20000); + m_creature->SummonCreature(NPC_BLACKROCK_AMBUSHER, -8009.5f, -1222.1f, 139.2f, 3.9f, TEMPSUMMON_TIMED_OOC_DESPAWN, 20000); + m_creature->SummonCreature(NPC_FLAMESCALE_DRAGONSPAWN, -8007.1f, -1219.4f, 140.1f, 3.9f, TEMPSUMMON_TIMED_OOC_DESPAWN, 20000); + break; + case 28: + m_creature->SummonCreature(NPC_SEARSCALE_DRAKE, -7897.8f, -1123.1f, 233.4f, 3.0f, TEMPSUMMON_TIMED_OOC_DESPAWN, 60000); + m_creature->SummonCreature(NPC_SEARSCALE_DRAKE, -7898.8f, -1125.1f, 193.9f, 3.0f, TEMPSUMMON_TIMED_OOC_DESPAWN, 60000); + m_creature->SummonCreature(NPC_SEARSCALE_DRAKE, -7895.6f, -1119.5f, 194.5f, 3.1f, TEMPSUMMON_TIMED_OOC_DESPAWN, 60000); + break; + case 30: + { + SetEscortPaused(true); + DoScriptText(SAY_THIRD_AMBUSH_START, m_creature); + + Player* pPlayer = GetPlayerForEscort(); + if (!pPlayer) + { + return; + } + + // Set all the dragons in combat + for (GuidList::const_iterator itr = m_lSearscaleGuidList.begin(); itr != m_lSearscaleGuidList.end(); ++itr) + { + if (Creature* pTemp = m_creature->GetMap()->GetCreature(*itr)) + { + pTemp->AI()->AttackStart(pPlayer); + } + } + break; + } + case 36: + DoScriptText(EMOTE_LAUGH, m_creature); + break; + case 45: + StartNextDialogueText(SAY_LAST_STAND); + SetEscortPaused(true); + + m_creature->SummonCreature(NPC_HIGH_EXECUTIONER_NUZARK, -7532.3f, -1029.4f, 258.0f, 2.7f, TEMPSUMMON_TIMED_DESPAWN, 40000); + m_creature->SummonCreature(NPC_SHADOW_OF_LEXLORT, -7532.8f, -1032.9f, 258.2f, 2.5f, TEMPSUMMON_TIMED_DESPAWN, 40000); + break; + } + } + + void JustDidDialogueStep(int32 iEntry) override + { + switch (iEntry) + { + case SAY_LEXLORT_1: + m_creature->SetStandState(UNIT_STAND_STATE_KNEEL); + break; + case SAY_LEXLORT_3: + // Note: this part isn't very clear. Should he just simply attack him, or charge him? + if (Creature* pNuzark = m_creature->GetMap()->GetCreature(m_nuzarkGuid)) + { + pNuzark->HandleEmote(EMOTE_ONESHOT_ATTACK2HTIGHT); + } + break; + case NPC_GRARK_LORKRUB: + // Fake death creature when the axe is lowered. This will allow us to finish the event + m_creature->InterruptNonMeleeSpells(true); + m_creature->SetHealth(1); + m_creature->StopMoving(); + m_creature->ClearComboPointHolders(); + m_creature->RemoveAllAurasOnDeath(); + m_creature->ModifyAuraState(AURA_STATE_HEALTHLESS_20_PERCENT, false); + m_creature->ModifyAuraState(AURA_STATE_HEALTHLESS_35_PERCENT, false); + m_creature->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE); + m_creature->ClearAllReactives(); + m_creature->GetMotionMaster()->Clear(); + m_creature->GetMotionMaster()->MoveIdle(); + m_creature->SetStandState(UNIT_STAND_STATE_DEAD); + break; + case SAY_LEXLORT_4: + // Finish the quest + if (Player* pPlayer = GetPlayerForEscort()) + { + pPlayer->GroupEventHappens(QUEST_ID_PRECARIOUS_PREDICAMENT, m_creature); + } + // Kill self + m_creature->DealDamage(m_creature, m_creature->GetHealth(), NULL, DIRECT_DAMAGE, SPELL_SCHOOL_MASK_NONE, NULL, false); + break; + } + } + + void JustSummoned(Creature* pSummoned) override + { + switch (pSummoned->GetEntry()) + { + case NPC_HIGH_EXECUTIONER_NUZARK: + m_nuzarkGuid = pSummoned->GetObjectGuid(); + break; + case NPC_SHADOW_OF_LEXLORT: + m_lexlortGuid = pSummoned->GetObjectGuid(); + break; + case NPC_SEARSCALE_DRAKE: + // If it's the flying drake allow him to move in circles + if (m_bIsFirstSearScale) + { + m_bIsFirstSearScale = false; + + pSummoned->SetLevitate(true); + // ToDo: this guy should fly in circles above the creature + } + m_lSearscaleGuidList.push_back(pSummoned->GetObjectGuid()); + break; + + default: + // The hostile mobs should attack the player only + if (Player* pPlayer = GetPlayerForEscort()) + { + pSummoned->AI()->AttackStart(pPlayer); + } + break; + } + } + + void SummonedCreatureJustDied(Creature* /*pSummoned*/) override + { + ++m_uiKilledCreatures; + + switch (m_uiKilledCreatures) + { + case 4: + DoScriptText(SAY_FIRST_AMBUSH_END, m_creature); + SetEscortPaused(false); + break; + case 8: + DoScriptText(SAY_SEC_AMBUSH_END, m_creature); + SetEscortPaused(false); + break; + case 11: + DoScriptText(SAY_THIRD_AMBUSH_END, m_creature); + SetEscortPaused(false); + break; + } + } + + Creature* GetSpeakerByEntry(uint32 uiEntry) override + { + switch (uiEntry) + { + case NPC_GRARK_LORKRUB: + return m_creature; + case NPC_HIGH_EXECUTIONER_NUZARK: + return m_creature->GetMap()->GetCreature(m_nuzarkGuid); + case NPC_SHADOW_OF_LEXLORT: + return m_creature->GetMap()->GetCreature(m_lexlortGuid); + + default: + return NULL; + } + } + + void UpdateEscortAI(const uint32 uiDiff) override + { + DialogueUpdate(uiDiff); + + if (!m_creature->SelectHostileTarget() || !m_creature->getVictim()) + { + return; + } + + DoMeleeAttackIfReady(); + } +}; + +CreatureAI* GetAI_npc_grark_lorkrub(Creature* pCreature) +{ + return new npc_grark_lorkrubAI(pCreature); +} + +bool QuestAccept_npc_grark_lorkrub(Player* pPlayer, Creature* pCreature, const Quest* pQuest) +{ + if (pQuest->GetQuestId() == QUEST_ID_PRECARIOUS_PREDICAMENT) + { + if (npc_grark_lorkrubAI* pEscortAI = dynamic_cast(pCreature->AI())) + { + pEscortAI->Start(false, pPlayer, pQuest); + } + + return true; + } + + return false; +} + +bool EffectDummyCreature_spell_capture_grark(Unit* /*pCaster*/, uint32 uiSpellId, SpellEffectIndex uiEffIndex, Creature* pCreatureTarget, ObjectGuid /*originalCasterGuid*/) +{ + // always check spellid and effectindex + if (uiSpellId == SPELL_CAPTURE_GRARK && uiEffIndex == EFFECT_INDEX_0) + { + // Note: this implementation needs additional research! There is a lot of guesswork involved in this! + if (pCreatureTarget->GetHealthPercent() > 25.0f) + { + return false; + } + + // The faction is guesswork - needs more research + DoScriptText(EMOTE_SUBMIT, pCreatureTarget); + pCreatureTarget->SetFactionTemporary(FACTION_FRIENDLY, TEMPFACTION_RESTORE_RESPAWN); + pCreatureTarget->AI()->EnterEvadeMode(); + + // always return true when we are handling this spell and effect + return true; + } + + return false; +} + +void AddSC_burning_steppes() +{ + Script* pNewScript; + + pNewScript = new Script; + pNewScript->Name = "npc_ragged_john"; + pNewScript->GetAI = &GetAI_npc_ragged_john; + pNewScript->pGossipHello = &GossipHello_npc_ragged_john; + pNewScript->pGossipSelect = &GossipSelect_npc_ragged_john; + pNewScript->RegisterSelf(); + + pNewScript = new Script; + pNewScript->Name = "npc_grark_lorkrub"; + pNewScript->GetAI = &GetAI_npc_grark_lorkrub; + pNewScript->pQuestAcceptNPC = &QuestAccept_npc_grark_lorkrub; + pNewScript->pEffectDummyNPC = &EffectDummyCreature_spell_capture_grark; + pNewScript->RegisterSelf(); +} diff --git a/src/modules/SD2/scripts/eastern_kingdoms/deadmines/boss_mr_smite.cpp b/src/modules/SD2/scripts/eastern_kingdoms/deadmines/boss_mr_smite.cpp new file mode 100644 index 000000000..534cc3422 --- /dev/null +++ b/src/modules/SD2/scripts/eastern_kingdoms/deadmines/boss_mr_smite.cpp @@ -0,0 +1,270 @@ +/* Copyright (C) 2006 - 2013 ScriptDev2 + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +/* ScriptData +SDName: Boss_Mr_Smite +SD%Complete: 100 +SDComment: +SDCategory: Deadmines +EndScriptData */ + +#include "precompiled.h" +#include "deadmines.h" + +enum +{ + SAY_PHASE_2 = -1036002, + SAY_PHASE_3 = -1036003, + + // EQUIP_ID_SWORD = 2179, // default equipment, not used in code + EQUIP_ID_AXE = 2183, + EQUIP_ID_HAMMER = 10756, + + SPELL_NIBLE_REFLEXES = 6433, // removed after phase 1 + SPELL_SMITE_SLAM = 6435, // only casted in phase 3 + SPELL_SMITE_STOMP = 6432, + SPELL_SMITE_HAMMER = 6436, // unclear, not casted in sniff + SPELL_THRASH = 12787, // only casted in phase 2; unclear, 3391 directly casted in sniff + + PHASE_1 = 1, + PHASE_2 = 2, + PHASE_3 = 3, + PHASE_EQUIP_NULL = 4, + PHASE_EQUIP_START = 5, + PHASE_EQUIP_PROCESS = 6, + PHASE_EQUIP_END = 7, +}; + +struct boss_mr_smiteAI : public ScriptedAI +{ + boss_mr_smiteAI(Creature* pCreature) : ScriptedAI(pCreature) { Reset(); } + + uint32 m_uiPhase; + uint32 m_uiEquipTimer; + uint32 m_uiSlamTimer; + + void Reset() override + { + m_uiPhase = PHASE_1; + m_uiEquipTimer = 0; + m_uiSlamTimer = 9000; + + DoCastSpellIfCan(m_creature, SPELL_NIBLE_REFLEXES, CAST_TRIGGERED); + + // must assume database has the default equipment set + SetEquipmentSlots(true); + } + + void AttackedBy(Unit* pAttacker) override + { + if (m_creature->getVictim()) + return; + + if (m_uiPhase > PHASE_3) + return; + + AttackStart(pAttacker); + } + + void AttackStart(Unit* pWho) override + { + if (m_uiPhase > PHASE_3) + return; + + if (m_creature->Attack(pWho, true)) + { + m_creature->AddThreat(pWho); + m_creature->SetInCombatWith(pWho); + pWho->SetInCombatWith(m_creature); + + m_creature->GetMotionMaster()->MoveChase(pWho); + } + } + + void MovementInform(uint32 uiMotionType, uint32 /*uiPointId*/) override + { + if (uiMotionType != POINT_MOTION_TYPE) + return; + + m_creature->SetSheath(SHEATH_STATE_UNARMED); + m_creature->SetStandState(UNIT_STAND_STATE_KNEEL); + + m_uiEquipTimer = 3000; + m_uiPhase = PHASE_EQUIP_PROCESS; + } + + void PhaseEquipStart() + { + ScriptedInstance* pInstance = (ScriptedInstance*)m_creature->GetInstanceData(); + + if (!pInstance) + return; + + GameObject* pChest = pInstance->GetSingleGameObjectFromStorage(GO_SMITE_CHEST); + + if (!pChest) + return; + + m_uiPhase = PHASE_EQUIP_NULL; + + float fX, fY, fZ; + pChest->GetContactPoint(m_creature, fX, fY, fZ, CONTACT_DISTANCE); + + m_creature->GetMotionMaster()->Clear(); + m_creature->SetFacingToObject(pChest); + m_creature->GetMotionMaster()->MovePoint(0, fX, fY, fZ); + } + + void PhaseEquipProcess() + { + if (m_creature->GetHealthPercent() < 33.0f) + { + // It's Hammer, go Hammer! + SetEquipmentSlots(false, EQUIP_ID_HAMMER, EQUIP_UNEQUIP); + DoCastSpellIfCan(m_creature, SPELL_SMITE_HAMMER); + } + else + SetEquipmentSlots(false, EQUIP_ID_AXE, EQUIP_ID_AXE); + + m_creature->SetStandState(UNIT_STAND_STATE_STAND); + m_uiPhase = PHASE_EQUIP_END; + m_uiEquipTimer = 1000; + } + + void PhaseEquipEnd() + { + // We don't have getVictim, so select from threat list + Unit* pVictim = m_creature->SelectAttackingTarget(ATTACKING_TARGET_TOPAGGRO, 0); + + if (!pVictim) + { + EnterEvadeMode(); + return; + } + + m_creature->SetSheath(SHEATH_STATE_MELEE); + + m_uiPhase = m_creature->GetHealthPercent() < 33.0f ? PHASE_3 : PHASE_2; + + if (m_uiPhase == PHASE_2) + DoCastSpellIfCan(m_creature, SPELL_THRASH, CAST_TRIGGERED); + + AttackStart(pVictim); + } + + void UpdateAI(const uint32 uiDiff) override + { + if (!m_creature->SelectHostileTarget() || !m_creature->getVictim()) + { + if (m_uiEquipTimer) + { + // decrease the cooldown in between equipment change phases + if (m_uiEquipTimer > uiDiff) + { + m_uiEquipTimer -= uiDiff; + return; + } + else + m_uiEquipTimer = 0; + } + + switch (m_uiPhase) + { + case PHASE_EQUIP_START: + PhaseEquipStart(); + break; + case PHASE_EQUIP_PROCESS: + PhaseEquipProcess(); + break; + case PHASE_EQUIP_END: + PhaseEquipEnd(); + break; + } + + return; + } + + // the normal combat phases + switch (m_uiPhase) + { + case PHASE_1: + { + if (m_creature->GetHealthPercent() < 66.0f) + { + if (DoCastSpellIfCan(m_creature, SPELL_SMITE_STOMP) == CAST_OK) + { + DoScriptText(m_creature->GetHealthPercent() < 33.0f ? SAY_PHASE_3 : SAY_PHASE_2, m_creature); + m_uiPhase = PHASE_EQUIP_START; + m_uiEquipTimer = 2500; + + // will clear getVictim (m_attacking) + m_creature->AttackStop(true); + m_creature->RemoveAurasDueToSpell(SPELL_NIBLE_REFLEXES); + } + return; + } + break; + } + case PHASE_2: + { + if (m_creature->GetHealthPercent() < 33.0f) + { + if (DoCastSpellIfCan(m_creature, SPELL_SMITE_STOMP) == CAST_OK) + { + DoScriptText(SAY_PHASE_3, m_creature); + m_uiPhase = PHASE_EQUIP_START; + m_uiEquipTimer = 2500; + + // will clear getVictim (m_attacking) + m_creature->AttackStop(true); + m_creature->RemoveAurasDueToSpell(SPELL_THRASH); + } + return; + } + break; + } + case PHASE_3: + { + if (m_uiSlamTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature->getVictim(), SPELL_SMITE_SLAM) == CAST_OK) + m_uiSlamTimer = 11000; + } + else + m_uiSlamTimer -= uiDiff; + + break; + } + } + + DoMeleeAttackIfReady(); + } +}; + +CreatureAI* GetAI_boss_mr_smite(Creature* pCreature) +{ + return new boss_mr_smiteAI(pCreature); +} + +void AddSC_boss_mr_smite() +{ + Script* pNewScript; + + pNewScript = new Script; + pNewScript->Name = "boss_mr_smite"; + pNewScript->GetAI = &GetAI_boss_mr_smite; + pNewScript->RegisterSelf(); +} diff --git a/src/modules/SD2/scripts/eastern_kingdoms/deadmines/deadmines.cpp b/src/modules/SD2/scripts/eastern_kingdoms/deadmines/deadmines.cpp new file mode 100644 index 000000000..30bfe3c10 --- /dev/null +++ b/src/modules/SD2/scripts/eastern_kingdoms/deadmines/deadmines.cpp @@ -0,0 +1,49 @@ +/* Copyright (C) 2006 - 2013 ScriptDev2 + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along 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: 100 +SDComment: Contains GO for Iron Clad door event +SDCategory: Deadmines +EndScriptData */ + +#include "precompiled.h" +#include "deadmines.h" + +bool GOUse_go_defias_cannon(Player* /*pPlayer*/, GameObject* pGo) +{ + ScriptedInstance* pInstance = (ScriptedInstance*)pGo->GetInstanceData(); + + if (!pInstance) + return false; + + if (pInstance->GetData(TYPE_IRON_CLAD_DOOR) == DONE) + return false; + + pInstance->SetData(TYPE_IRON_CLAD_DOOR, DONE); + return false; +} + +void AddSC_deadmines() +{ + Script* pNewScript; + + pNewScript = new Script; + pNewScript->Name = "go_defias_cannon"; + pNewScript->pGOUse = &GOUse_go_defias_cannon; + pNewScript->RegisterSelf(); +} diff --git a/src/modules/SD2/scripts/eastern_kingdoms/deadmines/deadmines.h b/src/modules/SD2/scripts/eastern_kingdoms/deadmines/deadmines.h new file mode 100644 index 000000000..c90a040f6 --- /dev/null +++ b/src/modules/SD2/scripts/eastern_kingdoms/deadmines/deadmines.h @@ -0,0 +1,70 @@ +/* Copyright (C) 2006 - 2013 ScriptDev2 + * This program is free software licensed under GPL version 2 + * Please see the included DOCS/LICENSE.TXT for more information */ + +#ifndef DEF_DEADMINES_H +#define DEF_DEADMINES_H + +enum +{ + MAX_ENCOUNTER = 4, + + TYPE_RHAHKZOR = 0, + TYPE_SNEED = 1, + TYPE_GILNID = 2, + TYPE_IRON_CLAD_DOOR = 3, + + INST_SAY_ALARM1 = -1036000, + INST_SAY_ALARM2 = -1036001, + + GO_FACTORY_DOOR = 13965, // rhahk'zor + GO_MAST_ROOM_DOOR = 16400, // sneed + GO_FOUNDRY_DOOR = 16399, // gilnid + GO_HEAVY_DOOR_1 = 17153, // to sneed + GO_HEAVY_DOOR_2 = 17154, // to gilnid + GO_IRON_CLAD_DOOR = 16397, + GO_DEFIAS_CANNON = 16398, + GO_SMITE_CHEST = 144111, // use to get correct location of mr.smites equipment changes + GO_MYSTERIOUS_CHEST = 180024, // used for quest 7938; spawns in the instance only if one of the players has the quest + + NPC_RHAHKZOR = 644, + NPC_SNEED = 643, + NPC_GILNID = 1763, + NPC_MR_SMITE = 646, + NPC_PIRATE = 657, + NPC_SQUALLSHAPER = 1732, + + QUEST_FORTUNE_AWAITS = 7938, +}; + +class instance_deadmines : public ScriptedInstance +{ + public: + instance_deadmines(Map* pMap); + + void Initialize() override; + + void OnPlayerEnter(Player* pPlayer) override; + + void OnCreatureCreate(Creature* pCreature) override; + void OnObjectCreate(GameObject* pGo) override; + + void OnCreatureDeath(Creature* pCreature) override; + + void SetData(uint32 uiType, uint32 uiData) override; + uint32 GetData(uint32 uiType) const override; + + const char* Save() const override { return m_strInstData.c_str(); } + void Load(const char* chrIn) override; + + void Update(uint32 uiDiff) override; + + private: + uint32 m_auiEncounter[MAX_ENCOUNTER]; + std::string m_strInstData; + + uint32 m_uiIronDoorTimer; + uint32 m_uiDoorStep; +}; + +#endif diff --git a/src/modules/SD2/scripts/eastern_kingdoms/deadmines/instance_deadmines.cpp b/src/modules/SD2/scripts/eastern_kingdoms/deadmines/instance_deadmines.cpp new file mode 100644 index 000000000..536ab772a --- /dev/null +++ b/src/modules/SD2/scripts/eastern_kingdoms/deadmines/instance_deadmines.cpp @@ -0,0 +1,242 @@ +/* Copyright (C) 2006 - 2013 ScriptDev2 + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +/* ScriptData +SDName: Instance_Deadmines +SD%Complete: 0 +SDComment: Placeholder +SDCategory: Deadmines +EndScriptData */ + +#include "precompiled.h" +#include "deadmines.h" + +instance_deadmines::instance_deadmines(Map* pMap) : ScriptedInstance(pMap), + m_uiIronDoorTimer(0), + m_uiDoorStep(0) +{ + Initialize(); +} + +void instance_deadmines::Initialize() +{ + memset(&m_auiEncounter, 0, sizeof(m_auiEncounter)); +} + +void instance_deadmines::OnPlayerEnter(Player* pPlayer) +{ + // Respawn the Mysterious chest if one of the players who enter the instance has the quest in his log + if (pPlayer->GetQuestStatus(QUEST_FORTUNE_AWAITS) == QUEST_STATUS_COMPLETE && + !pPlayer->GetQuestRewardStatus(QUEST_FORTUNE_AWAITS)) + DoRespawnGameObject(GO_MYSTERIOUS_CHEST, HOUR); +} + +void instance_deadmines::OnCreatureCreate(Creature* pCreature) +{ + if (pCreature->GetEntry() == NPC_MR_SMITE) + m_mNpcEntryGuidStore[NPC_MR_SMITE] = pCreature->GetObjectGuid(); +} + +void instance_deadmines::OnObjectCreate(GameObject* pGo) +{ + switch (pGo->GetEntry()) + { + case GO_FACTORY_DOOR: + if (m_auiEncounter[TYPE_RHAHKZOR] == DONE) + pGo->SetGoState(GO_STATE_ACTIVE); + + break; + case GO_MAST_ROOM_DOOR: + if (m_auiEncounter[TYPE_SNEED] == DONE) + pGo->SetGoState(GO_STATE_ACTIVE); + + break; + case GO_FOUNDRY_DOOR: + if (m_auiEncounter[TYPE_GILNID] == DONE) + pGo->SetGoState(GO_STATE_ACTIVE); + + break; + case GO_IRON_CLAD_DOOR: + if (m_auiEncounter[TYPE_IRON_CLAD_DOOR] == DONE) + pGo->SetGoState(GO_STATE_ACTIVE_ALTERNATIVE); + + break; + case GO_DEFIAS_CANNON: + case GO_SMITE_CHEST: + case GO_MYSTERIOUS_CHEST: + break; + + default: + return; + } + + m_mGoEntryGuidStore[pGo->GetEntry()] = pGo->GetObjectGuid(); +} + +void instance_deadmines::OnCreatureDeath(Creature* pCreature) +{ + switch (pCreature->GetEntry()) + { + case NPC_RHAHKZOR: SetData(TYPE_RHAHKZOR, DONE); break; + case NPC_SNEED: SetData(TYPE_SNEED, DONE); break; + case NPC_GILNID: SetData(TYPE_GILNID, DONE); break; + } +} + +void instance_deadmines::SetData(uint32 uiType, uint32 uiData) +{ + switch (uiType) + { + case TYPE_RHAHKZOR: + { + if (uiData == DONE) + DoUseDoorOrButton(GO_FACTORY_DOOR); + + m_auiEncounter[uiType] = uiData; + break; + } + case TYPE_SNEED: + { + if (uiData == DONE) + DoUseDoorOrButton(GO_MAST_ROOM_DOOR); + + m_auiEncounter[uiType] = uiData; + break; + } + case TYPE_GILNID: + { + if (uiData == DONE) + DoUseDoorOrButton(GO_FOUNDRY_DOOR); + + m_auiEncounter[uiType] = uiData; + break; + } + case TYPE_IRON_CLAD_DOOR: + { + // delayed door animation to sync with Defias Cannon animation + if (uiData == DONE) + m_uiIronDoorTimer = 500; + + m_auiEncounter[uiType] = uiData; + 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_strInstData = saveStream.str(); + + SaveToDB(); + OUT_SAVE_INST_DATA_COMPLETE; + } +} + +uint32 instance_deadmines::GetData(uint32 uiType) const +{ + if (uiType < MAX_ENCOUNTER) + return m_auiEncounter[uiType]; + + return 0; +} + +void instance_deadmines::Load(const char* chrIn) +{ + if (!chrIn) + { + 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 < MAX_ENCOUNTER; ++i) + { + if (m_auiEncounter[i] == IN_PROGRESS) + m_auiEncounter[i] = NOT_STARTED; + } + + OUT_LOAD_INST_DATA_COMPLETE; +} + +void instance_deadmines::Update(uint32 uiDiff) +{ + if (m_uiIronDoorTimer) + { + if (m_uiIronDoorTimer <= uiDiff) + { + switch (m_uiDoorStep) + { + case 0: + DoUseDoorOrButton(GO_IRON_CLAD_DOOR, 0, true); + + if (Creature* pMrSmite = GetSingleCreatureFromStorage(NPC_MR_SMITE)) + DoScriptText(INST_SAY_ALARM1, pMrSmite); + + if (GameObject* pDoor = GetSingleGameObjectFromStorage(GO_IRON_CLAD_DOOR)) + { + // should be static spawns, fetch the closest ones at the pier + if (Creature* pi1 = GetClosestCreatureWithEntry(pDoor, NPC_PIRATE, 40.0f)) + { + pi1->SetWalk(false); + pi1->GetMotionMaster()->MovePoint(0, pDoor->GetPositionX(), pDoor->GetPositionY(), pDoor->GetPositionZ()); + } + + if (Creature* pi2 = GetClosestCreatureWithEntry(pDoor, NPC_SQUALLSHAPER, 40.0f)) + { + pi2->SetWalk(false); + pi2->GetMotionMaster()->MovePoint(0, pDoor->GetPositionX(), pDoor->GetPositionY(), pDoor->GetPositionZ()); + } + } + + ++m_uiDoorStep; + m_uiIronDoorTimer = 15000; + break; + case 1: + if (Creature* pMrSmite = GetSingleCreatureFromStorage(NPC_MR_SMITE)) + DoScriptText(INST_SAY_ALARM2, pMrSmite); + + m_uiDoorStep = 0; + m_uiIronDoorTimer = 0; + break; + } + } + else + m_uiIronDoorTimer -= uiDiff; + } +} + +InstanceData* GetInstanceData_instance_deadmines(Map* pMap) +{ + return new instance_deadmines(pMap); +} + +void AddSC_instance_deadmines() +{ + Script* pNewScript; + + pNewScript = new Script; + pNewScript->Name = "instance_deadmines"; + pNewScript->GetInstanceData = &GetInstanceData_instance_deadmines; + pNewScript->RegisterSelf(); +} diff --git a/src/modules/SD2/scripts/eastern_kingdoms/dun_morogh.cpp b/src/modules/SD2/scripts/eastern_kingdoms/dun_morogh.cpp new file mode 100644 index 000000000..2a69b3bfb --- /dev/null +++ b/src/modules/SD2/scripts/eastern_kingdoms/dun_morogh.cpp @@ -0,0 +1,44 @@ +/** + * ScriptDev2 is an extension for mangos providing enhanced features for + * area triggers, creatures, game objects, instances, items, and spells beyond + * the default database scripting in mangos. + * + * Copyright (C) 2006-2013 ScriptDev2 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * World of Warcraft, and all World of Warcraft or Warcraft art, images, + * and lore are copyrighted by Blizzard Entertainment, Inc. + */ + +/** + * ScriptData + * SDName: Dun_Morogh + * SD%Complete: 0 + * SDComment: Placeholder + * SDCategory: Dun Morogh + * EndScriptData + */ + +/** + * ContentData + * EndContentData + */ + +#include "precompiled.h" + +void AddSC_dun_morogh() +{ +} diff --git a/src/modules/SD2/scripts/eastern_kingdoms/eastern_plaguelands.cpp b/src/modules/SD2/scripts/eastern_kingdoms/eastern_plaguelands.cpp new file mode 100644 index 000000000..b9c77a489 --- /dev/null +++ b/src/modules/SD2/scripts/eastern_kingdoms/eastern_plaguelands.cpp @@ -0,0 +1,364 @@ +/** + * ScriptDev2 is an extension for mangos providing enhanced features for + * area triggers, creatures, game objects, instances, items, and spells beyond + * the default database scripting in mangos. + * + * Copyright (C) 2006-2013 ScriptDev2 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * World of Warcraft, and all World of Warcraft or Warcraft art, images, + * and lore are copyrighted by Blizzard Entertainment, Inc. + */ + +/** + * ScriptData + * SDName: Eastern_Plaguelands + * SD%Complete: 100 + * SDComment: Quest support: 7622. + * SDCategory: Eastern Plaguelands + * EndScriptData + */ + +/** + * ContentData + * npc_eris_havenfire + * EndContentData + */ + +#include "precompiled.h" + +/*###### +## npc_eris_havenfire +######*/ + +enum +{ + SAY_PHASE_HEAL = -1000815, + SAY_EVENT_END = -1000816, + SAY_EVENT_FAIL_1 = -1000817, + SAY_EVENT_FAIL_2 = -1000818, + SAY_PEASANT_APPEAR_1 = -1000819, + SAY_PEASANT_APPEAR_2 = -1000820, + SAY_PEASANT_APPEAR_3 = -1000821, + + // SPELL_DEATHS_DOOR = 23127, // damage spells cast on the peasants + // SPELL_SEETHING_PLAGUE = 23072, + SPELL_ENTER_THE_LIGHT_DND = 23107, + SPELL_BLESSING_OF_NORDRASSIL = 23108, + + NPC_INJURED_PEASANT = 14484, + NPC_PLAGUED_PEASANT = 14485, + NPC_SCOURGE_ARCHER = 14489, + NPC_SCOURGE_FOOTSOLDIER = 14486, + NPC_THE_CLEANER = 14503, // can be summoned if the priest has more players in the party for help. requires further research + + QUEST_BALANCE_OF_LIGHT_AND_SHADOW = 7622, + + MAX_PEASANTS = 12, + MAX_ARCHERS = 8, +}; + +static const float aArcherSpawn[8][4] = +{ + {3327.42f, -3021.11f, 170.57f, 6.01f}, + {3335.4f, -3054.3f, 173.63f, 0.49f}, + {3351.3f, -3079.08f, 178.67f, 1.15f}, + {3358.93f, -3076.1f, 174.87f, 1.57f}, + {3371.58f, -3069.24f, 175.20f, 1.99f}, + {3369.46f, -3023.11f, 171.83f, 3.69f}, + {3383.25f, -3057.01f, 181.53f, 2.21f}, + {3380.03f, -3062.73f, 181.90f, 2.31f}, +}; + +static const float aPeasantSpawnLoc[3] = {3360.12f, -3047.79f, 165.26f}; +static const float aPeasantMoveLoc[3] = {3335.0f, -2994.04f, 161.14f}; + +static const int32 aPeasantSpawnYells[3] = {SAY_PEASANT_APPEAR_1, SAY_PEASANT_APPEAR_2, SAY_PEASANT_APPEAR_3}; + +struct npc_eris_havenfireAI : public ScriptedAI +{ + npc_eris_havenfireAI(Creature* pCreature) : ScriptedAI(pCreature) { Reset(); } + + uint32 m_uiEventTimer; + uint32 m_uiSadEndTimer; + uint8 m_uiPhase; + uint8 m_uiCurrentWave; + uint8 m_uiKillCounter; + uint8 m_uiSaveCounter; + + ObjectGuid m_playerGuid; + GuidList m_lSummonedGuidList; + + void Reset() override + { + m_uiEventTimer = 0; + m_uiSadEndTimer = 0; + m_uiPhase = 0; + m_uiCurrentWave = 0; + m_uiKillCounter = 0; + m_uiSaveCounter = 0; + + m_playerGuid.Clear(); + m_lSummonedGuidList.clear(); + m_creature->SetFlag(UNIT_NPC_FLAGS, UNIT_NPC_FLAG_QUESTGIVER); + } + + void JustSummoned(Creature* pSummoned) override + { + switch (pSummoned->GetEntry()) + { + case NPC_INJURED_PEASANT: + case NPC_PLAGUED_PEASANT: + float fX, fY, fZ; + pSummoned->GetRandomPoint(aPeasantMoveLoc[0], aPeasantMoveLoc[1], aPeasantMoveLoc[2], 10.0f, fX, fY, fZ); + pSummoned->GetMotionMaster()->MovePoint(1, fX, fY, fZ); + m_lSummonedGuidList.push_back(pSummoned->GetObjectGuid()); + break; + case NPC_SCOURGE_FOOTSOLDIER: + case NPC_THE_CLEANER: + if (Player* pPlayer = m_creature->GetMap()->GetPlayer(m_playerGuid)) + { + pSummoned->AI()->AttackStart(pPlayer); + } + break; + case NPC_SCOURGE_ARCHER: + // ToDo: make these ones attack the peasants + break; + } + + m_lSummonedGuidList.push_back(pSummoned->GetObjectGuid()); + } + + void SummonedMovementInform(Creature* pSummoned, uint32 uiMotionType, uint32 uiPointId) override + { + if (uiMotionType != POINT_MOTION_TYPE || !uiPointId) + { + return; + } + + if (uiPointId) + { + ++m_uiSaveCounter; + pSummoned->GetMotionMaster()->Clear(); + + pSummoned->RemoveAllAuras(); + pSummoned->CastSpell(pSummoned, SPELL_ENTER_THE_LIGHT_DND, false); + pSummoned->ForcedDespawn(10000); + + // Event ended + if (m_uiSaveCounter >= 50 && m_uiCurrentWave == 5) + { + DoBalanceEventEnd(); + } + // Phase ended + else if (m_uiSaveCounter + m_uiKillCounter == m_uiCurrentWave * MAX_PEASANTS) + { + DoHandlePhaseEnd(); + } + } + } + + void SummonedCreatureJustDied(Creature* pSummoned) override + { + if (pSummoned->GetEntry() == NPC_INJURED_PEASANT || pSummoned->GetEntry() == NPC_PLAGUED_PEASANT) + { + ++m_uiKillCounter; + + // If more than 15 peasants have died, then fail the quest + if (m_uiKillCounter == MAX_PEASANTS) + { + if (Player* pPlayer = m_creature->GetMap()->GetPlayer(m_playerGuid)) + { + pPlayer->FailQuest(QUEST_BALANCE_OF_LIGHT_AND_SHADOW); + } + + DoScriptText(SAY_EVENT_FAIL_1, m_creature); + m_uiSadEndTimer = 4000; + } + else if (m_uiSaveCounter + m_uiKillCounter == m_uiCurrentWave * MAX_PEASANTS) + { + DoHandlePhaseEnd(); + } + } + } + + void DoSummonWave(uint32 uiSummonId = 0) + { + float fX, fY, fZ; + + if (!uiSummonId) + { + for (uint8 i = 0; i < MAX_PEASANTS; ++i) + { + uint32 uiSummonEntry = roll_chance_i(70) ? NPC_INJURED_PEASANT : NPC_PLAGUED_PEASANT; + m_creature->GetRandomPoint(aPeasantSpawnLoc[0], aPeasantSpawnLoc[1], aPeasantSpawnLoc[2], 10.0f, fX, fY, fZ); + if (Creature* pTemp = m_creature->SummonCreature(uiSummonEntry, fX, fY, fZ, 0, TEMPSUMMON_DEAD_DESPAWN, 0)) + { + // Only the first mob needs to yell + if (!i) + { + DoScriptText(aPeasantSpawnYells[urand(0, 2)], pTemp); + } + } + } + + ++m_uiCurrentWave; + } + else if (uiSummonId == NPC_SCOURGE_FOOTSOLDIER) + { + uint8 uiRand = urand(2, 3); + for (uint8 i = 0; i < uiRand; ++i) + { + m_creature->GetRandomPoint(aPeasantSpawnLoc[0], aPeasantSpawnLoc[1], aPeasantSpawnLoc[2], 15.0f, fX, fY, fZ); + m_creature->SummonCreature(NPC_SCOURGE_FOOTSOLDIER, fX, fY, fZ, 0, TEMPSUMMON_DEAD_DESPAWN, 0); + } + } + else if (uiSummonId == NPC_SCOURGE_ARCHER) + { + for (uint8 i = 0; i < MAX_ARCHERS; ++i) + { + m_creature->SummonCreature(NPC_SCOURGE_ARCHER, aArcherSpawn[i][0], aArcherSpawn[i][1], aArcherSpawn[i][2], aArcherSpawn[i][3], TEMPSUMMON_DEAD_DESPAWN, 0); + } + } + } + + void DoHandlePhaseEnd() + { + if (Player* pPlayer = m_creature->GetMap()->GetPlayer(m_playerGuid)) + { + pPlayer->CastSpell(pPlayer, SPELL_BLESSING_OF_NORDRASSIL, true); + } + + DoScriptText(SAY_PHASE_HEAL, m_creature); + + // Send next wave + if (m_uiCurrentWave < 5) + { + DoSummonWave(); + } + } + + void DoStartBalanceEvent(Player* pPlayer) + { + m_creature->RemoveFlag(UNIT_NPC_FLAGS, UNIT_NPC_FLAG_QUESTGIVER); + m_playerGuid = pPlayer->GetObjectGuid(); + m_uiEventTimer = 5000; + } + + void DoBalanceEventEnd() + { + if (Player* pPlayer = m_creature->GetMap()->GetPlayer(m_playerGuid)) + { + pPlayer->AreaExploredOrEventHappens(QUEST_BALANCE_OF_LIGHT_AND_SHADOW); + } + + DoScriptText(SAY_EVENT_END, m_creature); + DoDespawnSummons(true); + EnterEvadeMode(); + } + + void DoDespawnSummons(bool bIsEventEnd = false) + { + for (GuidList::const_iterator itr = m_lSummonedGuidList.begin(); itr != m_lSummonedGuidList.end(); ++itr) + { + if (Creature* pTemp = m_creature->GetMap()->GetCreature(*itr)) + { + if (bIsEventEnd && (pTemp->GetEntry() == NPC_INJURED_PEASANT || pTemp->GetEntry() == NPC_PLAGUED_PEASANT)) + { + continue; + } + + pTemp->ForcedDespawn(); + } + } + } + + void UpdateAI(const uint32 uiDiff) override + { + if (m_uiEventTimer) + { + if (m_uiEventTimer <= uiDiff) + { + switch (m_uiPhase) + { + case 0: + DoSummonWave(NPC_SCOURGE_ARCHER); + m_uiEventTimer = 5000; + break; + case 1: + DoSummonWave(); + m_uiEventTimer = urand(60000, 80000); + break; + default: + // The summoning timer of the soldiers isn't very clear + DoSummonWave(NPC_SCOURGE_FOOTSOLDIER); + m_uiEventTimer = urand(5000, 30000); + break; + } + ++m_uiPhase; + } + else + { + m_uiEventTimer -= uiDiff; + } + } + + // Handle event end in case of fail + if (m_uiSadEndTimer) + { + if (m_uiSadEndTimer <= uiDiff) + { + DoScriptText(SAY_EVENT_FAIL_2, m_creature); + m_creature->ForcedDespawn(5000); + DoDespawnSummons(); + m_uiSadEndTimer = 0; + } + else + { + m_uiSadEndTimer -= uiDiff; + } + } + } +}; + +CreatureAI* GetAI_npc_eris_havenfire(Creature* pCreature) +{ + return new npc_eris_havenfireAI(pCreature); +} + +bool QuestAccept_npc_eris_havenfire(Player* pPlayer, Creature* pCreature, const Quest* pQuest) +{ + if (pQuest->GetQuestId() == QUEST_BALANCE_OF_LIGHT_AND_SHADOW) + { + if (npc_eris_havenfireAI* pErisAI = dynamic_cast(pCreature->AI())) + { + pErisAI->DoStartBalanceEvent(pPlayer); + } + } + + return true; +} + +void AddSC_eastern_plaguelands() +{ + Script* pNewScript; + + pNewScript = new Script; + pNewScript->Name = "npc_eris_havenfire"; + pNewScript->GetAI = &GetAI_npc_eris_havenfire; + pNewScript->pQuestAcceptNPC = &QuestAccept_npc_eris_havenfire; + pNewScript->RegisterSelf(); +} diff --git a/src/modules/SD2/scripts/eastern_kingdoms/elwynn_forest.cpp b/src/modules/SD2/scripts/eastern_kingdoms/elwynn_forest.cpp new file mode 100644 index 000000000..8a80abef9 --- /dev/null +++ b/src/modules/SD2/scripts/eastern_kingdoms/elwynn_forest.cpp @@ -0,0 +1,231 @@ +/** + * ScriptDev2 is an extension for mangos providing enhanced features for + * area triggers, creatures, game objects, instances, items, and spells beyond + * the default database scripting in mangos. + * + * Copyright (C) 2006-2013 ScriptDev2 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * World of Warcraft, and all World of Warcraft or Warcraft art, images, + * and lore are copyrighted by Blizzard Entertainment, Inc. + */ + +/* ScriptData +SDName: Elwynn_Forest +SD%Complete: 100 +SDComment: Hogger event +SDCategory: Elwynn Forest +EndScriptData */ + +/* ContentData +npc_hogger +EndContentData */ + +#include "precompiled.h" + +/*###### +## npc_hogger +######*/ + +enum +{ + SAY_CALL_HELP = -1004000, + WHISPER_EATING = -1004001, // note: this type may need to be updated in the future + SAY_HOGGER_BEATEN = -1004002, + + SPELL_EATING = 87351, + SPELL_SUMMON_MINIONS = 87366, + SPELL_VICIOUS_SLICE = 87337, + SPELL_BLOODY_STRIKE = 87359, + + NPC_MINION_OF_HOOGER = 46932, + NPC_GENERAL_BUNNY = 45979, +}; + +struct npc_hoggerAI : public ScriptedAI +{ + npc_hoggerAI(Creature* pCreature) : ScriptedAI(pCreature) { Reset(); } + + uint32 m_uiViciousSliceTimer; + uint32 m_uiEatingEndTimer; + uint32 m_uiBloodyStrikeTimer; + bool m_bHasSpawnedMinions; + bool m_bEncounterFinished; + + ObjectGuid m_bunnyGuid; + + void Reset() override + { + m_uiViciousSliceTimer = urand(15000, 20000); + m_uiEatingEndTimer = 0; + m_uiBloodyStrikeTimer = 1000; + m_bHasSpawnedMinions = false; + m_bEncounterFinished = false; + } + + void EnterEvadeMode() override + { + m_creature->RemoveAllAurasOnEvade(); + m_creature->DeleteThreatList(); + m_creature->CombatStop(true); + + // start epilogue event + if (m_bEncounterFinished) + { + m_creature->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_OOC_NOT_ATTACKABLE); + m_creature->GetMotionMaster()->MoveWaypoint(); + } + else + { + if (m_creature->IsAlive()) + m_creature->GetMotionMaster()->MoveTargetedHome(); + + Reset(); + } + + m_creature->SetLootRecipient(NULL); + } + + void AttackStart(Unit* pWho) override + { + // special case for epilogue + if (m_creature->HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_OOC_NOT_ATTACKABLE)) + return; + + ScriptedAI::AttackStart(pWho); + } + + void MoveInLineOfSight(Unit* pWho) override + { + // special case for epilogue + if (m_creature->HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_OOC_NOT_ATTACKABLE)) + return; + + ScriptedAI::MoveInLineOfSight(pWho); + } + + void MovementInform(uint32 uiMoveType, uint32 uiPointId) override + { + if (uiMoveType != POINT_MOTION_TYPE || !uiPointId) + return; + + DoCastSpellIfCan(m_creature, SPELL_EATING); + m_uiEatingEndTimer = 12000; + } + + void JustSummoned(Creature* pSummoned) override + { + if (pSummoned->GetEntry() == NPC_MINION_OF_HOOGER) + pSummoned->AI()->AttackStart(m_creature->getVictim()); + } + + void DamageTaken(Unit* pDealer, uint32& uiDamage) override + { + // evade and start epilogue when near death + if (uiDamage >= m_creature->GetHealth()) + { + uiDamage = 0; + + if (!m_bEncounterFinished) + { + m_bEncounterFinished = true; + m_creature->AI()->EnterEvadeMode(); + DoScriptText(SAY_HOGGER_BEATEN, m_creature); + + if (pDealer->GetTypeId() == TYPEID_PLAYER) + ((Player*)pDealer)->KilledMonsterCredit(m_creature->GetEntry()); + } + } + + // start eating at 50% hp + if (!m_bHasSpawnedMinions && !m_bEncounterFinished && m_creature->GetHealthPercent() < 50.0f) + { + if (DoCastSpellIfCan(m_creature, SPELL_SUMMON_MINIONS) == CAST_OK) + { + DoScriptText(SAY_CALL_HELP, m_creature); + DoScriptText(WHISPER_EATING, m_creature, pDealer); + + SetCombatMovement(false); + if (Creature* pBunny = GetClosestCreatureWithEntry(m_creature, NPC_GENERAL_BUNNY, 25.0f)) + { + float fX, fY, fZ; + pBunny->GetContactPoint(m_creature, fX, fY, fZ); + m_creature->GetMotionMaster()->MovePoint(1, fX, fY, fZ); + m_bunnyGuid = pBunny->GetObjectGuid(); + } + + m_bHasSpawnedMinions = true; + } + } + } + + void UpdateAI(const uint32 uiDiff) override + { + if (!m_creature->SelectHostileTarget() || !m_creature->getVictim()) + return; + + // resume combat after eat + if (m_uiEatingEndTimer) + { + if (m_uiEatingEndTimer <= uiDiff) + { + SetCombatMovement(true); + DoStartMovement(m_creature->getVictim()); + m_uiEatingEndTimer = 0; + } + else + m_uiEatingEndTimer -= uiDiff; + + if (m_uiBloodyStrikeTimer < uiDiff) + { + if (Creature* pTarget = m_creature->GetMap()->GetCreature(m_bunnyGuid)) + { + if (DoCastSpellIfCan(pTarget, SPELL_BLOODY_STRIKE) == CAST_OK) + m_uiBloodyStrikeTimer = 2000; + } + } + else + m_uiBloodyStrikeTimer -= uiDiff; + + return; + } + + if (m_uiViciousSliceTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature->getVictim(), SPELL_VICIOUS_SLICE) == CAST_OK) + m_uiViciousSliceTimer = urand(15000, 20000); + } + else + m_uiViciousSliceTimer -= uiDiff; + + DoMeleeAttackIfReady(); + } +}; + +CreatureAI* GetAI_npc_hogger(Creature* pCreature) +{ + return new npc_hoggerAI(pCreature); +} + +void AddSC_elwynn_forest() +{ + Script* pNewScript; + + pNewScript = new Script; + pNewScript->Name = "npc_hogger"; + pNewScript->GetAI = &GetAI_npc_hogger; + pNewScript->RegisterSelf(); +} diff --git a/src/modules/SD2/scripts/eastern_kingdoms/eversong_woods.cpp b/src/modules/SD2/scripts/eastern_kingdoms/eversong_woods.cpp new file mode 100644 index 000000000..e42a94549 --- /dev/null +++ b/src/modules/SD2/scripts/eastern_kingdoms/eversong_woods.cpp @@ -0,0 +1,579 @@ +/** + * ScriptDev2 is an extension for mangos providing enhanced features for + * area triggers, creatures, game objects, instances, items, and spells beyond + * the default database scripting in mangos. + * + * Copyright (C) 2006-2013 ScriptDev2 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * World of Warcraft, and all World of Warcraft or Warcraft art, images, + * and lore are copyrighted by Blizzard Entertainment, Inc. + */ + +/* ScriptData +SDName: Eversong_Woods +SD%Complete: 100 +SDComment: Quest support: 8483, 8488, 8490, 9686 +SDCategory: Eversong Woods +EndScriptData */ + +/* ContentData +npc_kelerun_bloodmourn +go_harbinger_second_trial +npc_prospector_anvilward +npc_apprentice_mirveda +npc_infused_crystal +EndContentData */ + +#include "precompiled.h" +#include "escort_ai.h" +#include "TemporarySummon.h" + +/*###### +## npc_kelerun_bloodmourn +######*/ + +enum +{ + NPC_KELERUN = 17807, + NPC_BLOODWRATH = 17809, + NPC_LIGHTREND = 17810, + NPC_SWIFTBLADE = 17811, + NPC_SUNSTRIKER = 17812, + + GO_SECOND_TRIAL = 182052, + QUEST_SECOND_TRIAL = 9686, + MAX_CHALLENGER = 4 +}; + +const uint32 uiChallengerId[4] = {NPC_BLOODWRATH, NPC_LIGHTREND, NPC_SWIFTBLADE, NPC_SUNSTRIKER}; + +const int32 uiSayId[4] = +{ + -1000319, + -1000320, + -1000321, + -1000322 +}; + +float fChallengerLoc[4][4] = +{ + {10110.667f, -6628.059f, 4.100f, 2.708f}, + {10093.919f, -6634.340f, 4.098f, 1.106f}, + {10087.565f, -6617.282f, 4.098f, 5.887f}, + {10104.807f, -6611.145f, 4.101f, 4.265f} +}; + +struct npc_kelerun_bloodmournAI : public ScriptedAI +{ + npc_kelerun_bloodmournAI(Creature* pCreature) : ScriptedAI(pCreature) + { + m_uiNpcFlags = pCreature->GetUInt32Value(UNIT_NPC_FLAGS); + Reset(); + } + + uint32 m_uiNpcFlags; + ObjectGuid m_playerGuid; + ObjectGuid m_aChallengerGuids[MAX_CHALLENGER]; + + uint8 m_uiChallengerCount; + + uint32 m_uiTimeOutTimer; + uint32 m_uiCheckAliveStateTimer; + uint32 m_uiEngageTimer; + + bool m_bIsEventInProgress; + + void Reset() override + { + m_creature->SetUInt32Value(UNIT_NPC_FLAGS, m_uiNpcFlags); + + m_playerGuid.Clear(); + + m_uiChallengerCount = 0; + + m_uiTimeOutTimer = 60000; + m_uiCheckAliveStateTimer = 2500; + m_uiEngageTimer = 0; + + m_bIsEventInProgress = false; + for (uint8 i = 0; i < MAX_CHALLENGER; ++i) // Despawn challengers + { + if (Creature* pChallenger = m_creature->GetMap()->GetCreature(m_aChallengerGuids[i])) + { pChallenger->ForcedDespawn(1000); } + m_aChallengerGuids[i].Clear(); + } + } + + void StartEvent() + { + m_creature->SetUInt32Value(UNIT_NPC_FLAGS, UNIT_NPC_FLAG_NONE); + m_bIsEventInProgress = true; + } + + bool CanProgressEvent(Player* pPlayer) + { + if (m_bIsEventInProgress) + { + m_playerGuid = pPlayer->GetObjectGuid(); + DoSpawnChallengers(); + m_uiEngageTimer = 15000; + + return true; + } + + return false; + } + + void DoSpawnChallengers() + { + for (uint8 i = 0; i < MAX_CHALLENGER; ++i) + { + if (Creature* pCreature = m_creature->SummonCreature(uiChallengerId[i], + fChallengerLoc[i][0], fChallengerLoc[i][1], + fChallengerLoc[i][2], fChallengerLoc[i][3], + TEMPSUMMON_TIMED_OOC_OR_DEAD_DESPAWN, 600000)) + { + m_aChallengerGuids[i] = pCreature->GetObjectGuid(); + pCreature->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE); + } + } + } + + void UpdateAI(const uint32 uiDiff) override + { + if (m_bIsEventInProgress) + { + if (m_uiTimeOutTimer) + { + if (m_uiTimeOutTimer <= uiDiff) + { + if (!m_playerGuid) // player are expected to use GO within a minute, if not, event will fail. + { + Reset(); + return; + } + m_uiTimeOutTimer = 0; + } + else + { m_uiTimeOutTimer -= uiDiff; } + } + + if (m_uiCheckAliveStateTimer < uiDiff) + { + Creature* pChallenger = m_creature->GetMap()->GetCreature(m_aChallengerGuids[m_uiChallengerCount]); + if (pChallenger && !pChallenger->IsAlive()) + { + Player* pPlayer = m_creature->GetMap()->GetPlayer(m_playerGuid); + if (!pPlayer || !pPlayer->IsAlive()) + { + Reset(); + return; + } + + ++m_uiChallengerCount; + + // count starts at 0 + if (m_uiChallengerCount == MAX_CHALLENGER) + { + pPlayer->GroupEventHappens(QUEST_SECOND_TRIAL, m_creature); + Reset(); + return; + } + else + { m_uiEngageTimer = 15000; } + } + m_uiCheckAliveStateTimer = 2500; + } + else + { m_uiCheckAliveStateTimer -= uiDiff; } + + if (m_uiEngageTimer) + { + if (m_uiEngageTimer <= uiDiff) + { + Player* pPlayer = m_creature->GetMap()->GetPlayer(m_playerGuid); + if (!pPlayer || !pPlayer->IsAlive()) + { + Reset(); + return; + } + + if (Creature* pCreature = m_creature->GetMap()->GetCreature(m_aChallengerGuids[m_uiChallengerCount])) + { + DoScriptText(uiSayId[m_uiChallengerCount], m_creature, pPlayer); + pCreature->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE); + pCreature->AI()->AttackStart(pPlayer); + } + + m_uiEngageTimer = 0; + } + else + { m_uiEngageTimer -= uiDiff; } + } + } + } +}; + +CreatureAI* GetAI_npc_kelerun_bloodmourn(Creature* pCreature) +{ + return new npc_kelerun_bloodmournAI(pCreature); +} + +// easiest way is to expect database to respawn GO at quest accept (quest_start_script) +bool QuestAccept_npc_kelerun_bloodmourn(Player* /*pPlayer*/, Creature* pCreature, const Quest* pQuest) +{ + if (pQuest->GetQuestId() == QUEST_SECOND_TRIAL) + { + if (npc_kelerun_bloodmournAI* pKelrunAI = dynamic_cast(pCreature->AI())) + { pKelrunAI->StartEvent(); } + } + + return true; +} + +bool GOUse_go_harbinger_second_trial(Player* pPlayer, GameObject* pGO) +{ + if (pGO->GetGoType() == GAMEOBJECT_TYPE_GOOBER) + { + if (Creature* pCreature = GetClosestCreatureWithEntry(pGO, NPC_KELERUN, 30.0f)) + { + if (npc_kelerun_bloodmournAI* pKelrunAI = dynamic_cast(pCreature->AI())) + { pKelrunAI->CanProgressEvent(pPlayer); } + } + } + + return false; +} + +/*###### +## npc_prospector_anvilward +######*/ +enum +{ + SAY_ANVIL1 = -1000209, + SAY_ANVIL2 = -1000210, + + GOSSIP_ITEM_MOMENT = -3000108, + GOSSIP_ITEM_SHOW = -3000110, + + GOSSIP_TEXT_ID_MOMENT = 8239, + GOSSIP_TEXT_ID_SHOW = 8240, + + FACTION_HOSTILE = 24, + + QUEST_THE_DWARVEN_SPY = 8483 +}; + +struct npc_prospector_anvilwardAI : public npc_escortAI +{ + // CreatureAI functions + npc_prospector_anvilwardAI(Creature* pCreature) : npc_escortAI(pCreature) {Reset();} + + void Reset() override { } + + // Pure Virtual Functions + void WaypointReached(uint32 uiPointId) override + { + Player* pPlayer = GetPlayerForEscort(); + + if (!pPlayer) + { return; } + + switch (uiPointId) + { + case 0: + DoScriptText(SAY_ANVIL1, m_creature, pPlayer); + break; + case 5: + DoScriptText(SAY_ANVIL2, m_creature, pPlayer); + break; + case 6: + m_creature->SetFactionTemporary(FACTION_HOSTILE, TEMPFACTION_RESTORE_REACH_HOME | TEMPFACTION_RESTORE_RESPAWN); + AttackStart(pPlayer); + break; + } + } +}; + +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_ID(GOSSIP_ICON_CHAT, GOSSIP_ITEM_MOMENT, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 1); } + + pPlayer->SEND_GOSSIP_MENU(GOSSIP_TEXT_ID_MOMENT, pCreature->GetObjectGuid()); + return true; +} + +bool GossipSelect_npc_prospector_anvilward(Player* pPlayer, Creature* pCreature, uint32 /*uiSender*/, uint32 uiAction) +{ + switch (uiAction) + { + case GOSSIP_ACTION_INFO_DEF+1: + pPlayer->ADD_GOSSIP_ITEM_ID(GOSSIP_ICON_CHAT, GOSSIP_ITEM_SHOW, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 2); + pPlayer->SEND_GOSSIP_MENU(GOSSIP_TEXT_ID_SHOW, pCreature->GetObjectGuid()); + break; + case GOSSIP_ACTION_INFO_DEF+2: + pPlayer->CLOSE_GOSSIP_MENU(); + + if (npc_prospector_anvilwardAI* pEscortAI = dynamic_cast(pCreature->AI())) + { pEscortAI->Start(false, pPlayer); } + + break; + } + return true; +} + +/*###### +## npc_apprentice_mirveda +######*/ + +enum +{ + QUEST_UNEXPECTED_RESULT = 8488, + + SPELL_FIREBALL = 20811, + + NPC_GHARSUL = 15958, + NPC_ANGERSHADE = 15656 +}; + +struct npc_apprentice_mirvedaAI : public ScriptedAI +{ + npc_apprentice_mirvedaAI(Creature* pCreature) : ScriptedAI(pCreature) { Reset(); } + + uint8 m_uiMobCount; + uint32 m_uiFireballTimer; + ObjectGuid m_playerGuid; + + void Reset() override + { + m_uiMobCount = 0; + m_playerGuid.Clear(); + m_uiFireballTimer = 0; + } + + void JustDied(Unit* /*pKiller*/) override + { + Player* pPlayer = m_creature->GetMap()->GetPlayer(m_playerGuid); + + if (pPlayer && pPlayer->GetQuestStatus(QUEST_UNEXPECTED_RESULT) == QUEST_STATUS_INCOMPLETE) + { pPlayer->SendQuestFailed(QUEST_UNEXPECTED_RESULT); } + } + + void JustSummoned(Creature* pSummoned) override + { + pSummoned->AI()->AttackStart(m_creature); + ++m_uiMobCount; + } + + void SummonedCreatureJustDied(Creature* /*pKilled*/) override + { + --m_uiMobCount; + + if (m_uiMobCount) + { return; } + + Player* pPlayer = m_creature->GetMap()->GetPlayer(m_playerGuid); + + if (pPlayer && pPlayer->GetQuestStatus(QUEST_UNEXPECTED_RESULT) == QUEST_STATUS_INCOMPLETE) + { pPlayer->GroupEventHappens(QUEST_UNEXPECTED_RESULT, m_creature); } + + m_playerGuid.Clear(); + m_creature->SetFlag(UNIT_NPC_FLAGS, UNIT_NPC_FLAG_QUESTGIVER); + } + + void StartEvent(Player* pPlayer) + { + m_creature->RemoveFlag(UNIT_NPC_FLAGS, UNIT_NPC_FLAG_QUESTGIVER); + m_playerGuid = pPlayer->GetObjectGuid(); + + m_creature->SummonCreature(NPC_GHARSUL, 8745.0f, -7134.32f, 35.22f, 0.0f, TEMPSUMMON_CORPSE_DESPAWN, 4000); + m_creature->SummonCreature(NPC_ANGERSHADE, 8745.0f, -7134.32f, 35.22f, 0.0f, TEMPSUMMON_CORPSE_DESPAWN, 4000); + m_creature->SummonCreature(NPC_ANGERSHADE, 8745.0f, -7134.32f, 35.22f, 0.0f, TEMPSUMMON_CORPSE_DESPAWN, 4000); + } + + void UpdateAI(const uint32 uiDiff) override + { + // Return since we have no target + if (!m_creature->SelectHostileTarget() || !m_creature->getVictim()) + { return; } + + if (m_uiFireballTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature->getVictim(), SPELL_FIREBALL) == CAST_OK) + { m_uiFireballTimer = urand(4000, 6000); } + } + else + { m_uiFireballTimer -= uiDiff; } + + DoMeleeAttackIfReady(); + } +}; + +bool QuestAccept_unexpected_results(Player* pPlayer, Creature* pCreature, const Quest* pQuest) +{ + if (pQuest->GetQuestId() == QUEST_UNEXPECTED_RESULT) + if (npc_apprentice_mirvedaAI* pMirvedaAI = dynamic_cast(pCreature->AI())) + { pMirvedaAI->StartEvent(pPlayer); } + return true; +} + +CreatureAI* GetAI_npc_apprentice_mirvedaAI(Creature* pCreature) +{ + return new npc_apprentice_mirvedaAI(pCreature); +} + +/*###### +## npc_infused_crystal +######*/ + +enum +{ + QUEST_POWERING_OUR_DEFENSES = 8490, + SAY_DEFENSE_FINISH = -1000668, + NPC_ENRAGED_WRAITH = 17086, +}; + +static const float aSummonPos[6][4] = +{ + {8250.539f, -7239.028f, 139.7099f, 0.8975816f}, + {8263.437f, -7181.188f, 139.4102f, 5.237229f}, + {8317.124f, -7210.098f, 140.1064f, 3.022202f}, + {8293.848f, -7179.062f, 138.6693f, 4.153376f}, + {8239.229f, -7207.673f, 139.1196f, 0.06059111f}, + {8301.548f, -7247.548f, 139.974f, 1.828518f} +}; + +struct npc_infused_crystalAI : public Scripted_NoMovementAI +{ + npc_infused_crystalAI(Creature* pCreature) : Scripted_NoMovementAI(pCreature) + { + m_bFirstWave = true; + m_uiWaveTimer = 1000; + m_uiKilledCount = 0; + m_uiFinishTimer = 60 * IN_MILLISECONDS; + Reset(); + } + + bool m_bFirstWave; + uint32 m_uiWaveTimer; + uint8 m_uiKilledCount; + uint32 m_uiFinishTimer; + + void Reset() override {} + + void JustSummoned(Creature* pSummoned) override + { + pSummoned->AI()->AttackStart(m_creature); + } + + void SummonedCreatureJustDied(Creature* /*pSummoned*/) override + { + ++m_uiKilledCount; + + if (m_uiKilledCount == 3) + { m_uiWaveTimer = std::min(m_uiWaveTimer, (uint32)10000); } + } + + void UpdateAI(const uint32 uiDiff) override + { + if (m_uiWaveTimer) + { + if (m_uiWaveTimer <= uiDiff) + { + if (m_bFirstWave) + { + for (uint8 i = 0; i < 3; ++i) + { m_creature->SummonCreature(NPC_ENRAGED_WRAITH, aSummonPos[i][0], aSummonPos[i][1], aSummonPos[i][2], aSummonPos[i][3], TEMPSUMMON_TIMED_OOC_OR_DEAD_DESPAWN, 5 * MINUTE); } + m_uiWaveTimer = 29000; + m_bFirstWave = false; + } + else + { + for (uint8 i = 3; i < 6; ++i) + { m_creature->SummonCreature(NPC_ENRAGED_WRAITH, aSummonPos[i][0], aSummonPos[i][1], aSummonPos[i][2], aSummonPos[i][3], TEMPSUMMON_TIMED_OOC_OR_DEAD_DESPAWN, 5 * MINUTE); } + m_uiWaveTimer = 0; + } + } + else + { m_uiWaveTimer -= uiDiff; } + } + + if (m_uiFinishTimer) + { + if (m_uiFinishTimer <= uiDiff) + { + DoScriptText(SAY_DEFENSE_FINISH, m_creature); + if (m_creature->IsTemporarySummon()) + { + TemporarySummon* pTemporary = (TemporarySummon*)m_creature; + + if (Player* pPlayer = m_creature->GetMap()->GetPlayer(pTemporary->GetSummonerGuid())) + { pPlayer->KilledMonsterCredit(m_creature->GetEntry(), m_creature->GetObjectGuid()); } + } + m_uiFinishTimer = 0; + m_creature->ForcedDespawn(1000); + } + else + { m_uiFinishTimer -= uiDiff; } + } + } +}; + +CreatureAI* GetAI_npc_infused_crystalAI(Creature* pCreature) +{ + return new npc_infused_crystalAI(pCreature); +} + +void AddSC_eversong_woods() +{ + Script* pNewScript; + + pNewScript = new Script; + pNewScript->Name = "npc_kelerun_bloodmourn"; + pNewScript->GetAI = &GetAI_npc_kelerun_bloodmourn; + pNewScript->pQuestAcceptNPC = &QuestAccept_npc_kelerun_bloodmourn; + pNewScript->RegisterSelf(); + + pNewScript = new Script; + pNewScript->Name = "go_harbinger_second_trial"; + pNewScript->pGOUse = &GOUse_go_harbinger_second_trial; + pNewScript->RegisterSelf(); + + pNewScript = new Script; + pNewScript->Name = "npc_prospector_anvilward"; + pNewScript->GetAI = &GetAI_npc_prospector_anvilward; + pNewScript->pGossipHello = &GossipHello_npc_prospector_anvilward; + pNewScript->pGossipSelect = &GossipSelect_npc_prospector_anvilward; + pNewScript->RegisterSelf(); + + pNewScript = new Script; + pNewScript->Name = "npc_apprentice_mirveda"; + pNewScript->GetAI = &GetAI_npc_apprentice_mirvedaAI; + pNewScript->pQuestAcceptNPC = &QuestAccept_unexpected_results; + pNewScript->RegisterSelf(); + + pNewScript = new Script; + pNewScript->Name = "npc_infused_crystal"; + pNewScript->GetAI = &GetAI_npc_infused_crystalAI; + pNewScript->RegisterSelf(); +} diff --git a/src/modules/SD2/scripts/eastern_kingdoms/ghostlands.cpp b/src/modules/SD2/scripts/eastern_kingdoms/ghostlands.cpp new file mode 100644 index 000000000..0661e728e --- /dev/null +++ b/src/modules/SD2/scripts/eastern_kingdoms/ghostlands.cpp @@ -0,0 +1,174 @@ +/** + * ScriptDev2 is an extension for mangos providing enhanced features for + * area triggers, creatures, game objects, instances, items, and spells beyond + * the default database scripting in mangos. + * + * Copyright (C) 2006-2013 ScriptDev2 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * World of Warcraft, and all World of Warcraft or Warcraft art, images, + * and lore are copyrighted by Blizzard Entertainment, Inc. + */ + +/* ScriptData +SDName: Ghostlands +SD%Complete: 100 +SDComment: Quest support: 9212. +SDCategory: Ghostlands +EndScriptData */ + +/* ContentData +npc_ranger_lilatha +EndContentData */ + +#include "precompiled.h" +#include "escort_ai.h" + +/*###### +## npc_ranger_lilatha +######*/ + +enum +{ + SAY_START = -1000140, + SAY_PROGRESS1 = -1000141, + SAY_PROGRESS2 = -1000142, + SAY_PROGRESS3 = -1000143, + SAY_END1 = -1000144, + SAY_END2 = -1000145, + CAPTAIN_ANSWER = -1000146, + + QUEST_CATACOMBS = 9212, + GO_CAGE = 181152, + NPC_CAPTAIN_HELIOS = 16220, + FACTION_SMOON_E = 1603, +}; + +struct npc_ranger_lilathaAI : public npc_escortAI +{ + npc_ranger_lilathaAI(Creature* pCreature) : npc_escortAI(pCreature) { Reset(); } + + ObjectGuid m_goCageGuid; + ObjectGuid m_heliosGuid; + + void MoveInLineOfSight(Unit* pUnit) override + { + if (HasEscortState(STATE_ESCORT_ESCORTING)) + { + if (!m_heliosGuid && pUnit->GetEntry() == NPC_CAPTAIN_HELIOS) + { + if (m_creature->IsWithinDistInMap(pUnit, 30.0f)) + { m_heliosGuid = pUnit->GetObjectGuid(); } + } + } + + npc_escortAI::MoveInLineOfSight(pUnit); + } + + void WaypointReached(uint32 i) override + { + Player* pPlayer = GetPlayerForEscort(); + + if (!pPlayer) + { return; } + + switch (i) + { + case 0: + if (GameObject* pGoTemp = GetClosestGameObjectWithEntry(m_creature, GO_CAGE, 10.0f)) + { + m_goCageGuid = pGoTemp->GetObjectGuid(); + pGoTemp->SetGoState(GO_STATE_ACTIVE); + } + + m_creature->SetStandState(UNIT_STAND_STATE_STAND); + + DoScriptText(SAY_START, m_creature, pPlayer); + break; + case 1: + if (GameObject* pGo = m_creature->GetMap()->GetGameObject(m_goCageGuid)) + { pGo->SetGoState(GO_STATE_READY); } + break; + case 5: + DoScriptText(SAY_PROGRESS1, m_creature, pPlayer); + break; + case 11: + DoScriptText(SAY_PROGRESS2, m_creature, pPlayer); + break; + case 18: + DoScriptText(SAY_PROGRESS3, m_creature, pPlayer); + if (Creature* pSum1 = m_creature->SummonCreature(16342, 7627.083984f, -7532.538086f, 152.128616f, 1.082733f, TEMPSUMMON_DEAD_DESPAWN, 0)) + { pSum1->AI()->AttackStart(m_creature); } + if (Creature* pSum2 = m_creature->SummonCreature(16343, 7620.432129f, -7532.550293f, 152.454865f, 0.827478f, TEMPSUMMON_DEAD_DESPAWN, 0)) + { pSum2->AI()->AttackStart(pPlayer); } + break; + case 19: + SetRun(); + break; + case 25: + SetRun(false); + break; + case 30: + pPlayer->GroupEventHappens(QUEST_CATACOMBS, m_creature); + break; + case 32: + DoScriptText(SAY_END1, m_creature, pPlayer); + break; + case 33: + DoScriptText(SAY_END2, m_creature, pPlayer); + if (Creature* pHelios = m_creature->GetMap()->GetCreature(m_heliosGuid)) + { DoScriptText(CAPTAIN_ANSWER, pHelios, m_creature); } + break; + } + } + + void Reset() override + { + if (!HasEscortState(STATE_ESCORT_ESCORTING)) + { + m_goCageGuid.Clear(); + m_heliosGuid.Clear(); + } + } +}; + +CreatureAI* GetAI_npc_ranger_lilathaAI(Creature* pCreature) +{ + return new npc_ranger_lilathaAI(pCreature); +} + +bool QuestAccept_npc_ranger_lilatha(Player* pPlayer, Creature* pCreature, const Quest* pQuest) +{ + if (pQuest->GetQuestId() == QUEST_CATACOMBS) + { + pCreature->SetFactionTemporary(FACTION_SMOON_E, TEMPFACTION_RESTORE_RESPAWN); + + if (npc_ranger_lilathaAI* pEscortAI = dynamic_cast(pCreature->AI())) + { pEscortAI->Start(false, pPlayer, pQuest); } + } + return true; +} + +void AddSC_ghostlands() +{ + Script* pNewScript; + + pNewScript = new Script; + pNewScript->Name = "npc_ranger_lilatha"; + pNewScript->GetAI = &GetAI_npc_ranger_lilathaAI; + pNewScript->pQuestAcceptNPC = &QuestAccept_npc_ranger_lilatha; + pNewScript->RegisterSelf(); +} diff --git a/src/modules/SD2/scripts/eastern_kingdoms/gilneas.cpp b/src/modules/SD2/scripts/eastern_kingdoms/gilneas.cpp new file mode 100644 index 000000000..c36458127 --- /dev/null +++ b/src/modules/SD2/scripts/eastern_kingdoms/gilneas.cpp @@ -0,0 +1,35 @@ +/* Copyright (C) 2006 - 2013 ScriptDev2 + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +/* ScriptData +SDName: Gilneas +SD%Complete: 0 +SDComment: Placeholder +SDCategory: Gilneas +EndScriptData */ + +/* ContentData +EndContentData */ + +#include "precompiled.h" + +/*###### +# +######*/ + +void AddSC_gilneas() +{ +} diff --git a/src/modules/SD2/scripts/eastern_kingdoms/gilneas_city.cpp b/src/modules/SD2/scripts/eastern_kingdoms/gilneas_city.cpp new file mode 100644 index 000000000..650fcb6e8 --- /dev/null +++ b/src/modules/SD2/scripts/eastern_kingdoms/gilneas_city.cpp @@ -0,0 +1,429 @@ +/* Copyright (C) 2006 - 2013 ScriptDev2 + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +/* ScriptData +SDName: Gilneas City +SD%Complete: 0 Work in progress... +SDComment: Placeholder +SDCategory: Gilneas City +EndScriptData */ + +/* ContentData +npc_prince_liam_greymane_phase1 - Fully Scripted +npc_prince_liam_greymane_phase2 - Fully Scripted +rampaging_worgen - Enrage Spell Scripted +EndContentData */ + +#include "precompiled.h" +#include "escort_ai.h" +#include + +/*###### +# npc_prince_liam_greymane_phase1 +######*/ + +enum { + SAY_STORYLINE1 = -1654001, + SAY_STORYLINE2 = -1654002, + SAY_STORYLINE3 = -1654003 +}; + +struct npc_prince_liam_greymane_phase1AI : public ScriptedAI +{ + npc_prince_liam_greymane_phase1AI(Creature* pCreature) : ScriptedAI(pCreature) + { + m_uiNpcFlags = pCreature->GetUInt32Value(UNIT_NPC_FLAGS); + Reset(); + } + + ObjectGuid m_casterGuid; + uint32 m_uiNpcFlags; + uint32 m_uiSayStoryDelay; + uint32 m_uiSayStoryTimer; + int m_uiSayStoryLast; + + bool m_bCanSayStory; + + void Reset() override + { + m_casterGuid.Clear(); + + m_uiSayStoryDelay = 15000; + m_uiSayStoryTimer = m_uiSayStoryDelay; + m_uiSayStoryLast = 0; + + m_bCanSayStory = true; + + m_creature->SetUInt32Value(UNIT_NPC_FLAGS, m_uiNpcFlags); + } + + void UpdateAI(const uint32 uiDiff) override + { + if (m_bCanSayStory) + { + m_uiSayStoryTimer = m_uiSayStoryDelay; + + // Random switch between 3 texts + switch (m_uiSayStoryLast) + { + case 0: DoScriptText(SAY_STORYLINE1, m_creature); m_uiSayStoryLast++; break; + case 1: DoScriptText(SAY_STORYLINE2, m_creature); m_uiSayStoryLast++; break; + case 2: DoScriptText(SAY_STORYLINE3, m_creature); m_uiSayStoryLast = 0; m_uiSayStoryTimer = 60000; break; + //case 3: DoScriptText(SAY_HELP4, m_creature); break; + } + + m_bCanSayStory = false; + } + + if (m_uiSayStoryTimer < uiDiff) + { + m_bCanSayStory = true; + m_uiSayStoryTimer = m_uiSayStoryDelay; + } + else m_uiSayStoryTimer -= uiDiff; + } +}; + +CreatureAI* GetAI_npc_prince_liam_greymane_phase1(Creature* pCreature) +{ + return new npc_prince_liam_greymane_phase1AI(pCreature); +} + +/*###### +# npc_prince_liam_greymane_phase2 +######*/ + +enum { + SAY_STORYLINE4 = -1654004, + SAY_STORYLINE5 = -1654005, + SAY_STORYLINE6 = -1654006, + SAY_STORYLINE7 = -1654007, + SAY_STORYLINE8 = -1654008, + + SAY_STORY_DELAY = 30000, + SPELL_SHOOT = 50092, + SPELL_CNOCKING = 67869, + + QUEST_EVAQUATE_THE_MERCHANT_SQUARE = 14098 +}; + +struct npc_prince_liam_greymane_phase2AI : public ScriptedAI +{ + npc_prince_liam_greymane_phase2AI(Creature* pCreature) : ScriptedAI(pCreature) + { + Reset(); + } + + uint32 m_uiNpcFlags; + uint32 m_uiSayStoryTimer; + + Unit* lastVictim; + + int m_uiSayStoryLast; + + bool m_bCanSayStory; + bool IsSelfRooted; + + void Reset() override + { + m_uiSayStoryTimer = SAY_STORY_DELAY; + m_uiSayStoryLast = 0; + lastVictim = NULL; + + m_bCanSayStory = true; + IsSelfRooted = false; + + } + + void Aggro(Unit* who) override + { + if (!m_creature->CanReachWithMeleeAttack(who) && !m_creature->CanUseEquippedWeapon(RANGED_ATTACK)) + { + IsSelfRooted = true; + } + } + + void AttackedBy(Unit* pAttacker) override + { + // Check if Liam is attacking who attack he and don't jump on multiple attackers + if (m_creature->getVictim() && (m_creature->getVictim() == pAttacker || lastVictim == pAttacker)) + return; + + if (m_creature->IsFriendlyTo(pAttacker)) + return; + + lastVictim = pAttacker; + AttackStart(pAttacker); + } + + void UpdateAI(const uint32 uiDiff) override + { + // Ready to say emote + if (m_bCanSayStory) + { + m_uiSayStoryTimer = SAY_STORY_DELAY; + + // Sequence switch between 3 texts + switch (m_uiSayStoryLast) + { + case 0: DoScriptText(SAY_STORYLINE4, m_creature); m_uiSayStoryLast++; break; + case 1: DoScriptText(SAY_STORYLINE5, m_creature); m_uiSayStoryLast++; break; + case 2: DoScriptText(SAY_STORYLINE6, m_creature); m_uiSayStoryLast++; break; + case 3: DoScriptText(SAY_STORYLINE7, m_creature); m_uiSayStoryLast++; break; + case 4: DoScriptText(SAY_STORYLINE8, m_creature); m_uiSayStoryLast = 0; break; + //case 3: DoScriptText(SAY_HELP4, m_creature); break; + } + + m_bCanSayStory = false; + } + + if (m_uiSayStoryTimer < uiDiff) + { + m_bCanSayStory = true; + m_uiSayStoryTimer = SAY_STORY_DELAY; + } + else m_uiSayStoryTimer -= uiDiff; + + + // Return since we have no target + if (!m_creature->SelectHostileTarget() || !m_creature->getVictim()) + return; + + // Equipped ranged weapon usable and not close to victim + if (m_creature->GetCombatDistance(m_creature->getVictim(), false) > 0 && m_creature->CanUseEquippedWeapon(RANGED_ATTACK) ) + { + // Make sure our attack is ready + if (m_creature->isAttackReady(RANGED_ATTACK)){ + // Use spell instead of normal ranged attack that seem not working. TODO: check ranged attack! + DoCastSpellIfCan(m_creature->getVictim(), SPELL_SHOOT); + //m_creature->AttackerStateUpdate(m_creature->getVictim(),RANGED_ATTACK); + m_creature->resetAttackTimer(RANGED_ATTACK); + } + + } + else if (m_creature->CanReachWithMeleeAttack(m_creature->getVictim())) + { + // If we are within range melee the target + // Make sure our attack is ready + if (m_creature->isAttackReady()) + { + printf("---> %s melee is ready %s! \n",m_creature->GetName(), m_creature->getVictim()->GetName()); + m_creature->AttackerStateUpdate(m_creature->getVictim()); + + m_creature->resetAttackTimer(); + } + + } + else if (IsSelfRooted) + { + // Cancel our current spell and then allow movement again + m_creature->InterruptNonMeleeSpells(false); + IsSelfRooted = false; + } + + } +}; + +bool QuestAccept_npc_prince_liam_greymane_phase2(Player* pPlayer, Creature* pCreature, const Quest* pQuest) +{ + if (pQuest->GetQuestId() == QUEST_EVAQUATE_THE_MERCHANT_SQUARE) + { + // TODO: fix player's spell for use Knocking spell properly without this!!! + pPlayer->addSpell(SPELL_CNOCKING, true, false, false, false); + } + return true; +} + +CreatureAI* GetAI_npc_prince_liam_greymane_phase2(Creature* pCreature) +{ + return new npc_prince_liam_greymane_phase2AI(pCreature); +} + +/*###### +# rampaging_worgen +######*/ + + +enum { + SPELL_ENRAGE = 8599, + SPELL_ENRAGE_DELAY = 120000 +}; + +struct rampaging_worgenAI : public ScriptedAI +{ + rampaging_worgenAI(Creature* pCreature) : ScriptedAI(pCreature) {Reset();} + + uint32 spellCooldown; // This variable acts like the global cooldown that players have (2 minute) + bool IsSelfRooted; + + void Reset() override + { + spellCooldown = 0; + IsSelfRooted = false; + } + + void Aggro(Unit* who) override + { + if (!m_creature->CanReachWithMeleeAttack(who)) + { + IsSelfRooted = true; + } + } + + void UpdateAI(const uint32 diff) override + { + // Always decrease our spell cooldown first + if (spellCooldown > diff) + spellCooldown -= diff; + else spellCooldown = 0; + + // Enrage timer (only enrage when we are alive and in combat) + if (m_creature->IsInCombat() && m_creature->IsAlive()) + { + // Cast enrage spell if less than 70% hp ONLY and not casted already + if (m_creature->GetHealthPercent() < 70.0f && !spellCooldown) { + DoCastSpellIfCan(m_creature, SPELL_ENRAGE); + spellCooldown = SPELL_ENRAGE_DELAY; + } + + } + + // Return since we have no target + if (!m_creature->SelectHostileTarget() || !m_creature->getVictim()) + return; + + // Return if we already cast a spell + if (m_creature->IsNonMeleeSpellCasted(false)) + return; + + // If we are within range melee the target + if (m_creature->CanReachWithMeleeAttack(m_creature->getVictim())) + { + // Make sure our attack is ready + if (m_creature->isAttackReady()) + { + m_creature->AttackerStateUpdate(m_creature->getVictim()); + + m_creature->resetAttackTimer(); + } + } + else if (IsSelfRooted) + { + // Cancel our current spell and then allow movement again + m_creature->InterruptNonMeleeSpells(false); + IsSelfRooted = false; + } + + } +}; + +CreatureAI* GetAI_rampaging_worgen(Creature* pCreature) +{ + return new rampaging_worgenAI(pCreature); +} + + +/*###### +## creature_frightened_citizen_quest +######*/ +enum { + SAY_ON_ESCAPE1 = -1654009, + SAY_ON_ESCAPE2 = -1654010, + SAY_ON_ESCAPE3 = -1654011, + SAY_ON_ESCAPE4 = -1654012, + SAY_ON_ESCAPE5 = -1654013, + SAY_ON_ESCAPE6 = -1654014, + SAY_ON_ESCAPE7 = -1654015, + SAY_ON_ESCAPE_DELAY = 12000 +}; + +struct frightened_citizen_questAI : public ScriptedAI +{ + frightened_citizen_questAI(Creature* pCreature) : ScriptedAI(pCreature) {Reset();} + + uint32 m_uiSayOnEscapeTimer; + bool m_bCanSayOnEscape; + + void Reset() override + { + m_uiSayOnEscapeTimer = SAY_ON_ESCAPE_DELAY; + m_bCanSayOnEscape = true; + } + + + void UpdateAI(const uint32 uiDiff) override + { + // Ready to say emote + if (m_bCanSayOnEscape) + { + m_uiSayOnEscapeTimer = SAY_ON_ESCAPE_DELAY; + + // switch between 7 texts randomly + switch (urand(0, 6)) + { + case 0: DoScriptText(SAY_ON_ESCAPE1, m_creature); break; + case 1: DoScriptText(SAY_ON_ESCAPE2, m_creature); break; + case 2: DoScriptText(SAY_ON_ESCAPE3, m_creature); break; + case 3: DoScriptText(SAY_ON_ESCAPE4, m_creature); break; + case 4: DoScriptText(SAY_ON_ESCAPE5, m_creature); break; + case 5: DoScriptText(SAY_ON_ESCAPE6, m_creature); break; + case 6: DoScriptText(SAY_ON_ESCAPE7, m_creature); break; + } + + m_bCanSayOnEscape = false; + } + + if (m_uiSayOnEscapeTimer < uiDiff) + { + m_bCanSayOnEscape = true; + m_uiSayOnEscapeTimer = SAY_ON_ESCAPE_DELAY; + } + else m_uiSayOnEscapeTimer -= uiDiff; + + } +}; + +CreatureAI* GetAI_frightened_citizen_quest(Creature* pCreature) +{ + return new frightened_citizen_questAI(pCreature); +} + + +void AddSC_gilneas_city() +{ + Script* pNewScript; + + pNewScript = new Script; + pNewScript->Name = "npc_prince_liam_greymane_phase1"; + pNewScript->GetAI = &GetAI_npc_prince_liam_greymane_phase1; + pNewScript->RegisterSelf(); + + pNewScript = new Script; + pNewScript->Name = "npc_prince_liam_greymane_phase2"; + pNewScript->GetAI = &GetAI_npc_prince_liam_greymane_phase2; + pNewScript->pQuestAcceptNPC = &QuestAccept_npc_prince_liam_greymane_phase2; + pNewScript->RegisterSelf(); + + pNewScript = new Script; + pNewScript->Name = "rampaging_worgen"; + pNewScript->GetAI = &GetAI_rampaging_worgen; + pNewScript->RegisterSelf(); + + pNewScript = new Script; + pNewScript->Name = "frightened_citizen_quest"; + pNewScript->GetAI = &GetAI_frightened_citizen_quest; + pNewScript->RegisterSelf(); +} diff --git a/src/modules/SD2/scripts/eastern_kingdoms/gnomeregan/boss_thermaplugg.cpp b/src/modules/SD2/scripts/eastern_kingdoms/gnomeregan/boss_thermaplugg.cpp new file mode 100644 index 000000000..43f741b2d --- /dev/null +++ b/src/modules/SD2/scripts/eastern_kingdoms/gnomeregan/boss_thermaplugg.cpp @@ -0,0 +1,283 @@ +/* Copyright (C) 2006 - 2013 ScriptDev2 + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with 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_Thermaplugg +SD%Complete: 90 - Timer +SDComment: Timer need improvement, especially for bomb-spawning +SDCategory: Gnomeregan +EndScriptData */ + +#include "precompiled.h" +#include "gnomeregan.h" + +enum +{ + SAY_AGGRO = -1090024, + SAY_PHASE = -1090025, + SAY_BOMB = -1090026, + SAY_SLAY = -1090027, + + SPELL_ACTIVATE_BOMB_A = 11511, // Target Dest = -530.754 670.571 -313.784 + SPELL_ACTIVATE_BOMB_B = 11795, // Target Dest = -530.754 670.571 -313.784 + SPELL_KNOCK_AWAY = 10101, + SPELL_KNOCK_AWAY_AOE = 11130, + SPELL_WALKING_BOMB_EFFECT = 11504, + + NPC_WALKING_BOMB = 7915, +}; + +static const float fBombSpawnZ = -316.2625f; + +struct boss_thermapluggAI : public ScriptedAI +{ + boss_thermapluggAI(Creature* pCreature) : ScriptedAI(pCreature) + { + m_pInstance = (instance_gnomeregan*)pCreature->GetInstanceData(); + Reset(); + } + + instance_gnomeregan* m_pInstance; + bool m_bIsPhaseTwo; + + uint32 m_uiKnockAwayTimer; + uint32 m_uiActivateBombTimer; + + sBombFace* m_asBombFaces; + float m_afSpawnPos[3]; + + GuidList m_lSummonedBombGUIDs; + GuidList m_lLandedBombGUIDs; + + void Reset() override + { + m_uiKnockAwayTimer = urand(17000, 20000); + m_uiActivateBombTimer = urand(10000, 15000); + m_bIsPhaseTwo = false; + m_asBombFaces = NULL; + + memset(&m_afSpawnPos, 0.0f, sizeof(m_afSpawnPos)); + m_lLandedBombGUIDs.clear(); + } + + void GetAIInformation(ChatHandler& reader) override + { + reader.PSendSysMessage("Thermaplugg, currently phase %s", m_bIsPhaseTwo ? "two" : "one"); + + if (m_asBombFaces) + { + for (uint8 i = 0; i < MAX_GNOME_FACES; ++i) + reader.PSendSysMessage("Bomb face %u is %s ", (uint32)i, m_asBombFaces[i].m_bActivated ? "activated" : "not activated"); + } + } + + void KilledUnit(Unit* /*pVictim*/) override + { + DoScriptText(SAY_SLAY, m_creature); + } + + void JustDied(Unit* /*pKiller*/) override + { + if (m_pInstance) + m_pInstance->SetData(TYPE_THERMAPLUGG, DONE); + + m_lSummonedBombGUIDs.clear(); + } + + void Aggro(Unit* /*pWho*/) override + { + DoScriptText(SAY_AGGRO, m_creature); + + if (m_pInstance) + { + m_pInstance->SetData(TYPE_THERMAPLUGG, IN_PROGRESS); + m_asBombFaces = m_pInstance->GetBombFaces(); + } + + m_afSpawnPos[0] = m_creature->GetPositionX(); + m_afSpawnPos[1] = m_creature->GetPositionY(); + m_afSpawnPos[2] = m_creature->GetPositionZ(); + } + + void JustReachedHome() override + { + if (m_pInstance) + m_pInstance->SetData(TYPE_THERMAPLUGG, FAIL); + + // Remove remaining bombs + for (GuidList::const_iterator itr = m_lSummonedBombGUIDs.begin(); itr != m_lSummonedBombGUIDs.end(); ++itr) + { + if (Creature* pBomb = m_creature->GetMap()->GetCreature(*itr)) + pBomb->ForcedDespawn(); + } + m_lSummonedBombGUIDs.clear(); + } + + void JustSummoned(Creature* pSummoned) override + { + if (pSummoned->GetEntry() == NPC_WALKING_BOMB) + { + m_lSummonedBombGUIDs.push_back(pSummoned->GetObjectGuid()); + // calculate point for falling down + float fX, fY; + fX = 0.2 * m_afSpawnPos[0] + 0.8 * pSummoned->GetPositionX(); + fY = 0.2 * m_afSpawnPos[1] + 0.8 * pSummoned->GetPositionY(); + pSummoned->GetMotionMaster()->MovePoint(1, fX, fY, m_afSpawnPos[2] - 2.0f); + } + } + + void SummonedMovementInform(Creature* pSummoned, uint32 uiMotionType, uint32 uiPointId) override + { + if (pSummoned->GetEntry() == NPC_WALKING_BOMB && uiMotionType == POINT_MOTION_TYPE && uiPointId == 1) + m_lLandedBombGUIDs.push_back(pSummoned->GetObjectGuid()); + } + + void SummonedCreatureDespawn(Creature* pSummoned) override + { + m_lSummonedBombGUIDs.remove(pSummoned->GetObjectGuid()); + } + + void UpdateAI(const uint32 uiDiff) override + { + if (!m_creature->SelectHostileTarget() || !m_creature->getVictim()) + return; + + // Movement of Summoned mobs + if (!m_lLandedBombGUIDs.empty()) + { + for (GuidList::const_iterator itr = m_lLandedBombGUIDs.begin(); itr != m_lLandedBombGUIDs.end(); ++itr) + { + if (Creature* pBomb = m_creature->GetMap()->GetCreature(*itr)) + pBomb->GetMotionMaster()->MoveFollow(m_creature, 0.0f, 0.0f); + } + m_lLandedBombGUIDs.clear(); + } + + if (!m_bIsPhaseTwo && m_creature->GetHealthPercent() < 50.0f) + { + DoScriptText(SAY_PHASE, m_creature); + m_bIsPhaseTwo = true; + } + + if (m_uiKnockAwayTimer < uiDiff) + { + if (m_bIsPhaseTwo) + { + if (DoCastSpellIfCan(m_creature, SPELL_KNOCK_AWAY_AOE) == CAST_OK) + m_uiKnockAwayTimer = 12000; + } + else + { + if (DoCastSpellIfCan(m_creature->getVictim(), SPELL_KNOCK_AWAY) == CAST_OK) + m_uiKnockAwayTimer = urand(17000, 20000); + } + } + else + m_uiKnockAwayTimer -= uiDiff; + + if (m_uiActivateBombTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature, m_bIsPhaseTwo ? SPELL_ACTIVATE_BOMB_B : SPELL_ACTIVATE_BOMB_A) == CAST_OK) + { + m_uiActivateBombTimer = (m_bIsPhaseTwo ? urand(6, 12) : urand(12, 17)) * IN_MILLISECONDS; + if (!urand(0, 5)) // TODO, chance/ place for this correct? + DoScriptText(SAY_BOMB, m_creature); + } + } + else + m_uiActivateBombTimer -= uiDiff; + + // Spawn bombs + if (m_asBombFaces) + { + for (uint8 i = 0; i < MAX_GNOME_FACES; ++i) + { + if (m_asBombFaces[i].m_bActivated) + { + if (m_asBombFaces[i].m_uiBombTimer < uiDiff) + { + // Calculate the spawning position as 90% between face and thermaplugg spawn-pos, and hight hardcoded + float fX = 0.0f, fY = 0.0f; + if (GameObject* pFace = m_creature->GetMap()->GetGameObject(m_asBombFaces[i].m_gnomeFaceGuid)) + { + fX = 0.35 * m_afSpawnPos[0] + 0.65 * pFace->GetPositionX(); + fY = 0.35 * m_afSpawnPos[1] + 0.65 * pFace->GetPositionY(); + } + m_creature->SummonCreature(NPC_WALKING_BOMB, fX, fY, fBombSpawnZ, 0.0f, TEMPSUMMON_CORPSE_DESPAWN, 0); + m_asBombFaces[i].m_uiBombTimer = urand(10000, 25000); // TODO + } + else + m_asBombFaces[i].m_uiBombTimer -= uiDiff; + } + } + } + + DoMeleeAttackIfReady(); + } +}; + +CreatureAI* GetAI_boss_thermaplugg(Creature* pCreature) +{ + return new boss_thermapluggAI(pCreature); +} + +bool EffectDummyCreature_spell_boss_thermaplugg(Unit* /*pCaster*/, uint32 uiSpellId, SpellEffectIndex uiEffIndex, Creature* pCreatureTarget, ObjectGuid /*originalCasterGuid*/) +{ + if ((uiSpellId != SPELL_ACTIVATE_BOMB_A && uiSpellId != SPELL_ACTIVATE_BOMB_B) || uiEffIndex != EFFECT_INDEX_0) + return false; + + // This spell should select a random Bomb-Face and activate it if needed + if (instance_gnomeregan* pInstance = (instance_gnomeregan*)pCreatureTarget->GetInstanceData()) + pInstance->DoActivateBombFace(urand(0, MAX_GNOME_FACES - 1)); + + return true; +} + +bool GOUse_go_gnomeface_button(Player* pPlayer, GameObject* pGo) +{ + instance_gnomeregan* pInstance = (instance_gnomeregan*)pPlayer->GetInstanceData(); + if (!pInstance) + return false; + + // If a button is used, the related face should be deactivated (if already activated) + switch (pGo->GetEntry()) + { + case GO_BUTTON_1: pInstance->DoDeactivateBombFace(0); break; + case GO_BUTTON_2: pInstance->DoDeactivateBombFace(1); break; + case GO_BUTTON_3: pInstance->DoDeactivateBombFace(2); break; + case GO_BUTTON_4: pInstance->DoDeactivateBombFace(3); break; + case GO_BUTTON_5: pInstance->DoDeactivateBombFace(4); break; + case GO_BUTTON_6: pInstance->DoDeactivateBombFace(5); break; + } + + return false; +} + +void AddSC_boss_thermaplugg() +{ + Script* pNewScript; + + pNewScript = new Script; + pNewScript->Name = "boss_thermaplugg"; + pNewScript->GetAI = &GetAI_boss_thermaplugg; + pNewScript->pEffectDummyNPC = &EffectDummyCreature_spell_boss_thermaplugg; + pNewScript->RegisterSelf(); + + pNewScript = new Script; + pNewScript->Name = "go_gnomeface_button"; + pNewScript->pGOUse = &GOUse_go_gnomeface_button; + pNewScript->RegisterSelf(); +} diff --git a/src/modules/SD2/scripts/eastern_kingdoms/gnomeregan/gnomeregan.cpp b/src/modules/SD2/scripts/eastern_kingdoms/gnomeregan/gnomeregan.cpp new file mode 100644 index 000000000..39c0ec315 --- /dev/null +++ b/src/modules/SD2/scripts/eastern_kingdoms/gnomeregan/gnomeregan.cpp @@ -0,0 +1,722 @@ +/* Copyright (C) 2006 - 2013 ScriptDev2 + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +/* ScriptData +SDName: gnomeregan +SD%Complete: 90 +SDComment: Grubbis Encounter, quest 2904 (A fine mess) +SDCategory: Gnomeregan +EndScriptData */ + +/* ContentData +npc_blastmaster_emi_shortfuse +npc_kernobee +EndContentData */ + +#include "precompiled.h" +#include "gnomeregan.h" +#include "escort_ai.h" +#include "follower_ai.h" + +/*###### +## npc_blastmaster_emi_shortfuse +######*/ + +enum +{ + SAY_START = -1090000, + SAY_INTRO_1 = -1090001, + SAY_INTRO_2 = -1090002, + SAY_INTRO_3 = -1090003, + SAY_INTRO_4 = -1090004, + SAY_LOOK_1 = -1090005, + SAY_HEAR_1 = -1090006, + SAY_AGGRO_1 = -1090007, + SAY_CHARGE_1 = -1090008, + SAY_CHARGE_2 = -1090009, + SAY_BLOW_1_10 = -1090010, + SAY_BLOW_1_5 = -1090011, + SAY_BLOW_1 = -1090012, + SAY_FINISH_1 = -1090013, + SAY_LOOK_2 = -1090014, + SAY_HEAR_2 = -1090015, + SAY_CHARGE_3 = -1090016, + SAY_CHARGE_4 = -1090017, + SAY_BLOW_2_10 = -1090018, + SAY_BLOW_2_5 = -1090019, + SAY_BLOW_SOON = -1090020, + SAY_BLOW_2 = -1090021, + SAY_FINISH_2 = -1090022, + + SAY_AGGRO_2 = -1090028, + + SAY_GRUBBIS_SPAWN = -1090023, + + GOSSIP_ITEM_START = -3090000, + + SPELL_EXPLOSION_NORTH = 12158, + SPELL_EXPLOSION_SOUTH = 12159, + SPELL_FIREWORKS_RED = 11542, + + MAX_SUMMON_POSITIONS = 33, + + NPC_GRUBBIS = 7361, + NPC_CHOMPER = 6215, + NPC_CAVERNDEEP_BURROWER = 6206, + NPC_CAVERNDEEP_AMBUSHER = 6207 +}; + +struct sSummonInformation +{ + uint32 uiPosition, uiEntry; + float fX, fY, fZ, fO; +}; + +static const sSummonInformation asSummonInfo[MAX_SUMMON_POSITIONS] = +{ + // Entries must be sorted by pack + // First Cave-In + {1, NPC_CAVERNDEEP_AMBUSHER, -566.8114f, -111.7036f, -151.1891f, 5.986479f}, + {1, NPC_CAVERNDEEP_AMBUSHER, -568.5875f, -113.7559f, -151.1869f, 0.06981317f}, + {1, NPC_CAVERNDEEP_AMBUSHER, -570.2333f, -116.8126f, -151.2272f, 0.296706f}, + {1, NPC_CAVERNDEEP_AMBUSHER, -550.6331f, -108.7592f, -153.965f, 0.8901179f}, + {1, NPC_CAVERNDEEP_AMBUSHER, -558.9717f, -115.0669f, -151.8799f, 0.5235988f}, + {1, NPC_CAVERNDEEP_AMBUSHER, -556.6719f, -112.0526f, -152.8255f, 0.4886922f}, + {1, NPC_CAVERNDEEP_AMBUSHER, -552.6419f, -113.4385f, -153.0727f, 0.8028514f}, + {1, NPC_CAVERNDEEP_AMBUSHER, -549.1248f, -112.1469f, -153.7987f, 0.7504916f}, + {1, NPC_CAVERNDEEP_AMBUSHER, -546.7435f, -112.3051f, -154.2225f, 0.9250245f}, + {2, NPC_CAVERNDEEP_AMBUSHER, -571.4071f, -108.7721f, -150.6547f, 5.480334f}, + {2, NPC_CAVERNDEEP_AMBUSHER, -573.797f, -106.5265f, -150.4106f, 5.550147f}, + {2, NPC_CAVERNDEEP_AMBUSHER, -576.3784f, -108.0483f, -150.4227f, 5.585053f}, + {2, NPC_CAVERNDEEP_AMBUSHER, -576.697f, -111.7413f, -150.6484f, 5.759586f}, + {3, NPC_CAVERNDEEP_AMBUSHER, -571.3161f, -114.4412f, -151.0931f, 6.021386f}, + {3, NPC_CAVERNDEEP_AMBUSHER, -570.3127f, -111.7964f, -151.04f, 2.042035f}, + + // Second Cave-In + {4, NPC_CAVERNDEEP_AMBUSHER, -474.5954f, -104.074f, -146.0483f, 2.338741f}, + {4, NPC_CAVERNDEEP_AMBUSHER, -477.9396f, -108.6563f, -145.7394f, 1.553343f}, + {4, NPC_CAVERNDEEP_AMBUSHER, -475.6625f, -97.12168f, -146.5959f, 1.291544f}, + {4, NPC_CAVERNDEEP_AMBUSHER, -480.5233f, -88.40702f, -146.3772f, 3.001966f}, + {5, NPC_CAVERNDEEP_AMBUSHER, -474.2943f, -105.2212f, -145.9747f, 2.251475f}, + {5, NPC_CAVERNDEEP_AMBUSHER, -481.1831f, -101.4225f, -146.377f, 2.146755f}, + {5, NPC_CAVERNDEEP_BURROWER, -475.0871f, -100.016f, -146.4382f, 2.303835f}, + {5, NPC_CAVERNDEEP_AMBUSHER, -478.8562f, -106.9321f, -145.8533f, 1.658063f}, + {6, NPC_CAVERNDEEP_AMBUSHER, -473.8762f, -107.4022f, -145.838f, 2.024582f}, + {6, NPC_CAVERNDEEP_AMBUSHER, -490.5134f, -92.72843f, -148.0954f, 3.054326f}, + {6, NPC_CAVERNDEEP_AMBUSHER, -491.401f, -88.25341f, -148.0358f, 3.560472f}, + {6, NPC_CAVERNDEEP_AMBUSHER, -479.1431f, -106.227f, -145.9097f, 1.727876f}, + {6, NPC_CAVERNDEEP_AMBUSHER, -475.3185f, -101.4804f, -146.2717f, 2.234021f}, + {6, NPC_CAVERNDEEP_AMBUSHER, -485.1559f, -89.57419f, -146.9299f, 3.071779f}, + {6, NPC_CAVERNDEEP_AMBUSHER, -482.2516f, -96.80614f, -146.6596f, 2.303835f}, + {6, NPC_CAVERNDEEP_AMBUSHER, -477.9874f, -92.82047f, -146.6944f, 3.124139f}, + + // Grubbis and add + {7, NPC_GRUBBIS, -476.3761f, -108.1901f, -145.7763f, 1.919862f}, + {7, NPC_CHOMPER, -473.1326f, -103.0901f, -146.1155f, 2.042035f} +}; + +struct npc_blastmaster_emi_shortfuseAI : public npc_escortAI +{ + npc_blastmaster_emi_shortfuseAI(Creature* pCreature) : npc_escortAI(pCreature) + { + m_pInstance = (instance_gnomeregan*)pCreature->GetInstanceData(); + // Remove Gossip-Menu in reload case for DONE enounter + if (m_pInstance && m_pInstance->GetData(TYPE_GRUBBIS) == DONE) + pCreature->SetUInt32Value(UNIT_NPC_FLAGS, UNIT_NPC_FLAG_NONE); + Reset(); + } + + instance_gnomeregan* m_pInstance; + + uint8 m_uiPhase; + uint32 m_uiPhaseTimer; + ObjectGuid m_playerGuid; + bool m_bDidAggroText, m_bSouthernCaveInOpened, m_bNorthernCaveInOpened; + GuidList m_luiSummonedMobGUIDs; + + void Reset() override + { + m_bDidAggroText = false; // Used for 'defend' text, is triggered when the npc is attacked + + if (!HasEscortState(STATE_ESCORT_ESCORTING)) + { + m_uiPhase = 0; + m_uiPhaseTimer = 0; + m_bSouthernCaveInOpened = m_bNorthernCaveInOpened = false; + m_luiSummonedMobGUIDs.clear(); + } + } + + void DoSummonPack(uint8 uiIndex) + { + for (uint8 i = 0; i < MAX_SUMMON_POSITIONS; ++i) + { + // This requires order of the array + if (asSummonInfo[i].uiPosition > uiIndex) + break; + if (asSummonInfo[i].uiPosition == uiIndex) + m_creature->SummonCreature(asSummonInfo[i].uiEntry, asSummonInfo[i].fX, asSummonInfo[i].fY, asSummonInfo[i].fZ, asSummonInfo[i].fO, TEMPSUMMON_DEAD_DESPAWN, 0); + } + } + + void JustSummoned(Creature* pSummoned) override + { + switch (pSummoned->GetEntry()) + { + case NPC_CAVERNDEEP_BURROWER: + case NPC_CAVERNDEEP_AMBUSHER: + { + if (GameObject* pDoor = m_pInstance->GetSingleGameObjectFromStorage(m_uiPhase > 20 ? GO_CAVE_IN_NORTH : GO_CAVE_IN_SOUTH)) + { + float fX, fY, fZ; + pDoor->GetNearPoint(pDoor, fX, fY, fZ, 0.0f, 2.0f, frand(0.0f, 2 * M_PI_F)); + pSummoned->GetMotionMaster()->MovePoint(1, fX, fY, fZ); + } + break; + } + case NPC_GRUBBIS: + // Movement of Grubbis and Add to be handled by DB waypoints + DoScriptText(SAY_GRUBBIS_SPAWN, pSummoned); + break; + } + m_luiSummonedMobGUIDs.push_back(pSummoned->GetObjectGuid()); + } + + void SummonedCreatureJustDied(Creature* pSummoned) override + { + if (pSummoned->GetEntry() == NPC_GRUBBIS) + { + if (m_pInstance) + m_pInstance->SetData(TYPE_GRUBBIS, DONE); + m_uiPhaseTimer = 1000; + } + m_luiSummonedMobGUIDs.remove(pSummoned->GetObjectGuid()); + } + + bool IsPreparingExplosiveCharge() + { + return m_uiPhase == 11 || m_uiPhase == 13 || m_uiPhase == 26 || m_uiPhase == 28; + } + + void MoveInLineOfSight(Unit* pWho) override + { + // In case we are preparing the explosive charges, we won't start attacking mobs + if (IsPreparingExplosiveCharge()) + return; + + npc_escortAI::MoveInLineOfSight(pWho); + } + + void AttackStart(Unit* pWho) override + { + // In case we are preparing the explosive charges, we won't start attacking mobs + if (IsPreparingExplosiveCharge()) + return; + + npc_escortAI::AttackStart(pWho); + } + + void AttackedBy(Unit* pAttacker) override + { + // Possibility for Aggro-Text only once per combat + if (m_bDidAggroText) + return; + + m_bDidAggroText = true; + + if (!urand(0, 2)) + DoScriptText(urand(0, 1) ? SAY_AGGRO_1 : SAY_AGGRO_2, m_creature, pAttacker); + } + + void JustDied(Unit* /*pKiller*/) override + { + if (!m_pInstance) + return; + + m_pInstance->SetData(TYPE_GRUBBIS, FAIL); + + if (m_bSouthernCaveInOpened) // close southern cave-in door + m_pInstance->DoUseDoorOrButton(GO_CAVE_IN_SOUTH); + if (m_bNorthernCaveInOpened) // close northern cave-in door + m_pInstance->DoUseDoorOrButton(GO_CAVE_IN_NORTH); + + for (GuidList::const_iterator itr = m_luiSummonedMobGUIDs.begin(); itr != m_luiSummonedMobGUIDs.end(); ++itr) + { + if (Creature* pSummoned = m_creature->GetMap()->GetCreature(*itr)) + pSummoned->ForcedDespawn(); + } + } + + void StartEvent(Player* pPlayer) + { + if (!m_pInstance) + return; + + m_pInstance->SetData(TYPE_GRUBBIS, IN_PROGRESS); + + m_uiPhase = 1; + m_uiPhaseTimer = 1000; + m_playerGuid = pPlayer->GetObjectGuid(); + } + + void WaypointStart(uint32 uiPointId) override + { + switch (uiPointId) + { + case 10: + // Open Southern Cave-In + if (m_pInstance && !m_bSouthernCaveInOpened) + m_pInstance->DoUseDoorOrButton(GO_CAVE_IN_SOUTH); + m_bSouthernCaveInOpened = true; + break; + case 12: + DoScriptText(SAY_CHARGE_1, m_creature); + break; + case 16: + DoScriptText(SAY_CHARGE_3, m_creature); + // Open Northern Cave-In + if (m_pInstance && !m_bNorthernCaveInOpened) + m_pInstance->DoUseDoorOrButton(GO_CAVE_IN_NORTH); + m_bNorthernCaveInOpened = true; + break; + } + } + + void WaypointReached(uint32 uiPointId) override + { + switch (uiPointId) + { + case 4: + m_uiPhaseTimer = 1000; + break; + case 9: + m_uiPhaseTimer = 2000; + break; + case 11: + m_creature->HandleEmote(EMOTE_STATE_USESTANDING); + m_uiPhaseTimer = 15000; + break; + case 13: + m_creature->HandleEmote(EMOTE_STATE_USESTANDING); + m_uiPhaseTimer = 10000; + break; + case 15: + SetEscortPaused(true); + if (m_pInstance) + { + if (GameObject* pDoor = m_pInstance->GetSingleGameObjectFromStorage(GO_CAVE_IN_SOUTH)) + m_creature->SetFacingToObject(pDoor); + } + DoScriptText(SAY_BLOW_1_10, m_creature); + m_uiPhaseTimer = 5000; + break; + case 16: + m_creature->HandleEmote(EMOTE_STATE_USESTANDING); + m_uiPhaseTimer = 15000; + break; + case 17: + m_creature->HandleEmote(EMOTE_STATE_USESTANDING); + m_uiPhaseTimer = 10000; + break; + case 19: + m_uiPhaseTimer = 2000; + SetEscortPaused(true); // And keep paused from now on! + break; + } + } + + void UpdateEscortAI(uint32 const uiDiff) override + { + // the phases are handled OOC (keeps them in sync with the waypoints) + if (m_uiPhaseTimer && !m_creature->getVictim()) + { + if (m_uiPhaseTimer <= uiDiff) + { + switch (m_uiPhase) + { + case 1: + DoScriptText(SAY_START, m_creature); + m_creature->SetFactionTemporary(FACTION_ESCORT_N_NEUTRAL_PASSIVE, TEMPFACTION_RESTORE_RESPAWN); + m_creature->SetUInt32Value(UNIT_NPC_FLAGS, UNIT_NPC_FLAG_NONE); + m_creature->SetUInt32Value(UNIT_DYNAMIC_FLAGS, 0); + m_uiPhaseTimer = 5000; + break; + case 2: + DoScriptText(SAY_INTRO_1, m_creature); + m_uiPhaseTimer = 3500; // 6s delay, but 2500ms for escortstarting + break; + case 3: + Start(false, m_creature->GetMap()->GetPlayer(m_playerGuid), NULL, false, false); + m_uiPhaseTimer = 0; + break; + + case 4: // Shortly after reached WP 4 + DoScriptText(SAY_INTRO_2, m_creature); + m_uiPhaseTimer = 0; + break; + + case 5: // Shortly after reached WP 9 + DoScriptText(SAY_INTRO_3, m_creature); + m_uiPhaseTimer = 6000; + break; + case 6: + DoScriptText(SAY_INTRO_4, m_creature); + m_uiPhaseTimer = 9000; + break; + case 7: + if (m_pInstance) + { + if (GameObject* pDoor = m_pInstance->GetSingleGameObjectFromStorage(GO_CAVE_IN_SOUTH)) + m_creature->SetFacingToObject(pDoor); + } + m_uiPhaseTimer = 2000; + break; + case 8: + DoScriptText(SAY_LOOK_1, m_creature); + m_uiPhaseTimer = 5000; + break; + case 9: + DoScriptText(SAY_HEAR_1, m_creature); + m_uiPhaseTimer = 2000; + break; + case 10: // Shortly shortly before starting WP 11 + DoSummonPack(1); + m_uiPhaseTimer = 0; + break; + + case 11: // 15s after reached WP 11 + DoSummonPack(2); + + // Summon first explosive charge + if (m_pInstance) + m_pInstance->SetData(TYPE_EXPLOSIVE_CHARGE, DATA_EXPLOSIVE_CHARGE_1); + // Remove EMOTE_STATE_USESTANDING state-emote + m_creature->HandleEmote(EMOTE_ONESHOT_NONE); + + m_uiPhaseTimer = 1; + break; + case 12: // Empty Phase, used to store information about set charge + m_uiPhaseTimer = 0; + break; + + case 13: // 10s after reached WP 13 + DoSummonPack(3); + + // Summon second explosive charge + if (m_pInstance) + m_pInstance->SetData(TYPE_EXPLOSIVE_CHARGE, DATA_EXPLOSIVE_CHARGE_2); + // Remove EMOTE_STATE_USESTANDING state-emote + m_creature->HandleEmote(EMOTE_ONESHOT_NONE); + + m_uiPhaseTimer = 11000; + break; + case 14: // Empty Phase, used to store information about set charge + m_uiPhaseTimer = 1; + break; + case 15: // shortly before starting WP 14 + if (Player* pPlayer = m_creature->GetMap()->GetPlayer(m_playerGuid)) + m_creature->SetFacingToObject(pPlayer); + DoScriptText(SAY_CHARGE_2, m_creature); + m_uiPhaseTimer = 0; + break; + + case 16: // 5s after reaching WP 15 + DoScriptText(SAY_BLOW_1_5, m_creature); + m_uiPhaseTimer = 5000; + break; + case 17: + DoScriptText(SAY_BLOW_1, m_creature); + m_uiPhaseTimer = 1000; + break; + case 18: + DoCastSpellIfCan(m_creature, SPELL_EXPLOSION_SOUTH); + m_uiPhaseTimer = 500; + break; + case 19: + // Close southern cave-in and let charges explode + if (m_pInstance) + { + m_pInstance->DoUseDoorOrButton(GO_CAVE_IN_SOUTH); + m_bSouthernCaveInOpened = false; + m_pInstance->SetData(TYPE_EXPLOSIVE_CHARGE, DATA_EXPLOSIVE_CHARGE_USE); + } + m_uiPhaseTimer = 5000; + break; + case 20: + m_creature->HandleEmote(EMOTE_ONESHOT_CHEER); + m_uiPhaseTimer = 6000; + break; + case 21: + DoScriptText(SAY_FINISH_1, m_creature); + m_uiPhaseTimer = 6000; + break; + case 22: + DoScriptText(SAY_LOOK_2, m_creature); + m_uiPhaseTimer = 3000; + break; + case 23: + if (m_pInstance) + { + if (GameObject* pDoor = m_pInstance->GetSingleGameObjectFromStorage(GO_CAVE_IN_NORTH)) + m_creature->SetFacingToObject(pDoor); + } + m_uiPhaseTimer = 3000; + break; + case 24: + DoScriptText(SAY_HEAR_2, m_creature); + m_uiPhaseTimer = 8000; + break; + case 25: // shortly before starting WP 16 + SetEscortPaused(false); + DoSummonPack(4); + m_uiPhaseTimer = 0; + break; + + case 26: // 15s after reaching WP 16 + DoSummonPack(5); + + // Summon third explosive charge + if (m_pInstance) + m_pInstance->SetData(TYPE_EXPLOSIVE_CHARGE, DATA_EXPLOSIVE_CHARGE_3); + // Remove EMOTE_STATE_USESTANDING state-emote + m_creature->HandleEmote(EMOTE_ONESHOT_NONE); + + m_uiPhaseTimer = 1; + break; + case 27: // Empty Phase, used to store information about set charge + m_uiPhaseTimer = 0; + break; + + case 28: // 10s after reaching WP 17 + DoSummonPack(6); + + // Summon forth explosive charge + if (m_pInstance) + m_pInstance->SetData(TYPE_EXPLOSIVE_CHARGE, DATA_EXPLOSIVE_CHARGE_4); + // Remove EMOTE_STATE_USESTANDING state-emote + m_creature->HandleEmote(EMOTE_ONESHOT_NONE); + + m_uiPhaseTimer = 10000; + break; + case 29: // Empty Phase, used to store information about set charge + m_uiPhaseTimer = 1; + break; + case 30: // shortly before starting WP 18 + DoScriptText(SAY_CHARGE_4, m_creature); + m_uiPhaseTimer = 0; + break; + + case 31: // shortly after reaching WP 19 + if (m_pInstance) + { + if (GameObject* pDoor = m_pInstance->GetSingleGameObjectFromStorage(GO_CAVE_IN_NORTH)) + m_creature->SetFacingToObject(pDoor); + } + DoScriptText(SAY_BLOW_2_10, m_creature); + m_uiPhaseTimer = 5000; + break; + case 32: + DoScriptText(SAY_BLOW_2_5, m_creature); + m_uiPhaseTimer = 1000; + break; + case 33: + DoSummonPack(7); // Summon Grubbis and add + m_uiPhaseTimer = 0; + break; + + case 34: // 1 sek after Death of Grubbis + if (m_pInstance) + { + if (GameObject* pDoor = m_pInstance->GetSingleGameObjectFromStorage(GO_CAVE_IN_NORTH)) + m_creature->SetFacingToObject(pDoor); + } + m_creature->HandleEmote(EMOTE_ONESHOT_CHEER); + m_uiPhaseTimer = 5000; + break; + case 35: + DoScriptText(SAY_BLOW_SOON, m_creature); + m_uiPhaseTimer = 5000; + break; + case 36: + DoScriptText(SAY_BLOW_2, m_creature); + m_uiPhaseTimer = 2000; + break; + case 37: + m_creature->HandleEmote(EMOTE_ONESHOT_POINT); + m_uiPhaseTimer = 1000; + break; + case 38: + DoCastSpellIfCan(m_creature, SPELL_EXPLOSION_NORTH); + m_uiPhaseTimer = 500; + break; + case 39: + // Close northern cave-in and let charges explode + if (m_pInstance) + { + m_pInstance->DoUseDoorOrButton(GO_CAVE_IN_NORTH); + m_bNorthernCaveInOpened = false; + m_pInstance->SetData(TYPE_EXPLOSIVE_CHARGE, DATA_EXPLOSIVE_CHARGE_USE); + } + m_uiPhaseTimer = 8000; + break; + case 40: + DoCastSpellIfCan(m_creature, SPELL_FIREWORKS_RED); + DoScriptText(SAY_FINISH_2, m_creature); + m_uiPhaseTimer = 0; + break; + } + ++m_uiPhase; + } + else + m_uiPhaseTimer -= uiDiff; + } + + if (!m_creature->SelectHostileTarget() || !m_creature->getVictim()) + return; + + DoMeleeAttackIfReady(); + } +}; + +CreatureAI* GetAI_npc_blastmaster_emi_shortfuse(Creature* pCreature) +{ + return new npc_blastmaster_emi_shortfuseAI(pCreature); +} + +bool GossipHello_npc_blastmaster_emi_shortfuse(Player* pPlayer, Creature* pCreature) +{ + if (instance_gnomeregan* pInstance = (instance_gnomeregan*)pCreature->GetInstanceData()) + { + if (pInstance->GetData(TYPE_GRUBBIS) == NOT_STARTED || pInstance->GetData(TYPE_GRUBBIS) == FAIL) + { + pPlayer->ADD_GOSSIP_ITEM_ID(GOSSIP_ICON_CHAT, GOSSIP_ITEM_START, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 1); + pPlayer->SEND_GOSSIP_MENU(pPlayer->GetGossipTextId(pCreature), pCreature->GetObjectGuid()); + } + } + return true; +} + +bool GossipSelect_npc_blastmaster_emi_shortfuse(Player* pPlayer, Creature* pCreature, uint32 /*uiSender*/, uint32 uiAction) +{ + if (uiAction == GOSSIP_ACTION_INFO_DEF + 1) + { + if (instance_gnomeregan* pInstance = (instance_gnomeregan*)pCreature->GetInstanceData()) + { + if (pInstance->GetData(TYPE_GRUBBIS) == NOT_STARTED || pInstance->GetData(TYPE_GRUBBIS) == FAIL) + { + if (npc_blastmaster_emi_shortfuseAI* pEmiAI = dynamic_cast(pCreature->AI())) + pEmiAI->StartEvent(pPlayer); + } + } + } + pPlayer->CLOSE_GOSSIP_MENU(); + + return true; +} + +/*###### +## npc_kernobee +## TODO: It appears there are some things missing, including his? alarm-bot +######*/ + +enum +{ + QUEST_A_FINE_MESS = 2904, + TRIGGER_GNOME_EXIT = 324, // Add scriptlib support for it, atm simply use hardcoded values +}; + +static const float aKernobeePositions[2][3] = +{ + {-330.92f, -3.03f, -152.85f}, // End position + {-297.32f, -7.32f, -152.85f} // Walk out of the door +}; + +struct npc_kernobeeAI : public FollowerAI +{ + npc_kernobeeAI(Creature* pCreature) : FollowerAI(pCreature) + { + m_uiCheckEndposTimer = 10000; + Reset(); + } + + uint32 m_uiCheckEndposTimer; + + void Reset() override {} + + void ReceiveAIEvent(AIEventType eventType, Creature* /*pSender*/, Unit* pInvoker, uint32 uiMiscValue) override + { + if (eventType == AI_EVENT_START_EVENT && pInvoker->GetTypeId() == TYPEID_PLAYER) + { + // No idea why he has UNIT_STAND_STATE_DEAD in UDB .. + m_creature->SetStandState(UNIT_STAND_STATE_STAND); + StartFollow((Player*)pInvoker, 0, GetQuestTemplateStore(uiMiscValue)); + } + } + + void UpdateFollowerAI(const uint32 uiDiff) + { + FollowerAI::UpdateFollowerAI(uiDiff); // Do combat handling + + if (m_creature->IsInCombat() || !HasFollowState(STATE_FOLLOW_INPROGRESS) || HasFollowState(STATE_FOLLOW_COMPLETE)) + return; + + if (m_uiCheckEndposTimer < uiDiff) + { + m_uiCheckEndposTimer = 500; + if (m_creature->IsWithinDist3d(aKernobeePositions[0][0], aKernobeePositions[0][1], aKernobeePositions[0][2], 2 * INTERACTION_DISTANCE)) + { + SetFollowComplete(true); + if (Player* pPlayer = GetLeaderForFollower()) + pPlayer->GroupEventHappens(QUEST_A_FINE_MESS, m_creature); + m_creature->GetMotionMaster()->MovePoint(1, aKernobeePositions[1][0], aKernobeePositions[1][1], aKernobeePositions[1][2], false); + m_creature->ForcedDespawn(2000); + } + } + else + m_uiCheckEndposTimer -= uiDiff; + } +}; + +CreatureAI* GetAI_npc_kernobee(Creature* pCreature) +{ + return new npc_kernobeeAI(pCreature); +} + +bool QuestAccept_npc_kernobee(Player* pPlayer, Creature* pCreature, const Quest* pQuest) +{ + if (pQuest->GetQuestId() == QUEST_A_FINE_MESS) + pCreature->AI()->SendAIEvent(AI_EVENT_START_EVENT, pPlayer, pCreature, pQuest->GetQuestId()); + + return true; +} + +void AddSC_gnomeregan() +{ + Script* pNewScript; + + pNewScript = new Script; + pNewScript->Name = "npc_blastmaster_emi_shortfuse"; + pNewScript->GetAI = &GetAI_npc_blastmaster_emi_shortfuse; + pNewScript->pGossipHello = &GossipHello_npc_blastmaster_emi_shortfuse; + pNewScript->pGossipSelect = &GossipSelect_npc_blastmaster_emi_shortfuse; + pNewScript->RegisterSelf(); + + pNewScript = new Script; + pNewScript->Name = "npc_kernobee"; + pNewScript->GetAI = &GetAI_npc_kernobee; + pNewScript->pQuestAcceptNPC = &QuestAccept_npc_kernobee; + pNewScript->RegisterSelf(); +} diff --git a/src/modules/SD2/scripts/eastern_kingdoms/gnomeregan/gnomeregan.h b/src/modules/SD2/scripts/eastern_kingdoms/gnomeregan/gnomeregan.h new file mode 100644 index 000000000..042e0c065 --- /dev/null +++ b/src/modules/SD2/scripts/eastern_kingdoms/gnomeregan/gnomeregan.h @@ -0,0 +1,87 @@ +/* Copyright (C) 2006 - 2013 ScriptDev2 + * This program is free software licensed under GPL version 2 + * Please see the included DOCS/LICENSE.TXT for more information */ + +#ifndef DEF_GNOMEREGAN_H +#define DEF_GNOMEREGAN_H + +enum +{ + MAX_ENCOUNTER = 2, // Only Grubbis and Thermaplugg need treatment + MAX_GNOME_FACES = 6, + MAX_EXPLOSIVES_PER_SIDE = 2, + + TYPE_GRUBBIS = 1, + TYPE_THERMAPLUGG = 2, + TYPE_EXPLOSIVE_CHARGE = 3, + + DATA_EXPLOSIVE_CHARGE_1 = 1, + DATA_EXPLOSIVE_CHARGE_2 = 2, + DATA_EXPLOSIVE_CHARGE_3 = 3, + DATA_EXPLOSIVE_CHARGE_4 = 4, + DATA_EXPLOSIVE_CHARGE_USE = 5, + + NPC_BLASTMASTER_SHORTFUSE = 7998, + + GO_RED_ROCKET = 103820, + GO_CAVE_IN_NORTH = 146085, + GO_CAVE_IN_SOUTH = 146086, + GO_EXPLOSIVE_CHARGE = 144065, + GO_THE_FINAL_CHAMBER = 142207, + + GO_GNOME_FACE_1 = 142211, + GO_GNOME_FACE_2 = 142210, + GO_GNOME_FACE_3 = 142209, + GO_GNOME_FACE_4 = 142208, + GO_GNOME_FACE_5 = 142213, + GO_GNOME_FACE_6 = 142212, + + GO_BUTTON_1 = 142214, + GO_BUTTON_2 = 142215, + GO_BUTTON_3 = 142216, + GO_BUTTON_4 = 142217, + GO_BUTTON_5 = 142218, + GO_BUTTON_6 = 142219 +}; + +struct sBombFace +{ + ObjectGuid m_gnomeFaceGuid; + bool m_bActivated; + uint32 m_uiBombTimer; +}; + +class instance_gnomeregan : public ScriptedInstance +{ + public: + instance_gnomeregan(Map* pMap); + ~instance_gnomeregan() {} + + void Initialize() override; + + void OnCreatureCreate(Creature* pCreature) override; + void OnObjectCreate(GameObject* pGo) override; + + void SetData(uint32 uiType, uint32 uiData) override; + uint32 GetData(uint32 uiType) const override; + + sBombFace* GetBombFaces(); + void DoActivateBombFace(uint8 uiIndex); + void DoDeactivateBombFace(uint8 uiIndex); + + const char* Save() const override { return m_strInstData.c_str(); } + void Load(const char* chrIn) override; + + protected: + uint32 m_auiEncounter[MAX_ENCOUNTER]; + std::string m_strInstData; + + sBombFace m_asBombFaces[MAX_GNOME_FACES]; + ObjectGuid m_aExplosiveSortedGuids[2][MAX_EXPLOSIVES_PER_SIDE]; + + GuidList m_luiExplosiveChargeGUIDs; + GuidList m_luiSpawnedExplosiveChargeGUIDs; + GuidList m_lRedRocketGUIDs; +}; + +#endif diff --git a/src/modules/SD2/scripts/eastern_kingdoms/gnomeregan/instance_gnomeregan.cpp b/src/modules/SD2/scripts/eastern_kingdoms/gnomeregan/instance_gnomeregan.cpp new file mode 100644 index 000000000..701e11d76 --- /dev/null +++ b/src/modules/SD2/scripts/eastern_kingdoms/gnomeregan/instance_gnomeregan.cpp @@ -0,0 +1,287 @@ +/* Copyright (C) 2006 - 2013 ScriptDev2 + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along 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_Gnomeregan +SD%Complete: 90% +SDComment: Support for Grubbis and Thermaplugg Encounters +SDCategory: Gnomeregan +EndScriptData */ + +#include "precompiled.h" +#include "gnomeregan.h" + +instance_gnomeregan::instance_gnomeregan(Map* pMap) : ScriptedInstance(pMap) +{ + Initialize(); +} + +void instance_gnomeregan::Initialize() +{ + memset(&m_auiEncounter, 0, sizeof(m_auiEncounter)); + + for (uint8 i = 0; i < MAX_GNOME_FACES; ++i) + { + m_asBombFaces[i].m_bActivated = false; + m_asBombFaces[i].m_uiBombTimer = 0; + } +} + +void instance_gnomeregan::OnCreatureCreate(Creature* pCreature) +{ + if (pCreature->GetEntry() == NPC_BLASTMASTER_SHORTFUSE) + m_mNpcEntryGuidStore[NPC_BLASTMASTER_SHORTFUSE] = pCreature->GetObjectGuid(); +} + +void instance_gnomeregan::OnObjectCreate(GameObject* pGo) +{ + switch (pGo->GetEntry()) + { + case GO_CAVE_IN_NORTH: + case GO_CAVE_IN_SOUTH: + case GO_THE_FINAL_CHAMBER: + m_mGoEntryGuidStore[pGo->GetEntry()] = pGo->GetObjectGuid(); + break; + + case GO_RED_ROCKET: m_lRedRocketGUIDs.push_back(pGo->GetObjectGuid()); return; + case GO_EXPLOSIVE_CHARGE: m_luiExplosiveChargeGUIDs.push_back(pGo->GetObjectGuid()); return; + + case GO_GNOME_FACE_1: m_asBombFaces[0].m_gnomeFaceGuid = pGo->GetObjectGuid(); return; + case GO_GNOME_FACE_2: m_asBombFaces[1].m_gnomeFaceGuid = pGo->GetObjectGuid(); return; + case GO_GNOME_FACE_3: m_asBombFaces[2].m_gnomeFaceGuid = pGo->GetObjectGuid(); return; + case GO_GNOME_FACE_4: m_asBombFaces[3].m_gnomeFaceGuid = pGo->GetObjectGuid(); return; + case GO_GNOME_FACE_5: m_asBombFaces[4].m_gnomeFaceGuid = pGo->GetObjectGuid(); return; + case GO_GNOME_FACE_6: m_asBombFaces[5].m_gnomeFaceGuid = pGo->GetObjectGuid(); return; + } +} + +static bool sortFromEastToWest(GameObject* pFirst, GameObject* pSecond) +{ + return pFirst && pSecond && pFirst->GetPositionY() < pSecond->GetPositionY(); +} + +void instance_gnomeregan::SetData(uint32 uiType, uint32 uiData) +{ + switch (uiType) + { + case TYPE_GRUBBIS: + m_auiEncounter[0] = uiData; + if (uiData == IN_PROGRESS) + { + // Sort the explosive charges if needed + if (!m_luiExplosiveChargeGUIDs.empty()) + { + std::list lExplosiveCharges; + for (GuidList::const_iterator itr = m_luiExplosiveChargeGUIDs.begin(); itr != m_luiExplosiveChargeGUIDs.end(); ++itr) + { + if (GameObject* pCharge = instance->GetGameObject(*itr)) + lExplosiveCharges.push_back(pCharge); + } + m_luiExplosiveChargeGUIDs.clear(); + + // Sort from east to west + lExplosiveCharges.sort(sortFromEastToWest); + + // Sort to south and north + uint8 uiCounterSouth = 0; + uint8 uiCounterNorth = 0; + GameObject* pCaveInSouth = GetSingleGameObjectFromStorage(GO_CAVE_IN_SOUTH); + GameObject* pCaveInNorth = GetSingleGameObjectFromStorage(GO_CAVE_IN_NORTH); + if (pCaveInSouth && pCaveInNorth) + { + for (std::list::iterator itr = lExplosiveCharges.begin(); itr != lExplosiveCharges.end(); ++itr) + { + if ((*itr)->GetDistanceOrder(pCaveInSouth, pCaveInNorth) && uiCounterSouth < MAX_EXPLOSIVES_PER_SIDE) + { + m_aExplosiveSortedGuids[0][uiCounterSouth] = (*itr)->GetObjectGuid(); + ++uiCounterSouth; + } + else if (uiCounterNorth < MAX_EXPLOSIVES_PER_SIDE) + { + m_aExplosiveSortedGuids[1][uiCounterNorth] = (*itr)->GetObjectGuid(); + ++uiCounterNorth; + } + } + } + } + } + if (uiData == FAIL) + { + // Despawn possible spawned explosive charges + SetData(TYPE_EXPLOSIVE_CHARGE, DATA_EXPLOSIVE_CHARGE_USE); + } + if (uiData == DONE) + { + for (GuidList::const_iterator itr = m_lRedRocketGUIDs.begin(); itr != m_lRedRocketGUIDs.end(); ++itr) + DoRespawnGameObject(*itr, HOUR); + } + break; + case TYPE_EXPLOSIVE_CHARGE: + switch (uiData) + { + case DATA_EXPLOSIVE_CHARGE_1: + DoRespawnGameObject(m_aExplosiveSortedGuids[0][0], HOUR); + m_luiSpawnedExplosiveChargeGUIDs.push_back(m_aExplosiveSortedGuids[0][0]); + break; + case DATA_EXPLOSIVE_CHARGE_2: + DoRespawnGameObject(m_aExplosiveSortedGuids[0][1], HOUR); + m_luiSpawnedExplosiveChargeGUIDs.push_back(m_aExplosiveSortedGuids[0][1]); + break; + case DATA_EXPLOSIVE_CHARGE_3: + DoRespawnGameObject(m_aExplosiveSortedGuids[1][0], HOUR); + m_luiSpawnedExplosiveChargeGUIDs.push_back(m_aExplosiveSortedGuids[1][0]); + break; + case DATA_EXPLOSIVE_CHARGE_4: + DoRespawnGameObject(m_aExplosiveSortedGuids[1][1], HOUR); + m_luiSpawnedExplosiveChargeGUIDs.push_back(m_aExplosiveSortedGuids[1][1]); + break; + case DATA_EXPLOSIVE_CHARGE_USE: + Creature* pBlastmaster = GetSingleCreatureFromStorage(NPC_BLASTMASTER_SHORTFUSE); + if (!pBlastmaster) + break; + for (GuidList::const_iterator itr = m_luiSpawnedExplosiveChargeGUIDs.begin(); itr != m_luiSpawnedExplosiveChargeGUIDs.end(); ++itr) + { + if (GameObject* pExplosive = instance->GetGameObject(*itr)) + pExplosive->Use(pBlastmaster); + } + m_luiSpawnedExplosiveChargeGUIDs.clear(); + break; + } + return; + case TYPE_THERMAPLUGG: + m_auiEncounter[1] = uiData; + if (uiData == IN_PROGRESS) + { + // Make Door locked + if (GameObject* pDoor = GetSingleGameObjectFromStorage(GO_THE_FINAL_CHAMBER)) + { + pDoor->SetFlag(GAMEOBJECT_FLAGS, GO_FLAG_LOCKED); + if (pDoor->getLootState() == GO_ACTIVATED) + pDoor->ResetDoorOrButton(); + } + + // Always directly activates this bomb-face + DoActivateBombFace(2); + } + else if (uiData == DONE || uiData == FAIL) + { + // Make Door unlocked again + if (GameObject* pDoor = GetSingleGameObjectFromStorage(GO_THE_FINAL_CHAMBER)) + { + pDoor->RemoveFlag(GAMEOBJECT_FLAGS, GO_FLAG_LOCKED); + if (pDoor->getLootState() == GO_READY) + pDoor->UseDoorOrButton(); + } + + // Deactivate all remaining BombFaces + for (uint8 i = 0; i < MAX_GNOME_FACES; ++i) + DoDeactivateBombFace(i); + } + break; + } + + if (uiData == DONE) + { + OUT_SAVE_INST_DATA; + + std::ostringstream saveStream; + saveStream << m_auiEncounter[0] << " " << m_auiEncounter[1]; + + m_strInstData = saveStream.str(); + + SaveToDB(); + OUT_SAVE_INST_DATA_COMPLETE; + } +} + +void instance_gnomeregan::Load(const char* chrIn) +{ + if (!chrIn) + { + OUT_LOAD_INST_DATA_FAIL; + return; + } + + OUT_LOAD_INST_DATA(chrIn); + + std::istringstream loadStream(chrIn); + loadStream >> m_auiEncounter[0] >> m_auiEncounter[1]; + + for (uint8 i = 0; i < MAX_ENCOUNTER; ++i) + { + if (m_auiEncounter[i] == IN_PROGRESS) + m_auiEncounter[i] = NOT_STARTED; + } + + OUT_LOAD_INST_DATA_COMPLETE; +} + +uint32 instance_gnomeregan::GetData(uint32 uiType) const +{ + switch (uiType) + { + case TYPE_GRUBBIS: return m_auiEncounter[0]; + case TYPE_THERMAPLUGG: return m_auiEncounter[1]; + default: + return 0; + } +} + +sBombFace* instance_gnomeregan::GetBombFaces() +{ + return m_asBombFaces; +} + +void instance_gnomeregan::DoActivateBombFace(uint8 uiIndex) +{ + if (uiIndex >= MAX_GNOME_FACES) + return; + + if (!m_asBombFaces[uiIndex].m_bActivated) + { + DoUseDoorOrButton(m_asBombFaces[uiIndex].m_gnomeFaceGuid); + m_asBombFaces[uiIndex].m_bActivated = true; + m_asBombFaces[uiIndex].m_uiBombTimer = 3000; + } +} + +void instance_gnomeregan::DoDeactivateBombFace(uint8 uiIndex) +{ + if (uiIndex >= MAX_GNOME_FACES) + return; + + if (m_asBombFaces[uiIndex].m_bActivated) + { + DoUseDoorOrButton(m_asBombFaces[uiIndex].m_gnomeFaceGuid); + m_asBombFaces[uiIndex].m_bActivated = false; + m_asBombFaces[uiIndex].m_uiBombTimer = 0; + } +} + +InstanceData* GetInstanceData_instance_gnomeregan(Map* pMap) +{ + return new instance_gnomeregan(pMap); +} + +void AddSC_instance_gnomeregan() +{ + Script* pNewScript; + + pNewScript = new Script; + pNewScript->Name = "instance_gnomeregan"; + pNewScript->GetInstanceData = &GetInstanceData_instance_gnomeregan; + pNewScript->RegisterSelf(); +} diff --git a/src/modules/SD2/scripts/eastern_kingdoms/grim_batol/boss_drahga_shadowburner.cpp b/src/modules/SD2/scripts/eastern_kingdoms/grim_batol/boss_drahga_shadowburner.cpp new file mode 100644 index 000000000..48229e5ca --- /dev/null +++ b/src/modules/SD2/scripts/eastern_kingdoms/grim_batol/boss_drahga_shadowburner.cpp @@ -0,0 +1,28 @@ +/* Copyright (C) 2006 - 2013 ScriptDev2 + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +/* ScriptData +SDName: boss_drahga_shadowburner +SD%Complete: 0 +SDComment: Placeholder +SDCategory: Grim Batol +EndScriptData */ + +#include "precompiled.h" + +void AddSC_boss_drahga_shadowburner() +{ +} diff --git a/src/modules/SD2/scripts/eastern_kingdoms/grim_batol/boss_erudax.cpp b/src/modules/SD2/scripts/eastern_kingdoms/grim_batol/boss_erudax.cpp new file mode 100644 index 000000000..769b0f056 --- /dev/null +++ b/src/modules/SD2/scripts/eastern_kingdoms/grim_batol/boss_erudax.cpp @@ -0,0 +1,28 @@ +/* Copyright (C) 2006 - 2013 ScriptDev2 + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +/* ScriptData +SDName: boss_erudax +SD%Complete: 0 +SDComment: Placeholder +SDCategory: Grim Batol +EndScriptData */ + +#include "precompiled.h" + +void AddSC_boss_erudax() +{ +} diff --git a/src/modules/SD2/scripts/eastern_kingdoms/grim_batol/boss_forgemaster_throngus.cpp b/src/modules/SD2/scripts/eastern_kingdoms/grim_batol/boss_forgemaster_throngus.cpp new file mode 100644 index 000000000..6318aae18 --- /dev/null +++ b/src/modules/SD2/scripts/eastern_kingdoms/grim_batol/boss_forgemaster_throngus.cpp @@ -0,0 +1,28 @@ +/* Copyright (C) 2006 - 2013 ScriptDev2 + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +/* ScriptData +SDName: boss_forgemaster_throngus +SD%Complete: 0 +SDComment: Placeholder +SDCategory: Grim Batol +EndScriptData */ + +#include "precompiled.h" + +void AddSC_boss_forgemaster_throngus() +{ +} diff --git a/src/modules/SD2/scripts/eastern_kingdoms/grim_batol/boss_general_umbriss.cpp b/src/modules/SD2/scripts/eastern_kingdoms/grim_batol/boss_general_umbriss.cpp new file mode 100644 index 000000000..be47a0be4 --- /dev/null +++ b/src/modules/SD2/scripts/eastern_kingdoms/grim_batol/boss_general_umbriss.cpp @@ -0,0 +1,28 @@ +/* Copyright (C) 2006 - 2013 ScriptDev2 + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with 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_umbriss +SD%Complete: 0 +SDComment: Placeholder +SDCategory: Grim Batol +EndScriptData */ + +#include "precompiled.h" + +void AddSC_boss_general_umbriss() +{ +} diff --git a/src/modules/SD2/scripts/eastern_kingdoms/grim_batol/grim_batol.h b/src/modules/SD2/scripts/eastern_kingdoms/grim_batol/grim_batol.h new file mode 100644 index 000000000..6b5f9ef47 --- /dev/null +++ b/src/modules/SD2/scripts/eastern_kingdoms/grim_batol/grim_batol.h @@ -0,0 +1,3 @@ +/* Copyright (C) 2006 - 2013 ScriptDev2 + * This program is free software licensed under GPL version 2 + * Please see the included DOCS/LICENSE.TXT for more information */ diff --git a/src/modules/SD2/scripts/eastern_kingdoms/grim_batol/instance_grim_batol.cpp b/src/modules/SD2/scripts/eastern_kingdoms/grim_batol/instance_grim_batol.cpp new file mode 100644 index 000000000..a71009920 --- /dev/null +++ b/src/modules/SD2/scripts/eastern_kingdoms/grim_batol/instance_grim_batol.cpp @@ -0,0 +1,28 @@ +/* Copyright (C) 2006 - 2013 ScriptDev2 + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along 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_grim_batol +SD%Complete: 0 +SDComment: Placeholder +SDCategory: Grim Batol +EndScriptData */ + +#include "precompiled.h" + +void AddSC_instance_grim_batol() +{ +} diff --git a/src/modules/SD2/scripts/eastern_kingdoms/hinterlands.cpp b/src/modules/SD2/scripts/eastern_kingdoms/hinterlands.cpp new file mode 100644 index 000000000..14f2c4d0c --- /dev/null +++ b/src/modules/SD2/scripts/eastern_kingdoms/hinterlands.cpp @@ -0,0 +1,372 @@ +/** + * ScriptDev2 is an extension for mangos providing enhanced features for + * area triggers, creatures, game objects, instances, items, and spells beyond + * the default database scripting in mangos. + * + * Copyright (C) 2006-2013 ScriptDev2 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * World of Warcraft, and all World of Warcraft or Warcraft art, images, + * and lore are copyrighted by Blizzard Entertainment, Inc. + */ + +/** + * ScriptData + * SDName: Hinterlands + * SD%Complete: 100 + * SDComment: Quest support: 836, 2742. + * SDCategory: The Hinterlands + * EndScriptData + */ + +/** + * ContentData + * npc_00x09hl + * npc_rinji + * EndContentData + */ + +#include "precompiled.h" +#include "escort_ai.h" + +/*###### +## npc_00x09hl +######*/ + +enum +{ + SAY_OOX_START = -1000287, + SAY_OOX_AGGRO1 = -1000288, + 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 +}; + +struct npc_00x09hlAI : public npc_escortAI +{ + npc_00x09hlAI(Creature* pCreature) : npc_escortAI(pCreature) { Reset(); } + + void Reset() override { } + + void WaypointReached(uint32 uiPointId) override + { + switch (uiPointId) + { + case 26: + DoScriptText(SAY_OOX_AMBUSH, m_creature); + break; + case 43: + DoScriptText(SAY_OOX_AMBUSH, m_creature); + break; + case 64: + DoScriptText(SAY_OOX_END, m_creature); + if (Player* pPlayer = GetPlayerForEscort()) + { + pPlayer->GroupEventHappens(QUEST_RESQUE_OOX_09, m_creature); + } + break; + } + } + + void WaypointStart(uint32 uiPointId) override + { + switch (uiPointId) + { + case 27: + for (uint8 i = 0; i < 3; ++i) + { + float fX, fY, fZ; + m_creature->GetRandomPoint(147.927444f, -3851.513428f, 130.893f, 7.0f, fX, fY, fZ); + + m_creature->SummonCreature(NPC_MARAUDING_OWL, fX, fY, fZ, 0.0f, TEMPSUMMON_CORPSE_TIMED_DESPAWN, 25000); + } + break; + case 44: + for (uint8 i = 0; i < 3; ++i) + { + float fX, fY, fZ; + m_creature->GetRandomPoint(-141.151581f, -4291.213867f, 120.130f, 7.0f, fX, fY, fZ); + + m_creature->SummonCreature(NPC_VILE_AMBUSHER, fX, fY, fZ, 0.0f, TEMPSUMMON_CORPSE_TIMED_DESPAWN, 25000); + } + break; + } + } + + void Aggro(Unit* pWho) override + { + if (pWho->GetEntry() == NPC_MARAUDING_OWL || pWho->GetEntry() == NPC_VILE_AMBUSHER) + { + return; + } + + DoScriptText(urand(0, 1) ? SAY_OOX_AGGRO1 : SAY_OOX_AGGRO2, m_creature); + } + + void JustSummoned(Creature* pSummoned) override + { + 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); + pCreature->SetFactionTemporary(pPlayer->GetTeam() == ALLIANCE ? FACTION_ESCORT_A_PASSIVE : FACTION_ESCORT_H_PASSIVE, TEMPFACTION_RESTORE_RESPAWN); + + DoScriptText(SAY_OOX_START, pCreature, pPlayer); + + if (npc_00x09hlAI* pEscortAI = dynamic_cast(pCreature->AI())) + { + pEscortAI->Start(false, pPlayer, pQuest); + } + } + return true; +} + +CreatureAI* GetAI_npc_00x09hl(Creature* pCreature) +{ + return new npc_00x09hlAI(pCreature); +} + +/*###### +## npc_rinji +######*/ + +enum +{ + SAY_RIN_FREE = -1000403, + SAY_RIN_BY_OUTRUNNER = -1000404, + SAY_RIN_HELP_1 = -1000405, + SAY_RIN_HELP_2 = -1000406, + 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.29620f, -2839.329346f, 107.388f}, + {70.972466f, -2848.674805f, 109.459f} +}; + +Location m_afAmbushMoveTo[] = +{ + {166.63038f, -2824.780273f, 108.153f}, + {70.886589f, -2874.335449f, 116.675f} +}; + +struct npc_rinjiAI : public npc_escortAI +{ + npc_rinjiAI(Creature* pCreature) : npc_escortAI(pCreature) + { + m_bIsByOutrunner = false; + m_iSpawnId = 0; + Reset(); + } + + bool m_bIsByOutrunner; + uint32 m_uiPostEventCount; + uint32 m_uiPostEventTimer; + int m_iSpawnId; + + void Reset() override + { + m_uiPostEventCount = 0; + m_uiPostEventTimer = 3000; + } + + void JustRespawned() override + { + m_bIsByOutrunner = false; + m_iSpawnId = 0; + + npc_escortAI::JustRespawned(); + } + + void Aggro(Unit* pWho) override + { + if (HasEscortState(STATE_ESCORT_ESCORTING)) + { + if (pWho->GetEntry() == NPC_OUTRUNNER && !m_bIsByOutrunner) + { + DoScriptText(SAY_RIN_BY_OUTRUNNER, pWho); + m_bIsByOutrunner = true; + } + + if (urand(0, 3)) + { + return; + } + + // only if attacked and escorter is not in combat? + DoScriptText(urand(0, 1) ? 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_OOC_OR_CORPSE_DESPAWN, 60000); + + 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_OOC_OR_CORPSE_DESPAWN, 60000); + } + } + + void JustSummoned(Creature* pSummoned) override + { + m_creature->SetWalk(false); + 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) override + { + Player* pPlayer = GetPlayerForEscort(); + + if (!pPlayer) + { + return; + } + + switch (uiPointId) + { + case 1: + DoScriptText(SAY_RIN_FREE, m_creature, pPlayer); + break; + case 7: + DoSpawnAmbush(true); + break; + case 13: + DoSpawnAmbush(false); + break; + case 17: + DoScriptText(SAY_RIN_COMPLETE, m_creature, pPlayer); + pPlayer->GroupEventHappens(QUEST_RINJI_TRAPPED, m_creature); + SetRun(); + m_uiPostEventCount = 1; + break; + } + } + + void UpdateEscortAI(const uint32 uiDiff) override + { + // Check if we have a current target + if (!m_creature->SelectHostileTarget() || !m_creature->getVictim()) + { + if (HasEscortState(STATE_ESCORT_ESCORTING) && m_uiPostEventCount) + { + if (m_uiPostEventTimer < uiDiff) + { + m_uiPostEventTimer = 3000; + + if (Player* pPlayer = GetPlayerForEscort()) + { + switch (m_uiPostEventCount) + { + case 1: + DoScriptText(SAY_RIN_PROGRESS_1, m_creature, pPlayer); + ++m_uiPostEventCount; + break; + case 2: + DoScriptText(SAY_RIN_PROGRESS_2, m_creature, pPlayer); + m_uiPostEventCount = 0; + break; + } + } + else + { + m_creature->ForcedDespawn(); + return; + } + } + 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 = GetClosestGameObjectWithEntry(pCreature, GO_RINJI_CAGE, INTERACTION_DISTANCE)) + { + pGo->UseDoorOrButton(); + } + + if (npc_rinjiAI* pEscortAI = dynamic_cast(pCreature->AI())) + { + pEscortAI->Start(false, pPlayer, pQuest); + } + } + return true; +} + +CreatureAI* GetAI_npc_rinji(Creature* pCreature) +{ + return new npc_rinjiAI(pCreature); +} + +void AddSC_hinterlands() +{ + Script* pNewScript; + + pNewScript = new Script; + pNewScript->Name = "npc_00x09hl"; + pNewScript->GetAI = &GetAI_npc_00x09hl; + pNewScript->pQuestAcceptNPC = &QuestAccept_npc_00x09hl; + pNewScript->RegisterSelf(); + + pNewScript = new Script; + pNewScript->Name = "npc_rinji"; + pNewScript->GetAI = &GetAI_npc_rinji; + pNewScript->pQuestAcceptNPC = &QuestAccept_npc_rinji; + pNewScript->RegisterSelf(); +} diff --git a/src/modules/SD2/scripts/eastern_kingdoms/ironforge.cpp b/src/modules/SD2/scripts/eastern_kingdoms/ironforge.cpp new file mode 100644 index 000000000..3d1d83168 --- /dev/null +++ b/src/modules/SD2/scripts/eastern_kingdoms/ironforge.cpp @@ -0,0 +1,44 @@ +/** + * ScriptDev2 is an extension for mangos providing enhanced features for + * area triggers, creatures, game objects, instances, items, and spells beyond + * the default database scripting in mangos. + * + * Copyright (C) 2006-2013 ScriptDev2 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * World of Warcraft, and all World of Warcraft or Warcraft art, images, + * and lore are copyrighted by Blizzard Entertainment, Inc. + */ + +/** + * ScriptData + * SDName: Ironforge + * SD%Complete: 0 + * SDComment: Placeholder + * SDCategory: Ironforge + * EndScriptData + */ + +/** + * ContentData + * EndContentData + */ + +#include "precompiled.h" + +void AddSC_ironforge() +{ +} diff --git a/src/modules/SD2/scripts/eastern_kingdoms/isle_of_queldanas.cpp b/src/modules/SD2/scripts/eastern_kingdoms/isle_of_queldanas.cpp new file mode 100644 index 000000000..fe4010995 --- /dev/null +++ b/src/modules/SD2/scripts/eastern_kingdoms/isle_of_queldanas.cpp @@ -0,0 +1,96 @@ +/** + * ScriptDev2 is an extension for mangos providing enhanced features for + * area triggers, creatures, game objects, instances, items, and spells beyond + * the default database scripting in mangos. + * + * Copyright (C) 2006-2013 ScriptDev2 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * World of Warcraft, and all World of Warcraft or Warcraft art, images, + * and lore are copyrighted by Blizzard Entertainment, Inc. + */ + +/* ScriptData +SDName: Isle_of_Queldanas +SD%Complete: 100 +SDComment: Quest support: 11524, 11525 +SDCategory: Isle Of Quel'Danas +EndScriptData */ + +/* ContentData +npc_converted_sentry +EndContentData */ + +#include "precompiled.h" + +/*###### +## npc_converted_sentry +######*/ + +enum +{ + SAY_CONVERTED_1 = -1000188, + SAY_CONVERTED_2 = -1000189, + + SPELL_CONVERT_CREDIT = 45009, + TIME_PET_DURATION = 7500 +}; + +struct npc_converted_sentryAI : public ScriptedAI +{ + npc_converted_sentryAI(Creature* pCreature) : ScriptedAI(pCreature) { Reset(); } + + uint32 m_uiCreditTimer; + + void Reset() override + { + m_uiCreditTimer = 2500; + } + + void MoveInLineOfSight(Unit* /*pWho*/) override {} + + void UpdateAI(const uint32 uiDiff) override + { + if (m_uiCreditTimer) + { + if (m_uiCreditTimer <= uiDiff) + { + DoScriptText(urand(0, 1) ? SAY_CONVERTED_1 : SAY_CONVERTED_2, m_creature); + + DoCastSpellIfCan(m_creature, SPELL_CONVERT_CREDIT); + ((Pet*)m_creature)->SetDuration(TIME_PET_DURATION); + m_uiCreditTimer = 0; + } + else + { m_uiCreditTimer -= uiDiff; } + } + } +}; + +CreatureAI* GetAI_npc_converted_sentry(Creature* pCreature) +{ + return new npc_converted_sentryAI(pCreature); +} + +void AddSC_isle_of_queldanas() +{ + Script* pNewScript; + + pNewScript = new Script; + pNewScript->Name = "npc_converted_sentry"; + pNewScript->GetAI = &GetAI_npc_converted_sentry; + pNewScript->RegisterSelf(); +} diff --git a/src/modules/SD2/scripts/eastern_kingdoms/karazhan/boss_curator.cpp b/src/modules/SD2/scripts/eastern_kingdoms/karazhan/boss_curator.cpp new file mode 100644 index 000000000..400380804 --- /dev/null +++ b/src/modules/SD2/scripts/eastern_kingdoms/karazhan/boss_curator.cpp @@ -0,0 +1,228 @@ +/* Copyright (C) 2006 - 2013 ScriptDev2 + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with 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: 90% +SDComment: +SDCategory: Karazhan +EndScriptData */ + +#include "precompiled.h" +#include "karazhan.h" + +enum +{ + SAY_AGGRO = -1532057, + SAY_SUMMON1 = -1532058, + SAY_SUMMON2 = -1532059, + SAY_EVOCATE = -1532060, + SAY_ENRAGE = -1532061, + SAY_KILL1 = -1532062, + SAY_KILL2 = -1532063, + SAY_DEATH = -1532064, + + // Flare + NPC_ASTRAL_FLARE = 17096, + SPELL_ASTRAL_FLARE_PASSIVE = 30234, + SPELL_ASTRAL_FLARE_VISUAL = 30237, + + // The Curator + SPELL_HATEFUL_BOLT = 30383, + SPELL_EVOCATION = 30254, + SPELL_ARCANE_INFUSION = 30403, + SPELL_BERSERK = 26662 +}; + +struct boss_curatorAI : public ScriptedAI +{ + boss_curatorAI(Creature* pCreature) : ScriptedAI(pCreature) + { + m_pInstance = (ScriptedInstance*)pCreature->GetInstanceData(); + Reset(); + } + + ScriptedInstance* m_pInstance; + + uint32 m_uiFlareTimer; + uint32 m_uiHatefulBoltTimer; + uint32 m_uiBerserkTimer; + + bool m_bIsEnraged; + + void Reset() override + { + m_uiFlareTimer = 10000; + m_uiHatefulBoltTimer = 15000; // This time may be wrong + m_uiBerserkTimer = 10 * MINUTE * IN_MILLISECONDS; + m_bIsEnraged = false; + + m_creature->ApplySpellImmune(0, IMMUNITY_DAMAGE, SPELL_SCHOOL_MASK_ARCANE, true); + } + + void KilledUnit(Unit* /*pVictim*/) override + { + DoScriptText(urand(0, 1) ? SAY_KILL1 : SAY_KILL2, m_creature); + } + + void JustDied(Unit* /*pKiller*/) override + { + DoScriptText(SAY_DEATH, m_creature); + + if (m_pInstance) + m_pInstance->SetData(TYPE_CURATOR, DONE); + } + + void Aggro(Unit* /*pWho*/) override + { + DoScriptText(SAY_AGGRO, m_creature); + + if (m_pInstance) + m_pInstance->SetData(TYPE_CURATOR, IN_PROGRESS); + } + + void JustReachedHome() override + { + if (m_pInstance) + m_pInstance->SetData(TYPE_CURATOR, FAIL); + } + + void JustSummoned(Creature* pSummoned) override + { + if (pSummoned->GetEntry() == NPC_ASTRAL_FLARE) + { + // Flare start with aggro on it's target, should be immune to arcane + pSummoned->CastSpell(pSummoned, SPELL_ASTRAL_FLARE_PASSIVE, true); + pSummoned->CastSpell(pSummoned, SPELL_ASTRAL_FLARE_VISUAL, true); + pSummoned->ApplySpellImmune(0, IMMUNITY_DAMAGE, SPELL_SCHOOL_MASK_ARCANE, true); + + if (Unit* pTarget = m_creature->SelectAttackingTarget(ATTACKING_TARGET_RANDOM, 0)) + pSummoned->AI()->AttackStart(pTarget); + } + } + + void UpdateAI(const uint32 uiDiff) override + { + if (!m_creature->SelectHostileTarget() || !m_creature->getVictim()) + return; + + // always decrease BerserkTimer + if (m_uiBerserkTimer) + { + if (m_uiBerserkTimer <= uiDiff) + { + // Also interrupt evocation + m_creature->RemoveAurasDueToSpell(SPELL_EVOCATION); + + if (DoCastSpellIfCan(m_creature, SPELL_BERSERK, CAST_INTERRUPT_PREVIOUS) == CAST_OK) + { + // ScriptText needs confirmation + DoScriptText(SAY_ENRAGE, m_creature); + + // don't know if he's supposed to do summon/evocate after hard enrage (probably not) + m_uiBerserkTimer = 0; + } + } + else + m_uiBerserkTimer -= uiDiff; + } + + // not supposed to do anything while evocate + if (m_creature->HasAura(SPELL_EVOCATION)) + return; + + if (!m_bIsEnraged) + { + if (m_uiFlareTimer < uiDiff) + { + m_uiFlareTimer = 10000; + + // summon Astral Flare + float fX, fY, fZ; + m_creature->GetRandomPoint(m_creature->GetPositionX(), m_creature->GetPositionY(), m_creature->GetPositionZ(), 10.0f, fX, fY, fZ); + m_creature->SummonCreature(NPC_ASTRAL_FLARE, fX, fY, fZ, 0, TEMPSUMMON_TIMED_OOC_DESPAWN, 5000); + + // reduce mana by 10% of maximum + if (int32 iMana = m_creature->GetMaxPower(POWER_MANA)) + { + m_creature->ModifyPower(POWER_MANA, -(iMana / 10)); + + // if this get's us below 10%, then we evocate (the 10th should be summoned now + if (m_creature->GetPower(POWER_MANA) * 10 < m_creature->GetMaxPower(POWER_MANA)) + { + if (DoCastSpellIfCan(m_creature, SPELL_EVOCATION, CAST_INTERRUPT_PREVIOUS) == CAST_OK) + { + DoScriptText(SAY_EVOCATE, m_creature); + // this small delay should make first flare appear fast after evocate, and also prevent possible spawn flood + m_uiFlareTimer = 1000; + } + return; + } + else + { + switch (urand(0, 3)) + { + case 0: DoScriptText(SAY_SUMMON1, m_creature); break; + case 1: DoScriptText(SAY_SUMMON2, m_creature); break; + } + } + } + } + else + m_uiFlareTimer -= uiDiff; + + if (m_creature->GetHealthPercent() < 15.0f) + { + // Also stop evocation + m_creature->RemoveAurasDueToSpell(SPELL_EVOCATION); + + if (DoCastSpellIfCan(m_creature, SPELL_ARCANE_INFUSION, CAST_INTERRUPT_PREVIOUS) == CAST_OK) + { + DoScriptText(SAY_ENRAGE, m_creature); + m_bIsEnraged = true; + } + } + } + + if (m_uiHatefulBoltTimer < uiDiff) + { + if (Unit* pTarget = m_creature->SelectAttackingTarget(ATTACKING_TARGET_TOPAGGRO, 1)) + { + if (DoCastSpellIfCan(pTarget, SPELL_HATEFUL_BOLT) == CAST_OK) + m_uiHatefulBoltTimer = m_bIsEnraged ? 7000 : 15000; + } + } + else + m_uiHatefulBoltTimer -= uiDiff; + + DoMeleeAttackIfReady(); + } +}; + +CreatureAI* GetAI_boss_curator(Creature* pCreature) +{ + return new boss_curatorAI(pCreature); +} + +void AddSC_boss_curator() +{ + Script* pNewScript; + + pNewScript = new Script; + pNewScript->Name = "boss_curator"; + pNewScript->GetAI = &GetAI_boss_curator; + pNewScript->RegisterSelf(); +} diff --git a/src/modules/SD2/scripts/eastern_kingdoms/karazhan/boss_maiden_of_virtue.cpp b/src/modules/SD2/scripts/eastern_kingdoms/karazhan/boss_maiden_of_virtue.cpp new file mode 100644 index 000000000..180a5d4a3 --- /dev/null +++ b/src/modules/SD2/scripts/eastern_kingdoms/karazhan/boss_maiden_of_virtue.cpp @@ -0,0 +1,160 @@ +/* Copyright (C) 2006 - 2013 ScriptDev2 + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with 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" +#include "karazhan.h" + +enum +{ + SAY_AGGRO = -1532018, + SAY_SLAY1 = -1532019, + SAY_SLAY2 = -1532020, + SAY_SLAY3 = -1532021, + SAY_REPENTANCE1 = -1532022, + SAY_REPENTANCE2 = -1532023, + SAY_DEATH = -1532024, + + SPELL_REPENTANCE = 29511, + SPELL_HOLYFIRE = 29522, + SPELL_HOLYWRATH = 32445, + SPELL_HOLYGROUND = 29512 +}; + +struct boss_maiden_of_virtueAI : public ScriptedAI +{ + boss_maiden_of_virtueAI(Creature* pCreature) : ScriptedAI(pCreature) + { + m_pInstance = (ScriptedInstance*)pCreature->GetInstanceData(); + Reset(); + } + + ScriptedInstance* m_pInstance; + + uint32 m_uiRepentanceTimer; + uint32 m_uiHolyfireTimer; + uint32 m_uiHolywrathTimer; + uint32 m_uiHolygroundTimer; + + void Reset() override + { + m_uiRepentanceTimer = urand(25000, 40000); + m_uiHolyfireTimer = urand(8000, 25000); + m_uiHolywrathTimer = urand(15000, 25000); + m_uiHolygroundTimer = 3000; + } + + void KilledUnit(Unit* /*pVictim*/) override + { + switch (urand(0, 5)) // 50% chance to say something out of 3 texts + { + case 0: DoScriptText(SAY_SLAY1, m_creature); break; + case 1: DoScriptText(SAY_SLAY2, m_creature); break; + case 2: DoScriptText(SAY_SLAY3, m_creature); break; + } + } + + void JustDied(Unit* /*pKiller*/) override + { + DoScriptText(SAY_DEATH, m_creature); + + if (m_pInstance) + m_pInstance->SetData(TYPE_MAIDEN, DONE); + } + + void Aggro(Unit* /*pWho*/) override + { + DoScriptText(SAY_AGGRO, m_creature); + + if (m_pInstance) + m_pInstance->SetData(TYPE_MAIDEN, IN_PROGRESS); + } + + void JustReachedHome() override + { + if (m_pInstance) + m_pInstance->SetData(TYPE_MAIDEN, FAIL); + } + + void UpdateAI(const uint32 uiDiff) override + { + if (!m_creature->SelectHostileTarget() || !m_creature->getVictim()) + return; + + if (m_uiHolygroundTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature, SPELL_HOLYGROUND, CAST_TRIGGERED) == CAST_OK) + m_uiHolygroundTimer = 3000; + } + else + m_uiHolygroundTimer -= uiDiff; + + if (m_uiRepentanceTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature, SPELL_REPENTANCE) == CAST_OK) + { + DoScriptText(urand(0, 1) ? SAY_REPENTANCE1 : SAY_REPENTANCE2, m_creature); + m_uiRepentanceTimer = urand(25000, 35000); + } + } + else + m_uiRepentanceTimer -= uiDiff; + + if (m_uiHolyfireTimer < uiDiff) + { + if (Unit* pTarget = m_creature->SelectAttackingTarget(ATTACKING_TARGET_RANDOM, 0, SPELL_HOLYFIRE, SELECT_FLAG_NOT_IN_MELEE_RANGE)) + { + if (DoCastSpellIfCan(pTarget, SPELL_HOLYFIRE) == CAST_OK) + m_uiHolyfireTimer = urand(8000, 23000); + } + } + else + m_uiHolyfireTimer -= uiDiff; + + if (m_uiHolywrathTimer < uiDiff) + { + if (Unit* pTarget = m_creature->SelectAttackingTarget(ATTACKING_TARGET_RANDOM, 0)) + DoCastSpellIfCan(pTarget, SPELL_HOLYWRATH); + + m_uiHolywrathTimer = urand(20000, 25000); + } + else + m_uiHolywrathTimer -= uiDiff; + + DoMeleeAttackIfReady(); + } +}; + +CreatureAI* GetAI_boss_maiden_of_virtue(Creature* pCreature) +{ + return new boss_maiden_of_virtueAI(pCreature); +} + +void AddSC_boss_maiden_of_virtue() +{ + Script* pNewScript; + + pNewScript = new Script; + pNewScript->Name = "boss_maiden_of_virtue"; + pNewScript->GetAI = &GetAI_boss_maiden_of_virtue; + pNewScript->RegisterSelf(); +} diff --git a/src/modules/SD2/scripts/eastern_kingdoms/karazhan/boss_midnight.cpp b/src/modules/SD2/scripts/eastern_kingdoms/karazhan/boss_midnight.cpp new file mode 100644 index 000000000..d5ca5501b --- /dev/null +++ b/src/modules/SD2/scripts/eastern_kingdoms/karazhan/boss_midnight.cpp @@ -0,0 +1,357 @@ +/* Copyright (C) 2006 - 2013 ScriptDev2 + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with 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" +#include "karazhan.h" + +enum +{ + SAY_MIDNIGHT_KILL = -1532000, + SAY_APPEAR1 = -1532001, + SAY_APPEAR2 = -1532002, + SAY_APPEAR3 = -1532003, + SAY_MOUNT = -1532004, + SAY_KILL1 = -1532005, + SAY_KILL2 = -1532006, + SAY_DISARMED = -1532007, + SAY_DEATH = -1532008, + SAY_RANDOM1 = -1532009, + SAY_RANDOM2 = -1532010, + + // Midnight + SPELL_MOUNT = 29770, + SPELL_KNOCKDOWN = 29711, + SPELL_SUMMON_ATTUMEN = 29714, + SPELL_SUMMON_ATTUMEN_MOUNTED = 29799, + + // Attumen + SPELL_SHADOWCLEAVE = 29832, + SPELL_INTANGIBLE_PRESENCE = 29833, + SPELL_UPPERCUT = 29850, + SPELL_BERSERKER_CHARGE = 26561, // Only when mounted + + NPC_ATTUMEN_MOUNTED = 16152, +}; + +struct boss_midnightAI : public ScriptedAI +{ + boss_midnightAI(Creature* pCreature) : ScriptedAI(pCreature) + { + m_pInstance = (instance_karazhan*)pCreature->GetInstanceData(); + Reset(); + } + + instance_karazhan* m_pInstance; + + uint8 m_uiPhase; + uint32 m_uiKnockDown; + + void Reset() override + { + m_uiPhase = 0; + m_uiKnockDown = urand(6000, 9000); + + SetCombatMovement(true); + } + + void Aggro(Unit* /*pWho*/) override + { + if (m_pInstance) + m_pInstance->SetData(TYPE_ATTUMEN, IN_PROGRESS); + } + + void KilledUnit(Unit* /*pVictim*/) override + { + if (m_uiPhase == 1 && m_pInstance) + { + if (Creature* pAttumen = m_pInstance->GetSingleCreatureFromStorage(NPC_ATTUMEN)) + DoScriptText(SAY_MIDNIGHT_KILL, pAttumen); + } + } + + void JustReachedHome() override + { + if (m_pInstance) + m_pInstance->SetData(TYPE_ATTUMEN, FAIL); + } + + void JustSummoned(Creature* pSummoned) override + { + if (m_creature->getVictim()) + pSummoned->AI()->AttackStart(m_creature->getVictim()); + + if (pSummoned->GetEntry() == NPC_ATTUMEN) + { + // Attumen yells when spawned + switch (urand(0, 2)) + { + case 0: DoScriptText(SAY_APPEAR1, pSummoned); break; + case 1: DoScriptText(SAY_APPEAR2, pSummoned); break; + case 2: DoScriptText(SAY_APPEAR3, pSummoned); break; + } + } + else if (pSummoned->GetEntry() == NPC_ATTUMEN_MOUNTED) + { + DoScriptText(SAY_MOUNT, pSummoned); + + if (!m_pInstance) + return; + + // The summoned has the health equal to the one which has the higher HP percentage of both + if (Creature* pAttumen = m_pInstance->GetSingleCreatureFromStorage(NPC_ATTUMEN)) + pSummoned->SetHealth(pAttumen->GetHealth() > m_creature->GetHealth() ? pAttumen->GetHealth() : m_creature->GetHealth()); + } + } + + void MovementInform(uint32 uiMoveType, uint32 uiPointId) override + { + if (uiMoveType != POINT_MOTION_TYPE || !uiPointId || !m_pInstance) + return; + + // Spawn the mounted Attumen and despawn + if (DoCastSpellIfCan(m_creature, SPELL_SUMMON_ATTUMEN_MOUNTED, CAST_TRIGGERED) == CAST_OK) + { + if (Creature* pAttumen = m_pInstance->GetSingleCreatureFromStorage(NPC_ATTUMEN)) + pAttumen->ForcedDespawn(); + + m_creature->ForcedDespawn(); + } + } + + // Wrapper to prepare phase 3 + void DoPrepareMount(Creature* pTarget) + { + if (pTarget) + { + m_uiPhase = 2; + + SetCombatMovement(false); + m_creature->GetMotionMaster()->MovePoint(1, pTarget->GetPositionX(), pTarget->GetPositionY(), pTarget->GetPositionZ()); + } + } + + void UpdateAI(const uint32 uiDiff) override + { + if (!m_creature->SelectHostileTarget() || !m_creature->getVictim()) + return; + + // Stop attacking during the mount phase + if (m_uiPhase == 2) + return; + + // Spawn Attumen on 95% hp + if (m_uiPhase == 0 && m_creature->GetHealthPercent() < 95.0f) + { + if (DoCastSpellIfCan(m_creature, SPELL_SUMMON_ATTUMEN) == CAST_OK) + m_uiPhase = 1; + } + + // Spawn Attumen mounted at 25% + if (m_uiPhase == 1 && m_creature->GetHealthPercent() < 25.0f) + { + if (DoCastSpellIfCan(m_creature, SPELL_MOUNT, CAST_TRIGGERED) == CAST_OK) + m_uiPhase = 2; + } + + if (m_uiKnockDown < uiDiff) + { + if (DoCastSpellIfCan(m_creature->getVictim(), SPELL_KNOCKDOWN) == CAST_OK) + m_uiKnockDown = urand(6000, 9000); + } + else + m_uiKnockDown -= uiDiff; + + DoMeleeAttackIfReady(); + } +}; + +CreatureAI* GetAI_boss_midnight(Creature* pCreature) +{ + return new boss_midnightAI(pCreature); +} + +struct boss_attumenAI : public ScriptedAI +{ + boss_attumenAI(Creature* pCreature) : ScriptedAI(pCreature) + { + m_pInstance = (instance_karazhan*)pCreature->GetInstanceData(); + Reset(); + } + + instance_karazhan* m_pInstance; + + uint32 m_uiCleaveTimer; + uint32 m_uiCurseTimer; + uint32 m_uiRandomYellTimer; + uint32 m_uiKnockDown; + uint32 m_uiChargeTimer; // only when mounted + + bool m_bHasSummonRider; + + void Reset() override + { + m_uiCleaveTimer = urand(10000, 16000); + m_uiCurseTimer = 30000; + m_uiRandomYellTimer = urand(30000, 60000); // Occasionally yell + m_uiChargeTimer = 20000; + m_uiKnockDown = urand(6000, 9000); + + m_bHasSummonRider = false; + } + + void KilledUnit(Unit* /*pVictim*/) override + { + DoScriptText(urand(0, 1) ? SAY_KILL1 : SAY_KILL2, m_creature); + } + + void SpellHit(Unit* /*pSource*/, const SpellEntry* pSpell) override + { + if (pSpell->GetMechanic() == MECHANIC_DISARM) + DoScriptText(SAY_DISARMED, m_creature); + } + + void JustDied(Unit* /*pVictim*/) override + { + DoScriptText(SAY_DEATH, m_creature); + + if (m_pInstance) + m_pInstance->SetData(TYPE_ATTUMEN, DONE); + } + + void JustReachedHome() override + { + if (m_pInstance) + m_pInstance->SetData(TYPE_ATTUMEN, FAIL); + + // Despawn Attumen on fail + m_creature->ForcedDespawn(); + } + + void UpdateAI(const uint32 uiDiff) override + { + if (!m_creature->SelectHostileTarget() || !m_creature->getVictim()) + return; + + if (m_uiCleaveTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature->getVictim(), SPELL_SHADOWCLEAVE) == CAST_OK) + m_uiCleaveTimer = urand(10000, 16000); + } + else + m_uiCleaveTimer -= uiDiff; + + if (m_uiCurseTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature, SPELL_INTANGIBLE_PRESENCE) == CAST_OK) + m_uiCurseTimer = 30000; + } + else + m_uiCurseTimer -= uiDiff; + + if (m_uiRandomYellTimer < uiDiff) + { + DoScriptText(urand(0, 1) ? SAY_RANDOM1 : SAY_RANDOM2, m_creature); + m_uiRandomYellTimer = urand(30000, 60000); + } + else + m_uiRandomYellTimer -= uiDiff; + + if (m_uiKnockDown < uiDiff) + { + // Cast knockdown when mounted, otherwise uppercut + if (DoCastSpellIfCan(m_creature->getVictim(), m_creature->GetEntry() == NPC_ATTUMEN_MOUNTED ? SPELL_KNOCKDOWN : SPELL_UPPERCUT) == CAST_OK) + m_uiKnockDown = urand(6000, 9000); + } + else + m_uiKnockDown -= uiDiff; + + // If creature is mounted then cast charge + if (m_creature->GetEntry() == NPC_ATTUMEN_MOUNTED) + { + if (m_uiChargeTimer < uiDiff) + { + if (Unit* pTarget = m_creature->SelectAttackingTarget(ATTACKING_TARGET_RANDOM, 0, SPELL_BERSERKER_CHARGE, SELECT_FLAG_NOT_IN_MELEE_RANGE | SELECT_FLAG_IN_LOS)) + { + if (DoCastSpellIfCan(pTarget, SPELL_BERSERKER_CHARGE) == CAST_OK) + m_uiChargeTimer = 20000; + } + else + m_uiChargeTimer = 2000; + } + else + m_uiChargeTimer -= uiDiff; + } + // Else, mount if below 25% + else if (!m_bHasSummonRider && m_creature->GetHealthPercent() < 25.0f) + { + if (Creature* pMidnight = m_pInstance->GetSingleCreatureFromStorage(NPC_MIDNIGHT)) + { + pMidnight->CastSpell(m_creature, SPELL_MOUNT, true); + m_bHasSummonRider = true; + } + } + + DoMeleeAttackIfReady(); + } +}; + +CreatureAI* GetAI_boss_attumen(Creature* pCreature) +{ + return new boss_attumenAI(pCreature); +} + +bool EffectDummyCreature_spell_mount_attumen(Unit* pCaster, uint32 uiSpellId, SpellEffectIndex uiEffIndex, Creature* pCreatureTarget, ObjectGuid /*originalCasterGuid*/) +{ + // always check spellid and effectindex + if (uiSpellId == SPELL_MOUNT && uiEffIndex == EFFECT_INDEX_0) + { + // Avoid possible DB errors + if (pCaster->GetEntry() == NPC_MIDNIGHT && pCreatureTarget->GetEntry() == NPC_ATTUMEN) + { + // Prepare for mount + if (boss_midnightAI* pMidnightAI = dynamic_cast(((Creature*)pCaster)->AI())) + pMidnightAI->DoPrepareMount(pCreatureTarget); + } + + // always return true when we are handling this spell and effect + return true; + } + + return false; +} + +void AddSC_boss_attumen() +{ + Script* pNewScript; + + pNewScript = new Script; + pNewScript->Name = "boss_attumen"; + pNewScript->GetAI = &GetAI_boss_attumen; + pNewScript->pEffectDummyNPC = &EffectDummyCreature_spell_mount_attumen; + pNewScript->RegisterSelf(); + + pNewScript = new Script; + pNewScript->Name = "boss_midnight"; + pNewScript->GetAI = &GetAI_boss_midnight; + pNewScript->RegisterSelf(); +} diff --git a/src/modules/SD2/scripts/eastern_kingdoms/karazhan/boss_moroes.cpp b/src/modules/SD2/scripts/eastern_kingdoms/karazhan/boss_moroes.cpp new file mode 100644 index 000000000..d6bf1067d --- /dev/null +++ b/src/modules/SD2/scripts/eastern_kingdoms/karazhan/boss_moroes.cpp @@ -0,0 +1,284 @@ +/* Copyright (C) 2006 - 2013 ScriptDev2 + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with 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: Timers. +SDCategory: Karazhan +EndScriptData */ + +#include "precompiled.h" +#include "karazhan.h" + +enum +{ + MAX_ACTIVE_GUESTS = 4, + MAX_GUESTS = 6, + + SAY_AGGRO = -1532011, + SAY_SPECIAL_1 = -1532012, + SAY_SPECIAL_2 = -1532013, + SAY_KILL_1 = -1532014, + SAY_KILL_2 = -1532015, + SAY_KILL_3 = -1532016, + SAY_DEATH = -1532017, + + SPELL_VANISH = 29448, + SPELL_GARROTE = 37066, + SPELL_BLIND = 34694, + SPELL_GOUGE = 29425, + SPELL_FRENZY = 37023 +}; + +static const float afLocations[MAX_ACTIVE_GUESTS][4] = +{ + { -10991.0f, -1884.33f, 81.73f, 0.614315f}, + { -10989.4f, -1885.88f, 81.73f, 0.904913f}, + { -10978.1f, -1887.07f, 81.73f, 2.035550f}, + { -10975.9f, -1885.81f, 81.73f, 2.253890f} +}; + +static const uint32 auiGuests[MAX_GUESTS] = +{ + NPC_LADY_KEIRA_BERRYBUCK, + NPC_LADY_CATRIONA_VON_INDI, + NPC_LORD_CRISPIN_FERENCE, + NPC_BARON_RAFE_DREUGER, + NPC_BARONESS_DOROTHEA_MILLSTIPE, + NPC_LORD_ROBIN_DARIS +}; + +struct boss_moroesAI : public ScriptedAI +{ + boss_moroesAI(Creature* pCreature) : ScriptedAI(pCreature) + { + m_pInstance = (ScriptedInstance*)pCreature->GetInstanceData(); + Reset(); + } + + ScriptedInstance* m_pInstance; + + std::vector m_vGuestsEntryList; + + uint32 m_uiVanishTimer; + uint32 m_uiBlindTimer; + uint32 m_uiGougeTimer; + uint32 m_uiWaitTimer; + + bool m_bEnrage; + + void Reset() override + { + m_uiVanishTimer = 30000; + m_uiBlindTimer = 35000; + m_uiGougeTimer = 23000; + m_uiWaitTimer = 0; + + m_bEnrage = false; + + DoSpawnGuests(); + } + + void Aggro(Unit* /*pWho*/) override + { + DoScriptText(SAY_AGGRO, m_creature); + + if (m_pInstance) + m_pInstance->SetData(TYPE_MOROES, IN_PROGRESS); + } + + void KilledUnit(Unit* /*pVictim*/) override + { + switch (urand(0, 2)) + { + case 0: DoScriptText(SAY_KILL_1, m_creature); break; + case 1: DoScriptText(SAY_KILL_2, m_creature); break; + case 2: DoScriptText(SAY_KILL_3, m_creature); break; + } + } + + void JustReachedHome() override + { + DoRemoveGarroteAura(); + + if (m_pInstance) + m_pInstance->SetData(TYPE_MOROES, FAIL); + } + + void JustDied(Unit* /*pVictim*/) override + { + DoScriptText(SAY_DEATH, m_creature); + DoRemoveGarroteAura(); + + if (m_pInstance) + m_pInstance->SetData(TYPE_MOROES, DONE); + } + + void EnterEvadeMode() override + { + // Don't evade during vanish phase + if (m_uiWaitTimer) + return; + + ScriptedAI::EnterEvadeMode(); + } + + void DoSpawnGuests() + { + // not if m_creature are dead, so avoid + if (!m_creature->IsAlive()) + return; + + // it's empty, so first time + if (m_vGuestsEntryList.empty()) + { + // pre-allocate size for speed + m_vGuestsEntryList.resize(MAX_GUESTS); + + // fill vector array with entries from creature array + for (uint8 i = 0; i < MAX_GUESTS; ++i) + m_vGuestsEntryList[i] = auiGuests[i]; + + std::random_shuffle(m_vGuestsEntryList.begin(), m_vGuestsEntryList.end()); + + // Summon the 4 entries + for (uint8 i = 0; i < MAX_ACTIVE_GUESTS; ++i) + m_creature->SummonCreature(m_vGuestsEntryList[i], afLocations[i][0], afLocations[i][1], afLocations[i][2], afLocations[i][3], TEMPSUMMON_CORPSE_DESPAWN, 0); + } + // Resummon the killed adds + else + { + if (!m_pInstance) + return; + + for (uint8 i = 0; i < MAX_ACTIVE_GUESTS; ++i) + { + // If we already have the creature on the map, then don't summon it + if (m_pInstance->GetSingleCreatureFromStorage(m_vGuestsEntryList[i], true)) + continue; + + m_creature->SummonCreature(m_vGuestsEntryList[i], afLocations[i][0], afLocations[i][1], afLocations[i][2], afLocations[i][3], TEMPSUMMON_CORPSE_DESPAWN, 0); + } + } + } + + // Wrapper to remove the Garrote aura on death and on evade - ToDo: maybe find a better way for this! + void DoRemoveGarroteAura() + { + // 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)) + i->getSource()->RemoveAurasDueToSpell(SPELL_GARROTE); + } + } + } + + void UpdateAI(const uint32 uiDiff) override + { + if (!m_creature->SelectHostileTarget() || !m_creature->getVictim()) + return; + + // Note: because the Vanish spell adds invisibility effect on the target, the timers won't be decreased during the vanish phase + if (m_uiWaitTimer) + { + if (m_uiWaitTimer <= uiDiff) + { + // It's not very clear how to handle this spell + if (Unit* pTarget = m_creature->SelectAttackingTarget(ATTACKING_TARGET_RANDOM, 0)) + { + DoScriptText(urand(0, 1) ? SAY_SPECIAL_1 : SAY_SPECIAL_2, m_creature); + DoResetThreat(); + AttackStart(pTarget); + pTarget->CastSpell(pTarget, SPELL_GARROTE, true); + } + m_uiWaitTimer = 0; + } + else + m_uiWaitTimer -= uiDiff; + + // Don't user other abilities in vanish + return; + } + + if (!m_bEnrage && m_creature->GetHealthPercent() < 30.0f) + { + if (DoCastSpellIfCan(m_creature, SPELL_FRENZY) == CAST_OK) + m_bEnrage = true; + } + + // No other spells are cast after enrage + if (!m_bEnrage) + { + if (m_uiVanishTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature, SPELL_VANISH) == CAST_OK) + { + m_uiVanishTimer = 30000; + m_uiWaitTimer = 1000; + } + } + else + m_uiVanishTimer -= uiDiff; + + // Gouge highest aggro, and attack second highest + if (m_uiGougeTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature->getVictim(), SPELL_GOUGE) == CAST_OK) + m_uiGougeTimer = 40000; + } + else + m_uiGougeTimer -= uiDiff; + + if (m_uiBlindTimer < uiDiff) + { + if (Unit* pTarget = m_creature->SelectAttackingTarget(ATTACKING_TARGET_RANDOM, 0, SPELL_BLIND, SELECT_FLAG_PLAYER)) + { + if (DoCastSpellIfCan(pTarget, SPELL_BLIND) == CAST_OK) + m_uiBlindTimer = 40000; + } + } + else + m_uiBlindTimer -= uiDiff; + } + + DoMeleeAttackIfReady(); + } +}; + +CreatureAI* GetAI_boss_moroes(Creature* pCreature) +{ + return new boss_moroesAI(pCreature); +} + +void AddSC_boss_moroes() +{ + Script* pNewScript; + + pNewScript = new Script; + pNewScript->Name = "boss_moroes"; + pNewScript->GetAI = &GetAI_boss_moroes; + pNewScript->RegisterSelf(); +} diff --git a/src/modules/SD2/scripts/eastern_kingdoms/karazhan/boss_netherspite.cpp b/src/modules/SD2/scripts/eastern_kingdoms/karazhan/boss_netherspite.cpp new file mode 100644 index 000000000..f0fdb864a --- /dev/null +++ b/src/modules/SD2/scripts/eastern_kingdoms/karazhan/boss_netherspite.cpp @@ -0,0 +1,433 @@ +/* Copyright (C) 2006 - 2013 ScriptDev2 + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with 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: 75 +SDComment: Nether portals partially implemented. Find spell ID for tail swipe added in patch 3.0.2 +SDCategory: Karazhan +EndScriptData */ + +#include "precompiled.h" +#include "karazhan.h" + +enum +{ + // netherspite spells + SPELL_NETHERBURN = 30522, + SPELL_VOID_ZONE = 37063, + SPELL_NETHERBREATH = 38523, + SPELL_EMPOWERMENT = 38549, + SPELL_NETHER_INFUSION = 38688, // hard enrage spell + SPELL_NETHERSPITE_ROAR = 38684, // on banish phase begin + SPELL_SHADOWFORM = 38542, // banish visual spell + SPELL_FACE_RANDOM_TARGET = 38546, // triggered by spell 38684 - currently not used + SPELL_PORTAL_ATTUNEMENT = 30425, + + // void zone spells + SPELL_CONSUMPTION = 28865, + + // ***** Netherspite portals spells ***** // + // beam buffs + SPELL_SERENITY_NS = 30467, + SPELL_SERENITY_PLR = 30422, + SPELL_DOMINANCE_NS = 30468, + SPELL_DOMINANCE_PLR = 30423, + SPELL_PERSEVERENCE_NS = 30466, + SPELL_PERSEVERENCE_PLR = 30421, + + // beam debuffs (player with this aura cannot gain the same color buff) + SPELL_EXHAUSTION_SER = 38638, + SPELL_EXHAUSTION_DOM = 38639, + SPELL_EXHAUSTION_PER = 38637, + + // spells which hit players (used only for visual - as seen from spell description) + SPELL_BEAM_SER = 30401, + SPELL_BEAM_DOM = 30402, + SPELL_BEAM_PER = 30400, + + // spells which hit Netherspite + SPELL_BEAM_GREEN = 30464, + SPELL_BEAM_BLUE = 30463, + SPELL_BEAM_RED = 30465, + + // portal visual spells + SPELL_GREEN_PORTAL = 30490, + SPELL_BLUE_PORTAL = 30491, + SPELL_RED_PORTAL = 30487, + + // passive auras + SPELL_SERENITY_PASSIVE = 30397, + SPELL_DOMINANCE_PASSIVE = 30398, + // note: for Perseverence, there isn't any passive spell - currently we use script timer + SPELL_NETHER_BEAM = 30469, // spell triggered by the passive auras + // SPELL_CLEAR_NETHER_BEAM = 37072, // not clear how to use this + + // emotes + EMOTE_PHASE_BEAM = -1532089, + EMOTE_PHASE_BANISH = -1532090, + + // npcs + NPC_PORTAL_GREEN = 17367, + NPC_PORTAL_BLUE = 17368, + NPC_PORTAL_RED = 17369, + NPC_VOID_ZONE = 16697, + + MAX_PORTALS = 3, +}; + +struct SpawnLocation +{ + float fX, fY, fZ, fO; +}; + +// at first spawn portals got fixed coords, should be shuffled in subsequent beam phases +static const SpawnLocation aPortalCoordinates[MAX_PORTALS] = +{ + { -11195.14f, -1616.375f, 278.3217f, 6.230825f}, + { -11108.13f, -1602.839f, 280.0323f, 3.717551f}, + { -11139.78f, -1681.278f, 278.3217f, 1.396263f}, +}; + +enum NetherspitePhases +{ + BEAM_PHASE = 0, + BANISH_PHASE = 1, +}; + +static const uint32 auiPortals[MAX_PORTALS] = +{ + NPC_PORTAL_GREEN, + NPC_PORTAL_BLUE, + NPC_PORTAL_RED, +}; + +/*###### +## boss_netherspite +######*/ + +struct boss_netherspiteAI : public ScriptedAI +{ + boss_netherspiteAI(Creature* pCreature) : ScriptedAI(pCreature) + { + m_pInstance = (ScriptedInstance*)pCreature->GetInstanceData(); + Reset(); + } + + ScriptedInstance* m_pInstance; + + NetherspitePhases m_uiActivePhase; + + uint32 m_uiEnrageTimer; + uint32 m_uiVoidZoneTimer; + uint32 m_uiPhaseSwitchTimer; + uint32 m_uiNetherbreathTimer; + uint32 m_uiEmpowermentTimer; + + std::vector m_vPortalEntryList; + + void Reset() override + { + m_uiActivePhase = BEAM_PHASE; + + m_uiEmpowermentTimer = 10000; + m_uiEnrageTimer = 9 * MINUTE * IN_MILLISECONDS; + m_uiVoidZoneTimer = 15000; + m_uiPhaseSwitchTimer = MINUTE * IN_MILLISECONDS; + + SetCombatMovement(true); + + // initialize the portal list + m_vPortalEntryList.clear(); + m_vPortalEntryList.resize(MAX_PORTALS); + + for (uint8 i = 0; i < MAX_PORTALS; ++i) + m_vPortalEntryList[i] = auiPortals[i]; + } + + void Aggro(Unit* /*pWho*/) override + { + if (m_pInstance) + m_pInstance->SetData(TYPE_NETHERSPITE, IN_PROGRESS); + + DoSummonPortals(); + DoCastSpellIfCan(m_creature, SPELL_NETHERBURN); + } + + void JustDied(Unit* /*pKiller*/) override + { + if (m_pInstance) + m_pInstance->SetData(TYPE_NETHERSPITE, DONE); + } + + void JustReachedHome() override + { + if (m_pInstance) + m_pInstance->SetData(TYPE_NETHERSPITE, FAIL); + } + + void SwitchPhases() + { + if (m_uiActivePhase == BEAM_PHASE) + { + if (DoCastSpellIfCan(m_creature, SPELL_NETHERSPITE_ROAR) == CAST_OK) + { + DoCastSpellIfCan(m_creature, SPELL_SHADOWFORM, CAST_TRIGGERED); + m_creature->RemoveAurasDueToSpell(SPELL_EMPOWERMENT); + + SetCombatMovement(false); + m_creature->GetMotionMaster()->MoveIdle(); + + m_uiActivePhase = BANISH_PHASE; + DoScriptText(EMOTE_PHASE_BANISH, m_creature); + + m_uiNetherbreathTimer = 2000; + m_uiPhaseSwitchTimer = 30000; + } + } + else + { + m_creature->RemoveAurasDueToSpell(SPELL_SHADOWFORM); + SetCombatMovement(true); + DoStartMovement(m_creature->getVictim()); + + m_uiActivePhase = BEAM_PHASE; + DoScriptText(EMOTE_PHASE_BEAM, m_creature); + + DoSummonPortals(); + m_uiEmpowermentTimer = 10000; + m_uiPhaseSwitchTimer = MINUTE * IN_MILLISECONDS; + } + + // reset threat every phase switch + DoResetThreat(); + } + + void DoSummonPortals() + { + for (uint8 i = 0; i < MAX_PORTALS; ++i) + m_creature->SummonCreature(m_vPortalEntryList[i], aPortalCoordinates[i].fX, aPortalCoordinates[i].fY, aPortalCoordinates[i].fZ, aPortalCoordinates[i].fO, TEMPSUMMON_TIMED_DESPAWN, 60000); + + // randomize the portals after the first summon + std::random_shuffle(m_vPortalEntryList.begin(), m_vPortalEntryList.end()); + } + + void JustSummoned(Creature* pSummoned) override + { + switch (pSummoned->GetEntry()) + { + case NPC_VOID_ZONE: + pSummoned->CastSpell(pSummoned, SPELL_CONSUMPTION, false); + break; + case NPC_PORTAL_RED: + pSummoned->CastSpell(pSummoned, SPELL_RED_PORTAL, false); + break; + case NPC_PORTAL_GREEN: + pSummoned->CastSpell(pSummoned, SPELL_GREEN_PORTAL, false); + break; + case NPC_PORTAL_BLUE: + pSummoned->CastSpell(pSummoned, SPELL_BLUE_PORTAL, false); + break; + } + } + + void UpdateAI(const uint32 uiDiff) override + { + if (!m_creature->SelectHostileTarget() || !m_creature->getVictim()) + return; + + if (m_uiPhaseSwitchTimer <= uiDiff) + SwitchPhases(); + else + m_uiPhaseSwitchTimer -= uiDiff; + + if (m_uiEnrageTimer) + { + if (m_uiEnrageTimer <= uiDiff) + { + if (DoCastSpellIfCan(m_creature, SPELL_NETHER_INFUSION) == CAST_OK) + m_uiEnrageTimer = 0; + } + else + m_uiEnrageTimer -= uiDiff; + } + + if (m_uiActivePhase == BEAM_PHASE) + { + if (m_uiVoidZoneTimer < uiDiff) + { + if (Unit* pTarget = m_creature->SelectAttackingTarget(ATTACKING_TARGET_RANDOM, 0)) + { + if (DoCastSpellIfCan(pTarget, SPELL_VOID_ZONE) == CAST_OK) + m_uiVoidZoneTimer = 15000; + } + } + else + m_uiVoidZoneTimer -= uiDiff; + + if (m_uiEmpowermentTimer) + { + if (m_uiEmpowermentTimer <= uiDiff) + { + if (DoCastSpellIfCan(m_creature, SPELL_EMPOWERMENT) == CAST_OK) + { + DoCastSpellIfCan(m_creature, SPELL_PORTAL_ATTUNEMENT, CAST_TRIGGERED); + m_uiEmpowermentTimer = 0; + } + } + else + m_uiEmpowermentTimer -= uiDiff; + } + + DoMeleeAttackIfReady(); + } + else + { + if (m_uiNetherbreathTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature, SPELL_NETHERBREATH) == CAST_OK) + m_uiNetherbreathTimer = urand(4000, 5000); + } + else + m_uiNetherbreathTimer -= uiDiff; + } + } +}; + +CreatureAI* GetAI_boss_netherspite(Creature* pCreature) +{ + return new boss_netherspiteAI(pCreature); +} + +/*###### +## npc_netherspite_portal +######*/ + +struct npc_netherspite_portalAI : public Scripted_NoMovementAI +{ + npc_netherspite_portalAI(Creature* pCreature) : Scripted_NoMovementAI(pCreature) + { + m_pInstance = (ScriptedInstance*)pCreature->GetInstanceData(); + Reset(); + } + + ScriptedInstance* m_pInstance; + + uint32 m_uiPassiveSpellTimer; + uint32 m_uiOrientationTimer; + + void Reset() + { + m_uiPassiveSpellTimer = 0; + m_uiOrientationTimer = 0; + } + + void MoveInLineOfSight(Unit* pWho) { } + void AttackStart(Unit* pWho) { } + + void ReceiveAIEvent(AIEventType eventType, Creature* /*pSender*/, Unit* pInvoker, uint32 /*uiMiscValue*/) override + { + if (eventType == AI_EVENT_CUSTOM_A) + { + if (pInvoker->GetEntry() != NPC_NETHERSPITE) + return; + + // update orientation every second to focus on Netherspite + m_uiOrientationTimer = 1000; + m_creature->SetFacingToObject(pInvoker); + + switch (m_creature->GetEntry()) + { + case NPC_PORTAL_GREEN: + if (!m_creature->HasAura(SPELL_SERENITY_PASSIVE)) + DoCastSpellIfCan(m_creature, SPELL_SERENITY_PASSIVE, CAST_TRIGGERED); + break; + case NPC_PORTAL_BLUE: + if (!m_creature->HasAura(SPELL_DOMINANCE_PASSIVE)) + DoCastSpellIfCan(m_creature, SPELL_DOMINANCE_PASSIVE, CAST_TRIGGERED); + break; + case NPC_PORTAL_RED: + // Red portal spell is missing - handled in script + if (!m_uiPassiveSpellTimer) + m_uiPassiveSpellTimer = 1000; + break; + } + } + } + + void UpdateAI(const uint32 uiDiff) + { + if (m_uiPassiveSpellTimer) + { + if (m_uiPassiveSpellTimer <= uiDiff) + { + if (DoCastSpellIfCan(m_creature, SPELL_NETHER_BEAM, CAST_TRIGGERED) == CAST_OK) + m_uiPassiveSpellTimer = 1000; + } + else + m_uiPassiveSpellTimer -= uiDiff; + } + + if (m_uiOrientationTimer) + { + if (m_uiOrientationTimer <= uiDiff) + { + if (m_pInstance) + { + if (Creature* pNetherspite = m_pInstance->GetSingleCreatureFromStorage(NPC_NETHERSPITE)) + m_creature->SetFacingToObject(pNetherspite); + } + m_uiOrientationTimer = 1000; + } + else + m_uiOrientationTimer -= uiDiff; + } + } +}; + +CreatureAI* GetAI_npc_netherspite_portal(Creature* pCreature) +{ + return new npc_netherspite_portalAI(pCreature); +} + +bool EffectScriptEffectCreature_spell_portal_attunement(Unit* pCaster, uint32 uiSpellId, SpellEffectIndex uiEffIndex, Creature* pCreatureTarget, ObjectGuid /*originalCasterGuid*/) +{ + if (uiSpellId == SPELL_PORTAL_ATTUNEMENT && uiEffIndex == EFFECT_INDEX_0) + { + if (pCreatureTarget->GetEntry() == NPC_PORTAL_RED || pCreatureTarget->GetEntry() == NPC_PORTAL_GREEN || pCreatureTarget->GetEntry() == NPC_PORTAL_BLUE) + pCreatureTarget->AI()->SendAIEvent(AI_EVENT_CUSTOM_A, pCaster, pCreatureTarget); + + return true; + } + + return false; +} + +void AddSC_boss_netherspite() +{ + Script* pNewScript; + + pNewScript = new Script; + pNewScript->Name = "boss_netherspite"; + pNewScript->GetAI = &GetAI_boss_netherspite; + pNewScript->RegisterSelf(); + + pNewScript = new Script; + pNewScript->Name = "npc_netherspite_portal"; + pNewScript->GetAI = &GetAI_npc_netherspite_portal; + pNewScript->pEffectScriptEffectNPC = &EffectScriptEffectCreature_spell_portal_attunement; + pNewScript->RegisterSelf(); +} diff --git a/src/modules/SD2/scripts/eastern_kingdoms/karazhan/boss_nightbane.cpp b/src/modules/SD2/scripts/eastern_kingdoms/karazhan/boss_nightbane.cpp new file mode 100644 index 000000000..192ae861a --- /dev/null +++ b/src/modules/SD2/scripts/eastern_kingdoms/karazhan/boss_nightbane.cpp @@ -0,0 +1,400 @@ +/* Copyright (C) 2006 - 2013 ScriptDev2 + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with 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: Intro movement is a little choppy because of the lack of waypoint movement support. Air phase movement requires improvement. Timers need adjustment. +SDCategory: Karazhan +EndScriptData */ + +#include "precompiled.h" +#include "karazhan.h" +#include "escort_ai.h" + +enum +{ + EMOTE_AWAKEN = -1532125, + SAY_AGGRO = -1532126, + SAY_AIR_PHASE = -1532127, + SAY_LAND_PHASE_1 = -1532128, + SAY_LAND_PHASE_2 = -1532129, + EMOTE_DEEP_BREATH = -1532130, + + // ground phase spells + SPELL_BELLOWING_ROAR = 39427, + SPELL_CHARRED_EARTH = 30129, // Also 30209 (Target Charred Earth) triggers this + SPELL_SMOLDERING_BREATH = 30210, + SPELL_TAIL_SWEEP = 25653, + SPELL_CLEAVE = 30131, + + // air phase spells + SPELL_DISTRACTING_ASH = 30130, + SPELL_RAIN_OF_BONES = 37098, // should trigger 30170 + SPELL_SMOKING_BLAST = 37057, + SPELL_FIREBALL_BARRAGE = 30282, + + PHASE_GROUND = 1, + PHASE_AIR = 2, + PHASE_TRANSITION = 3, + + // These points are a placeholder for the air phase movement. The dragon should do some circles around the area before landing again + POINT_ID_AIR = 1, + POINT_ID_GROUND = 2, +}; + +struct boss_nightbaneAI : public npc_escortAI +{ + boss_nightbaneAI(Creature* pCreature) : npc_escortAI(pCreature) + { + m_pInstance = (instance_karazhan*)pCreature->GetInstanceData(); + Reset(); + } + + instance_karazhan* m_pInstance; + + uint8 m_uiPhase; + uint8 m_uiFlightPhase; + uint32 m_uiPhaseResetTimer; + + uint32 m_uiBellowingRoarTimer; + uint32 m_uiCharredEarthTimer; + uint32 m_uiSmolderingBreathTimer; + uint32 m_uiTailSweepTimer; + uint32 m_uiCleavetimer; + + uint32 m_uiDistractingAshTimer; + uint32 m_uiRainBonesTimer; + uint32 m_uiSmokingBlastTimer; + uint32 m_uiFireballBarrageTimer; + + void Reset() override + { + m_uiPhase = PHASE_GROUND; + m_uiFlightPhase = 1; + + m_uiBellowingRoarTimer = urand(20000, 30000); + m_uiCharredEarthTimer = urand(10000, 15000); + m_uiSmolderingBreathTimer = urand(9000, 13000); + m_uiTailSweepTimer = urand(12000, 15000); + m_uiCleavetimer = urand(4000, 8000); + + SetCombatMovement(true); + m_creature->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE); + } + + void DoResetAirTimers() + { + m_uiPhaseResetTimer = urand(20000, 40000); + m_uiRainBonesTimer = 3000; + m_uiDistractingAshTimer = urand(10000, 12000); + m_uiSmokingBlastTimer = urand(10000, 12000); + m_uiFireballBarrageTimer = 10000; + } + + void Aggro(Unit* /*pWho*/) override + { + DoScriptText(SAY_AGGRO, m_creature); + } + + void JustDied(Unit* /*pKiller*/) override + { + if (m_pInstance) + m_pInstance->SetData(TYPE_NIGHTBANE, DONE); + } + + void JustReachedHome() override + { + if (m_pInstance) + m_pInstance->SetData(TYPE_NIGHTBANE, FAIL); + + // Reset escort waypoints + npc_escortAI::JustRespawned(); + } + + void EnterEvadeMode() override + { + // Use standard AI evade, in order to reset position + ScriptedAI::EnterEvadeMode(); + } + + void WaypointReached(uint32 uiPointId) override + { + // Set in combat after the intro is done + if (uiPointId == 31) + { + SetEscortPaused(true); + m_creature->SetLevitate(false); + m_creature->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE); + m_creature->RemoveByteFlag(UNIT_FIELD_BYTES_1, 3, UNIT_BYTE1_FLAG_ALWAYS_STAND | UNIT_BYTE1_FLAG_UNK_2); + + m_creature->SetInCombatWithZone(); + } + } + + void MovementInform(uint32 uiMotionType, uint32 uiPointId) override + { + if (uiMotionType != POINT_MOTION_TYPE) + return; + + if (!m_creature->getVictim()) + npc_escortAI::MovementInform(uiMotionType, uiPointId); + else + { + switch (uiPointId) + { + case POINT_ID_AIR: + m_uiPhase = PHASE_AIR; + break; + case POINT_ID_GROUND: + m_creature->SetLevitate(false); + m_creature->RemoveByteFlag(UNIT_FIELD_BYTES_1, 3, UNIT_BYTE1_FLAG_ALWAYS_STAND | UNIT_BYTE1_FLAG_UNK_2); + + m_uiPhase = PHASE_GROUND; + SetCombatMovement(true); + DoStartMovement(m_creature->getVictim()); + break; + } + } + } + + void JustSummoned(Creature* pSummoned) override + { + if (m_creature->getVictim()) + pSummoned->AI()->AttackStart(m_creature->getVictim()); + } + + // Wrapper to handle movement to the closest trigger + void DoMoveToClosestTrigger(bool bGround) + { + if (!m_pInstance) + return; + + Unit* pChosenTrigger = NULL; + GuidList lTriggersList; + float fX, fY, fZ; + + // get the list of wanted triggers + m_pInstance->GetNightbaneTriggers(lTriggersList, bGround); + + // calculate the closest trigger from the list + for (GuidList::const_iterator itr = lTriggersList.begin(); itr != lTriggersList.end(); ++itr) + { + if (Creature* pTrigger = m_creature->GetMap()->GetCreature(*itr)) + { + if (!pChosenTrigger || m_creature->GetDistanceOrder(pTrigger, pChosenTrigger, false)) + pChosenTrigger = pTrigger; + } + } + + // Move to trigger position + if (pChosenTrigger) + { + pChosenTrigger->GetPosition(fX, fY, fZ); + m_creature->GetMotionMaster()->MovePoint(bGround ? POINT_ID_GROUND : POINT_ID_AIR, fX, fY, fZ); + } + } + + void UpdateEscortAI(const uint32 uiDiff) override + { + if (!m_creature->SelectHostileTarget() || !m_creature->getVictim()) + return; + + switch (m_uiPhase) + { + case PHASE_GROUND: + + if (m_uiBellowingRoarTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature, SPELL_BELLOWING_ROAR) == CAST_OK) + m_uiBellowingRoarTimer = urand(20000, 30000); + } + else + m_uiBellowingRoarTimer -= uiDiff; + + if (m_uiSmolderingBreathTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature, SPELL_SMOLDERING_BREATH) == CAST_OK) + m_uiSmolderingBreathTimer = urand(14000, 20000); + } + else + m_uiSmolderingBreathTimer -= uiDiff; + + if (m_uiCharredEarthTimer < uiDiff) + { + if (Unit* pTarget = m_creature->SelectAttackingTarget(ATTACKING_TARGET_RANDOM, 0)) + { + if (DoCastSpellIfCan(pTarget, SPELL_CHARRED_EARTH) == CAST_OK) + m_uiCharredEarthTimer = urand(25000, 35000); + } + } + else + m_uiCharredEarthTimer -= uiDiff; + + if (m_uiTailSweepTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature, SPELL_TAIL_SWEEP) == CAST_OK) + m_uiTailSweepTimer = urand(14000, 20000); + } + else + m_uiTailSweepTimer -= uiDiff; + + if (m_uiCleavetimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature->getVictim(), SPELL_CLEAVE) == CAST_OK) + m_uiCleavetimer = urand(6000, 12000); + } + else + m_uiCleavetimer -= uiDiff; + + if (m_creature->GetHealthPercent() < 100 - 25 * m_uiFlightPhase) + { + // Start air phase movement (handled by creature_movement_template) + SetCombatMovement(false); + m_creature->SetByteValue(UNIT_FIELD_BYTES_1, 3, UNIT_BYTE1_FLAG_ALWAYS_STAND | UNIT_BYTE1_FLAG_UNK_2); + m_creature->SetLevitate(true); + DoMoveToClosestTrigger(false); + + DoScriptText(SAY_AIR_PHASE, m_creature); + m_uiPhase = PHASE_TRANSITION; + DoResetAirTimers(); + ++m_uiFlightPhase; + } + + DoMeleeAttackIfReady(); + + break; + case PHASE_AIR: + + if (m_uiRainBonesTimer) + { + if (m_uiRainBonesTimer <= uiDiff) + { + if (Unit* pTarget = m_creature->SelectAttackingTarget(ATTACKING_TARGET_RANDOM, 0)) + { + if (DoCastSpellIfCan(pTarget, SPELL_RAIN_OF_BONES) == CAST_OK) + { + DoScriptText(EMOTE_DEEP_BREATH, m_creature); + m_uiRainBonesTimer = 0; + } + } + } + else + m_uiRainBonesTimer -= uiDiff; + } + + if (m_uiDistractingAshTimer < uiDiff) + { + if (Unit* pTarget = m_creature->SelectAttackingTarget(ATTACKING_TARGET_RANDOM, 0)) + { + if (DoCastSpellIfCan(pTarget, SPELL_DISTRACTING_ASH) == CAST_OK) + m_uiDistractingAshTimer = urand(7000, 13000); + } + } + else + m_uiDistractingAshTimer -= uiDiff; + + if (m_uiSmokingBlastTimer < uiDiff) + { + if (Unit* pTarget = m_creature->SelectAttackingTarget(ATTACKING_TARGET_RANDOM, 0)) + { + if (DoCastSpellIfCan(pTarget, SPELL_SMOKING_BLAST) == CAST_OK) + m_uiSmokingBlastTimer = urand(1000, 3000); + } + } + else + m_uiSmokingBlastTimer -= uiDiff; + + if (m_uiFireballBarrageTimer < uiDiff) + { + if (Unit* pTarget = m_creature->SelectAttackingTarget(ATTACKING_TARGET_RANDOM, 0, SPELL_FIREBALL_BARRAGE, SELECT_FLAG_NOT_IN_MELEE_RANGE)) + { + if (DoCastSpellIfCan(pTarget, SPELL_FIREBALL_BARRAGE) == CAST_OK) + m_uiFireballBarrageTimer = urand(3000, 6000); + } + } + else + m_uiFireballBarrageTimer -= uiDiff; + + if (m_uiPhaseResetTimer < uiDiff) + { + // ToDo: more circle movement should be done here! + DoScriptText(urand(0, 1) ? SAY_LAND_PHASE_1 : SAY_LAND_PHASE_2, m_creature); + DoMoveToClosestTrigger(true); + + m_uiPhase = PHASE_TRANSITION; + m_uiPhaseResetTimer = 20000; + } + else + m_uiPhaseResetTimer -= uiDiff; + + break; + case PHASE_TRANSITION: + // nothing here + break; + } + } +}; + +CreatureAI* GetAI_boss_nightbane(Creature* pCreature) +{ + return new boss_nightbaneAI(pCreature); +} + +bool ProcessEventId_event_spell_summon_nightbane(uint32 /*uiEventId*/, Object* pSource, Object* /*pTarget*/, bool bIsStart) +{ + if (bIsStart && pSource->GetTypeId() == TYPEID_PLAYER) + { + ScriptedInstance* pInstance = (ScriptedInstance*)((Player*)pSource)->GetInstanceData(); + if (!pInstance) + return false; + + if (pInstance->GetData(TYPE_NIGHTBANE) == NOT_STARTED || pInstance->GetData(TYPE_NIGHTBANE) == FAIL) + { + if (Creature* pNightbane = pInstance->GetSingleCreatureFromStorage(NPC_NIGHTBANE)) + { + DoScriptText(EMOTE_AWAKEN, ((Player*)pSource)); + pInstance->SetData(TYPE_NIGHTBANE, IN_PROGRESS); + + // Sort of a hack, it is unclear how this really work but the values appear to be valid (see Onyxia, too) + pNightbane->SetStandState(UNIT_STAND_STATE_STAND); + pNightbane->SetByteValue(UNIT_FIELD_BYTES_1, 3, UNIT_BYTE1_FLAG_ALWAYS_STAND | UNIT_BYTE1_FLAG_UNK_2); + pNightbane->SetLevitate(true); + + // Switch to waypoint movement + if (boss_nightbaneAI* pNightbaneAI = dynamic_cast(pNightbane->AI())) + pNightbaneAI->Start(true); + } + } + } + + return true; +} + +void AddSC_boss_nightbane() +{ + Script* pNewScript; + + pNewScript = new Script; + pNewScript->Name = "boss_nightbane"; + pNewScript->GetAI = &GetAI_boss_nightbane; + pNewScript->RegisterSelf(); + + pNewScript = new Script; + pNewScript->Name = "event_spell_summon_nightbane"; + pNewScript->pProcessEventId = &ProcessEventId_event_spell_summon_nightbane; + pNewScript->RegisterSelf(); +} diff --git a/src/modules/SD2/scripts/eastern_kingdoms/karazhan/boss_prince_malchezaar.cpp b/src/modules/SD2/scripts/eastern_kingdoms/karazhan/boss_prince_malchezaar.cpp new file mode 100644 index 000000000..f3860cc0e --- /dev/null +++ b/src/modules/SD2/scripts/eastern_kingdoms/karazhan/boss_prince_malchezaar.cpp @@ -0,0 +1,364 @@ +/* Copyright (C) 2006 - 2013 ScriptDev2 + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with 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 "karazhan.h" + +enum +{ + SAY_AGGRO = -1532091, + SAY_AXE_TOSS1 = -1532092, + SAY_AXE_TOSS2 = -1532093, + SAY_SPECIAL1 = -1532094, + SAY_SPECIAL2 = -1532095, + SAY_SPECIAL3 = -1532096, + SAY_SLAY1 = -1532097, + SAY_SLAY2 = -1532098, + SAY_SLAY3 = -1532099, + SAY_SUMMON1 = -1532100, + SAY_SUMMON2 = -1532101, + SAY_DEATH = -1532102, + + // Enfeeble is supposed to reduce hp to 1 and then heal player back to full when it ends + SPELL_ENFEEBLE = 30843, // Enfeeble during phases 1 and 2 + SPELL_ENFEEBLE_EFFECT = 41624, // purpose unk - cast during the transition from phase 2 to 3 + SPELL_SHADOW_NOVA = 30852, // Shadownova used during all phases + SPELL_SW_PAIN_PHASE1 = 30854, // Shadow word pain during phase 1 + SPELL_SW_PAIN_PHASE3 = 30898, // Shadow word pain during phase 3 + SPELL_SUNDER_ARMOR = 30901, // Sunder armor during phase 2 + SPELL_THRASH_AURA = 12787, // Passive proc chance for thrash + SPELL_SUMMON_AXES = 30891, // Summon 17650 + SPELL_EQUIP_AXES = 30857, // Visual for axe equiping - transition to phase 2 + SPELL_AMPLIFY_DAMAGE = 39095, // Amplifiy during phase 3 3 + // SPELL_CLEAVE = 30131, // spell not confirmed + // SPELL_INFERNAL_RELAY = 30834, // purpose unk + SPELL_INFERNAL_RELAY_SUMMON = 30835, // triggers 30836, which summons an infernal + + SPELL_HELLFIRE = 30859, // Infernal damage aura + + NPC_NETHERSPITE_INFERNAL = 17646, // The netherspite infernal creature + NPC_MALCHEZARS_AXE = 17650, // Malchezar's axes summoned during phase 3 + + EQUIP_ID_AXE = 23996, // Axes info + + ATTACK_TIMER_DEFAULT = 2000, // note: for TBC it was 2400 + ATTACK_TIMER_AXES = 1333, // note: for TBC it was 1600 + + MAX_ENFEEBLE_TARGETS = 5, +}; + +struct boss_malchezaarAI : public ScriptedAI +{ + boss_malchezaarAI(Creature* pCreature) : ScriptedAI(pCreature) + { + m_pInstance = (ScriptedInstance*)pCreature->GetInstanceData(); + Reset(); + } + + ScriptedInstance* m_pInstance; + + uint8 m_uiEnfeebleIndex; + uint32 m_uiEnfeebleTimer; + uint32 m_uiEnfeebleResetTimer; + uint32 m_uiShadowNovaTimer; + uint32 m_uiSWPainTimer; + uint32 m_uiSunderArmorTimer; + uint32 m_uiAmplifyDamageTimer; + uint32 m_uiInfernalTimer; + + ObjectGuid m_aEnfeebleTargetGuid[MAX_ENFEEBLE_TARGETS]; + uint32 m_auiEnfeebleHealth[MAX_ENFEEBLE_TARGETS]; + + uint8 m_uiPhase; + + void Reset() override + { + for (uint8 i = 0; i < MAX_ENFEEBLE_TARGETS; ++i) + { + m_aEnfeebleTargetGuid[i].Clear(); + m_auiEnfeebleHealth[i] = 0; + } + + m_uiEnfeebleIndex = 0; + m_uiEnfeebleTimer = 30000; + m_uiEnfeebleResetTimer = 0; + m_uiShadowNovaTimer = 35500; + m_uiSWPainTimer = 20000; + m_uiAmplifyDamageTimer = 5000; + m_uiInfernalTimer = 40000; + m_uiSunderArmorTimer = urand(5000, 10000); + + m_uiPhase = 1; + + // Reset equipment and attack + SetEquipmentSlots(false, EQUIP_UNEQUIP, EQUIP_UNEQUIP, EQUIP_NO_CHANGE); + m_creature->SetAttackTime(BASE_ATTACK, ATTACK_TIMER_DEFAULT); + } + + void KilledUnit(Unit* /*pVictim*/) override + { + switch (urand(0, 2)) + { + case 0: DoScriptText(SAY_SLAY1, m_creature); break; + case 1: DoScriptText(SAY_SLAY2, m_creature); break; + case 2: DoScriptText(SAY_SLAY3, m_creature); break; + } + } + + void JustDied(Unit* /*pKiller*/) override + { + DoScriptText(SAY_DEATH, m_creature); + + // Remove the summoned axe - which is considered a guardian + m_creature->RemoveGuardians(); + + if (m_pInstance) + m_pInstance->SetData(TYPE_MALCHEZZAR, DONE); + } + + void Aggro(Unit* /*pWho*/) override + { + DoScriptText(SAY_AGGRO, m_creature); + + if (m_pInstance) + m_pInstance->SetData(TYPE_MALCHEZZAR, IN_PROGRESS); + } + + void JustReachedHome() override + { + if (m_pInstance) + m_pInstance->SetData(TYPE_MALCHEZZAR, FAIL); + + // Remove the summoned axe - which is considered a guardian + m_creature->RemoveGuardians(); + } + + void JustSummoned(Creature* pSummoned) override + { + if (pSummoned->GetEntry() == NPC_NETHERSPITE_INFERNAL) + pSummoned->CastSpell(pSummoned, SPELL_HELLFIRE, false); + else if (pSummoned->GetEntry() == NPC_MALCHEZARS_AXE) + pSummoned->SetInCombatWithZone(); + } + + void SpellHitTarget(Unit* pTarget, SpellEntry const* pSpellEntry) override + { + // Target selection is already handled properly in core (doesn't affect tank) + if (pSpellEntry->Id == SPELL_ENFEEBLE && pTarget->GetTypeId() == TYPEID_PLAYER) + { + // Workaround to handle health set to 1 + m_aEnfeebleTargetGuid[m_uiEnfeebleIndex] = pTarget->GetObjectGuid(); + m_auiEnfeebleHealth[m_uiEnfeebleIndex] = pTarget->GetHealth(); + pTarget->SetHealth(1); + ++m_uiEnfeebleIndex; + } + } + + // Wrapper to reset health of the Enfeebled targets + void DoHandleEnfeebleHealthReset() + { + for (int i = 0; i < m_uiEnfeebleIndex; ++i) + { + Player* pTarget = m_creature->GetMap()->GetPlayer(m_aEnfeebleTargetGuid[i]); + + if (pTarget && pTarget->IsAlive()) + pTarget->SetHealth(m_auiEnfeebleHealth[i]); + + m_aEnfeebleTargetGuid[i].Clear(); + m_auiEnfeebleHealth[i] = 0; + } + + m_uiEnfeebleIndex = 0; + } + + void UpdateAI(const uint32 uiDiff) override + { + if (!m_creature->SelectHostileTarget() || !m_creature->getVictim()) + return; + + // Phase 1 - over 60% HP + if (m_uiPhase == 1) + { + // transition to phase 2 + if (m_creature->GetHealthPercent() < 60.0f) + { + if (DoCastSpellIfCan(m_creature, SPELL_EQUIP_AXES, CAST_INTERRUPT_PREVIOUS) == CAST_OK) + { + DoCastSpellIfCan(m_creature, SPELL_THRASH_AURA, CAST_TRIGGERED); + DoScriptText(SAY_AXE_TOSS1, m_creature); + + SetEquipmentSlots(false, EQUIP_ID_AXE, EQUIP_ID_AXE, EQUIP_NO_CHANGE); + m_creature->SetAttackTime(BASE_ATTACK, ATTACK_TIMER_AXES); + m_uiPhase = 2; + } + } + } + // Phase 2 - over 30% HP + else if (m_uiPhase == 2) + { + // transition to phase 3 + if (m_creature->GetHealthPercent() < 30.0f) + { + if (DoCastSpellIfCan(m_creature, SPELL_SUMMON_AXES) == CAST_OK) + { + DoCastSpellIfCan(m_creature, SPELL_ENFEEBLE_EFFECT, CAST_TRIGGERED); + DoScriptText(SAY_SPECIAL3, m_creature); + + SetEquipmentSlots(false, EQUIP_UNEQUIP, EQUIP_UNEQUIP, EQUIP_NO_CHANGE); + m_creature->SetAttackTime(BASE_ATTACK, ATTACK_TIMER_DEFAULT); + + // Reset Enfeebled targets if necessary + DoHandleEnfeebleHealthReset(); + m_uiEnfeebleResetTimer = 0; + + m_creature->RemoveAurasDueToSpell(SPELL_THRASH_AURA); + m_uiShadowNovaTimer = m_uiEnfeebleTimer + 5000; + m_uiInfernalTimer = 15000; + m_uiPhase = 3; + + return; + } + } + + if (m_uiSunderArmorTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature->getVictim(), SPELL_SUNDER_ARMOR) == CAST_OK) + m_uiSunderArmorTimer = urand(10000, 18000); + } + else + m_uiSunderArmorTimer -= uiDiff; + } + // Phase 3 + else + { + if (m_uiAmplifyDamageTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature, SPELL_AMPLIFY_DAMAGE) == CAST_OK) + m_uiAmplifyDamageTimer = urand(20000, 30000); + } + else + m_uiAmplifyDamageTimer -= uiDiff; + } + + // Summon an infernal on timer + if (m_uiInfernalTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature, SPELL_INFERNAL_RELAY_SUMMON) == CAST_OK) + { + DoScriptText(urand(0, 1) ? SAY_SUMMON1 : SAY_SUMMON2, m_creature); + m_uiInfernalTimer = m_uiPhase == 3 ? 17000 : 45000; + } + } + else + m_uiInfernalTimer -= uiDiff; + + // Cast shadow nova - on timer during phase 3, or after Enfeeble during phases 1 and 2 + if (m_uiShadowNovaTimer) + { + if (m_uiShadowNovaTimer <= uiDiff) + { + if (DoCastSpellIfCan(m_creature, SPELL_SHADOW_NOVA) == CAST_OK) + m_uiShadowNovaTimer = m_uiPhase == 3 ? 30000 : 0; + } + else + m_uiShadowNovaTimer -= uiDiff; + } + + // Cast SW pain during phase 1 and 3 + if (m_uiPhase != 2) + { + if (m_uiSWPainTimer < uiDiff) + { + if (DoCastSpellIfCan(m_uiPhase == 1 ? m_creature->getVictim() : m_creature, m_uiPhase == 1 ? SPELL_SW_PAIN_PHASE1 : SPELL_SW_PAIN_PHASE3) == CAST_OK) + m_uiSWPainTimer = 20000; + } + else + m_uiSWPainTimer -= uiDiff; + } + + // Cast Enfeeble during phase 1 and 2 + if (m_uiPhase != 3) + { + if (m_uiEnfeebleTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature, SPELL_ENFEEBLE) == CAST_OK) + { + m_uiEnfeebleTimer = 30000; + m_uiShadowNovaTimer = 5000; + m_uiEnfeebleResetTimer = 9000; + } + } + else + m_uiEnfeebleTimer -= uiDiff; + } + + if (m_uiEnfeebleResetTimer) + { + if (m_uiEnfeebleResetTimer <= uiDiff) + { + DoHandleEnfeebleHealthReset(); + m_uiEnfeebleResetTimer = 0; + } + else + m_uiEnfeebleResetTimer -= uiDiff; + } + + DoMeleeAttackIfReady(); + } +}; + +CreatureAI* GetAI_boss_malchezaar(Creature* pCreature) +{ + return new boss_malchezaarAI(pCreature); +} + +// TODO Remove this 'script' when combat can be proper prevented from core-side +struct npc_infernal_targetAI : public Scripted_NoMovementAI +{ + npc_infernal_targetAI(Creature* pCreature) : Scripted_NoMovementAI(pCreature) { Reset(); } + + void Reset() override { } + void MoveInLineOfSight(Unit* /*pWho*/) override { } + void AttackStart(Unit* /*pWho*/) override { } + void UpdateAI(const uint32 /*uiDiff*/) override { } +}; + +CreatureAI* GetAI_npc_infernal_target(Creature* pCreature) +{ + return new npc_infernal_targetAI(pCreature); +} + +void AddSC_boss_prince_malchezaar() +{ + Script* pNewScript; + + pNewScript = new Script; + pNewScript->Name = "boss_malchezaar"; + pNewScript->GetAI = &GetAI_boss_malchezaar; + pNewScript->RegisterSelf(); + + pNewScript = new Script; + pNewScript->Name = "npc_infernal_target"; + pNewScript->GetAI = &GetAI_npc_infernal_target; + pNewScript->RegisterSelf(); +} diff --git a/src/modules/SD2/scripts/eastern_kingdoms/karazhan/boss_shade_of_aran.cpp b/src/modules/SD2/scripts/eastern_kingdoms/karazhan/boss_shade_of_aran.cpp new file mode 100644 index 000000000..ee6b331cc --- /dev/null +++ b/src/modules/SD2/scripts/eastern_kingdoms/karazhan/boss_shade_of_aran.cpp @@ -0,0 +1,430 @@ +/* Copyright (C) 2006 - 2013 ScriptDev2 + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with 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: When drinking mana, it should remove all negative damage auras and should sit. Timers may need adjustments. +SDCategory: Karazhan +EndScriptData */ + +#include "precompiled.h" +#include "karazhan.h" + +enum +{ + SAY_AGGRO1 = -1532073, + SAY_AGGRO2 = -1532074, + SAY_AGGRO3 = -1532075, + SAY_FLAMEWREATH1 = -1532076, + SAY_FLAMEWREATH2 = -1532077, + SAY_BLIZZARD1 = -1532078, + SAY_BLIZZARD2 = -1532079, + SAY_EXPLOSION1 = -1532080, + SAY_EXPLOSION2 = -1532081, + SAY_DRINK = -1532082, // Low Mana / AoE Pyroblast + SAY_ELEMENTALS = -1532083, + SAY_KILL1 = -1532084, + SAY_KILL2 = -1532085, + SAY_TIMEOVER = -1532086, + SAY_DEATH = -1532087, + SAY_ATIESH = -1532088, // Atiesh is equipped by a raid member + + // basic spells + SPELL_FROSTBOLT = 29954, + SPELL_FIREBALL = 29953, + SPELL_ARCANE_MISSILES = 29955, + // SPELL_DRAGONS_BREATH = 29964, // not used since 2.1.0 + SPELL_CHAINS_OF_ICE = 29991, + SPELL_COUNTERSPELL = 29961, + // SPELL_COMBUSTION = 29977, // spell not confirmed + // SPELL_PRESENCE_OF_MIND = 29976, // spell not confirmed + // SPELL_WATER_BREAK = 39177, // purpose unk + + // low mana spells + SPELL_MASS_POLYMORPH = 29963, + SPELL_CONJURE_WATER = 29975, + SPELL_DRINK = 30024, + SPELL_MANA_POTION = 32453, + SPELL_PYROBLAST = 29978, + + // super spells + SPELL_FLAME_WREATH = 30004, // triggers 29946 on targets + SPELL_SUMMON_BLIZZARD = 29969, // script target on npc 17161 - triggers spell 29952 on target + SPELL_BLINK_CENTER = 29967, + SPELL_MASSIVE_MAGNETIC_PULL = 29979, // triggers 30010 on target + SPELL_MASS_SLOW = 30035, + SPELL_ARCANE_EXPLOSION = 29973, + + // summon elemental spells + SPELL_SUMMON_WATER_ELEM_1 = 29962, + SPELL_SUMMON_WATER_ELEM_2 = 37051, + SPELL_SUMMON_WATER_ELEM_3 = 37052, + SPELL_SUMMON_WATER_ELEM_4 = 37053, + + // Creatures + NPC_WATER_ELEMENTAL = 17167, + NPC_SHADOW_OF_ARAN = 18254, + + MAX_SHADOWS_OF_ARAN = 5, // this is not confirmed +}; + +enum SuperSpells +{ + SUPER_FLAME_WREATH = 0, + SUPER_BLIZZARD = 1, + SUPER_ARCANE_EXPL = 2, +}; + +struct boss_aranAI : public ScriptedAI +{ + boss_aranAI(Creature* pCreature) : ScriptedAI(pCreature) + { + m_pInstance = (ScriptedInstance*)pCreature->GetInstanceData(); + Reset(); + } + + ScriptedInstance* m_pInstance; + + uint32 m_uiSecondarySpellTimer; + uint32 m_uiNormalCastTimer; + uint32 m_uiSuperCastTimer; + uint32 m_uiBerserkTimer; + + uint8 m_uiLastSuperSpell; + uint8 m_uiLastNormalSpell; + + uint32 m_uiManaRecoveryTimer; + uint8 m_uiManaRecoveryStage; + + bool m_bElementalsSpawned; + bool m_bIsDrinking; + bool m_bDrinkInturrupted; + + void Reset() override + { + m_uiLastSuperSpell = urand(SUPER_FLAME_WREATH, SUPER_ARCANE_EXPL); + m_uiLastNormalSpell = urand(0, 2); + + m_uiSecondarySpellTimer = 5000; + m_uiNormalCastTimer = 0; + m_uiSuperCastTimer = 35000; + m_uiManaRecoveryTimer = 0; + m_uiManaRecoveryStage = 0; + m_uiBerserkTimer = 12 * MINUTE * IN_MILLISECONDS; + + m_bElementalsSpawned = false; + m_bIsDrinking = false; + m_bDrinkInturrupted = false; + + SetCombatMovement(true); + } + + void KilledUnit(Unit* /*pVictim*/) override + { + DoScriptText(urand(0, 1) ? SAY_KILL1 : SAY_KILL2, m_creature); + } + + void JustDied(Unit* /*pVictim*/) override + { + DoScriptText(SAY_DEATH, m_creature); + + // Remove the summoned elementals - which are considered guardians + m_creature->RemoveGuardians(); + + if (m_pInstance) + m_pInstance->SetData(TYPE_ARAN, DONE); + } + + void Aggro(Unit* /*pWho*/) override + { + switch (urand(0, 2)) + { + case 0: DoScriptText(SAY_AGGRO1, m_creature); break; + case 1: DoScriptText(SAY_AGGRO2, m_creature); break; + case 2: DoScriptText(SAY_AGGRO3, m_creature); break; + } + + if (m_pInstance) + m_pInstance->SetData(TYPE_ARAN, IN_PROGRESS); + } + + void JustReachedHome() override + { + if (m_pInstance) + m_pInstance->SetData(TYPE_ARAN, FAIL); + + // Remove the summoned elementals - which are considered guardians + m_creature->RemoveGuardians(); + } + + void DamageTaken(Unit* /*pDoneBy*/, uint32& uiDamage) override + { + if (!m_bDrinkInturrupted && m_bIsDrinking && uiDamage > 0) + { + if (!m_creature->HasAura(SPELL_DRINK)) + return; + + if (DoCastSpellIfCan(m_creature, SPELL_MANA_POTION) == CAST_OK) + { + m_creature->RemoveAurasDueToSpell(SPELL_DRINK); + m_creature->SetStandState(UNIT_STAND_STATE_STAND); + m_uiManaRecoveryTimer = 1000; + m_uiManaRecoveryStage = 2; + m_bDrinkInturrupted = true; + } + } + } + + void JustSummoned(Creature* pSummoned) override + { + switch (pSummoned->GetEntry()) + { + case NPC_WATER_ELEMENTAL: + case NPC_SHADOW_OF_ARAN: + pSummoned->SetInCombatWithZone(); + break; + } + } + + void UpdateAI(const uint32 uiDiff) override + { + if (!m_creature->SelectHostileTarget() || !m_creature->getVictim()) + return; + + // Start drinking when below 20% mana + if (!m_bIsDrinking && m_creature->GetPowerType() == POWER_MANA && (m_creature->GetPower(POWER_MANA) * 100 / m_creature->GetMaxPower(POWER_MANA)) < 20) + { + if (DoCastSpellIfCan(m_creature, SPELL_MASS_POLYMORPH) == CAST_OK) + { + DoScriptText(SAY_DRINK, m_creature); + SetCombatMovement(false); + m_creature->GetMotionMaster()->MoveIdle(); + + m_uiManaRecoveryStage = 0; + m_uiManaRecoveryTimer = 2000; + m_bDrinkInturrupted = false; + m_bIsDrinking = true; + return; + } + } + + if (m_bIsDrinking) + { + // Do the mana recovery process + if (m_uiManaRecoveryTimer < uiDiff) + { + switch (m_uiManaRecoveryStage) + { + case 0: + if (DoCastSpellIfCan(m_creature, SPELL_CONJURE_WATER) == CAST_OK) + m_uiManaRecoveryTimer = 2000; + break; + case 1: + if (DoCastSpellIfCan(m_creature, SPELL_DRINK) == CAST_OK) + { + m_creature->SetStandState(UNIT_STAND_STATE_SIT); + m_uiManaRecoveryTimer = 5000; + } + break; + case 2: + if (DoCastSpellIfCan(m_creature, SPELL_PYROBLAST) == CAST_OK) + { + SetCombatMovement(true); + DoStartMovement(m_creature->getVictim()); + m_creature->SetStandState(UNIT_STAND_STATE_STAND); + + m_uiManaRecoveryTimer = 2000; + m_bIsDrinking = false; + } + break; + } + ++m_uiManaRecoveryStage; + } + else + m_uiManaRecoveryTimer -= uiDiff; + + // no other spells during mana recovery + return; + } + + // Normal spell casts + if (m_uiNormalCastTimer < uiDiff) + { + if (!m_creature->IsNonMeleeSpellCasted(false)) + { + Unit* pTarget = m_creature->SelectAttackingTarget(ATTACKING_TARGET_RANDOM, 0); + if (!pTarget) + return; + + uint8 uiCurrentSpell = urand(0, 2); + uint32 uiCurrentSpellId = 0; + + // randomize so it won't be the same spell twice in a row + while (uiCurrentSpell == m_uiLastNormalSpell) + uiCurrentSpell = urand(0, 2); + + m_uiLastNormalSpell = uiCurrentSpell; + + switch (uiCurrentSpell) + { + case 0: + uiCurrentSpellId = SPELL_ARCANE_MISSILES; + m_uiNormalCastTimer = urand(6000, 7000); + break; + case 1: + uiCurrentSpellId = SPELL_FIREBALL; + m_uiNormalCastTimer = urand(2000, 3000); + break; + case 2: + uiCurrentSpellId = SPELL_FROSTBOLT; + m_uiNormalCastTimer = urand(2000, 3000); + break; + } + + if (uiCurrentSpellId) + DoCastSpellIfCan(pTarget, uiCurrentSpellId); + } + } + else + m_uiNormalCastTimer -= uiDiff; + + // Secondary spells + if (m_uiSecondarySpellTimer < uiDiff) + { + CanCastResult spellResult = CAST_OK; + + switch (urand(0, 1)) + { + case 0: + spellResult = DoCastSpellIfCan(m_creature, SPELL_COUNTERSPELL); + break; + case 1: + if (Unit* pUnit = m_creature->SelectAttackingTarget(ATTACKING_TARGET_RANDOM, 0)) + spellResult = DoCastSpellIfCan(pUnit, SPELL_CHAINS_OF_ICE); + break; + } + if (spellResult == CAST_OK) + m_uiSecondarySpellTimer = urand(5000, 20000); + } + else + m_uiSecondarySpellTimer -= uiDiff; + + if (m_uiSuperCastTimer < uiDiff) + { + if (!m_creature->IsNonMeleeSpellCasted(false)) + { + uint8 uiAvailableSpell = urand(SUPER_FLAME_WREATH, SUPER_ARCANE_EXPL); + + // randomize so it won't be the same spell twice in a row + while (uiAvailableSpell == m_uiLastSuperSpell) + uiAvailableSpell = urand(SUPER_FLAME_WREATH, SUPER_ARCANE_EXPL); + + m_uiLastSuperSpell = uiAvailableSpell; + + switch (m_uiLastSuperSpell) + { + case SUPER_ARCANE_EXPL: + if (DoCastSpellIfCan(m_creature, SPELL_ARCANE_EXPLOSION) == CAST_OK) + { + DoCastSpellIfCan(m_creature, SPELL_BLINK_CENTER, CAST_TRIGGERED); + DoCastSpellIfCan(m_creature, SPELL_MASSIVE_MAGNETIC_PULL, CAST_TRIGGERED); + DoCastSpellIfCan(m_creature, SPELL_MASS_SLOW, CAST_TRIGGERED); + + DoScriptText(urand(0, 1) ? SAY_EXPLOSION1 : SAY_EXPLOSION2, m_creature); + } + break; + case SUPER_FLAME_WREATH: + if (DoCastSpellIfCan(m_creature, SPELL_FLAME_WREATH) == CAST_OK) + DoScriptText(urand(0, 1) ? SAY_FLAMEWREATH1 : SAY_FLAMEWREATH2, m_creature); + break; + case SUPER_BLIZZARD: + if (DoCastSpellIfCan(m_creature, SPELL_SUMMON_BLIZZARD) == CAST_OK) + DoScriptText(urand(0, 1) ? SAY_BLIZZARD1 : SAY_BLIZZARD2, m_creature); + break; + } + m_uiSuperCastTimer = 30000; + } + } + else + m_uiSuperCastTimer -= uiDiff; + + if (!m_bElementalsSpawned && m_creature->GetHealthPercent() < 40.0f) + { + DoCastSpellIfCan(m_creature, SPELL_SUMMON_WATER_ELEM_1, CAST_TRIGGERED); + DoCastSpellIfCan(m_creature, SPELL_SUMMON_WATER_ELEM_2, CAST_TRIGGERED); + DoCastSpellIfCan(m_creature, SPELL_SUMMON_WATER_ELEM_3, CAST_TRIGGERED); + DoCastSpellIfCan(m_creature, SPELL_SUMMON_WATER_ELEM_4, CAST_TRIGGERED); + + DoScriptText(SAY_ELEMENTALS, m_creature); + + m_bElementalsSpawned = true; + } + + // Berserk timer - the summons position is guesswork + if (m_uiBerserkTimer) + { + if (m_uiBerserkTimer <= uiDiff) + { + for (uint8 i = 0; i < MAX_SHADOWS_OF_ARAN; ++i) + DoSpawnCreature(NPC_SHADOW_OF_ARAN, 0.0f, 0.0f, 0.0f, 0.0f, TEMPSUMMON_TIMED_OOC_DESPAWN, 5000); + + DoScriptText(SAY_TIMEOVER, m_creature); + m_uiBerserkTimer = 0; + } + else + m_uiBerserkTimer -= uiDiff; + } + + DoMeleeAttackIfReady(); + } +}; + +CreatureAI* GetAI_boss_aran(Creature* pCreature) +{ + return new boss_aranAI(pCreature); +} + +// TODO Remove this 'script' when combat can be proper prevented from core-side +struct npc_shade_of_aran_blizzardAI : public ScriptedAI +{ + npc_shade_of_aran_blizzardAI(Creature* pCreature) : ScriptedAI(pCreature) { Reset(); } + + void Reset() override { } + void MoveInLineOfSight(Unit* /*pWho*/) override { } + void AttackStart(Unit* /*pWho*/) override { } + void UpdateAI(const uint32 /*uiDiff*/) override { } +}; + +CreatureAI* GetAI_npc_shade_of_aran_blizzard(Creature* pCreature) +{ + return new npc_shade_of_aran_blizzardAI(pCreature); +} + +void AddSC_boss_shade_of_aran() +{ + Script* pNewScript; + + pNewScript = new Script; + pNewScript->Name = "boss_shade_of_aran"; + pNewScript->GetAI = &GetAI_boss_aran; + pNewScript->RegisterSelf(); + + pNewScript = new Script; + pNewScript->Name = "npc_shade_of_aran_blizzard"; + pNewScript->GetAI = &GetAI_npc_shade_of_aran_blizzard; + pNewScript->RegisterSelf(); +} diff --git a/src/modules/SD2/scripts/eastern_kingdoms/karazhan/boss_terestian_illhoof.cpp b/src/modules/SD2/scripts/eastern_kingdoms/karazhan/boss_terestian_illhoof.cpp new file mode 100644 index 000000000..d69729461 --- /dev/null +++ b/src/modules/SD2/scripts/eastern_kingdoms/karazhan/boss_terestian_illhoof.cpp @@ -0,0 +1,303 @@ +/* Copyright (C) 2006 - 2013 ScriptDev2 + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +/* ScriptData +SDName: Boss_Terestian_Illhoof +SD%Complete: 100 +SDComment: +SDCategory: Karazhan +EndScriptData */ + +#include "precompiled.h" +#include "karazhan.h" + +enum +{ + SAY_SLAY1 = -1532065, + SAY_SLAY2 = -1532066, + SAY_DEATH = -1532067, + SAY_AGGRO = -1532068, + SAY_SACRIFICE1 = -1532069, + SAY_SACRIFICE2 = -1532070, + SAY_SUMMON1 = -1532071, + SAY_SUMMON2 = -1532072, + + // spells + SPELL_SUMMON_DEMONCHAINS = 30120, // Summons demonic chains that maintain the ritual of sacrifice. + SPELL_SHADOW_BOLT = 30055, // Hurls a bolt of dark magic at an enemy, inflicting Shadow damage. + SPELL_SACRIFICE = 30115, // Teleports and adds the debuff + SPELL_BERSERK = 32965, // Increases attack speed by 75%. Periodically casts Shadow Bolt Volley. + SPELL_SUMMON_IMP = 30066, // Summons Kil'rek + SPELL_FIENDISH_PORTAL = 30171, // Opens portal and summons Fiendish Portal, 2 sec cast + SPELL_FIENDISH_PORTAL_1 = 30179, // Opens portal and summons Fiendish Portal, instant cast + + // Chains spells + SPELL_DEMON_CHAINS = 30206, // Instant - Visual Effect + + // Portal spells + SPELL_SUMMON_FIENDISH_IMP = 30184, + + // Kilrek + SPELL_BROKEN_PACT = 30065, // All damage taken increased by 25%. + + // summoned npcs + NPC_DEMONCHAINS = 17248, + NPC_FIENDISHIMP = 17267, + NPC_PORTAL = 17265, + NPC_KILREK = 17229 +}; + +struct boss_terestianAI : public ScriptedAI +{ + boss_terestianAI(Creature* pCreature) : ScriptedAI(pCreature) + { + m_pInstance = (ScriptedInstance*)pCreature->GetInstanceData(); + Reset(); + } + + ScriptedInstance* m_pInstance; + + ObjectGuid m_sacrificeGuid; + + uint32 m_uiSummonKilrekTimer; + uint32 m_uiSacrificeTimer; + uint32 m_uiShadowboltTimer; + uint32 m_uiSummonTimer; + uint32 m_uiBerserkTimer; + + bool m_bSummonedPortals; + + void Reset() override + { + m_uiSummonKilrekTimer = 0; + m_uiSacrificeTimer = 30000; + m_uiShadowboltTimer = 5000; + m_uiSummonTimer = 10000; + m_uiBerserkTimer = 10 * MINUTE * IN_MILLISECONDS; + + m_bSummonedPortals = false; + } + + void Aggro(Unit* /*pWho*/) override + { + DoScriptText(SAY_AGGRO, m_creature); + + if (!m_creature->GetPet()) + DoCastSpellIfCan(m_creature, SPELL_SUMMON_IMP); + + if (m_pInstance) + m_pInstance->SetData(TYPE_TERESTIAN, IN_PROGRESS); + } + + void KilledUnit(Unit* /*pVictim*/) override + { + DoScriptText(urand(0, 1) ? SAY_SLAY1 : SAY_SLAY2, m_creature); + } + + void JustReachedHome() override + { + if (m_pInstance) + m_pInstance->SetData(TYPE_TERESTIAN, FAIL); + } + + void JustSummoned(Creature* pSummoned) override + { + switch (pSummoned->GetEntry()) + { + case NPC_PORTAL: + if (!m_bSummonedPortals) + { + m_bSummonedPortals = true; + DoCastSpellIfCan(m_creature, SPELL_FIENDISH_PORTAL_1, CAST_TRIGGERED); + } + break; + case NPC_KILREK: + m_creature->RemoveAurasDueToSpell(SPELL_BROKEN_PACT); + pSummoned->SetInCombatWithZone(); + break; + case NPC_DEMONCHAINS: + pSummoned->CastSpell(pSummoned, SPELL_DEMON_CHAINS, false); + break; + } + } + + void SummonedCreatureJustDied(Creature* pSummoned) override + { + switch (pSummoned->GetEntry()) + { + case NPC_KILREK: + pSummoned->CastSpell(m_creature, SPELL_BROKEN_PACT, true); + m_uiSummonKilrekTimer = 30000; + break; + case NPC_DEMONCHAINS: + if (Player* pPlayer = m_creature->GetMap()->GetPlayer(m_sacrificeGuid)) + pPlayer->RemoveAurasDueToSpell(SPELL_SACRIFICE); + break; + } + } + + void JustDied(Unit* /*pKiller*/) override + { + DoScriptText(SAY_DEATH, m_creature); + + if (m_pInstance) + m_pInstance->SetData(TYPE_TERESTIAN, DONE); + } + + void UpdateAI(const uint32 uiDiff) override + { + // Respawn Kilrek if killed + if (m_uiSummonKilrekTimer) + { + if (m_uiSummonKilrekTimer <= uiDiff) + { + if (DoCastSpellIfCan(m_creature, SPELL_SUMMON_IMP) == CAST_OK) + m_uiSummonKilrekTimer = 0; + } + else + m_uiSummonKilrekTimer -= uiDiff; + } + + if (!m_creature->SelectHostileTarget() || !m_creature->getVictim()) + return; + + if (m_uiSacrificeTimer < uiDiff) + { + if (Unit* pTarget = m_creature->SelectAttackingTarget(ATTACKING_TARGET_RANDOM, 1, SPELL_SACRIFICE, SELECT_FLAG_PLAYER)) + { + if (DoCastSpellIfCan(pTarget, SPELL_SACRIFICE) == CAST_OK) + { + DoCastSpellIfCan(m_creature, SPELL_SUMMON_DEMONCHAINS, CAST_TRIGGERED); + DoScriptText(urand(0, 1) ? SAY_SACRIFICE1 : SAY_SACRIFICE2, m_creature); + m_sacrificeGuid = pTarget->GetObjectGuid(); + m_uiSacrificeTimer = 43000; + } + } + } + else + m_uiSacrificeTimer -= uiDiff; + + if (m_uiShadowboltTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature->getVictim(), SPELL_SHADOW_BOLT) == CAST_OK) + m_uiShadowboltTimer = 10000; + } + else + m_uiShadowboltTimer -= uiDiff; + + if (m_uiSummonTimer) + { + if (m_uiSummonTimer <= uiDiff) + { + if (DoCastSpellIfCan(m_creature, SPELL_FIENDISH_PORTAL) == CAST_OK) + { + DoScriptText(urand(0, 1) ? SAY_SUMMON1 : SAY_SUMMON2, m_creature); + m_uiSummonTimer = 0; + } + } + else + m_uiSummonTimer -= uiDiff; + } + + if (m_uiBerserkTimer) + { + if (m_uiBerserkTimer <= uiDiff) + { + if (DoCastSpellIfCan(m_creature, SPELL_BERSERK) == CAST_OK) + m_uiBerserkTimer = 0; + } + else + m_uiBerserkTimer -= uiDiff; + } + + DoMeleeAttackIfReady(); + } +}; + +struct npc_fiendish_portalAI : public ScriptedAI +{ + npc_fiendish_portalAI(Creature* pCreature) : ScriptedAI(pCreature) { Reset(); } + + uint32 m_uiSummonTimer; + + void Reset() override + { + m_uiSummonTimer = 5000; + } + + void JustSummoned(Creature* pSummoned) override + { + pSummoned->ApplySpellImmune(0, IMMUNITY_SCHOOL, SPELL_SCHOOL_MASK_FIRE, true); + pSummoned->SetInCombatWithZone(); + } + + void UpdateAI(const uint32 uiDiff) override + { + if (m_uiSummonTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature, SPELL_SUMMON_FIENDISH_IMP) == CAST_OK) + m_uiSummonTimer = 5000; + } + else + m_uiSummonTimer -= uiDiff; + } +}; + +// TODO Remove this 'script' when combat can be proper prevented from core-side +struct mob_demon_chainAI : public Scripted_NoMovementAI +{ + mob_demon_chainAI(Creature* pCreature) : Scripted_NoMovementAI(pCreature) { Reset(); } + + void Reset() override { } + void MoveInLineOfSight(Unit* /*pWho*/) override { } + void AttackStart(Unit* /*pWho*/) override { } + void UpdateAI(const uint32 /*uiDiff*/) override { } +}; + +CreatureAI* GetAI_boss_terestian_illhoof(Creature* pCreature) +{ + return new boss_terestianAI(pCreature); +} + +CreatureAI* GetAI_npc_fiendish_portal(Creature* pCreature) +{ + return new npc_fiendish_portalAI(pCreature); +} + +CreatureAI* GetAI_mob_demon_chain(Creature* pCreature) +{ + return new mob_demon_chainAI(pCreature); +} + +void AddSC_boss_terestian_illhoof() +{ + Script* pNewScript; + + pNewScript = new Script; + pNewScript->Name = "boss_terestian_illhoof"; + pNewScript->GetAI = &GetAI_boss_terestian_illhoof; + pNewScript->RegisterSelf(); + + pNewScript = new Script; + pNewScript->Name = "npc_fiendish_portal"; + pNewScript->GetAI = &GetAI_npc_fiendish_portal; + pNewScript->RegisterSelf(); + + pNewScript = new Script; + pNewScript->Name = "mob_demon_chain"; + pNewScript->GetAI = &GetAI_mob_demon_chain; + pNewScript->RegisterSelf(); +} diff --git a/src/modules/SD2/scripts/eastern_kingdoms/karazhan/bosses_opera.cpp b/src/modules/SD2/scripts/eastern_kingdoms/karazhan/bosses_opera.cpp new file mode 100644 index 000000000..19497578e --- /dev/null +++ b/src/modules/SD2/scripts/eastern_kingdoms/karazhan/bosses_opera.cpp @@ -0,0 +1,1333 @@ +/* Copyright (C) 2006 - 2013 ScriptDev2 + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along 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: 95 +SDComment: Oz, Hood, and RAJ event implemented. Spell timers may need adjustments. +SDCategory: Karazhan +EndScriptData */ + +#include "precompiled.h" +#include "karazhan.h" + +/***********************************/ +/*** OPERA WIZARD OF OZ EVENT *****/ +/*********************************/ + +enum +{ + SAY_DOROTHEE_DEATH = -1532025, + SAY_DOROTHEE_SUMMON = -1532026, + SAY_DOROTHEE_TITO_DEATH = -1532027, + SAY_DOROTHEE_AGGRO = -1532028, + + SAY_ROAR_AGGRO = -1532029, + SAY_ROAR_DEATH = -1532030, + SAY_ROAR_SLAY = -1532031, + + SAY_STRAWMAN_AGGRO = -1532032, + SAY_STRAWMAN_DEATH = -1532033, + SAY_STRAWMAN_SLAY = -1532034, + + SAY_TINHEAD_AGGRO = -1532035, + SAY_TINHEAD_DEATH = -1532036, + SAY_TINHEAD_SLAY = -1532037, + EMOTE_RUST = -1532038, + + SAY_CRONE_AGGRO = -1532039, + SAY_CRONE_AGGRO2 = -1532040, + SAY_CRONE_DEATH = -1532041, + SAY_CRONE_SLAY = -1532042, + + /**** Spells ****/ + // Dorothee + SPELL_WATERBOLT = 31012, + SPELL_SCREAM = 31013, + SPELL_SUMMONTITO = 31014, + + // Strawman + SPELL_BRAIN_BASH = 31046, + SPELL_BRAIN_WIPE = 31069, + SPELL_CONFLAG_PROC = 31073, // procs 31075 on fire damage + + // Tinhead + SPELL_CLEAVE = 31043, + SPELL_RUST = 31086, + + // Roar + SPELL_MANGLE = 31041, + SPELL_SHRED = 31042, + SPELL_FRIGHTENED_SCREAM = 31013, + + // Crone + SPELL_CHAIN_LIGHTNING = 32337, + + // Cyclone + SPELL_CYCLONE = 32334, + SPELL_CYCLONE_VISUAL = 32332, + + /** Creature Entries **/ + NPC_TITO = 17548, + NPC_CYCLONE = 18412, +}; + +struct boss_dorotheeAI : public ScriptedAI +{ + boss_dorotheeAI(Creature* pCreature) : ScriptedAI(pCreature) + { + m_pInstance = (ScriptedInstance*)pCreature->GetInstanceData(); + Reset(); + } + + ScriptedInstance* m_pInstance; + + uint32 m_uiAggroTimer; + uint32 m_uiIntroTimer; + + uint32 m_uiWaterBoltTimer; + uint32 m_uiFearTimer; + uint32 m_uiSummonTitoTimer; + + bool m_bTitoDied; + + void Reset() override + { + m_uiIntroTimer = 2000; + m_uiAggroTimer = 12000; + + m_uiWaterBoltTimer = 5000; + m_uiFearTimer = 15000; + m_uiSummonTitoTimer = 47500; + + m_bTitoDied = false; + } + + void JustReachedHome() override + { + if (m_pInstance) + m_pInstance->SetData(TYPE_OPERA, FAIL); + + m_creature->ForcedDespawn(); + } + + void JustDied(Unit* /*pKiller*/) override + { + DoScriptText(SAY_DOROTHEE_DEATH, m_creature); + } + + void MoveInLineOfSight(Unit* pWho) override + { + // Allow a short delay before attacking + if (m_creature->HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_OOC_NOT_ATTACKABLE)) + return; + + ScriptedAI::MoveInLineOfSight(pWho); + } + + void JustSummoned(Creature* pSummoned) override + { + if (m_creature->getVictim()) + pSummoned->AI()->AttackStart(m_creature->getVictim()); + } + + void SummonedCreatureJustDied(Creature* pSummoned) override + { + if (pSummoned->GetEntry() == NPC_TITO) + { + DoScriptText(SAY_DOROTHEE_TITO_DEATH, m_creature); + m_bTitoDied = true; + } + } + + void UpdateAI(const uint32 uiDiff) override + { + if (m_uiIntroTimer) + { + if (m_uiIntroTimer <= uiDiff) + { + DoScriptText(SAY_DOROTHEE_AGGRO, m_creature); + m_uiIntroTimer = 0; + } + else + m_uiIntroTimer -= uiDiff; + } + + if (m_uiAggroTimer) + { + if (m_uiAggroTimer <= uiDiff) + { + m_creature->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_OOC_NOT_ATTACKABLE); + m_creature->SetInCombatWithZone(); + m_uiAggroTimer = 0; + } + else + m_uiAggroTimer -= uiDiff; + } + + if (!m_creature->SelectHostileTarget() || !m_creature->getVictim()) + return; + + if (m_uiWaterBoltTimer < uiDiff) + { + if (Unit* pTarget = m_creature->SelectAttackingTarget(ATTACKING_TARGET_RANDOM, 0)) + { + if (DoCastSpellIfCan(pTarget, SPELL_WATERBOLT) == CAST_OK) + m_uiWaterBoltTimer = m_bTitoDied ? 1500 : 5000; + } + } + else + m_uiWaterBoltTimer -= uiDiff; + + if (m_uiFearTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature, SPELL_SCREAM) == CAST_OK) + m_uiFearTimer = 30000; + } + else + m_uiFearTimer -= uiDiff; + + if (m_uiSummonTitoTimer) + { + if (m_uiSummonTitoTimer <= uiDiff) + { + if (DoCastSpellIfCan(m_creature, SPELL_SUMMONTITO) == CAST_OK) + { + DoScriptText(SAY_DOROTHEE_SUMMON, m_creature); + m_uiSummonTitoTimer = 0; + } + } + else + m_uiSummonTitoTimer -= uiDiff; + } + + DoMeleeAttackIfReady(); + } +}; + +struct boss_strawmanAI : public ScriptedAI +{ + boss_strawmanAI(Creature* pCreature) : ScriptedAI(pCreature) + { + m_pInstance = (ScriptedInstance*)pCreature->GetInstanceData(); + Reset(); + } + + ScriptedInstance* m_pInstance; + + uint32 m_uiAggroTimer; + uint32 m_uiBrainBashTimer; + uint32 m_uiBrainWipeTimer; + + void Reset() override + { + m_uiAggroTimer = 27000; + m_uiBrainBashTimer = 5000; + m_uiBrainWipeTimer = 7000; + } + + void MoveInLineOfSight(Unit* pWho) override + { + if (m_creature->HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_OOC_NOT_ATTACKABLE)) + return; + + ScriptedAI::MoveInLineOfSight(pWho); + } + + void AttackStart(Unit* pWho) override + { + if (m_creature->HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_OOC_NOT_ATTACKABLE)) + return; + + ScriptedAI::AttackStart(pWho); + } + + void Aggro(Unit* /*pWho*/) override + { + DoCastSpellIfCan(m_creature, SPELL_CONFLAG_PROC); + DoScriptText(SAY_STRAWMAN_AGGRO, m_creature); + } + + void JustReachedHome() override + { + if (m_pInstance) + m_pInstance->SetData(TYPE_OPERA, FAIL); + + m_creature->ForcedDespawn(); + } + + void JustDied(Unit* /*pKiller*/) override + { + DoScriptText(SAY_STRAWMAN_DEATH, m_creature); + } + + void KilledUnit(Unit* /*pVictim*/) override + { + DoScriptText(SAY_STRAWMAN_SLAY, m_creature); + } + + void UpdateAI(const uint32 uiDiff) override + { + if (m_uiAggroTimer) + { + if (m_uiAggroTimer <= uiDiff) + { + m_creature->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_OOC_NOT_ATTACKABLE); + m_creature->SetInCombatWithZone(); + m_uiAggroTimer = 0; + } + else + m_uiAggroTimer -= uiDiff; + } + + if (!m_creature->SelectHostileTarget() || !m_creature->getVictim()) + return; + + if (m_uiBrainBashTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature->getVictim(), SPELL_BRAIN_BASH) == CAST_OK) + m_uiBrainBashTimer = 15000; + } + else + m_uiBrainBashTimer -= uiDiff; + + if (m_uiBrainWipeTimer < uiDiff) + { + if (Unit* pTarget = m_creature->SelectAttackingTarget(ATTACKING_TARGET_RANDOM, 0)) + { + if (DoCastSpellIfCan(pTarget, SPELL_BRAIN_WIPE) == CAST_OK) + m_uiBrainWipeTimer = 20000; + } + } + else + m_uiBrainWipeTimer -= uiDiff; + + DoMeleeAttackIfReady(); + } +}; + +struct boss_tinheadAI : public ScriptedAI +{ + boss_tinheadAI(Creature* pCreature) : ScriptedAI(pCreature) + { + m_pInstance = (ScriptedInstance*)pCreature->GetInstanceData(); + Reset(); + } + + ScriptedInstance* m_pInstance; + + uint32 m_uiAggroTimer; + uint32 m_uiCleaveTimer; + uint32 m_uiRustTimer; + + void Reset() override + { + m_uiAggroTimer = 37000; + m_uiCleaveTimer = 5000; + m_uiRustTimer = 30000; + } + + void Aggro(Unit* /*pWho*/) override + { + DoScriptText(SAY_TINHEAD_AGGRO, m_creature); + } + + void JustReachedHome() override + { + if (m_pInstance) + m_pInstance->SetData(TYPE_OPERA, FAIL); + + m_creature->ForcedDespawn(); + } + + void MoveInLineOfSight(Unit* pWho) override + { + if (m_creature->HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_OOC_NOT_ATTACKABLE)) + return; + + ScriptedAI::MoveInLineOfSight(pWho); + } + + void AttackStart(Unit* pWho) override + { + if (m_creature->HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_OOC_NOT_ATTACKABLE)) + return; + + ScriptedAI::AttackStart(pWho); + } + + void JustDied(Unit* /*pKiller*/) override + { + DoScriptText(SAY_TINHEAD_DEATH, m_creature); + } + + void KilledUnit(Unit* /*pVictim*/) override + { + DoScriptText(SAY_TINHEAD_SLAY, m_creature); + } + + void UpdateAI(const uint32 uiDiff) override + { + if (m_uiAggroTimer) + { + if (m_uiAggroTimer <= uiDiff) + { + m_creature->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_OOC_NOT_ATTACKABLE); + m_creature->SetInCombatWithZone(); + m_uiAggroTimer = 0; + } + else + m_uiAggroTimer -= uiDiff; + } + + if (!m_creature->SelectHostileTarget() || !m_creature->getVictim()) + return; + + if (m_uiCleaveTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature->getVictim(), SPELL_CLEAVE) == CAST_OK) + m_uiCleaveTimer = 5000; + } + else + m_uiCleaveTimer -= uiDiff; + + if (m_uiRustTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature, SPELL_RUST) == CAST_OK) + { + DoScriptText(EMOTE_RUST, m_creature); + m_uiRustTimer = 6000; + } + } + else + m_uiRustTimer -= uiDiff; + + DoMeleeAttackIfReady(); + } +}; + +struct boss_roarAI : public ScriptedAI +{ + boss_roarAI(Creature* pCreature) : ScriptedAI(pCreature) + { + m_pInstance = (ScriptedInstance*)pCreature->GetInstanceData(); + Reset(); + } + + ScriptedInstance* m_pInstance; + + uint32 m_uiAggroTimer; + uint32 m_uiMangleTimer; + uint32 m_uiShredTimer; + uint32 m_uiScreamTimer; + + void Reset() override + { + m_uiAggroTimer = 17000; + m_uiMangleTimer = 5000; + m_uiShredTimer = 10000; + m_uiScreamTimer = 15000; + } + + void MoveInLineOfSight(Unit* pWho) override + { + if (m_creature->HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_OOC_NOT_ATTACKABLE)) + return; + + ScriptedAI::MoveInLineOfSight(pWho); + } + + void AttackStart(Unit* pWho) override + { + if (m_creature->HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_OOC_NOT_ATTACKABLE)) + return; + + ScriptedAI::AttackStart(pWho); + } + + void Aggro(Unit* /*pWho*/) override + { + DoScriptText(SAY_ROAR_AGGRO, m_creature); + } + + void JustReachedHome() override + { + if (m_pInstance) + m_pInstance->SetData(TYPE_OPERA, FAIL); + + m_creature->ForcedDespawn(); + } + + void JustDied(Unit* /*pKiller*/) override + { + DoScriptText(SAY_ROAR_DEATH, m_creature); + } + + void KilledUnit(Unit* /*pVictim*/) override + { + DoScriptText(SAY_ROAR_SLAY, m_creature); + } + + void UpdateAI(const uint32 uiDiff) override + { + if (m_uiAggroTimer) + { + if (m_uiAggroTimer <= uiDiff) + { + m_creature->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_OOC_NOT_ATTACKABLE); + m_creature->SetInCombatWithZone(); + m_uiAggroTimer = 0; + } + else + m_uiAggroTimer -= uiDiff; + } + + if (!m_creature->SelectHostileTarget() || !m_creature->getVictim()) + return; + + if (m_uiMangleTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature->getVictim(), SPELL_MANGLE) == CAST_OK) + m_uiMangleTimer = urand(5000, 8000); + } + else + m_uiMangleTimer -= uiDiff; + + if (m_uiShredTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature->getVictim(), SPELL_SHRED) == CAST_OK) + m_uiShredTimer = urand(10000, 15000); + } + else + m_uiShredTimer -= uiDiff; + + if (m_uiScreamTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature, SPELL_FRIGHTENED_SCREAM) == CAST_OK) + m_uiScreamTimer = urand(20000, 30000); + } + else + m_uiScreamTimer -= uiDiff; + + DoMeleeAttackIfReady(); + } +}; + +static const float afCycloneSpawnLoc[4] = { -10907.68f, -1778.651f, 90.56018f, 0.61f}; + +struct boss_croneAI : public ScriptedAI +{ + boss_croneAI(Creature* pCreature) : ScriptedAI(pCreature) + { + m_pInstance = (ScriptedInstance*)pCreature->GetInstanceData(); + Reset(); + } + + ScriptedInstance* m_pInstance; + + uint32 m_uiChainLightningTimer; + + void Reset() override + { + m_uiChainLightningTimer = 10000; + } + + void JustReachedHome() override + { + if (m_pInstance) + m_pInstance->SetData(TYPE_OPERA, FAIL); + + m_creature->ForcedDespawn(); + } + + void Aggro(Unit* /*pWho*/) override + { + DoScriptText(urand(0, 1) ? SAY_CRONE_AGGRO : SAY_CRONE_AGGRO2, m_creature); + + // spawn the cyclone on aggro + m_creature->SummonCreature(NPC_CYCLONE, afCycloneSpawnLoc[0], afCycloneSpawnLoc[1], afCycloneSpawnLoc[2], afCycloneSpawnLoc[3], TEMPSUMMON_DEAD_DESPAWN, 0); + } + + void JustDied(Unit* /*pKiller*/) override + { + DoScriptText(SAY_CRONE_DEATH, m_creature); + + if (m_pInstance) + m_pInstance->SetData(TYPE_OPERA, DONE); + } + + void JustSummoned(Creature* pSummoned) override + { + pSummoned->CastSpell(pSummoned, SPELL_CYCLONE, true); + pSummoned->CastSpell(pSummoned, SPELL_CYCLONE_VISUAL, true); + pSummoned->GetMotionMaster()->MoveRandomAroundPoint(m_creature->GetPositionX(), m_creature->GetPositionY(), m_creature->GetPositionZ(), 15.0f); + } + + void UpdateAI(const uint32 uiDiff) override + { + if (!m_creature->SelectHostileTarget() || !m_creature->getVictim()) + return; + + if (m_uiChainLightningTimer < uiDiff) + { + if (Unit* pTarget = m_creature->SelectAttackingTarget(ATTACKING_TARGET_RANDOM, 0)) + { + if (DoCastSpellIfCan(pTarget, SPELL_CHAIN_LIGHTNING) == CAST_OK) + m_uiChainLightningTimer = 15000; + } + } + else + m_uiChainLightningTimer -= uiDiff; + + DoMeleeAttackIfReady(); + } +}; + +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); +} + +/**************************************/ +/**** Opera Red Riding Hood Event ****/ +/************************************/ + +enum +{ + /**** Yells for the Wolf ****/ + SAY_WOLF_AGGRO = -1532043, + SAY_WOLF_SLAY = -1532044, + SAY_WOLF_HOOD = -1532045, + SOUND_WOLF_DEATH = 9275, // Only sound on death, no text. + + /**** Spells For The Wolf ****/ + SPELL_PICK_RED_RIDING_HOOD = 30769, // targeting spell - triggers 30768 + SPELL_TERRIFYING_HOWL = 30752, + SPELL_WIDE_SWIPE = 30761, + + GOSSIP_ITEM_GRANDMA = -3532005, + TEXT_ID_GRANDMA = 8990, + + /**** The Wolf's Entry ****/ + NPC_BIG_BAD_WOLF = 17521 +}; + +bool GossipHello_npc_grandmother(Player* pPlayer, Creature* pCreature) +{ + pPlayer->ADD_GOSSIP_ITEM_ID(GOSSIP_ICON_CHAT, GOSSIP_ITEM_GRANDMA, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF); + pPlayer->SEND_GOSSIP_MENU(TEXT_ID_GRANDMA, pCreature->GetObjectGuid()); + + 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(NPC_BIG_BAD_WOLF, 0, 0, 0, 0, TEMPSUMMON_DEAD_DESPAWN, 0)) + pBigBadWolf->AI()->AttackStart(pPlayer); + + pCreature->ForcedDespawn(); + } + + return true; +} + +struct boss_bigbadwolfAI : public ScriptedAI +{ + boss_bigbadwolfAI(Creature* pCreature) : ScriptedAI(pCreature) + { + m_pInstance = (ScriptedInstance*)pCreature->GetInstanceData(); + Reset(); + } + + ScriptedInstance* m_pInstance; + + uint32 m_uiRedRidingHoodTimer; + uint32 m_uiFearTimer; + uint32 m_uiSwipeTimer; + + void Reset() override + { + m_uiRedRidingHoodTimer = 30000; + m_uiFearTimer = urand(25000, 35000); + m_uiSwipeTimer = 5000; + } + + void Aggro(Unit* /*pWho*/) override + { + DoScriptText(SAY_WOLF_AGGRO, m_creature); + } + + void JustReachedHome() override + { + if (m_pInstance) + m_pInstance->SetData(TYPE_OPERA, FAIL); + + m_creature->ForcedDespawn(); + } + + void JustDied(Unit* /*pKiller*/) override + { + DoPlaySoundToSet(m_creature, SOUND_WOLF_DEATH); + + if (m_pInstance) + m_pInstance->SetData(TYPE_OPERA, DONE); + } + + void UpdateAI(const uint32 uiDiff) override + { + if (!m_creature->SelectHostileTarget() || !m_creature->getVictim()) + return; + + if (m_uiRedRidingHoodTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature, SPELL_PICK_RED_RIDING_HOOD) == CAST_OK) + { + DoScriptText(SAY_WOLF_HOOD, m_creature); + m_uiRedRidingHoodTimer = 30000; + } + } + else + m_uiRedRidingHoodTimer -= uiDiff; + + if (m_uiFearTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature, SPELL_TERRIFYING_HOWL) == CAST_OK) + m_uiFearTimer = 24000; + } + else + m_uiFearTimer -= uiDiff; + + if (m_uiSwipeTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature, SPELL_WIDE_SWIPE) == CAST_OK) + m_uiSwipeTimer = urand(25000, 30000); + } + else + m_uiSwipeTimer -= uiDiff; + + DoMeleeAttackIfReady(); + } +}; + +CreatureAI* GetAI_boss_bigbadwolf(Creature* pCreature) +{ + return new boss_bigbadwolfAI(pCreature); +} + +/**********************************************/ +/******** Opera Romeo and Juliet Event *******/ +/********************************************/ + +enum +{ + /**** Speech *****/ + SAY_JULIANNE_AGGRO = -1532046, + SAY_JULIANNE_ENTER = -1532047, + SAY_JULIANNE_DEATH01 = -1532048, + SAY_JULIANNE_DEATH02 = -1532049, + SAY_JULIANNE_RESURRECT = -1532050, + SAY_JULIANNE_SLAY = -1532051, + + SAY_ROMULO_AGGRO = -1532052, + SAY_ROMULO_DEATH = -1532053, + SAY_ROMULO_ENTER = -1532054, + SAY_ROMULO_RESURRECT = -1532055, + SAY_ROMULO_SLAY = -1532056, + + /***** Spells For Julianne *****/ + SPELL_BLINDING_PASSION = 30890, + SPELL_DEVOTION = 30887, + SPELL_ETERNAL_AFFECTION = 30878, + SPELL_POWERFUL_ATTRACTION = 30889, + SPELL_DRINK_POISON = 30907, + + /***** Spells For Romulo ****/ + SPELL_BACKWARD_LUNGE = 30815, + SPELL_DARING = 30841, + SPELL_DEADLY_SWATHE = 30817, + SPELL_POISON_THRUST = 30822, + + /**** Other Misc. Spells ****/ + SPELL_FULL_HEALTH = 43979, // res effect on Julianne + SPELL_UNDYING_LOVE = 30951, // res effect on Romulo +}; + +enum OperaPhase +{ + PHASE_JULIANNE = 0, + PHASE_ROMULO = 1, + PHASE_BOTH = 2, +}; + +static const float afRomuloSpawnLoc[4] = { -10893.62f, -1760.78f, 90.55f, 4.76f}; + +struct boss_julianneAI : public ScriptedAI +{ + boss_julianneAI(Creature* pCreature) : ScriptedAI(pCreature) + { + m_pInstance = (ScriptedInstance*)pCreature->GetInstanceData(); + Reset(); + } + + ScriptedInstance* m_pInstance; + + OperaPhase m_Phase; + + uint32 m_uiBlindingPassionTimer; + uint32 m_uiDevotionTimer; + uint32 m_uiEternalAffectionTimer; + uint32 m_uiPowerfulAttractionTimer; + uint32 m_uiSummonRomuloTimer; + uint32 m_uiResurrectSelfTimer; + + bool m_bIsFakingDeath; + + void Reset() override + { + m_Phase = PHASE_JULIANNE; + + m_uiBlindingPassionTimer = 30000; + m_uiDevotionTimer = 15000; + m_uiEternalAffectionTimer = 25000; + m_uiPowerfulAttractionTimer = 5000; + m_uiSummonRomuloTimer = 0; + m_uiResurrectSelfTimer = 0; + + m_bIsFakingDeath = false; + } + + void Aggro(Unit* /*pWho*/) override + { + DoScriptText(SAY_JULIANNE_AGGRO, m_creature); + } + + void JustReachedHome() override + { + if (m_pInstance) + m_pInstance->SetData(TYPE_OPERA, FAIL); + + m_creature->ForcedDespawn(); + } + + void DamageTaken(Unit* /*pDoneBy*/, uint32& uiDamage) override + { + if (uiDamage < m_creature->GetHealth()) + return; + + uiDamage = 0; + + if (m_bIsFakingDeath) + return; + + if (m_Phase == PHASE_JULIANNE) + { + // Prepare fake death + if (DoCastSpellIfCan(m_creature, SPELL_DRINK_POISON, CAST_INTERRUPT_PREVIOUS) == CAST_OK) + { + m_Phase = PHASE_BOTH; + m_bIsFakingDeath = true; + m_uiSummonRomuloTimer = 12000; + } + } + else if (m_Phase == PHASE_BOTH) + { + // set fake death and allow 10 sec timer to kill Romulos + DoScriptText(SAY_JULIANNE_DEATH02, m_creature); + DoSetFakeDeath(); + m_uiResurrectSelfTimer = 10000; + } + } + + void JustDied(Unit* /*pKiller*/) override + { + m_creature->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE); + + if (m_pInstance) + m_pInstance->SetData(TYPE_OPERA, DONE); + } + + void KilledUnit(Unit* pVictim) override + { + if (pVictim->GetTypeId() != TYPEID_PLAYER) + return; + + DoScriptText(SAY_JULIANNE_SLAY, m_creature); + } + + void JustSummoned(Creature* pSummoned) override + { + if (m_creature->getVictim()) + pSummoned->AI()->AttackStart(m_creature->getVictim()); + } + + // Wrapper to set fake death + void DoSetFakeDeath() + { + m_bIsFakingDeath = true; + + m_creature->InterruptNonMeleeSpells(false); + m_creature->SetHealth(1); + m_creature->StopMoving(); + m_creature->ClearComboPointHolders(); + m_creature->RemoveAllAurasOnDeath(); + m_creature->ModifyAuraState(AURA_STATE_HEALTHLESS_20_PERCENT, false); + m_creature->ModifyAuraState(AURA_STATE_HEALTHLESS_35_PERCENT, false); + m_creature->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE); + m_creature->ClearAllReactives(); + m_creature->SetTargetGuid(ObjectGuid()); + m_creature->GetMotionMaster()->Clear(); + m_creature->GetMotionMaster()->MoveIdle(); + m_creature->SetStandState(UNIT_STAND_STATE_DEAD); + } + + // Wrapper to remove fake death + void DoRemoveFakeDeath() + { + m_bIsFakingDeath = false; + + m_creature->SetStandState(UNIT_STAND_STATE_STAND); + m_creature->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE); + m_creature->GetMotionMaster()->Clear(); + DoStartMovement(m_creature->getVictim()); + } + + // Wrapper to start phase 3 + void DoHandleRomuloResurrect() + { + if (DoCastSpellIfCan(m_creature, SPELL_UNDYING_LOVE) == CAST_OK) + { + DoCastSpellIfCan(m_creature, SPELL_FULL_HEALTH, CAST_TRIGGERED); + DoScriptText(SAY_JULIANNE_RESURRECT, m_creature); + DoRemoveFakeDeath(); + } + } + + void UpdateAI(const uint32 uiDiff) override + { + // spawn Romulo on timer after fake death + if (m_uiSummonRomuloTimer) + { + if (m_uiSummonRomuloTimer <= uiDiff) + { + m_creature->SummonCreature(NPC_ROMULO, afRomuloSpawnLoc[0], afRomuloSpawnLoc[1], afRomuloSpawnLoc[2], afRomuloSpawnLoc[3], TEMPSUMMON_DEAD_DESPAWN, 0); + m_uiSummonRomuloTimer = 0; + } + else + m_uiSummonRomuloTimer -= uiDiff; + } + + if (m_uiResurrectSelfTimer) + { + if (m_uiResurrectSelfTimer <= uiDiff) + { + if (m_pInstance) + { + if (Creature* pRomulo = m_pInstance->GetSingleCreatureFromStorage(NPC_ROMULO)) + { + // if Romulos is dead, then self kill + if (pRomulo->HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE)) + { + m_creature->DealDamage(m_creature, m_creature->GetHealth(), NULL, DIRECT_DAMAGE, SPELL_SCHOOL_MASK_NONE, NULL, false); + pRomulo->DealDamage(pRomulo, pRomulo->GetHealth(), NULL, DIRECT_DAMAGE, SPELL_SCHOOL_MASK_NONE, NULL, false); + } + else + { + DoRemoveFakeDeath(); + DoCastSpellIfCan(m_creature, SPELL_FULL_HEALTH, CAST_TRIGGERED); + } + } + } + m_uiResurrectSelfTimer = 0; + } + else + m_uiResurrectSelfTimer -= uiDiff; + } + + if (!m_creature->SelectHostileTarget() || !m_creature->getVictim()) + return; + + // don't use spells during transition + if (m_bIsFakingDeath) + return; + + if (m_uiBlindingPassionTimer < uiDiff) + { + if (Unit* pTarget = m_creature->SelectAttackingTarget(ATTACKING_TARGET_RANDOM, 0)) + { + if (DoCastSpellIfCan(pTarget, SPELL_BLINDING_PASSION) == CAST_OK) + m_uiBlindingPassionTimer = urand(30000, 45000); + } + } + else + m_uiBlindingPassionTimer -= uiDiff; + + if (m_uiDevotionTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature, SPELL_DEVOTION) == CAST_OK) + m_uiDevotionTimer = urand(15000, 45000); + } + else + m_uiDevotionTimer -= uiDiff; + + if (m_uiPowerfulAttractionTimer < uiDiff) + { + if (Unit* pTarget = m_creature->SelectAttackingTarget(ATTACKING_TARGET_RANDOM, 0)) + { + if (DoCastSpellIfCan(pTarget, SPELL_POWERFUL_ATTRACTION) == CAST_OK) + m_uiPowerfulAttractionTimer = urand(5000, 30000); + } + } + else + m_uiPowerfulAttractionTimer -= uiDiff; + + if (m_uiEternalAffectionTimer < uiDiff) + { + if (Unit* pTarget = DoSelectLowestHpFriendly(30.0f)) + { + if (DoCastSpellIfCan(pTarget, SPELL_ETERNAL_AFFECTION) == CAST_OK) + m_uiEternalAffectionTimer = urand(45000, 60000); + } + } + else + m_uiEternalAffectionTimer -= uiDiff; + + DoMeleeAttackIfReady(); + } +}; + +bool EffectDummyCreature_spell_drink_poison(Unit* /*pCaster*/, uint32 uiSpellId, SpellEffectIndex uiEffIndex, Creature* pCreatureTarget, ObjectGuid /*originalCasterGuid*/) +{ + // always check spellid and effectindex + if (uiSpellId == SPELL_DRINK_POISON && uiEffIndex == EFFECT_INDEX_0) + { + // Set fake death on poison + if (boss_julianneAI* pJulianneAI = dynamic_cast(pCreatureTarget->AI())) + pJulianneAI->DoSetFakeDeath(); + + DoScriptText(SAY_JULIANNE_DEATH01, pCreatureTarget); + + // always return true when we are handling this spell and effect + return true; + } + + return false; +} + +struct boss_romuloAI : public ScriptedAI +{ + boss_romuloAI(Creature* pCreature) : ScriptedAI(pCreature) + { + m_pInstance = (ScriptedInstance*)pCreature->GetInstanceData(); + Reset(); + } + + ScriptedInstance* m_pInstance; + + OperaPhase m_Phase; + + uint32 m_uiBackwardLungeTimer; + uint32 m_uiDaringTimer; + uint32 m_uiDeadlySwatheTimer; + uint32 m_uiPoisonThrustTimer; + uint32 m_uiResurrectTimer; + uint32 m_uiResurrectSelfTimer; + + bool m_bIsFakingDeath; + + void Reset() override + { + m_Phase = PHASE_ROMULO; + + m_uiBackwardLungeTimer = 15000; + m_uiDaringTimer = 20000; + m_uiDeadlySwatheTimer = 25000; + m_uiPoisonThrustTimer = 10000; + m_uiResurrectTimer = 0; + m_uiResurrectSelfTimer = 0; + + m_bIsFakingDeath = false; + } + + void JustReachedHome() override + { + if (m_pInstance) + m_pInstance->SetData(TYPE_OPERA, FAIL); + + m_creature->ForcedDespawn(); + } + + void DamageTaken(Unit* /*pDoneBy*/, uint32& uiDamage) override + { + if (uiDamage < m_creature->GetHealth()) + return; + + uiDamage = 0; + + if (m_Phase == PHASE_ROMULO) + { + DoScriptText(SAY_ROMULO_DEATH, m_creature); + DoSetFakeDeath(); + m_Phase = PHASE_BOTH; + m_uiResurrectTimer = 10000; + } + else if (m_Phase == PHASE_BOTH) + { + // set fake death and allow 10 sec timer to kill Julianne + DoSetFakeDeath(); + m_uiResurrectSelfTimer = 10000; + } + } + + void Aggro(Unit* /*pWho*/) override + { + DoScriptText(SAY_ROMULO_AGGRO, m_creature); + } + + void JustDied(Unit* /*pKiller*/) override + { + m_creature->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE); + + if (m_pInstance) + m_pInstance->SetData(TYPE_OPERA, DONE); + } + + void KilledUnit(Unit* pVictim) override + { + if (pVictim->GetTypeId() != TYPEID_PLAYER) + return; + + DoScriptText(SAY_ROMULO_SLAY, m_creature); + } + + void SpellHit(Unit* pCaster, const SpellEntry* pSpell) override + { + // remove fake death on res + if (pSpell->Id == SPELL_UNDYING_LOVE && pCaster->GetEntry() == NPC_JULIANNE) + DoRemoveFakeDeath(); + } + + // Wrapper to set fake death + void DoSetFakeDeath() + { + m_bIsFakingDeath = true; + + m_creature->InterruptNonMeleeSpells(false); + m_creature->SetHealth(1); + m_creature->StopMoving(); + m_creature->ClearComboPointHolders(); + m_creature->RemoveAllAurasOnDeath(); + m_creature->ModifyAuraState(AURA_STATE_HEALTHLESS_20_PERCENT, false); + m_creature->ModifyAuraState(AURA_STATE_HEALTHLESS_35_PERCENT, false); + m_creature->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE); + m_creature->ClearAllReactives(); + m_creature->SetTargetGuid(ObjectGuid()); + m_creature->GetMotionMaster()->Clear(); + m_creature->GetMotionMaster()->MoveIdle(); + m_creature->SetStandState(UNIT_STAND_STATE_DEAD); + } + + // Wrapper to remove fake death + void DoRemoveFakeDeath() + { + m_bIsFakingDeath = false; + + m_creature->SetStandState(UNIT_STAND_STATE_STAND); + m_creature->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE); + m_creature->GetMotionMaster()->Clear(); + DoStartMovement(m_creature->getVictim()); + } + + void UpdateAI(const uint32 uiDiff) override + { + // Resurrect both of them at the beginning of phase 3 + if (m_uiResurrectTimer) + { + if (m_uiResurrectTimer <= uiDiff) + { + if (m_pInstance) + { + if (Creature* pJulianne = m_pInstance->GetSingleCreatureFromStorage(NPC_JULIANNE)) + { + if (boss_julianneAI* pJulianneAI = dynamic_cast(pJulianne->AI())) + pJulianneAI->DoHandleRomuloResurrect(); + } + } + m_uiResurrectTimer = 0; + } + else + m_uiResurrectTimer -= uiDiff; + } + + if (m_uiResurrectSelfTimer) + { + if (m_uiResurrectSelfTimer <= uiDiff) + { + if (m_pInstance) + { + if (Creature* pJulianne = m_pInstance->GetSingleCreatureFromStorage(NPC_JULIANNE)) + { + // if Julianne is dead, then self kill + if (pJulianne->HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE)) + { + m_creature->DealDamage(m_creature, m_creature->GetHealth(), NULL, DIRECT_DAMAGE, SPELL_SCHOOL_MASK_NONE, NULL, false); + pJulianne->DealDamage(pJulianne, pJulianne->GetHealth(), NULL, DIRECT_DAMAGE, SPELL_SCHOOL_MASK_NONE, NULL, false); + } + else + { + DoRemoveFakeDeath(); + DoScriptText(SAY_ROMULO_RESURRECT, m_creature); + DoCastSpellIfCan(m_creature, SPELL_FULL_HEALTH, CAST_TRIGGERED); + } + } + } + m_uiResurrectSelfTimer = 0; + } + else + m_uiResurrectSelfTimer -= uiDiff; + } + + if (!m_creature->SelectHostileTarget() || !m_creature->getVictim()) + return; + + // don't use spells on fake death + if (m_bIsFakingDeath) + return; + + if (m_uiBackwardLungeTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature, SPELL_BACKWARD_LUNGE) == CAST_OK) + m_uiBackwardLungeTimer = urand(15000, 30000); + } + else + m_uiBackwardLungeTimer -= uiDiff; + + if (m_uiDaringTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature, SPELL_DARING) == CAST_OK) + m_uiDaringTimer = urand(20000, 40000); + } + else + m_uiDaringTimer -= uiDiff; + + if (m_uiDeadlySwatheTimer < uiDiff) + { + if (Unit* pTarget = m_creature->SelectAttackingTarget(ATTACKING_TARGET_RANDOM, 0)) + { + if (DoCastSpellIfCan(pTarget, SPELL_DEADLY_SWATHE) == CAST_OK) + m_uiDeadlySwatheTimer = urand(15000, 25000); + } + } + else + m_uiDeadlySwatheTimer -= uiDiff; + + if (m_uiPoisonThrustTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature->getVictim(), SPELL_POISON_THRUST) == CAST_OK) + m_uiPoisonThrustTimer = urand(10000, 20000); + } + else + m_uiPoisonThrustTimer -= uiDiff; + + 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* pNewScript; + + // Oz + pNewScript = new Script; + pNewScript->Name = "boss_dorothee"; + pNewScript->GetAI = &GetAI_boss_dorothee; + pNewScript->RegisterSelf(); + + pNewScript = new Script; + pNewScript->Name = "boss_strawman"; + pNewScript->GetAI = &GetAI_boss_strawman; + pNewScript->RegisterSelf(); + + pNewScript = new Script; + pNewScript->Name = "boss_tinhead"; + pNewScript->GetAI = &GetAI_boss_tinhead; + pNewScript->RegisterSelf(); + + pNewScript = new Script; + pNewScript->Name = "boss_roar"; + pNewScript->GetAI = &GetAI_boss_roar; + pNewScript->RegisterSelf(); + + pNewScript = new Script; + pNewScript->Name = "boss_crone"; + pNewScript->GetAI = &GetAI_boss_crone; + pNewScript->RegisterSelf(); + + // Hood + pNewScript = new Script; + pNewScript->Name = "npc_grandmother"; + pNewScript->pGossipHello = &GossipHello_npc_grandmother; + pNewScript->pGossipSelect = &GossipSelect_npc_grandmother; + pNewScript->RegisterSelf(); + + pNewScript = new Script; + pNewScript->Name = "boss_bigbadwolf"; + pNewScript->GetAI = &GetAI_boss_bigbadwolf; + pNewScript->RegisterSelf(); + + // Romeo And Juliet + pNewScript = new Script; + pNewScript->Name = "boss_julianne"; + pNewScript->GetAI = &GetAI_boss_julianne; + pNewScript->pEffectDummyNPC = &EffectDummyCreature_spell_drink_poison; + pNewScript->RegisterSelf(); + + pNewScript = new Script; + pNewScript->Name = "boss_romulo"; + pNewScript->GetAI = &GetAI_boss_romulo; + pNewScript->RegisterSelf(); +} diff --git a/src/modules/SD2/scripts/eastern_kingdoms/karazhan/chess_event.cpp b/src/modules/SD2/scripts/eastern_kingdoms/karazhan/chess_event.cpp new file mode 100644 index 000000000..9eabaa63c --- /dev/null +++ b/src/modules/SD2/scripts/eastern_kingdoms/karazhan/chess_event.cpp @@ -0,0 +1,1765 @@ +/* Copyright (C) 2006 - 2013 ScriptDev2 + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +/* ScriptData +SDName: chess_event +SD%Complete: 80 +SDComment: Chess AI could use some improvements. +SDCategory: Karazhan +EndScriptData */ + +#include "precompiled.h" +#include "karazhan.h" + +enum +{ + // texts + EMOTE_LIFT_CURSE = -1532131, + EMOTE_CHEAT = -1532132, + + SOUND_ID_GAME_BEGIN = 10338, + SOUND_ID_LOSE_PAWN_PLAYER_1 = 10339, + SOUND_ID_LOSE_PAWN_PLAYER_2 = 10340, + SOUND_ID_LOSE_PAWN_PLAYER_3 = 10341, + SOUND_ID_LOSE_PAWN_MEDIVH_1 = 10342, + SOUND_ID_LOSE_PAWN_MEDIVH_2 = 10343, + SOUND_ID_LOSE_PAWN_MEDIVH_3 = 10344, + SOUND_ID_LOSE_ROOK_PLAYER = 10345, + SOUND_ID_LOSE_ROOK_MEDIVH = 10346, + SOUND_ID_LOSE_BISHOP_PLAYER = 10347, + SOUND_ID_LOSE_BISHOP_MEDIVH = 10348, + SOUND_ID_LOSE_KNIGHT_PLAYER = 10349, + SOUND_ID_LOSE_KNIGHT_MEDIVH = 10350, + SOUND_ID_LOSE_QUEEN_PLAYER = 10351, + SOUND_ID_LOSE_QUEEN_MEDIVH = 10352, + SOUND_ID_CHECK_PLAYER = 10353, + SOUND_ID_CHECK_MEDIVH = 10354, + SOUND_ID_WIN_PLAYER = 10355, + SOUND_ID_WIN_MEDIVH = 10356, + SOUND_ID_CHEAT_1 = 10357, + SOUND_ID_CHEAT_2 = 10358, + SOUND_ID_CHEAT_3 = 10359, + + // movement spells + SPELL_MOVE_GENERIC = 30012, // spell which sends the signal to move - handled in core + SPELL_MOVE_1 = 32312, // spell which selects AI move square (for short range pieces) + SPELL_MOVE_2 = 37388, // spell which selects AI move square (for long range pieces) + // SPELL_MOVE_PAWN = 37146, // individual move spells (used only by controlled npcs) + // SPELL_MOVE_KNIGHT = 37144, + // SPELL_MOVE_QUEEN = 37148, + // SPELL_MOVE_ROCK = 37151, + // SPELL_MOVE_BISHOP = 37152, + // SPELL_MOVE_KING = 37153, + + // additional movement spells + SPELL_CHANGE_FACING = 30284, // spell which sends the initial facing request - handled in core + SPELL_FACE_SQUARE = 30270, // change facing - finalize facing update + + SPELL_MOVE_TO_SQUARE = 30253, // spell which sends the move response from the square to the piece + SPELL_MOVE_COOLDOWN = 30543, // add some cooldown to movement + SPELL_MOVE_MARKER = 32261, // white beam visual - used to mark the movement as complete + SPELL_DISABLE_SQUARE = 32745, // used by the White / Black triggers on the squares when a chess piece moves into place + SPELL_IS_SQUARE_USED = 39400, // cast when a chess piece moves to another square + // SPELL_SQUARED_OCCUPIED = 39399, // triggered by 39400; used to check if the square is occupied (if hits a target); Missing in 2.4.3 + + // generic spells + SPELL_IN_GAME = 30532, // teleport player near the entrance + SPELL_CONTROL_PIECE = 30019, // control a chess piece + SPELL_RECENTLY_IN_GAME = 30529, // debuff on player after chess piece uncharm + + SPELL_CHESS_AI_ATTACK_TIMER = 32226, // melee action timer - triggers 32225 + SPELL_ACTION_MELEE = 32225, // handle melee attacks + SPELL_MELEE_DAMAGE = 32247, // melee damage spell - used by all chess pieces + // SPELL_AI_SNAPSHOT_TIMER = 37440, // used to trigger spell 32260; purpose and usage unk + // SPELL_DISABLE_SQUARE_SELF = 32260, // used when a piece moves to another square + // SPELL_AI_ACTION_TIMER = 37504, // handle some kind of event check. Cast by npc 17459. Currently the way it works is unk + // SPELL_DISABLE_SQUARE = 30271, // not used + // SPELL_FIND_ENEMY = 32303, // not used + // SPELL_MOVE_NEAR_UNIT = 30417, // not used + // SPELL_GET_EMPTY_SQUARE = 30418, // not used + // SPELL_FACE_NEARBY_ENEMY = 37787, // not used + // SPELL_POST_MOVE_FACING = 38011, // not used + + // melee action spells + SPELL_MELEE_FOOTMAN = 32227, + SPELL_MELEE_WATER_ELEM = 37142, + SPELL_MELEE_CHARGER = 37143, + SPELL_MELEE_CLERIC = 37147, + SPELL_MELEE_CONJURER = 37149, + SPELL_MELEE_KING_LLANE = 37150, + SPELL_MELEE_GRUNT = 32228, + SPELL_MELEE_DAEMON = 37220, + SPELL_MELEE_NECROLYTE = 37337, + SPELL_MELEE_WOLF = 37339, + SPELL_MELEE_WARLOCK = 37345, + SPELL_MELEE_WARCHIEF_BLACKHAND = 37348, + + // cheat spells + SPELL_HAND_OF_MEDIVH_HORDE = 39338, // triggers 39339 + SPELL_HAND_OF_MEDIVH_ALLIANCE = 39342, // triggers 39339 + SPELL_FURY_OF_MEDIVH_HORDE = 39341, // triggers 39343 + SPELL_FURY_OF_MEDIVH_ALLIANCE = 39344, // triggers 39345 + SPELL_FURY_OF_MEDIVH_AURA = 39383, + // SPELL_FULL_HEAL_HORDE = 39334, // spells are not confirmed (probably removed after 2.4.3) + // SPELL_FULL_HEAL_ALLIANCE = 39335, + + // spells used by the chess npcs + SPELL_HEROISM = 37471, // human king + SPELL_SWEEP = 37474, + SPELL_BLOODLUST = 37472, // orc king + SPELL_CLEAVE = 37476, + SPELL_HEROIC_BLOW = 37406, // human pawn + SPELL_SHIELD_BLOCK = 37414, + SPELL_VICIOUS_STRIKE = 37413, // orc pawn + SPELL_WEAPON_DEFLECTION = 37416, + SPELL_SMASH = 37453, // human knight + SPELL_STOMP = 37498, + SPELL_BITE = 37454, // orc knight + SPELL_HOWL = 37502, + SPELL_ELEMENTAL_BLAST = 37462, // human queen + SPELL_RAIN_OF_FIRE = 37465, + SPELL_FIREBALL = 37463, // orc queen + // SPELL_POISON_CLOUD = 37469, + SPELL_POISON_CLOUD_ACTION = 37775, // triggers 37469 - acts as a target selector spell for orc queen + SPELL_HEALING = 37455, // human bishop + SPELL_HOLY_LANCE = 37459, + // SPELL_SHADOW_MEND = 37456, // orc bishop + SPELL_SHADOW_MEND_ACTION = 37824, // triggers 37456 - acts as a target selector spell for orc bishop + SPELL_SHADOW_SPEAR = 37461, + SPELL_GEYSER = 37427, // human rook + SPELL_WATER_SHIELD = 37432, + SPELL_HELLFIRE = 37428, // orc rook + SPELL_FIRE_SHIELD = 37434, + + // spells used to transform side trigger when npc dies + SPELL_TRANSFORM_FOOTMAN = 39350, + SPELL_TRANSFORM_CHARGER = 39352, + SPELL_TRANSFORM_CLERIC = 39353, + SPELL_TRANSFORM_WATER_ELEM = 39354, + SPELL_TRANSFORM_CONJURER = 39355, + SPELL_TRANSFORM_KING_LLANE = 39356, + SPELL_TRANSFORM_GRUNT = 39357, + SPELL_TRANSFORM_WOLF = 39358, + SPELL_TRANSFORM_NECROLYTE = 39359, + SPELL_TRANSFORM_DAEMON = 39360, + SPELL_TRANSFORM_WARLOCK = 39361, + SPELL_TRANSFORM_BLACKHAND = 39362, + + // generic npcs + // NPC_SQUARE_OUTSIDE_B = 17316, // used to check the interior of the board + // NPC_SQUARE_OUTSIDE_W = 17317, // not used in our script; keep for reference only + NPC_FURY_MEDIVH_VISUAL = 22521, // has aura 39383 + + // gossip texts + GOSSIP_ITEM_ORC_GRUNT = -3532006, + GOSSIP_ITEM_ORC_WOLF = -3532007, + GOSSIP_ITEM_SUMMONED_DEAMON = -3532008, + GOSSIP_ITEM_ORC_WARLOCK = -3532009, + GOSSIP_ITEM_ORC_NECROLYTE = -3532010, + GOSSIP_ITEM_WARCHIEF_BLACKHAND = -3532011, + GOSSIP_ITEM_HUMAN_FOOTMAN = -3532012, + GOSSIP_ITEM_HUMAN_CHARGER = -3532013, + GOSSIP_ITEM_WATER_ELEMENTAL = -3532014, + GOSSIP_ITEM_HUMAN_CONJURER = -3532015, + GOSSIP_ITEM_HUMAN_CLERIC = -3532016, + GOSSIP_ITEM_KING_LLANE = -3532017, + GOSSIP_ITEM_RESET_BOARD = -3532018, + + // gossip menu + GOSSIP_MENU_ID_GRUNT = 10425, + GOSSIP_MENU_ID_WOLF = 10439, + GOSSIP_MENU_ID_WARLOCK = 10440, + GOSSIP_MENU_ID_NECROLYTE = 10434, + GOSSIP_MENU_ID_DEAMON = 10426, + GOSSIP_MENU_ID_BLACKHAND = 10442, + GOSSIP_MENU_ID_FOOTMAN = 8952, + GOSSIP_MENU_ID_CHARGER = 10414, + GOSSIP_MENU_ID_CONJURER = 10417, + GOSSIP_MENU_ID_CLERIC = 10416, + GOSSIP_MENU_ID_ELEMENTAL = 10413, + GOSSIP_MENU_ID_LLANE = 10418, + GOSSIP_MENU_ID_MEDIVH = 10506, + GOSSIP_MENU_ID_MEDIVH_BEATEN = 10718, + + // misc + TARGET_TYPE_RANDOM = 1, + TARGET_TYPE_FRIENDLY = 2, +}; + +/*###### +## npc_echo_of_medivh +######*/ + +struct npc_echo_of_medivhAI : public ScriptedAI +{ + npc_echo_of_medivhAI(Creature* pCreature) : ScriptedAI(pCreature) + { + m_pInstance = (instance_karazhan*)pCreature->GetInstanceData(); + Reset(); + } + + instance_karazhan* m_pInstance; + + uint32 m_uiCheatTimer; + + void Reset() override + { + m_uiCheatTimer = 90000; + } + + void MoveInLineOfSight(Unit* /*pWho*/) override { } + void AttackStart(Unit* /*pWho*/) override { } + + void JustSummoned(Creature* pSummoned) override + { + if (pSummoned->GetEntry() == NPC_FURY_MEDIVH_VISUAL) + pSummoned->CastSpell(pSummoned, SPELL_FURY_OF_MEDIVH_AURA, true); + } + + void UpdateAI(const uint32 uiDiff) override + { + if (!m_pInstance || m_pInstance->GetData(TYPE_CHESS) != IN_PROGRESS) + return; + + if (m_uiCheatTimer < uiDiff) + { + DoCastSpellIfCan(m_creature, urand(0, 1) ? (m_pInstance->GetPlayerTeam() == ALLIANCE ? SPELL_HAND_OF_MEDIVH_HORDE : SPELL_HAND_OF_MEDIVH_ALLIANCE) : + (m_pInstance->GetPlayerTeam() == ALLIANCE ? SPELL_FURY_OF_MEDIVH_ALLIANCE : SPELL_FURY_OF_MEDIVH_HORDE)); + + switch (urand(0, 2)) + { + case 0: DoPlaySoundToSet(m_creature, SOUND_ID_CHEAT_1); break; + case 1: DoPlaySoundToSet(m_creature, SOUND_ID_CHEAT_2); break; + case 2: DoPlaySoundToSet(m_creature, SOUND_ID_CHEAT_3); break; + } + + DoScriptText(EMOTE_CHEAT, m_creature); + m_uiCheatTimer = 90000; + } + else + m_uiCheatTimer -= uiDiff; + } +}; + +CreatureAI* GetAI_npc_echo_of_medivh(Creature* pCreature) +{ + return new npc_echo_of_medivhAI(pCreature); +} + +bool GossipHello_npc_echo_of_medivh(Player* pPlayer, Creature* pCreature) +{ + if (ScriptedInstance* pInstance = (ScriptedInstance*)pCreature->GetInstanceData()) + { + if (pInstance->GetData(TYPE_CHESS) != DONE && pInstance->GetData(TYPE_CHESS) != SPECIAL) + pPlayer->SEND_GOSSIP_MENU(GOSSIP_MENU_ID_MEDIVH, pCreature->GetObjectGuid()); + else + { + if (pInstance->GetData(TYPE_CHESS) == SPECIAL) + pPlayer->ADD_GOSSIP_ITEM_ID(GOSSIP_ICON_CHAT, GOSSIP_ITEM_RESET_BOARD, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 1); + + pPlayer->SEND_GOSSIP_MENU(GOSSIP_MENU_ID_MEDIVH_BEATEN, pCreature->GetObjectGuid()); + } + + return true; + } + + return false; +} + +bool GossipSelect_npc_echo_of_medivh(Player* pPlayer, Creature* pCreature, uint32 /*uiSender*/, uint32 uiAction) +{ + if (uiAction == GOSSIP_ACTION_INFO_DEF + 1) + { + // reset the board + if (ScriptedInstance* pInstance = (ScriptedInstance*)pCreature->GetInstanceData()) + pInstance->SetData(TYPE_CHESS, DONE); + + pPlayer->CLOSE_GOSSIP_MENU(); + } + + return true; +} + +/*###### +## npc_chess_piece_generic +######*/ + +struct npc_chess_piece_genericAI : public ScriptedAI +{ + npc_chess_piece_genericAI(Creature* pCreature) : ScriptedAI(pCreature) + { + m_pInstance = (instance_karazhan*)pCreature->GetInstanceData(); + Reset(); + } + + instance_karazhan* m_pInstance; + + ObjectGuid m_currentSquareGuid; + + uint32 m_uiMoveTimer; + uint32 m_uiMoveCommandTimer; + uint32 m_uiSpellCommandTimer; + + bool m_bIsPrimarySpell; + float m_fCurrentOrientation; + + void Reset() override + { + m_uiMoveTimer = 0; + m_uiMoveCommandTimer = 1000; + m_uiSpellCommandTimer = m_creature->HasAura(SPELL_CONTROL_PIECE) ? 0 : 1000; + m_bIsPrimarySpell = true; + + // cancel move timer for player faction npcs or for friendly games + if (m_pInstance) + { + if ((m_pInstance->GetPlayerTeam() == ALLIANCE && m_creature->getFaction() == FACTION_ID_CHESS_ALLIANCE) || + (m_pInstance->GetPlayerTeam() == HORDE && m_creature->getFaction() == FACTION_ID_CHESS_HORDE) || + m_pInstance->GetData(TYPE_CHESS) == DONE) + m_uiMoveCommandTimer = 0; + } + } + + // no default attacking or evading + void MoveInLineOfSight(Unit* /*pWho*/) override { } + void AttackStart(Unit* /*pWho*/) override { } + void EnterEvadeMode() override { } + + void JustDied(Unit* /*pKiller*/) override + { + if (Creature* pSquare = m_creature->GetMap()->GetCreature(m_currentSquareGuid)) + pSquare->RemoveAllAuras(); + + // ToDo: remove corpse after 10 sec + } + + void ReceiveAIEvent(AIEventType eventType, Creature* /*pSender*/, Unit* pInvoker, uint32 /*uiMiscValue*/) override + { + // handle move event + if (eventType == AI_EVENT_CUSTOM_A) + { + // clear the current square + if (Creature* pSquare = m_creature->GetMap()->GetCreature(m_currentSquareGuid)) + pSquare->RemoveAllAuras(); + + m_currentSquareGuid = pInvoker->GetObjectGuid(); + m_uiMoveTimer = 2000; + } + // handle encounter start event + else if (eventType == AI_EVENT_CUSTOM_B) + { + // reset the variables + Reset(); + m_currentSquareGuid = pInvoker->GetObjectGuid(); + + // ToDo: enable this when the scope of the spell is clear + //if (Creature* pStalker = m_pInstance->GetSingleCreatureFromStorage(NPC_WAITING_ROOM_STALKER)) + // pStalker->CastSpell(pStalker, SPELL_AI_ACTION_TIMER, true); + + //DoCastSpellIfCan(m_creature, SPELL_AI_SNAPSHOT_TIMER, CAST_TRIGGERED); + DoCastSpellIfCan(m_creature, SPELL_CHESS_AI_ATTACK_TIMER, CAST_TRIGGERED); + + pInvoker->CastSpell(pInvoker, SPELL_DISABLE_SQUARE, true); + pInvoker->CastSpell(pInvoker, SPELL_IS_SQUARE_USED, true); + } + } + + void MovementInform(uint32 uiMotionType, uint32 uiPointId) override + { + if (uiMotionType != POINT_MOTION_TYPE || !uiPointId) + return; + + // update facing + if (Unit* pTarget = GetTargetByType(TARGET_TYPE_RANDOM, 5.0f)) + DoCastSpellIfCan(pTarget, SPELL_CHANGE_FACING); + else + m_creature->SetFacingTo(m_fCurrentOrientation); + } + + void SpellHit(Unit* pCaster, const SpellEntry* pSpell) override + { + // do a soft reset when the piece is controlled + if (pCaster->GetTypeId() == TYPEID_PLAYER && pSpell->Id == SPELL_CONTROL_PIECE) + Reset(); + } + + // Function which returns a random target by type and range + Unit* GetTargetByType(uint8 uiType, float fRange, float fArc = M_PI_F) + { + if (!m_pInstance) + return NULL; + + uint32 uiTeam = m_creature->getFaction() == FACTION_ID_CHESS_ALLIANCE ? FACTION_ID_CHESS_HORDE : FACTION_ID_CHESS_ALLIANCE; + + // get friendly list for this type + if (uiType == TARGET_TYPE_FRIENDLY) + uiTeam = m_creature->getFaction(); + + // Get the list of enemies + GuidList lTempList; + std::vector vTargets; + vTargets.reserve(lTempList.size()); + + m_pInstance->GetChessPiecesByFaction(lTempList, uiTeam); + for (GuidList::const_iterator itr = lTempList.begin(); itr != lTempList.end(); ++itr) + { + Creature* pTemp = m_creature->GetMap()->GetCreature(*itr); + if (pTemp && pTemp->IsAlive()) + { + // check for specified range targets and angle; Note: to be checked if the angle is right + if (fRange && !m_creature->isInFrontInMap(pTemp, fRange, fArc)) + continue; + + // skip friendly targets which are at full HP + if (uiType == TARGET_TYPE_FRIENDLY && pTemp->GetHealth() == pTemp->GetMaxHealth()) + continue; + + vTargets.push_back(pTemp); + } + } + + if (vTargets.empty()) + return NULL; + + return vTargets[urand(0, vTargets.size() - 1)]; + } + + // Function to get a square as close as possible to the enemy + Unit* GetMovementSquare() + { + if (!m_pInstance) + return NULL; + + // define distance based on the spell radius + // this will replace the targeting sysmte of spells SPELL_MOVE_1 and SPELL_MOVE_2 + float fRadius = 10.0f; + std::list lSquaresList; + + // some pieces have special distance + switch (m_creature->GetEntry()) + { + case NPC_HUMAN_CONJURER: + case NPC_ORC_WARLOCK: + case NPC_HUMAN_CHARGER: + case NPC_ORC_WOLF: + fRadius = 15.0f; + break; + } + + // get all available squares for movement + GetCreatureListWithEntryInGrid(lSquaresList, m_creature, NPC_SQUARE_BLACK, fRadius); + GetCreatureListWithEntryInGrid(lSquaresList, m_creature, NPC_SQUARE_WHITE, fRadius); + + if (lSquaresList.empty()) + return NULL; + + // Get the list of enemies + GuidList lTempList; + std::list lEnemies; + + m_pInstance->GetChessPiecesByFaction(lTempList, m_creature->getFaction() == FACTION_ID_CHESS_ALLIANCE ? FACTION_ID_CHESS_HORDE : FACTION_ID_CHESS_ALLIANCE); + for (GuidList::const_iterator itr = lTempList.begin(); itr != lTempList.end(); ++itr) + { + Creature* pTemp = m_creature->GetMap()->GetCreature(*itr); + if (pTemp && pTemp->IsAlive()) + lEnemies.push_back(pTemp); + } + + if (lEnemies.empty()) + return NULL; + + // Sort the enemies by distance and the squares compared to the distance to the closest enemy + lEnemies.sort(ObjectDistanceOrder(m_creature)); + lSquaresList.sort(ObjectDistanceOrder(lEnemies.front())); + + return lSquaresList.front(); + } + + virtual uint32 DoCastPrimarySpell() { return 5000; } + virtual uint32 DoCastSecondarySpell() { return 5000; } + + void UpdateAI(const uint32 uiDiff) override + { + if (!m_pInstance || m_pInstance->GetData(TYPE_CHESS) != IN_PROGRESS) + return; + + // issue move command + if (m_uiMoveCommandTimer) + { + if (m_uiMoveCommandTimer <= uiDiff) + { + // just update facing if some enemy is near + if (Unit* pTarget = GetTargetByType(TARGET_TYPE_RANDOM, 5.0f)) + DoCastSpellIfCan(pTarget, SPELL_CHANGE_FACING); + else + { + // the npc doesn't have a 100% chance to move; also there should be some GCD check in core for this part + if (roll_chance_i(15)) + { + // Note: in a normal case the target would be chosen using the spells above + // However, because the core doesn't support special targeting, we'll provide explicit target + //uint32 uiMoveSpell = SPELL_MOVE_1; + //switch (m_creature->GetEntry()) + //{ + // case NPC_HUMAN_CONJURER: + // case NPC_ORC_WARLOCK: + // case NPC_HUMAN_CHARGER: + // case NPC_ORC_WOLF: + // uiMoveSpell = SPELL_MOVE_2; + // break; + //} + //DoCastSpellIfCan(m_creature, uiMoveSpell, CAST_TRIGGERED); + + // workaround which provides specific move target + if (Unit* pTarget = GetMovementSquare()) + DoCastSpellIfCan(pTarget, SPELL_MOVE_GENERIC, CAST_TRIGGERED | CAST_INTERRUPT_PREVIOUS); + + m_fCurrentOrientation = m_creature->GetOrientation(); + } + } + + m_uiMoveCommandTimer = 5000; + } + else + m_uiMoveCommandTimer -= uiDiff; + } + + // issue spell command + if (m_uiSpellCommandTimer) + { + if (m_uiSpellCommandTimer <= uiDiff) + { + // alternate the spells and also reset the timer + m_uiSpellCommandTimer = m_bIsPrimarySpell ? DoCastPrimarySpell() : DoCastSecondarySpell(); + m_bIsPrimarySpell = !m_bIsPrimarySpell; + } + else + m_uiSpellCommandTimer -= uiDiff; + } + + // finish move timer + if (m_uiMoveTimer) + { + if (m_uiMoveTimer <= uiDiff) + { + if (Creature* pSquare = m_creature->GetMap()->GetCreature(m_currentSquareGuid)) + { + DoCastSpellIfCan(pSquare, SPELL_MOVE_MARKER, CAST_TRIGGERED); + m_creature->GetMotionMaster()->MovePoint(1, pSquare->GetPositionX(), pSquare->GetPositionY(), pSquare->GetPositionZ()); + } + m_uiMoveTimer = 0; + } + else + m_uiMoveTimer -= uiDiff; + } + + if (!m_creature->SelectHostileTarget() || !m_creature->getVictim()) + return; + } +}; + +bool GossipSelect_npc_chess_generic(Player* pPlayer, Creature* pCreature, uint32 /*uiSender*/, uint32 uiAction) +{ + if (uiAction == GOSSIP_ACTION_INFO_DEF + 1) + { + // start event when used on the king + if (ScriptedInstance* pInstance = (ScriptedInstance*)pCreature->GetInstanceData()) + { + // teleport at the entrance and control the chess piece + pPlayer->CastSpell(pPlayer, SPELL_IN_GAME, true); + pPlayer->CastSpell(pCreature, SPELL_CONTROL_PIECE, true); + + if (pInstance->GetData(TYPE_CHESS) == NOT_STARTED) + pInstance->SetData(TYPE_CHESS, IN_PROGRESS); + else if (pInstance->GetData(TYPE_CHESS) == DONE) + pInstance->SetData(TYPE_CHESS, SPECIAL); + } + + pPlayer->CLOSE_GOSSIP_MENU(); + } + + return true; +} + +bool EffectDummyCreature_npc_chess_generic(Unit* pCaster, uint32 uiSpellId, SpellEffectIndex uiEffIndex, Creature* pCreatureTarget, ObjectGuid /*originalCasterGuid*/) +{ + // movement perform spell + if (uiSpellId == SPELL_MOVE_TO_SQUARE && uiEffIndex == EFFECT_INDEX_0) + { + if (pCaster->GetTypeId() == TYPEID_UNIT) + { + pCaster->CastSpell(pCaster, SPELL_DISABLE_SQUARE, true); + pCaster->CastSpell(pCaster, SPELL_IS_SQUARE_USED, true); + + pCreatureTarget->CastSpell(pCreatureTarget, SPELL_MOVE_COOLDOWN, true); + pCreatureTarget->AI()->SendAIEvent(AI_EVENT_CUSTOM_A, pCaster, pCreatureTarget); + } + + return true; + } + // generic melee tick + else if (uiSpellId == SPELL_ACTION_MELEE && uiEffIndex == EFFECT_INDEX_0) + { + uint32 uiMeleeSpell = 0; + + switch (pCreatureTarget->GetEntry()) + { + case NPC_KING_LLANE: uiMeleeSpell = SPELL_MELEE_KING_LLANE; break; + case NPC_HUMAN_CHARGER: uiMeleeSpell = SPELL_MELEE_CHARGER; break; + case NPC_HUMAN_CLERIC: uiMeleeSpell = SPELL_MELEE_CLERIC; break; + case NPC_HUMAN_CONJURER: uiMeleeSpell = SPELL_MELEE_CONJURER; break; + case NPC_HUMAN_FOOTMAN: uiMeleeSpell = SPELL_MELEE_FOOTMAN; break; + case NPC_CONJURED_WATER_ELEMENTAL: uiMeleeSpell = SPELL_MELEE_WATER_ELEM; break; + case NPC_WARCHIEF_BLACKHAND: uiMeleeSpell = SPELL_MELEE_WARCHIEF_BLACKHAND; break; + case NPC_ORC_GRUNT: uiMeleeSpell = SPELL_MELEE_GRUNT; break; + case NPC_ORC_NECROLYTE: uiMeleeSpell = SPELL_MELEE_NECROLYTE; break; + case NPC_ORC_WARLOCK: uiMeleeSpell = SPELL_MELEE_WARLOCK; break; + case NPC_ORC_WOLF: uiMeleeSpell = SPELL_MELEE_WOLF; break; + case NPC_SUMMONED_DAEMON: uiMeleeSpell = SPELL_MELEE_DAEMON; break; + } + + pCreatureTarget->CastSpell(pCreatureTarget, uiMeleeSpell, true); + return true; + } + // square facing + else if (uiSpellId == SPELL_FACE_SQUARE && uiEffIndex == EFFECT_INDEX_0) + { + if (pCaster->GetTypeId() == TYPEID_UNIT) + pCreatureTarget->SetFacingToObject(pCaster); + + return true; + } + + return false; +} + +/*###### +## npc_king_llane +######*/ + +struct npc_king_llaneAI : public npc_chess_piece_genericAI +{ + npc_king_llaneAI(Creature* pCreature) : npc_chess_piece_genericAI(pCreature) + { + m_bIsAttacked = false; + Reset(); + } + + bool m_bIsAttacked; + + void DamageTaken(Unit* pDoneBy, uint32& uiDamage) override + { + if (!uiDamage || !m_bIsAttacked || !m_pInstance || pDoneBy->GetTypeId() != TYPEID_UNIT) + return; + + if (Creature* pMedivh = m_pInstance->GetSingleCreatureFromStorage(NPC_ECHO_MEDIVH)) + { + if (m_pInstance->GetPlayerTeam() == ALLIANCE) + DoPlaySoundToSet(pMedivh, SOUND_ID_CHECK_PLAYER); + else + DoPlaySoundToSet(pMedivh, SOUND_ID_CHECK_MEDIVH); + } + + m_bIsAttacked = true; + } + + void JustDied(Unit* pKiller) override + { + npc_chess_piece_genericAI::JustDied(pKiller); + + if (!m_pInstance) + return; + + Creature* pMedivh = m_pInstance->GetSingleCreatureFromStorage(NPC_ECHO_MEDIVH); + if (!pMedivh) + return; + + if (m_pInstance->GetData(TYPE_CHESS) == SPECIAL) + m_pInstance->SetData(TYPE_CHESS, DONE); + else + { + if (m_pInstance->GetPlayerTeam() == HORDE) + { + DoPlaySoundToSet(pMedivh, SOUND_ID_WIN_PLAYER); + DoScriptText(EMOTE_LIFT_CURSE, pMedivh); + + m_pInstance->SetData(TYPE_CHESS, DONE); + } + else + { + DoPlaySoundToSet(pMedivh, SOUND_ID_WIN_MEDIVH); + m_pInstance->SetData(TYPE_CHESS, FAIL); + } + } + + m_pInstance->DoMoveChessPieceToSides(SPELL_TRANSFORM_KING_LLANE, FACTION_ID_CHESS_ALLIANCE, true); + } + + uint32 DoCastPrimarySpell() override + { + if (Unit* pTarget = GetTargetByType(TARGET_TYPE_RANDOM, 20.0f)) + { + DoCastSpellIfCan(m_creature, SPELL_HEROISM); + + // reset timer based on spell values + const SpellEntry* pSpell = GetSpellStore()->LookupEntry(SPELL_HEROISM); + return pSpell->GetRecoveryTime() ? pSpell->GetRecoveryTime() : pSpell->GetCategoryRecoveryTime(); + } + + return 5000; + } + + uint32 DoCastSecondarySpell() override + { + if (Unit* pTarget = GetTargetByType(TARGET_TYPE_RANDOM, 10.0f)) + { + DoCastSpellIfCan(m_creature, SPELL_SWEEP); + + // reset timer based on spell values + const SpellEntry* pSpell = GetSpellStore()->LookupEntry(SPELL_SWEEP); + return pSpell->GetRecoveryTime() ? pSpell->GetRecoveryTime() : pSpell->GetCategoryRecoveryTime(); + } + + return 5000; + } +}; + +CreatureAI* GetAI_npc_king_llane(Creature* pCreature) +{ + return new npc_king_llaneAI(pCreature); +} + +bool GossipHello_npc_king_llane(Player* pPlayer, Creature* pCreature) +{ + if (pPlayer->HasAura(SPELL_RECENTLY_IN_GAME) || pCreature->HasAura(SPELL_CONTROL_PIECE)) + return true; + + if (instance_karazhan* pInstance = (instance_karazhan*)pCreature->GetInstanceData()) + { + if ((pInstance->GetData(TYPE_CHESS) != DONE && pPlayer->GetTeam() == ALLIANCE) || pInstance->IsFriendlyGameReady()) + pPlayer->ADD_GOSSIP_ITEM_ID(GOSSIP_ICON_CHAT, GOSSIP_ITEM_KING_LLANE, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 1); + } + + pPlayer->SEND_GOSSIP_MENU(GOSSIP_MENU_ID_LLANE, pCreature->GetObjectGuid()); + return true; +} + +/*###### +## npc_warchief_blackhand +######*/ + +struct npc_warchief_blackhandAI : public npc_chess_piece_genericAI +{ + npc_warchief_blackhandAI(Creature* pCreature) : npc_chess_piece_genericAI(pCreature) + { + m_bIsAttacked = false; + Reset(); + } + + bool m_bIsAttacked; + + void DamageTaken(Unit* pDoneBy, uint32& uiDamage) override + { + if (!uiDamage || !m_bIsAttacked || !m_pInstance || pDoneBy->GetTypeId() != TYPEID_UNIT) + return; + + if (Creature* pMedivh = m_pInstance->GetSingleCreatureFromStorage(NPC_ECHO_MEDIVH)) + { + if (m_pInstance->GetPlayerTeam() == HORDE) + DoPlaySoundToSet(pMedivh, SOUND_ID_CHECK_PLAYER); + else + DoPlaySoundToSet(pMedivh, SOUND_ID_CHECK_MEDIVH); + } + + m_bIsAttacked = true; + } + + void JustDied(Unit* pKiller) override + { + npc_chess_piece_genericAI::JustDied(pKiller); + + if (!m_pInstance) + return; + + Creature* pMedivh = m_pInstance->GetSingleCreatureFromStorage(NPC_ECHO_MEDIVH); + if (!pMedivh) + return; + + if (m_pInstance->GetData(TYPE_CHESS) == SPECIAL) + m_pInstance->SetData(TYPE_CHESS, DONE); + else + { + if (m_pInstance->GetPlayerTeam() == ALLIANCE) + { + DoPlaySoundToSet(pMedivh, SOUND_ID_WIN_PLAYER); + DoScriptText(EMOTE_LIFT_CURSE, pMedivh); + + m_pInstance->SetData(TYPE_CHESS, DONE); + } + else + { + DoPlaySoundToSet(pMedivh, SOUND_ID_WIN_MEDIVH); + m_pInstance->SetData(TYPE_CHESS, FAIL); + } + } + + m_pInstance->DoMoveChessPieceToSides(SPELL_TRANSFORM_BLACKHAND, FACTION_ID_CHESS_HORDE, true); + } + + uint32 DoCastPrimarySpell() override + { + if (Unit* pTarget = GetTargetByType(TARGET_TYPE_RANDOM, 20.0f)) + { + DoCastSpellIfCan(m_creature, SPELL_BLOODLUST); + + // reset timer based on spell values + const SpellEntry* pSpell = GetSpellStore()->LookupEntry(SPELL_BLOODLUST); + return pSpell->GetRecoveryTime() ? pSpell->GetRecoveryTime() : pSpell->GetCategoryRecoveryTime(); + } + + return 5000; + } + + uint32 DoCastSecondarySpell() override + { + if (Unit* pTarget = GetTargetByType(TARGET_TYPE_RANDOM, 10.0f)) + { + DoCastSpellIfCan(m_creature, SPELL_CLEAVE); + + // reset timer based on spell values + const SpellEntry* pSpell = GetSpellStore()->LookupEntry(SPELL_CLEAVE); + return pSpell->GetRecoveryTime() ? pSpell->GetRecoveryTime() : pSpell->GetCategoryRecoveryTime(); + } + + return 5000; + } +}; + +CreatureAI* GetAI_npc_warchief_blackhand(Creature* pCreature) +{ + return new npc_warchief_blackhandAI(pCreature); +} + +bool GossipHello_npc_warchief_blackhand(Player* pPlayer, Creature* pCreature) +{ + if (pPlayer->HasAura(SPELL_RECENTLY_IN_GAME) || pCreature->HasAura(SPELL_CONTROL_PIECE)) + return true; + + if (instance_karazhan* pInstance = (instance_karazhan*)pCreature->GetInstanceData()) + { + if (pInstance->GetData(TYPE_CHESS) != DONE && pPlayer->GetTeam() == HORDE || pInstance->IsFriendlyGameReady()) + pPlayer->ADD_GOSSIP_ITEM_ID(GOSSIP_ICON_CHAT, GOSSIP_ITEM_WARCHIEF_BLACKHAND, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 1); + } + + pPlayer->SEND_GOSSIP_MENU(GOSSIP_MENU_ID_BLACKHAND, pCreature->GetObjectGuid()); + return true; +} + +/*###### +## npc_human_conjurer +######*/ + +struct npc_human_conjurerAI : public npc_chess_piece_genericAI +{ + npc_human_conjurerAI(Creature* pCreature) : npc_chess_piece_genericAI(pCreature) { Reset(); } + + void JustDied(Unit* pKiller) override + { + npc_chess_piece_genericAI::JustDied(pKiller); + + if (!m_pInstance) + return; + + Creature* pMedivh = m_pInstance->GetSingleCreatureFromStorage(NPC_ECHO_MEDIVH); + if (!pMedivh) + return; + + if (m_pInstance->GetPlayerTeam() == ALLIANCE) + DoPlaySoundToSet(pMedivh, SOUND_ID_LOSE_QUEEN_PLAYER); + else + DoPlaySoundToSet(pMedivh, SOUND_ID_LOSE_QUEEN_MEDIVH); + + m_pInstance->DoMoveChessPieceToSides(SPELL_TRANSFORM_CONJURER, FACTION_ID_CHESS_ALLIANCE); + } + + uint32 DoCastPrimarySpell() override + { + if (Unit* pTarget = GetTargetByType(TARGET_TYPE_RANDOM, 20.0f)) + { + DoCastSpellIfCan(pTarget, SPELL_ELEMENTAL_BLAST); + + // reset timer based on spell values + const SpellEntry* pSpell = GetSpellStore()->LookupEntry(SPELL_ELEMENTAL_BLAST); + return pSpell->GetRecoveryTime() ? pSpell->GetRecoveryTime() : pSpell->GetCategoryRecoveryTime(); + } + + return 5000; + } + + uint32 DoCastSecondarySpell() override + { + if (Unit* pTarget = GetTargetByType(TARGET_TYPE_RANDOM, 25.0f)) + { + DoCastSpellIfCan(pTarget, SPELL_RAIN_OF_FIRE); + + // reset timer based on spell values + const SpellEntry* pSpell = GetSpellStore()->LookupEntry(SPELL_RAIN_OF_FIRE); + return pSpell->GetRecoveryTime() ? pSpell->GetRecoveryTime() : pSpell->GetCategoryRecoveryTime(); + } + + return 5000; + } +}; + +CreatureAI* GetAI_npc_human_conjurer(Creature* pCreature) +{ + return new npc_human_conjurerAI(pCreature); +} + +bool GossipHello_npc_human_conjurer(Player* pPlayer, Creature* pCreature) +{ + if (pPlayer->HasAura(SPELL_RECENTLY_IN_GAME) || pCreature->HasAura(SPELL_CONTROL_PIECE)) + return true; + + if (ScriptedInstance* pInstance = (ScriptedInstance*)pCreature->GetInstanceData()) + { + if ((pInstance->GetData(TYPE_CHESS) == IN_PROGRESS && pPlayer->GetTeam() == ALLIANCE) || pInstance->GetData(TYPE_CHESS) == SPECIAL) + pPlayer->ADD_GOSSIP_ITEM_ID(GOSSIP_ICON_CHAT, GOSSIP_ITEM_HUMAN_CONJURER, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 1); + } + + pPlayer->SEND_GOSSIP_MENU(GOSSIP_MENU_ID_CONJURER, pCreature->GetObjectGuid()); + return true; +} + +/*###### +## npc_orc_warlock +######*/ + +struct npc_orc_warlockAI : public npc_chess_piece_genericAI +{ + npc_orc_warlockAI(Creature* pCreature) : npc_chess_piece_genericAI(pCreature) { Reset(); } + + void JustDied(Unit* pKiller) override + { + npc_chess_piece_genericAI::JustDied(pKiller); + + if (!m_pInstance) + return; + + Creature* pMedivh = m_pInstance->GetSingleCreatureFromStorage(NPC_ECHO_MEDIVH); + if (!pMedivh) + return; + + if (m_pInstance->GetPlayerTeam() == HORDE) + DoPlaySoundToSet(pMedivh, SOUND_ID_LOSE_QUEEN_PLAYER); + else + DoPlaySoundToSet(pMedivh, SOUND_ID_LOSE_QUEEN_MEDIVH); + + m_pInstance->DoMoveChessPieceToSides(SPELL_TRANSFORM_WARLOCK, FACTION_ID_CHESS_HORDE); + } + + uint32 DoCastPrimarySpell() override + { + if (Unit* pTarget = GetTargetByType(TARGET_TYPE_RANDOM, 20.0f)) + { + DoCastSpellIfCan(pTarget, SPELL_FIREBALL); + + // reset timer based on spell values + const SpellEntry* pSpell = GetSpellStore()->LookupEntry(SPELL_FIREBALL); + return pSpell->GetRecoveryTime() ? pSpell->GetRecoveryTime() : pSpell->GetCategoryRecoveryTime(); + } + + return 5000; + } + + uint32 DoCastSecondarySpell() override + { + if (Unit* pTarget = GetTargetByType(TARGET_TYPE_RANDOM, 25.0f)) + { + DoCastSpellIfCan(pTarget, SPELL_POISON_CLOUD_ACTION); + + // reset timer based on spell values + const SpellEntry* pSpell = GetSpellStore()->LookupEntry(SPELL_POISON_CLOUD_ACTION); + return pSpell->GetRecoveryTime() ? pSpell->GetRecoveryTime() : pSpell->GetCategoryRecoveryTime(); + } + + return 5000; + } +}; + +CreatureAI* GetAI_npc_orc_warlock(Creature* pCreature) +{ + return new npc_orc_warlockAI(pCreature); +} + +bool GossipHello_npc_orc_warlock(Player* pPlayer, Creature* pCreature) +{ + if (pPlayer->HasAura(SPELL_RECENTLY_IN_GAME) || pCreature->HasAura(SPELL_CONTROL_PIECE)) + return true; + + if (ScriptedInstance* pInstance = (ScriptedInstance*)pCreature->GetInstanceData()) + { + if ((pInstance->GetData(TYPE_CHESS) == IN_PROGRESS && pPlayer->GetTeam() == HORDE) || pInstance->GetData(TYPE_CHESS) == SPECIAL) + pPlayer->ADD_GOSSIP_ITEM_ID(GOSSIP_ICON_CHAT, GOSSIP_ITEM_ORC_WARLOCK, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 1); + } + + pPlayer->SEND_GOSSIP_MENU(GOSSIP_MENU_ID_WARLOCK, pCreature->GetObjectGuid()); + return true; +} + +/*###### +## npc_human_footman +######*/ + +struct npc_human_footmanAI : public npc_chess_piece_genericAI +{ + npc_human_footmanAI(Creature* pCreature) : npc_chess_piece_genericAI(pCreature) { Reset(); } + + void JustDied(Unit* pKiller) override + { + npc_chess_piece_genericAI::JustDied(pKiller); + + if (!m_pInstance) + return; + + Creature* pMedivh = m_pInstance->GetSingleCreatureFromStorage(NPC_ECHO_MEDIVH); + if (!pMedivh) + return; + + if (m_pInstance->GetPlayerTeam() == ALLIANCE) + { + switch (urand(0, 2)) + { + case 0: DoPlaySoundToSet(pMedivh, SOUND_ID_LOSE_PAWN_PLAYER_1); break; + case 1: DoPlaySoundToSet(pMedivh, SOUND_ID_LOSE_PAWN_PLAYER_2); break; + case 2: DoPlaySoundToSet(pMedivh, SOUND_ID_LOSE_PAWN_PLAYER_3); break; + } + } + else + { + switch (urand(0, 2)) + { + case 0: DoPlaySoundToSet(pMedivh, SOUND_ID_LOSE_PAWN_MEDIVH_1); break; + case 1: DoPlaySoundToSet(pMedivh, SOUND_ID_LOSE_PAWN_MEDIVH_2); break; + case 2: DoPlaySoundToSet(pMedivh, SOUND_ID_LOSE_PAWN_MEDIVH_3); break; + } + } + + m_pInstance->DoMoveChessPieceToSides(SPELL_TRANSFORM_FOOTMAN, FACTION_ID_CHESS_ALLIANCE); + } + + uint32 DoCastPrimarySpell() override + { + if (Unit* pTarget = GetTargetByType(TARGET_TYPE_RANDOM, 8.0f, M_PI_F / 12)) + { + DoCastSpellIfCan(m_creature, SPELL_HEROIC_BLOW); + + // reset timer based on spell values + const SpellEntry* pSpell = GetSpellStore()->LookupEntry(SPELL_HEROIC_BLOW); + return pSpell->GetRecoveryTime() ? pSpell->GetRecoveryTime() : pSpell->GetCategoryRecoveryTime(); + } + + return 5000; + } + + uint32 DoCastSecondarySpell() override + { + if (Unit* pTarget = GetTargetByType(TARGET_TYPE_RANDOM, 8.0f)) + { + DoCastSpellIfCan(m_creature, SPELL_SHIELD_BLOCK); + + // reset timer based on spell values + const SpellEntry* pSpell = GetSpellStore()->LookupEntry(SPELL_SHIELD_BLOCK); + return pSpell->GetRecoveryTime() ? pSpell->GetRecoveryTime() : pSpell->GetCategoryRecoveryTime(); + } + + return 5000; + } +}; + +CreatureAI* GetAI_npc_human_footman(Creature* pCreature) +{ + return new npc_human_footmanAI(pCreature); +} + +bool GossipHello_npc_human_footman(Player* pPlayer, Creature* pCreature) +{ + if (pPlayer->HasAura(SPELL_RECENTLY_IN_GAME) || pCreature->HasAura(SPELL_CONTROL_PIECE)) + return true; + + if (ScriptedInstance* pInstance = (ScriptedInstance*)pCreature->GetInstanceData()) + { + if ((pInstance->GetData(TYPE_CHESS) == IN_PROGRESS && pPlayer->GetTeam() == ALLIANCE) || pInstance->GetData(TYPE_CHESS) == SPECIAL) + pPlayer->ADD_GOSSIP_ITEM_ID(GOSSIP_ICON_CHAT, GOSSIP_ITEM_HUMAN_FOOTMAN, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 1); + } + + pPlayer->SEND_GOSSIP_MENU(GOSSIP_MENU_ID_FOOTMAN, pCreature->GetObjectGuid()); + return true; +} + +/*###### +## npc_orc_grunt +######*/ + +struct npc_orc_gruntAI : public npc_chess_piece_genericAI +{ + npc_orc_gruntAI(Creature* pCreature) : npc_chess_piece_genericAI(pCreature) { Reset(); } + + void JustDied(Unit* pKiller) override + { + npc_chess_piece_genericAI::JustDied(pKiller); + + if (!m_pInstance) + return; + + Creature* pMedivh = m_pInstance->GetSingleCreatureFromStorage(NPC_ECHO_MEDIVH); + if (!pMedivh) + return; + + if (m_pInstance->GetPlayerTeam() == HORDE) + { + switch (urand(0, 2)) + { + case 0: DoPlaySoundToSet(pMedivh, SOUND_ID_LOSE_PAWN_PLAYER_1); break; + case 1: DoPlaySoundToSet(pMedivh, SOUND_ID_LOSE_PAWN_PLAYER_2); break; + case 2: DoPlaySoundToSet(pMedivh, SOUND_ID_LOSE_PAWN_PLAYER_3); break; + } + } + else + { + switch (urand(0, 2)) + { + case 0: DoPlaySoundToSet(pMedivh, SOUND_ID_LOSE_PAWN_MEDIVH_1); break; + case 1: DoPlaySoundToSet(pMedivh, SOUND_ID_LOSE_PAWN_MEDIVH_2); break; + case 2: DoPlaySoundToSet(pMedivh, SOUND_ID_LOSE_PAWN_MEDIVH_3); break; + } + } + + m_pInstance->DoMoveChessPieceToSides(SPELL_TRANSFORM_GRUNT, FACTION_ID_CHESS_HORDE); + } + + uint32 DoCastPrimarySpell() override + { + if (Unit* pTarget = GetTargetByType(TARGET_TYPE_RANDOM, 8.0f, M_PI_F / 12)) + { + DoCastSpellIfCan(m_creature, SPELL_VICIOUS_STRIKE); + + // reset timer based on spell values + const SpellEntry* pSpell = GetSpellStore()->LookupEntry(SPELL_VICIOUS_STRIKE); + return pSpell->GetRecoveryTime() ? pSpell->GetRecoveryTime() : pSpell->GetCategoryRecoveryTime(); + } + + return 5000; + } + + uint32 DoCastSecondarySpell() override + { + if (Unit* pTarget = GetTargetByType(TARGET_TYPE_RANDOM, 8.0f)) + { + DoCastSpellIfCan(m_creature, SPELL_WEAPON_DEFLECTION); + + // reset timer based on spell values + const SpellEntry* pSpell = GetSpellStore()->LookupEntry(SPELL_WEAPON_DEFLECTION); + return pSpell->GetRecoveryTime() ? pSpell->GetRecoveryTime() : pSpell->GetCategoryRecoveryTime(); + } + + return 5000; + } +}; + +CreatureAI* GetAI_npc_orc_grunt(Creature* pCreature) +{ + return new npc_orc_gruntAI(pCreature); +} + +bool GossipHello_npc_orc_grunt(Player* pPlayer, Creature* pCreature) +{ + if (pPlayer->HasAura(SPELL_RECENTLY_IN_GAME) || pCreature->HasAura(SPELL_CONTROL_PIECE)) + return true; + + if (ScriptedInstance* pInstance = (ScriptedInstance*)pCreature->GetInstanceData()) + { + if ((pInstance->GetData(TYPE_CHESS) == IN_PROGRESS && pPlayer->GetTeam() == HORDE) || pInstance->GetData(TYPE_CHESS) == SPECIAL) + pPlayer->ADD_GOSSIP_ITEM_ID(GOSSIP_ICON_CHAT, GOSSIP_ITEM_ORC_GRUNT, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 1); + } + + pPlayer->SEND_GOSSIP_MENU(GOSSIP_MENU_ID_GRUNT, pCreature->GetObjectGuid()); + return true; +} + +/*###### +## npc_water_elemental +######*/ + +struct npc_water_elementalAI : public npc_chess_piece_genericAI +{ + npc_water_elementalAI(Creature* pCreature) : npc_chess_piece_genericAI(pCreature) { Reset(); } + + void JustDied(Unit* pKiller) override + { + npc_chess_piece_genericAI::JustDied(pKiller); + + if (!m_pInstance) + return; + + Creature* pMedivh = m_pInstance->GetSingleCreatureFromStorage(NPC_ECHO_MEDIVH); + if (!pMedivh) + return; + + if (m_pInstance->GetPlayerTeam() == ALLIANCE) + DoPlaySoundToSet(pMedivh, SOUND_ID_LOSE_ROOK_PLAYER); + else + DoPlaySoundToSet(pMedivh, SOUND_ID_LOSE_ROOK_MEDIVH); + + m_pInstance->DoMoveChessPieceToSides(SPELL_TRANSFORM_WATER_ELEM, FACTION_ID_CHESS_ALLIANCE); + } + + uint32 DoCastPrimarySpell() override + { + if (Unit* pTarget = GetTargetByType(TARGET_TYPE_RANDOM, 9.0f)) + { + DoCastSpellIfCan(m_creature, SPELL_GEYSER); + + // reset timer based on spell values + const SpellEntry* pSpell = GetSpellStore()->LookupEntry(SPELL_GEYSER); + return pSpell->GetRecoveryTime() ? pSpell->GetRecoveryTime() : pSpell->GetCategoryRecoveryTime(); + } + + return 5000; + } + + uint32 DoCastSecondarySpell() override + { + if (Unit* pTarget = GetTargetByType(TARGET_TYPE_RANDOM, 9.0f)) + { + DoCastSpellIfCan(m_creature, SPELL_WATER_SHIELD); + + // reset timer based on spell values + const SpellEntry* pSpell = GetSpellStore()->LookupEntry(SPELL_WATER_SHIELD); + return pSpell->GetRecoveryTime() ? pSpell->GetRecoveryTime() : pSpell->GetCategoryRecoveryTime(); + } + + return 5000; + } +}; + +CreatureAI* GetAI_npc_water_elemental(Creature* pCreature) +{ + return new npc_water_elementalAI(pCreature); +} + +bool GossipHello_npc_water_elemental(Player* pPlayer, Creature* pCreature) +{ + if (pPlayer->HasAura(SPELL_RECENTLY_IN_GAME) || pCreature->HasAura(SPELL_CONTROL_PIECE)) + return true; + + if (ScriptedInstance* pInstance = (ScriptedInstance*)pCreature->GetInstanceData()) + { + if ((pInstance->GetData(TYPE_CHESS) == IN_PROGRESS && pPlayer->GetTeam() == ALLIANCE) || pInstance->GetData(TYPE_CHESS) == SPECIAL) + pPlayer->ADD_GOSSIP_ITEM_ID(GOSSIP_ICON_CHAT, GOSSIP_ITEM_WATER_ELEMENTAL, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 1); + } + + pPlayer->SEND_GOSSIP_MENU(GOSSIP_MENU_ID_ELEMENTAL, pCreature->GetObjectGuid()); + return true; +} + +/*###### +## npc_summoned_daemon +######*/ + +struct npc_summoned_daemonAI : public npc_chess_piece_genericAI +{ + npc_summoned_daemonAI(Creature* pCreature) : npc_chess_piece_genericAI(pCreature) { Reset(); } + + void JustDied(Unit* pKiller) override + { + npc_chess_piece_genericAI::JustDied(pKiller); + + if (!m_pInstance) + return; + + Creature* pMedivh = m_pInstance->GetSingleCreatureFromStorage(NPC_ECHO_MEDIVH); + if (!pMedivh) + return; + + if (m_pInstance->GetPlayerTeam() == HORDE) + DoPlaySoundToSet(pMedivh, SOUND_ID_LOSE_ROOK_PLAYER); + else + DoPlaySoundToSet(pMedivh, SOUND_ID_LOSE_ROOK_MEDIVH); + + m_pInstance->DoMoveChessPieceToSides(SPELL_TRANSFORM_DAEMON, FACTION_ID_CHESS_HORDE); + } + + uint32 DoCastPrimarySpell() override + { + if (Unit* pTarget = GetTargetByType(TARGET_TYPE_RANDOM, 9.0f)) + { + DoCastSpellIfCan(m_creature, SPELL_HELLFIRE); + + // reset timer based on spell values + const SpellEntry* pSpell = GetSpellStore()->LookupEntry(SPELL_HELLFIRE); + return pSpell->GetRecoveryTime() ? pSpell->GetRecoveryTime() : pSpell->GetCategoryRecoveryTime(); + } + + return 5000; + } + + uint32 DoCastSecondarySpell() override + { + if (Unit* pTarget = GetTargetByType(TARGET_TYPE_RANDOM, 9.0f)) + { + DoCastSpellIfCan(m_creature, SPELL_FIRE_SHIELD); + + // reset timer based on spell values + const SpellEntry* pSpell = GetSpellStore()->LookupEntry(SPELL_FIRE_SHIELD); + return pSpell->GetRecoveryTime() ? pSpell->GetRecoveryTime() : pSpell->GetCategoryRecoveryTime(); + } + + return 5000; + } +}; + +CreatureAI* GetAI_npc_summoned_daemon(Creature* pCreature) +{ + return new npc_summoned_daemonAI(pCreature); +} + +bool GossipHello_npc_summoned_daemon(Player* pPlayer, Creature* pCreature) +{ + if (pPlayer->HasAura(SPELL_RECENTLY_IN_GAME) || pCreature->HasAura(SPELL_CONTROL_PIECE)) + return true; + + if (ScriptedInstance* pInstance = (ScriptedInstance*)pCreature->GetInstanceData()) + { + if ((pInstance->GetData(TYPE_CHESS) == IN_PROGRESS && pPlayer->GetTeam() == HORDE) || pInstance->GetData(TYPE_CHESS) == SPECIAL) + pPlayer->ADD_GOSSIP_ITEM_ID(GOSSIP_ICON_CHAT, GOSSIP_ITEM_SUMMONED_DEAMON, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 1); + } + + pPlayer->SEND_GOSSIP_MENU(GOSSIP_MENU_ID_DEAMON, pCreature->GetObjectGuid()); + return true; +} + +/*###### +## npc_human_charger +######*/ + +struct npc_human_chargerAI : public npc_chess_piece_genericAI +{ + npc_human_chargerAI(Creature* pCreature) : npc_chess_piece_genericAI(pCreature) { Reset(); } + + void JustDied(Unit* pKiller) override + { + npc_chess_piece_genericAI::JustDied(pKiller); + + if (!m_pInstance) + return; + + Creature* pMedivh = m_pInstance->GetSingleCreatureFromStorage(NPC_ECHO_MEDIVH); + if (!pMedivh) + return; + + if (m_pInstance->GetPlayerTeam() == ALLIANCE) + DoPlaySoundToSet(pMedivh, SOUND_ID_LOSE_KNIGHT_PLAYER); + else + DoPlaySoundToSet(pMedivh, SOUND_ID_LOSE_KNIGHT_MEDIVH); + + m_pInstance->DoMoveChessPieceToSides(SPELL_TRANSFORM_CHARGER, FACTION_ID_CHESS_ALLIANCE); + } + + uint32 DoCastPrimarySpell() override + { + if (Unit* pTarget = GetTargetByType(TARGET_TYPE_RANDOM, 8.0f, M_PI_F / 12)) + { + DoCastSpellIfCan(m_creature, SPELL_SMASH); + + // reset timer based on spell values + const SpellEntry* pSpell = GetSpellStore()->LookupEntry(SPELL_SMASH); + return pSpell->GetRecoveryTime() ? pSpell->GetRecoveryTime() : pSpell->GetCategoryRecoveryTime(); + } + + return 5000; + } + + uint32 DoCastSecondarySpell() override + { + if (Unit* pTarget = GetTargetByType(TARGET_TYPE_RANDOM, 10.0f, M_PI_F / 12)) + { + DoCastSpellIfCan(m_creature, SPELL_STOMP); + + // reset timer based on spell values + const SpellEntry* pSpell = GetSpellStore()->LookupEntry(SPELL_STOMP); + return pSpell->GetRecoveryTime() ? pSpell->GetRecoveryTime() : pSpell->GetCategoryRecoveryTime(); + } + + return 5000; + } +}; + +CreatureAI* GetAI_npc_human_charger(Creature* pCreature) +{ + return new npc_human_chargerAI(pCreature); +} + +bool GossipHello_npc_human_charger(Player* pPlayer, Creature* pCreature) +{ + if (pPlayer->HasAura(SPELL_RECENTLY_IN_GAME) || pCreature->HasAura(SPELL_CONTROL_PIECE)) + return true; + + if (ScriptedInstance* pInstance = (ScriptedInstance*)pCreature->GetInstanceData()) + { + if ((pInstance->GetData(TYPE_CHESS) == IN_PROGRESS && pPlayer->GetTeam() == ALLIANCE) || pInstance->GetData(TYPE_CHESS) == SPECIAL) + pPlayer->ADD_GOSSIP_ITEM_ID(GOSSIP_ICON_CHAT, GOSSIP_ITEM_HUMAN_CHARGER, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 1); + } + + pPlayer->SEND_GOSSIP_MENU(GOSSIP_MENU_ID_CHARGER, pCreature->GetObjectGuid()); + return true; +} + +/*###### +## npc_orc_wolf +######*/ + +struct npc_orc_wolfAI : public npc_chess_piece_genericAI +{ + npc_orc_wolfAI(Creature* pCreature) : npc_chess_piece_genericAI(pCreature) { Reset(); } + + void JustDied(Unit* pKiller) override + { + npc_chess_piece_genericAI::JustDied(pKiller); + + if (!m_pInstance) + return; + + Creature* pMedivh = m_pInstance->GetSingleCreatureFromStorage(NPC_ECHO_MEDIVH); + if (!pMedivh) + return; + + if (m_pInstance->GetPlayerTeam() == HORDE) + DoPlaySoundToSet(pMedivh, SOUND_ID_LOSE_KNIGHT_PLAYER); + else + DoPlaySoundToSet(pMedivh, SOUND_ID_LOSE_KNIGHT_MEDIVH); + + m_pInstance->DoMoveChessPieceToSides(SPELL_TRANSFORM_WOLF, FACTION_ID_CHESS_HORDE); + } + + uint32 DoCastPrimarySpell() override + { + if (Unit* pTarget = GetTargetByType(TARGET_TYPE_RANDOM, 8.0f, M_PI_F / 12)) + { + DoCastSpellIfCan(m_creature, SPELL_BITE); + + // reset timer based on spell values + const SpellEntry* pSpell = GetSpellStore()->LookupEntry(SPELL_BITE); + return pSpell->GetRecoveryTime() ? pSpell->GetRecoveryTime() : pSpell->GetCategoryRecoveryTime(); + } + + return 5000; + } + + uint32 DoCastSecondarySpell() override + { + if (Unit* pTarget = GetTargetByType(TARGET_TYPE_RANDOM, 10.0f, M_PI_F / 12)) + { + DoCastSpellIfCan(m_creature, SPELL_HOWL); + + // reset timer based on spell values + const SpellEntry* pSpell = GetSpellStore()->LookupEntry(SPELL_HOWL); + return pSpell->GetRecoveryTime() ? pSpell->GetRecoveryTime() : pSpell->GetCategoryRecoveryTime(); + } + + return 5000; + } +}; + +CreatureAI* GetAI_npc_orc_wolf(Creature* pCreature) +{ + return new npc_orc_wolfAI(pCreature); +} + +bool GossipHello_npc_orc_wolf(Player* pPlayer, Creature* pCreature) +{ + if (pPlayer->HasAura(SPELL_RECENTLY_IN_GAME) || pCreature->HasAura(SPELL_CONTROL_PIECE)) + return true; + + if (ScriptedInstance* pInstance = (ScriptedInstance*)pCreature->GetInstanceData()) + { + if ((pInstance->GetData(TYPE_CHESS) == IN_PROGRESS && pPlayer->GetTeam() == HORDE) || pInstance->GetData(TYPE_CHESS) == SPECIAL) + pPlayer->ADD_GOSSIP_ITEM_ID(GOSSIP_ICON_CHAT, GOSSIP_ITEM_ORC_WOLF, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 1); + } + + pPlayer->SEND_GOSSIP_MENU(GOSSIP_MENU_ID_WOLF, pCreature->GetObjectGuid()); + return true; +} + +/*###### +## npc_human_cleric +######*/ + +struct npc_human_clericAI : public npc_chess_piece_genericAI +{ + npc_human_clericAI(Creature* pCreature) : npc_chess_piece_genericAI(pCreature) { Reset(); } + + void JustDied(Unit* pKiller) override + { + npc_chess_piece_genericAI::JustDied(pKiller); + + if (!m_pInstance) + return; + + Creature* pMedivh = m_pInstance->GetSingleCreatureFromStorage(NPC_ECHO_MEDIVH); + if (!pMedivh) + return; + + if (m_pInstance->GetPlayerTeam() == ALLIANCE) + DoPlaySoundToSet(pMedivh, SOUND_ID_LOSE_BISHOP_PLAYER); + else + DoPlaySoundToSet(pMedivh, SOUND_ID_LOSE_BISHOP_MEDIVH); + + m_pInstance->DoMoveChessPieceToSides(SPELL_TRANSFORM_CLERIC, FACTION_ID_CHESS_ALLIANCE); + } + + uint32 DoCastPrimarySpell() override + { + if (Unit* pTarget = GetTargetByType(TARGET_TYPE_FRIENDLY, 25.0f)) + { + DoCastSpellIfCan(pTarget, SPELL_HEALING); + + // reset timer based on spell values + const SpellEntry* pSpell = GetSpellStore()->LookupEntry(SPELL_HEALING); + return pSpell->GetRecoveryTime() ? pSpell->GetRecoveryTime() : pSpell->GetCategoryRecoveryTime(); + } + + return 5000; + } + + uint32 DoCastSecondarySpell() override + { + if (Unit* pTarget = GetTargetByType(TARGET_TYPE_RANDOM, 18.0f, M_PI_F / 12)) + { + DoCastSpellIfCan(m_creature, SPELL_HOLY_LANCE); + + // reset timer based on spell values + const SpellEntry* pSpell = GetSpellStore()->LookupEntry(SPELL_HOLY_LANCE); + return pSpell->GetRecoveryTime() ? pSpell->GetRecoveryTime() : pSpell->GetCategoryRecoveryTime(); + } + + return 5000; + } +}; + +CreatureAI* GetAI_npc_human_cleric(Creature* pCreature) +{ + return new npc_human_clericAI(pCreature); +} + +bool GossipHello_npc_human_cleric(Player* pPlayer, Creature* pCreature) +{ + if (pPlayer->HasAura(SPELL_RECENTLY_IN_GAME) || pCreature->HasAura(SPELL_CONTROL_PIECE)) + return true; + + if (ScriptedInstance* pInstance = (ScriptedInstance*)pCreature->GetInstanceData()) + { + if ((pInstance->GetData(TYPE_CHESS) == IN_PROGRESS && pPlayer->GetTeam() == ALLIANCE) || pInstance->GetData(TYPE_CHESS) == SPECIAL) + pPlayer->ADD_GOSSIP_ITEM_ID(GOSSIP_ICON_CHAT, GOSSIP_ITEM_HUMAN_CLERIC, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 1); + } + + pPlayer->SEND_GOSSIP_MENU(GOSSIP_MENU_ID_CLERIC, pCreature->GetObjectGuid()); + return true; +} + +/*###### +## npc_orc_necrolyte +######*/ + +struct npc_orc_necrolyteAI : public npc_chess_piece_genericAI +{ + npc_orc_necrolyteAI(Creature* pCreature) : npc_chess_piece_genericAI(pCreature) { Reset(); } + + void JustDied(Unit* pKiller) override + { + npc_chess_piece_genericAI::JustDied(pKiller); + + if (!m_pInstance) + return; + + Creature* pMedivh = m_pInstance->GetSingleCreatureFromStorage(NPC_ECHO_MEDIVH); + if (!pMedivh) + return; + + if (m_pInstance->GetPlayerTeam() == HORDE) + DoPlaySoundToSet(pMedivh, SOUND_ID_LOSE_BISHOP_PLAYER); + else + DoPlaySoundToSet(pMedivh, SOUND_ID_LOSE_BISHOP_MEDIVH); + + m_pInstance->DoMoveChessPieceToSides(SPELL_TRANSFORM_NECROLYTE, FACTION_ID_CHESS_HORDE); + } + + uint32 DoCastPrimarySpell() override + { + if (Unit* pTarget = GetTargetByType(TARGET_TYPE_FRIENDLY, 25.0f)) + { + DoCastSpellIfCan(pTarget, SPELL_SHADOW_MEND_ACTION); + + // reset timer based on spell values + const SpellEntry* pSpell = GetSpellStore()->LookupEntry(SPELL_SHADOW_MEND_ACTION); + return pSpell->GetRecoveryTime() ? pSpell->GetRecoveryTime() : pSpell->GetCategoryRecoveryTime(); + } + + return 5000; + } + + uint32 DoCastSecondarySpell() override + { + if (Unit* pTarget = GetTargetByType(TARGET_TYPE_RANDOM, 18.0f, M_PI_F / 12)) + { + DoCastSpellIfCan(m_creature, SPELL_SHADOW_SPEAR); + + // reset timer based on spell values + const SpellEntry* pSpell = GetSpellStore()->LookupEntry(SPELL_SHADOW_SPEAR); + return pSpell->GetRecoveryTime() ? pSpell->GetRecoveryTime() : pSpell->GetCategoryRecoveryTime(); + } + + return 5000; + } +}; + +CreatureAI* GetAI_npc_orc_necrolyte(Creature* pCreature) +{ + return new npc_orc_necrolyteAI(pCreature); +} + +bool GossipHello_npc_orc_necrolyte(Player* pPlayer, Creature* pCreature) +{ + if (pPlayer->HasAura(SPELL_RECENTLY_IN_GAME) || pCreature->HasAura(SPELL_CONTROL_PIECE)) + return true; + + if (ScriptedInstance* pInstance = (ScriptedInstance*)pCreature->GetInstanceData()) + { + if ((pInstance->GetData(TYPE_CHESS) == IN_PROGRESS && pPlayer->GetTeam() == HORDE) || pInstance->GetData(TYPE_CHESS) == SPECIAL) + pPlayer->ADD_GOSSIP_ITEM_ID(GOSSIP_ICON_CHAT, GOSSIP_ITEM_ORC_NECROLYTE, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 1); + } + + pPlayer->SEND_GOSSIP_MENU(GOSSIP_MENU_ID_NECROLYTE, pCreature->GetObjectGuid()); + return true; +} + +void AddSC_chess_event() +{ + Script* pNewScript; + + pNewScript = new Script; + pNewScript->Name = "npc_echo_of_medivh"; + pNewScript->GetAI = GetAI_npc_echo_of_medivh; + pNewScript->pGossipHello = GossipHello_npc_echo_of_medivh; + pNewScript->pGossipSelect = GossipSelect_npc_echo_of_medivh; + pNewScript->RegisterSelf(); + + pNewScript = new Script; + pNewScript->Name = "npc_king_llane"; + pNewScript->GetAI = GetAI_npc_king_llane; + pNewScript->pGossipHello = GossipHello_npc_king_llane; + pNewScript->pGossipSelect = GossipSelect_npc_chess_generic; + pNewScript->pEffectDummyNPC = &EffectDummyCreature_npc_chess_generic; + pNewScript->RegisterSelf(); + + pNewScript = new Script; + pNewScript->Name = "npc_warchief_blackhand"; + pNewScript->GetAI = GetAI_npc_warchief_blackhand; + pNewScript->pGossipHello = GossipHello_npc_warchief_blackhand; + pNewScript->pGossipSelect = GossipSelect_npc_chess_generic; + pNewScript->pEffectDummyNPC = &EffectDummyCreature_npc_chess_generic; + pNewScript->RegisterSelf(); + + pNewScript = new Script; + pNewScript->Name = "npc_human_conjurer"; + pNewScript->GetAI = GetAI_npc_human_conjurer; + pNewScript->pGossipHello = GossipHello_npc_human_conjurer; + pNewScript->pGossipSelect = GossipSelect_npc_chess_generic; + pNewScript->pEffectDummyNPC = &EffectDummyCreature_npc_chess_generic; + pNewScript->RegisterSelf(); + + pNewScript = new Script; + pNewScript->Name = "npc_orc_warlock"; + pNewScript->GetAI = GetAI_npc_orc_warlock; + pNewScript->pGossipHello = GossipHello_npc_orc_warlock; + pNewScript->pGossipSelect = GossipSelect_npc_chess_generic; + pNewScript->pEffectDummyNPC = &EffectDummyCreature_npc_chess_generic; + pNewScript->RegisterSelf(); + + pNewScript = new Script; + pNewScript->Name = "npc_human_footman"; + pNewScript->GetAI = GetAI_npc_human_footman; + pNewScript->pGossipHello = GossipHello_npc_human_footman; + pNewScript->pGossipSelect = GossipSelect_npc_chess_generic; + pNewScript->pEffectDummyNPC = &EffectDummyCreature_npc_chess_generic; + pNewScript->RegisterSelf(); + + pNewScript = new Script; + pNewScript->Name = "npc_orc_grunt"; + pNewScript->GetAI = GetAI_npc_orc_grunt; + pNewScript->pGossipHello = GossipHello_npc_orc_grunt; + pNewScript->pGossipSelect = GossipSelect_npc_chess_generic; + pNewScript->pEffectDummyNPC = &EffectDummyCreature_npc_chess_generic; + pNewScript->RegisterSelf(); + + pNewScript = new Script; + pNewScript->Name = "npc_water_elemental"; + pNewScript->GetAI = GetAI_npc_water_elemental; + pNewScript->pGossipHello = GossipHello_npc_water_elemental; + pNewScript->pGossipSelect = GossipSelect_npc_chess_generic; + pNewScript->pEffectDummyNPC = &EffectDummyCreature_npc_chess_generic; + pNewScript->RegisterSelf(); + + pNewScript = new Script; + pNewScript->Name = "npc_summoned_daemon"; + pNewScript->GetAI = GetAI_npc_summoned_daemon; + pNewScript->pGossipHello = GossipHello_npc_summoned_daemon; + pNewScript->pGossipSelect = GossipSelect_npc_chess_generic; + pNewScript->pEffectDummyNPC = &EffectDummyCreature_npc_chess_generic; + pNewScript->RegisterSelf(); + + pNewScript = new Script; + pNewScript->Name = "npc_human_charger"; + pNewScript->GetAI = GetAI_npc_human_charger; + pNewScript->pGossipHello = GossipHello_npc_human_charger; + pNewScript->pGossipSelect = GossipSelect_npc_chess_generic; + pNewScript->pEffectDummyNPC = &EffectDummyCreature_npc_chess_generic; + pNewScript->RegisterSelf(); + + pNewScript = new Script; + pNewScript->Name = "npc_orc_wolf"; + pNewScript->GetAI = GetAI_npc_orc_wolf; + pNewScript->pGossipHello = GossipHello_npc_orc_wolf; + pNewScript->pGossipSelect = GossipSelect_npc_chess_generic; + pNewScript->pEffectDummyNPC = &EffectDummyCreature_npc_chess_generic; + pNewScript->RegisterSelf(); + + pNewScript = new Script; + pNewScript->Name = "npc_human_cleric"; + pNewScript->GetAI = GetAI_npc_human_cleric; + pNewScript->pGossipHello = GossipHello_npc_human_cleric; + pNewScript->pGossipSelect = GossipSelect_npc_chess_generic; + pNewScript->pEffectDummyNPC = &EffectDummyCreature_npc_chess_generic; + pNewScript->RegisterSelf(); + + pNewScript = new Script; + pNewScript->Name = "npc_orc_necrolyte"; + pNewScript->GetAI = GetAI_npc_orc_necrolyte; + pNewScript->pGossipHello = GossipHello_npc_orc_necrolyte; + pNewScript->pGossipSelect = GossipSelect_npc_chess_generic; + pNewScript->pEffectDummyNPC = &EffectDummyCreature_npc_chess_generic; + pNewScript->RegisterSelf(); +} diff --git a/src/modules/SD2/scripts/eastern_kingdoms/karazhan/instance_karazhan.cpp b/src/modules/SD2/scripts/eastern_kingdoms/karazhan/instance_karazhan.cpp new file mode 100644 index 000000000..cf4c3e043 --- /dev/null +++ b/src/modules/SD2/scripts/eastern_kingdoms/karazhan/instance_karazhan.cpp @@ -0,0 +1,619 @@ +/* Copyright (C) 2006 - 2013 ScriptDev2 + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along 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. +SDCategory: Karazhan +EndScriptData */ + +#include "precompiled.h" +#include "karazhan.h" + +/* +0 - Attumen + Midnight (optional) +1 - Moroes +2 - Maiden of Virtue (optional) +3 - Opera Event +4 - Curator +5 - Terestian Illhoof (optional) +6 - Shade of Aran (optional) +7 - Netherspite (optional) +8 - Chess Event +9 - Prince Malchezzar +10 - Nightbane +*/ + +instance_karazhan::instance_karazhan(Map* pMap) : ScriptedInstance(pMap), + m_uiOperaEvent(0), + m_uiOzDeathCount(0), + m_uiTeam(0), + m_uiChessResetTimer(0), + m_uiAllianceStalkerCount(0), + m_uiHordeStalkerCount(0), + m_bFriendlyGame(false) +{ + Initialize(); +} + +void instance_karazhan::Initialize() +{ + memset(&m_auiEncounter, 0, sizeof(m_auiEncounter)); +} + +bool instance_karazhan::IsEncounterInProgress() const +{ + for (uint8 i = 0; i < MAX_ENCOUNTER; ++i) + { + if (m_auiEncounter[i] == IN_PROGRESS) + return true; + } + + return false; +} + +void instance_karazhan::OnPlayerEnter(Player* pPlayer) +{ + if (!m_uiTeam) // very first player to enter + m_uiTeam = pPlayer->GetTeam(); + + // If the opera event is already set, return + if (GetData(TYPE_OPERA_PERFORMANCE) != 0) + return; + + // Set the Opera Performance type on the first player enter + SetData(TYPE_OPERA_PERFORMANCE, urand(OPERA_EVENT_WIZARD_OZ, OPERA_EVENT_ROMULO_AND_JUL)); +} + +void instance_karazhan::OnCreatureCreate(Creature* pCreature) +{ + switch (pCreature->GetEntry()) + { + case NPC_ATTUMEN: + case NPC_MIDNIGHT: + case NPC_MOROES: + case NPC_BARNES: + case NPC_NIGHTBANE: + case NPC_NETHERSPITE: + case NPC_JULIANNE: + case NPC_ROMULO: + case NPC_LADY_KEIRA_BERRYBUCK: + case NPC_LADY_CATRIONA_VON_INDI: + case NPC_LORD_CRISPIN_FERENCE: + case NPC_BARON_RAFE_DREUGER: + case NPC_BARONESS_DOROTHEA_MILLSTIPE: + case NPC_LORD_ROBIN_DARIS: + case NPC_IMAGE_OF_MEDIVH: + case NPC_IMAGE_OF_ARCANAGOS: + case NPC_ECHO_MEDIVH: + case NPC_CHESS_VICTORY_CONTROLLER: + m_mNpcEntryGuidStore[pCreature->GetEntry()] = pCreature->GetObjectGuid(); + break; + case NPC_NIGHTBANE_HELPER: + if (pCreature->GetPositionZ() < 100.0f) + m_lNightbaneGroundTriggers.push_back(pCreature->GetObjectGuid()); + else + m_lNightbaneAirTriggers.push_back(pCreature->GetObjectGuid()); + break; + case NPC_INVISIBLE_STALKER: + if (pCreature->GetPositionY() < -1870.0f) + m_lChessHordeStalkerList.push_back(pCreature->GetObjectGuid()); + else + m_lChessAllianceStalkerList.push_back(pCreature->GetObjectGuid()); + break; + case NPC_CHESS_STATUS_BAR: + if (pCreature->GetPositionY() < -1870.0f) + m_HordeStatusGuid = pCreature->GetObjectGuid(); + else + m_AllianceStatusGuid = pCreature->GetObjectGuid(); + break; + case NPC_HUMAN_CHARGER: + case NPC_HUMAN_CLERIC: + case NPC_HUMAN_CONJURER: + case NPC_HUMAN_FOOTMAN: + case NPC_CONJURED_WATER_ELEMENTAL: + case NPC_KING_LLANE: + m_lChessPiecesAlliance.push_back(pCreature->GetObjectGuid()); + break; + case NPC_ORC_GRUNT: + case NPC_ORC_NECROLYTE: + case NPC_ORC_WARLOCK: + case NPC_ORC_WOLF: + case NPC_SUMMONED_DAEMON: + case NPC_WARCHIEF_BLACKHAND: + m_lChessPiecesHorde.push_back(pCreature->GetObjectGuid()); + break; + } +} + +void instance_karazhan::OnObjectCreate(GameObject* pGo) +{ + switch (pGo->GetEntry()) + { + case GO_STAGE_DOOR_LEFT: + case GO_STAGE_DOOR_RIGHT: + if (m_auiEncounter[3] == DONE) + pGo->SetGoState(GO_STATE_ACTIVE); + break; + case GO_GAMESMANS_HALL_EXIT_DOOR: + if (m_auiEncounter[8] == DONE) + pGo->SetGoState(GO_STATE_ACTIVE); + break; + case GO_SIDE_ENTRANCE_DOOR: + if (m_auiEncounter[3] == DONE) + pGo->RemoveFlag(GAMEOBJECT_FLAGS, GO_FLAG_LOCKED); + break; + case GO_STAGE_CURTAIN: + case GO_PRIVATE_LIBRARY_DOOR: + case GO_MASSIVE_DOOR: + case GO_GAMESMANS_HALL_DOOR: + case GO_NETHERSPACE_DOOR: + case GO_DUST_COVERED_CHEST: + case GO_MASTERS_TERRACE_DOOR_1: + case GO_MASTERS_TERRACE_DOOR_2: + break; + + // Opera event backgrounds + case GO_OZ_BACKDROP: + case GO_HOOD_BACKDROP: + case GO_HOOD_HOUSE: + case GO_RAJ_BACKDROP: + case GO_RAJ_MOON: + case GO_RAJ_BALCONY: + break; + case GO_OZ_HAY: + m_lOperaHayGuidList.push_back(pGo->GetObjectGuid()); + return; + case GO_HOOD_TREE: + m_lOperaTreeGuidList.push_back(pGo->GetObjectGuid()); + return; + + default: + return; + } + m_mGoEntryGuidStore[pGo->GetEntry()] = pGo->GetObjectGuid(); +} + +void instance_karazhan::SetData(uint32 uiType, uint32 uiData) +{ + switch (uiType) + { + case TYPE_ATTUMEN: + m_auiEncounter[uiType] = uiData; + if (uiData == FAIL) + { + // Respawn Midnight on Fail + if (Creature* pMidnight = GetSingleCreatureFromStorage(NPC_MIDNIGHT)) + { + if (!pMidnight->IsAlive()) + pMidnight->Respawn(); + } + } + break; + case TYPE_MOROES: + case TYPE_MAIDEN: + m_auiEncounter[uiType] = uiData; + break; + case TYPE_OPERA: + // Don't store the same data twice + if (uiData == m_auiEncounter[uiType]) + break; + m_auiEncounter[uiType] = uiData; + if (uiData == IN_PROGRESS) + m_uiOzDeathCount = 0; + if (uiData == DONE) + { + DoUseDoorOrButton(GO_STAGE_DOOR_LEFT); + DoUseDoorOrButton(GO_STAGE_DOOR_RIGHT); + DoToggleGameObjectFlags(GO_SIDE_ENTRANCE_DOOR, GO_FLAG_LOCKED, false); + } + // use curtain only for event start or fail + else + DoUseDoorOrButton(GO_STAGE_CURTAIN); + break; + case TYPE_CURATOR: + case TYPE_TERESTIAN: + m_auiEncounter[uiType] = uiData; + break; + case TYPE_ARAN: + if (uiData == FAIL || uiData == DONE) + DoToggleGameObjectFlags(GO_PRIVATE_LIBRARY_DOOR, GO_FLAG_LOCKED, false); + if (uiData == IN_PROGRESS) + DoToggleGameObjectFlags(GO_PRIVATE_LIBRARY_DOOR, GO_FLAG_LOCKED, true); + m_auiEncounter[uiType] = uiData; + break; + case TYPE_NETHERSPITE: + m_auiEncounter[uiType] = uiData; + DoUseDoorOrButton(GO_MASSIVE_DOOR); + break; + case TYPE_CHESS: + if (uiData == DONE) + { + // doors and loot are not handled for friendly games + if (GetData(TYPE_CHESS) != SPECIAL) + { + DoUseDoorOrButton(GO_GAMESMANS_HALL_EXIT_DOOR); + DoRespawnGameObject(GO_DUST_COVERED_CHEST, DAY); + DoToggleGameObjectFlags(GO_DUST_COVERED_CHEST, GO_FLAG_NO_INTERACT, false); + } + + // cast game end spells + if (Creature* pMedivh = GetSingleCreatureFromStorage(NPC_ECHO_MEDIVH)) + { + pMedivh->CastSpell(pMedivh, SPELL_FORCE_KILL_BUNNY, true); + pMedivh->CastSpell(pMedivh, SPELL_GAME_OVER, true); + pMedivh->CastSpell(pMedivh, SPELL_CLEAR_BOARD, true); + } + if (Creature* pController = GetSingleCreatureFromStorage(NPC_CHESS_VICTORY_CONTROLLER)) + pController->CastSpell(pController, SPELL_VICTORY_VISUAL, true); + + // remove silence debuff + Map::PlayerList const& players = instance->GetPlayers(); + for (Map::PlayerList::const_iterator itr = players.begin(); itr != players.end(); ++itr) + { + if (Player* pPlayer = itr->getSource()) + pPlayer->RemoveAurasDueToSpell(SPELL_GAME_IN_SESSION); + } + + m_bFriendlyGame = false; + m_uiChessResetTimer = 35000; + } + else if (uiData == FAIL) + { + // clean the board for reset + if (Creature* pMedivh = GetSingleCreatureFromStorage(NPC_ECHO_MEDIVH)) + { + pMedivh->CastSpell(pMedivh, SPELL_GAME_OVER, true); + pMedivh->CastSpell(pMedivh, SPELL_CLEAR_BOARD, true); + } + + // remove silence debuff + Map::PlayerList const& players = instance->GetPlayers(); + for (Map::PlayerList::const_iterator itr = players.begin(); itr != players.end(); ++itr) + { + if (Player* pPlayer = itr->getSource()) + pPlayer->RemoveAurasDueToSpell(SPELL_GAME_IN_SESSION); + } + + m_uiChessResetTimer = 35000; + } + else if (uiData == IN_PROGRESS || uiData == SPECIAL) + DoPrepareChessEvent(); + m_auiEncounter[uiType] = uiData; + break; + case TYPE_MALCHEZZAR: + DoUseDoorOrButton(GO_NETHERSPACE_DOOR); + m_auiEncounter[uiType] = uiData; + break; + case TYPE_NIGHTBANE: + m_auiEncounter[uiType] = uiData; + DoUseDoorOrButton(GO_MASTERS_TERRACE_DOOR_1); + DoUseDoorOrButton(GO_MASTERS_TERRACE_DOOR_2); + break; + // Store the event type for the Opera + case TYPE_OPERA_PERFORMANCE: + m_uiOperaEvent = uiData; + break; + } + + // Also save the opera performance, once it's set + if (uiData == DONE || uiType == TYPE_OPERA_PERFORMANCE) + { + 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_uiOperaEvent; + + m_strInstData = saveStream.str(); + + SaveToDB(); + OUT_SAVE_INST_DATA_COMPLETE; + } +} + +uint32 instance_karazhan::GetData(uint32 uiType) const +{ + if (uiType < MAX_ENCOUNTER) + return m_auiEncounter[uiType]; + + if (uiType == TYPE_OPERA_PERFORMANCE) + return m_uiOperaEvent; + + return 0; +} + +void instance_karazhan::Load(const char* chrIn) +{ + if (!chrIn) + { + 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_uiOperaEvent; + + 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; +} + +void instance_karazhan::OnCreatureDeath(Creature* pCreature) +{ + switch (pCreature->GetEntry()) + { + case NPC_DOROTHEE: + case NPC_ROAR: + case NPC_TINHEAD: + case NPC_STRAWMAN: + ++m_uiOzDeathCount; + // Summon Chrone when all 4 Oz mobs are killed + if (m_uiOzDeathCount == MAX_OZ_OPERA_MOBS) + { + if (Creature* pCrone = pCreature->SummonCreature(NPC_CRONE, afChroneSpawnLoc[0], afChroneSpawnLoc[1], afChroneSpawnLoc[2], afChroneSpawnLoc[3], TEMPSUMMON_DEAD_DESPAWN, 0)) + { + if (pCreature->getVictim()) + pCrone->AI()->AttackStart(pCreature->getVictim()); + } + } + break; + } +} + +void instance_karazhan::DoPrepareChessEvent() +{ + // Allow all the chess pieces to init start position + for (GuidList::const_iterator itr = m_lChessPiecesAlliance.begin(); itr != m_lChessPiecesAlliance.end(); ++itr) + { + if (Creature* pChessPiece = instance->GetCreature(*itr)) + { + Creature* pSquare = GetClosestCreatureWithEntry(pChessPiece, NPC_SQUARE_BLACK, 2.0f); + if (!pSquare) + pSquare = GetClosestCreatureWithEntry(pChessPiece, NPC_SQUARE_WHITE, 2.0f); + if (!pSquare) + { + script_error_log("Instance Karazhan: ERROR Failed to properly load the Chess square for %s.", pChessPiece->GetGuidStr().c_str()); + return; + } + + // send event which will prepare the current square + pChessPiece->AI()->SendAIEvent(AI_EVENT_CUSTOM_B, pSquare, pChessPiece); + } + } + + for (GuidList::const_iterator itr = m_lChessPiecesHorde.begin(); itr != m_lChessPiecesHorde.end(); ++itr) + { + if (Creature* pChessPiece = instance->GetCreature(*itr)) + { + Creature* pSquare = GetClosestCreatureWithEntry(pChessPiece, NPC_SQUARE_BLACK, 2.0f); + if (!pSquare) + pSquare = GetClosestCreatureWithEntry(pChessPiece, NPC_SQUARE_WHITE, 2.0f); + if (!pSquare) + { + script_error_log("Instance Karazhan: ERROR Failed to properly load the Chess square for %s.", pChessPiece->GetGuidStr().c_str()); + return; + } + + // send event which will prepare the current square + pChessPiece->AI()->SendAIEvent(AI_EVENT_CUSTOM_B, pSquare, pChessPiece); + } + } + + // add silence debuff + Map::PlayerList const& players = instance->GetPlayers(); + for (Map::PlayerList::const_iterator itr = players.begin(); itr != players.end(); ++itr) + { + if (Player* pPlayer = itr->getSource()) + pPlayer->CastSpell(pPlayer, SPELL_GAME_IN_SESSION, true); + } + + m_uiAllianceStalkerCount = 0; + m_uiHordeStalkerCount = 0; + m_vHordeStalkers.clear(); + m_vAllianceStalkers.clear(); + + // sort stalkers depending on side + std::list lStalkers; + for (GuidList::const_iterator itr = m_lChessHordeStalkerList.begin(); itr != m_lChessHordeStalkerList.end(); ++itr) + { + if (Creature* pTemp = instance->GetCreature(*itr)) + lStalkers.push_back(pTemp); + } + + if (lStalkers.empty()) + { + script_error_log("Instance Karazhan: ERROR Failed to properly load the horde side stalkers for the Chess Event."); + return; + } + + // get the proper statusBar npc + Creature* pStatusBar = instance->GetCreature(m_HordeStatusGuid); + if (!pStatusBar) + return; + + lStalkers.sort(ObjectDistanceOrder(pStatusBar)); + for (std::list::const_iterator itr = lStalkers.begin(); itr != lStalkers.end(); ++itr) + m_vHordeStalkers.push_back((*itr)->GetObjectGuid()); + + lStalkers.clear(); + for (GuidList::const_iterator itr = m_lChessAllianceStalkerList.begin(); itr != m_lChessAllianceStalkerList.end(); ++itr) + { + if (Creature* pTemp = instance->GetCreature(*itr)) + lStalkers.push_back(pTemp); + } + + if (lStalkers.empty()) + { + script_error_log("Instance Karazhan: ERROR Failed to properly load the alliance side stalkers for the Chess Event."); + return; + } + + // get the proper statusBar npc + pStatusBar = instance->GetCreature(m_AllianceStatusGuid); + if (!pStatusBar) + return; + + lStalkers.sort(ObjectDistanceOrder(pStatusBar)); + for (std::list::const_iterator itr = lStalkers.begin(); itr != lStalkers.end(); ++itr) + m_vAllianceStalkers.push_back((*itr)->GetObjectGuid()); +} + +void instance_karazhan::DoMoveChessPieceToSides(uint32 uiSpellId, uint32 uiFaction, bool bGameEnd /*= false*/) +{ + // assign proper faction variables + GuidVector& vStalkers = uiFaction == FACTION_ID_CHESS_ALLIANCE ? m_vAllianceStalkers : m_vHordeStalkers; + uint32 uiCount = uiFaction == FACTION_ID_CHESS_ALLIANCE ? m_uiAllianceStalkerCount : m_uiHordeStalkerCount; + + // get the proper statusBar npc + Creature* pStatusBar = instance->GetCreature(uiFaction == FACTION_ID_CHESS_ALLIANCE ? m_AllianceStatusGuid : m_HordeStatusGuid); + if (!pStatusBar) + return; + + if (vStalkers.size() < uiCount + 1) + return; + + // handle stalker transformation + if (Creature* pStalker = instance->GetCreature(vStalkers[uiCount])) + { + // need to provide specific target, in order to ensure the logic of the event + pStatusBar->CastSpell(pStalker, uiSpellId, true); + uiFaction == FACTION_ID_CHESS_ALLIANCE ? ++m_uiAllianceStalkerCount : ++m_uiHordeStalkerCount; + } + + // handle emote on end game + if (bGameEnd) + { + // inverse factions + vStalkers.clear(); + vStalkers = uiFaction == FACTION_ID_CHESS_ALLIANCE ? m_vHordeStalkers : m_vAllianceStalkers; + + for (GuidVector::const_iterator itr = vStalkers.begin(); itr != vStalkers.end(); ++itr) + { + if (Creature* pStalker = instance->GetCreature(*itr)) + pStalker->HandleEmote(EMOTE_STATE_APPLAUD); + } + } +} + +void instance_karazhan::DoPrepareOperaStage(Creature* pOrganizer) +{ + if (!pOrganizer) + return; + + debug_log("SD2: Barnes Opera Event - Introduction complete - preparing encounter %d", GetData(TYPE_OPERA_PERFORMANCE)); + + // summon the bosses and respawn the stage background + switch (GetData(TYPE_OPERA_PERFORMANCE)) + { + case OPERA_EVENT_WIZARD_OZ: + for (uint8 i = 0; i < MAX_OZ_OPERA_MOBS; ++i) + pOrganizer->SummonCreature(aOperaLocOz[i].uiEntry, aOperaLocOz[i].fX, aOperaLocOz[i].fY, aOperaLocOz[i].fZ, aOperaLocOz[i].fO, TEMPSUMMON_DEAD_DESPAWN, 0); + DoRespawnGameObject(GO_OZ_BACKDROP, 12 * HOUR); + for (GuidList::const_iterator itr = m_lOperaHayGuidList.begin(); itr != m_lOperaHayGuidList.end(); ++itr) + DoRespawnGameObject(*itr, 12 * HOUR); + break; + case OPERA_EVENT_RED_RIDING_HOOD: + pOrganizer->SummonCreature(aOperaLocWolf.uiEntry, aOperaLocWolf.fX, aOperaLocWolf.fY, aOperaLocWolf.fZ, aOperaLocWolf.fO, TEMPSUMMON_DEAD_DESPAWN, 0); + DoRespawnGameObject(GO_HOOD_BACKDROP, 12 * HOUR); + DoRespawnGameObject(GO_HOOD_HOUSE, 12 * HOUR); + for (GuidList::const_iterator itr = m_lOperaTreeGuidList.begin(); itr != m_lOperaTreeGuidList.end(); ++itr) + DoRespawnGameObject(*itr, 12 * HOUR); + break; + case OPERA_EVENT_ROMULO_AND_JUL: + pOrganizer->SummonCreature(aOperaLocJul.uiEntry, aOperaLocJul.fX, aOperaLocJul.fY, aOperaLocJul.fZ, aOperaLocJul.fO, TEMPSUMMON_DEAD_DESPAWN, 0); + DoRespawnGameObject(GO_RAJ_BACKDROP, 12 * HOUR); + DoRespawnGameObject(GO_RAJ_MOON, 12 * HOUR); + DoRespawnGameObject(GO_RAJ_BALCONY, 12 * HOUR); + break; + } + + SetData(TYPE_OPERA, IN_PROGRESS); +} + +void instance_karazhan::Update(uint32 uiDiff) +{ + if (m_uiChessResetTimer) + { + // respawn all chess pieces and side stalkers on the original position + if (m_uiChessResetTimer <= uiDiff) + { + for (GuidList::const_iterator itr = m_lChessPiecesAlliance.begin(); itr != m_lChessPiecesAlliance.end(); ++itr) + { + if (Creature* pTemp = instance->GetCreature(*itr)) + pTemp->Respawn(); + } + for (GuidList::const_iterator itr = m_lChessPiecesHorde.begin(); itr != m_lChessPiecesHorde.end(); ++itr) + { + if (Creature* pTemp = instance->GetCreature(*itr)) + pTemp->Respawn(); + } + + for (GuidList::const_iterator itr = m_lChessAllianceStalkerList.begin(); itr != m_lChessAllianceStalkerList.end(); ++itr) + { + if (Creature* pTemp = instance->GetCreature(*itr)) + { + pTemp->Respawn(); + pTemp->HandleEmote(EMOTE_STATE_NONE); + } + } + for (GuidList::const_iterator itr = m_lChessHordeStalkerList.begin(); itr != m_lChessHordeStalkerList.end(); ++itr) + { + if (Creature* pTemp = instance->GetCreature(*itr)) + { + pTemp->Respawn(); + pTemp->HandleEmote(EMOTE_STATE_NONE); + } + } + + if (GetData(TYPE_CHESS) == FAIL) + SetData(TYPE_CHESS, NOT_STARTED); + else if (GetData(TYPE_CHESS) == DONE) + m_bFriendlyGame = true; + + m_uiChessResetTimer = 0; + } + else + m_uiChessResetTimer -= uiDiff; + } +} + +InstanceData* GetInstanceData_instance_karazhan(Map* pMap) +{ + return new instance_karazhan(pMap); +} + +void AddSC_instance_karazhan() +{ + Script* pNewScript; + + pNewScript = new Script; + pNewScript->Name = "instance_karazhan"; + pNewScript->GetInstanceData = &GetInstanceData_instance_karazhan; + pNewScript->RegisterSelf(); +} diff --git a/src/modules/SD2/scripts/eastern_kingdoms/karazhan/karazhan.cpp b/src/modules/SD2/scripts/eastern_kingdoms/karazhan/karazhan.cpp new file mode 100644 index 000000000..f9f261cda --- /dev/null +++ b/src/modules/SD2/scripts/eastern_kingdoms/karazhan/karazhan.cpp @@ -0,0 +1,541 @@ +/* Copyright (C) 2006 - 2013 ScriptDev2 + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along 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: Quest support: 9645. Support for Barnes (Opera controller) and Berthold (Doorman). +SDCategory: Karazhan +EndScriptData */ + +/* ContentData +npc_barnes +npc_berthold +npc_image_of_medivh +npc_image_arcanagos +event_spell_medivh_journal +EndContentData */ + +#include "precompiled.h" +#include "karazhan.h" +#include "escort_ai.h" + +/*###### +# npc_barnesAI +######*/ + +enum +{ + SAY_BARNES_EVENT_START = -1532115, + + SAY_BARNES_OZ_1 = -1532103, + SAY_BARNES_OZ_2 = -1532104, + SAY_BARNES_OZ_3 = -1532105, + SAY_BARNES_OZ_4 = -1532106, + + SAY_BARNES_HOOD_1 = -1532107, + SAY_BARNES_HOOD_2 = -1532108, + SAY_BARNES_HOOD_3 = -1532109, + SAY_BARNES_HOOD_4 = -1532110, + + SAY_BARNES_RAJ_1 = -1532111, + SAY_BARNES_RAJ_2 = -1532112, + SAY_BARNES_RAJ_3 = -1532113, + SAY_BARNES_RAJ_4 = -1532114, + + // ToDo: it's not very clear which is the gossip sequence for event FAIL case + GOSSIP_ITEM_OPERA_1 = -3532001, + GOSSIP_ITEM_OPERA_2 = -3532002, + GOSSIP_ITEM_JUL_WIPE = -3532003, + GOSSIP_ITEM_WOLF_WIPE = -3532004, + + TEXT_ID_OPERA_1 = 8970, + TEXT_ID_OPERA_2 = 8971, + TEXT_ID_OPERA_WOLF_WIPE = 8975, + TEXT_ID_OPERA_OZ_WIPE = 8781, // guesswork, not confirmed + // TEXT_ID_OPERA_JUL_WIPE = ????, // Item not found in DB: "The romantic plays are really tough, but you'll do better this time. You have TALENT. Ready?" + + // SPELL_SPOTLIGHT = 25824, // in creature_template_addon + SPELL_TUXEDO = 32616, + + NPC_SPOTLIGHT = 19525, +}; + +static const DialogueEntry aIntroDialogue[] = +{ + {SAY_BARNES_OZ_1, NPC_BARNES, 6000}, + {SAY_BARNES_OZ_2, NPC_BARNES, 18000}, + {SAY_BARNES_OZ_3, NPC_BARNES, 9000}, + {SAY_BARNES_OZ_4, NPC_BARNES, 15000}, + {OPERA_EVENT_WIZARD_OZ, 0, 0}, + {SAY_BARNES_HOOD_1, NPC_BARNES, 6000}, + {SAY_BARNES_HOOD_2, NPC_BARNES, 10000}, + {SAY_BARNES_HOOD_3, NPC_BARNES, 14000}, + {SAY_BARNES_HOOD_4, NPC_BARNES, 15000}, + {OPERA_EVENT_RED_RIDING_HOOD, 0, 0}, + {SAY_BARNES_RAJ_1, NPC_BARNES, 5000}, + {SAY_BARNES_RAJ_2, NPC_BARNES, 7000}, + {SAY_BARNES_RAJ_3, NPC_BARNES, 14000}, + {SAY_BARNES_RAJ_4, NPC_BARNES, 14000}, + {OPERA_EVENT_ROMULO_AND_JUL, 0, 0}, + {0, 0, 0}, +}; + +struct npc_barnesAI : public npc_escortAI, private DialogueHelper +{ + npc_barnesAI(Creature* pCreature) : npc_escortAI(pCreature), + DialogueHelper(aIntroDialogue) + { + m_pInstance = (instance_karazhan*)pCreature->GetInstanceData(); + InitializeDialogueHelper(m_pInstance); + Reset(); + } + + instance_karazhan* m_pInstance; + + ObjectGuid m_spotlightGuid; + + void Reset() override + { + m_spotlightGuid.Clear(); + } + + void JustSummoned(Creature* pSummoned) override + { + if (pSummoned->GetEntry() == NPC_SPOTLIGHT) + m_spotlightGuid = pSummoned->GetObjectGuid(); + } + + void WaypointReached(uint32 uiPointId) override + { + if (!m_pInstance) + return; + + switch (uiPointId) + { + case 0: + DoCastSpellIfCan(m_creature, SPELL_TUXEDO); + m_pInstance->DoUseDoorOrButton(GO_STAGE_DOOR_LEFT); + break; + case 4: + switch (m_pInstance->GetData(TYPE_OPERA_PERFORMANCE)) + { + case OPERA_EVENT_WIZARD_OZ: + StartNextDialogueText(SAY_BARNES_OZ_1); + break; + case OPERA_EVENT_RED_RIDING_HOOD: + StartNextDialogueText(SAY_BARNES_HOOD_1); + break; + case OPERA_EVENT_ROMULO_AND_JUL: + StartNextDialogueText(SAY_BARNES_RAJ_1); + break; + } + SetEscortPaused(true); + m_creature->SummonCreature(NPC_SPOTLIGHT, 0, 0, 0, 0, TEMPSUMMON_DEAD_DESPAWN, 0); + break; + case 8: + m_pInstance->DoUseDoorOrButton(GO_STAGE_DOOR_LEFT); + break; + case 9: + m_pInstance->DoPrepareOperaStage(m_creature); + break; + } + } + + void JustDidDialogueStep(int32 iEntry) override + { + switch (iEntry) + { + case OPERA_EVENT_WIZARD_OZ: + case OPERA_EVENT_RED_RIDING_HOOD: + case OPERA_EVENT_ROMULO_AND_JUL: + // Despawn spotlight and resume escort + if (Creature* pSpotlight = m_creature->GetMap()->GetCreature(m_spotlightGuid)) + pSpotlight->ForcedDespawn(); + SetEscortPaused(false); + break; + } + } + + void UpdateEscortAI(const uint32 uiDiff) { DialogueUpdate(uiDiff); } +}; + +CreatureAI* GetAI_npc_barnesAI(Creature* pCreature) +{ + return new npc_barnesAI(pCreature); +} + +bool GossipHello_npc_barnes(Player* pPlayer, Creature* pCreature) +{ + if (ScriptedInstance* pInstance = (ScriptedInstance*)pCreature->GetInstanceData()) + { + // Check if opera event is not yet in progress + if (pInstance->GetData(TYPE_OPERA) == IN_PROGRESS || pInstance->GetData(TYPE_OPERA) == DONE) + return true; + + // Check for death of Moroes + if (pInstance->GetData(TYPE_MOROES) == DONE) + { + pPlayer->ADD_GOSSIP_ITEM_ID(GOSSIP_ICON_CHAT, GOSSIP_ITEM_OPERA_1, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 1); + + // for GMs we add the possibility to change the event + if (pPlayer->isGameMaster()) + { + pPlayer->ADD_GOSSIP_ITEM(GOSSIP_ICON_DOT, "[GM] Change event to EVENT_OZ", GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 3); + pPlayer->ADD_GOSSIP_ITEM(GOSSIP_ICON_DOT, "[GM] Change event to EVENT_HOOD", GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 4); + pPlayer->ADD_GOSSIP_ITEM(GOSSIP_ICON_DOT, "[GM] Change event to EVENT_RAJ", GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 5); + } + + pPlayer->SEND_GOSSIP_MENU(TEXT_ID_OPERA_1, pCreature->GetObjectGuid()); + + return true; + } + } + + return true; +} + +bool GossipSelect_npc_barnes(Player* pPlayer, Creature* pCreature, uint32 /*uiSender*/, uint32 uiAction) +{ + switch (uiAction) + { + case GOSSIP_ACTION_INFO_DEF+1: + pPlayer->ADD_GOSSIP_ITEM_ID(GOSSIP_ICON_CHAT, GOSSIP_ITEM_OPERA_2, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 2); + pPlayer->SEND_GOSSIP_MENU(TEXT_ID_OPERA_2, pCreature->GetObjectGuid()); + break; + case GOSSIP_ACTION_INFO_DEF+2: + pPlayer->CLOSE_GOSSIP_MENU(); + DoScriptText(SAY_BARNES_EVENT_START, pCreature); + // start the stage escort + if (npc_barnesAI* pBarnesAI = dynamic_cast(pCreature->AI())) + pBarnesAI->Start(false, NULL, NULL, true); + break; + // GM gossip options + case GOSSIP_ACTION_INFO_DEF+3: + pPlayer->CLOSE_GOSSIP_MENU(); + if (ScriptedInstance* pInstance = (ScriptedInstance*)pCreature->GetInstanceData()) + { + pInstance->SetData(TYPE_OPERA_PERFORMANCE, OPERA_EVENT_WIZARD_OZ); + outstring_log("SD2: %s manually set Opera event to EVENT_OZ", pPlayer->GetGuidStr().c_str()); + } + break; + case GOSSIP_ACTION_INFO_DEF+4: + pPlayer->CLOSE_GOSSIP_MENU(); + if (ScriptedInstance* pInstance = (ScriptedInstance*)pCreature->GetInstanceData()) + { + pInstance->SetData(TYPE_OPERA_PERFORMANCE, OPERA_EVENT_RED_RIDING_HOOD); + outstring_log("SD2: %s manually set Opera event to EVENT_HOOD", pPlayer->GetGuidStr().c_str()); + } + break; + case GOSSIP_ACTION_INFO_DEF+5: + pPlayer->CLOSE_GOSSIP_MENU(); + if (ScriptedInstance* pInstance = (ScriptedInstance*)pCreature->GetInstanceData()) + { + pInstance->SetData(TYPE_OPERA_PERFORMANCE, OPERA_EVENT_ROMULO_AND_JUL); + outstring_log("SD2: %s manually set Opera event to EVENT_RAJ", pPlayer->GetGuidStr().c_str()); + } + break; + } + + return true; +} + +/*### +# npc_berthold +####*/ + +enum +{ + GOSSIP_ITEM_TELEPORT = -3532000, + + SPELL_TELEPORT = 39567 +}; + +bool GossipHello_npc_berthold(Player* pPlayer, Creature* pCreature) +{ + if (ScriptedInstance* pInstance = (ScriptedInstance*)pCreature->GetInstanceData()) + { + // Check if Shade of Aran event is done + if (pInstance->GetData(TYPE_ARAN) == DONE) + pPlayer->ADD_GOSSIP_ITEM_ID(GOSSIP_ICON_CHAT, GOSSIP_ITEM_TELEPORT, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 1); + } + + pPlayer->SEND_GOSSIP_MENU(pPlayer->GetGossipTextId(pCreature), pCreature->GetObjectGuid()); + 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 +######*/ + +enum +{ + // yells + SAY_MEDIVH_1 = -1532116, + SAY_ARCANAGOS_2 = -1532117, + SAY_MEDIVH_3 = -1532118, + SAY_ARCANAGOS_4 = -1532119, + SAY_MEDIVH_5 = -1532120, + SAY_ARCANAGOS_6 = -1532121, + EMOTE_CAST_SPELL = -1532122, + SAY_ARCANAGOS_7 = -1532123, + SAY_MEDIVH_8 = -1532124, + + // spells + // Arcanagos + // SPELL_NOTIFY_FLEE = 30985, // not used - allow Medivh to return inside after the dragon has escaped + // SPELL_PREPARE_FIREBALL= 30970, // not used - allow Medivh to cast fireball + SPELL_REFLECTION = 30969, + // SPELL_SHOOT_FIREBALL = 30968, // not used + SPELL_FIREBALL_REFLECT = 30971, + + // Medivh + // SPELL_FROST_BREATH = 30974, // not used + SPELL_CONFLAG_BLAST = 30977, // cast on Arcanagos + SPELL_EVOCATION = 30972, // prepare the Conflagration Blast + SPELL_FIREBALL = 30967, + // SPELL_FLY_TO_DEATH = 30936, // not used - inform the dragon to move to death Location + SPELL_MANA_SHIELD = 30973, + + // NPC_ARCANAGOS_CREDIT = 17665, // purpose unk + + QUEST_MASTERS_TERRACE = 9645, + + POINT_ID_INTRO = 1, + POINT_ID_DESPAWN = 2, +}; + +/* Notes for future development of the event: + * this whole event includes a lot of guesswork + * the dummy spells usage is unk; for the moment they are not used + * also all coords are guesswork + */ +static const float afMedivhSpawnLoc[4] = { -11153.18f, -1889.65f, 91.47f, 2.07f}; +static const float afMedivhExitLoc[3] = { -11121.81f, -1881.24f, 91.47f}; + +static const float afArcanagosSpawnLoc[4] = { -11242.66f, -1778.55f, 125.35f, 0.0f}; +static const float afArcanagosMoveLoc[3] = { -11170.28f, -1865.09f, 125.35f}; +static const float afArcanagosFleeLoc[3] = { -11003.70f, -1760.18f, 180.25f}; + +static const DialogueEntry aMedivhDialogue[] = +{ + {NPC_IMAGE_OF_MEDIVH, 0, 10000}, + {SAY_MEDIVH_1, NPC_IMAGE_OF_MEDIVH, 6000}, + {SAY_ARCANAGOS_2, NPC_IMAGE_OF_ARCANAGOS, 10000}, + {SAY_MEDIVH_3, NPC_IMAGE_OF_MEDIVH, 6000}, + {SAY_ARCANAGOS_4, NPC_IMAGE_OF_ARCANAGOS, 8000}, + {SAY_MEDIVH_5, NPC_IMAGE_OF_MEDIVH, 7000}, + {SAY_ARCANAGOS_6, NPC_IMAGE_OF_ARCANAGOS, 0}, + {SPELL_MANA_SHIELD, 0, 4000}, + {EMOTE_CAST_SPELL, NPC_IMAGE_OF_MEDIVH, 5000}, + {SPELL_CONFLAG_BLAST, 0, 1000}, + {SAY_ARCANAGOS_7, NPC_IMAGE_OF_ARCANAGOS, 10000}, + {SAY_MEDIVH_8, NPC_IMAGE_OF_MEDIVH, 0}, + {0, 0, 0}, +}; + +struct npc_image_of_medivhAI : public ScriptedAI, private DialogueHelper +{ + npc_image_of_medivhAI(Creature* pCreature) : ScriptedAI(pCreature), + DialogueHelper(aMedivhDialogue) + { + m_pInstance = (instance_karazhan*)pCreature->GetInstanceData(); + InitializeDialogueHelper(m_pInstance); + Reset(); + } + + instance_karazhan* m_pInstance; + + ObjectGuid m_eventStarterGuid; + + void Reset() override { } + + void AttackStart(Unit* /*pWho*/) override { } + void MoveInLineOfSight(Unit* /*pWho*/) override { } + + void JustSummoned(Creature* pSummoned) override + { + if (pSummoned->GetEntry() == NPC_IMAGE_OF_ARCANAGOS) + { + pSummoned->SetLevitate(true); + pSummoned->SetWalk(false); + pSummoned->GetMotionMaster()->MovePoint(POINT_ID_INTRO, afArcanagosMoveLoc[0], afArcanagosMoveLoc[1], afArcanagosMoveLoc[2]); + pSummoned->SetByteFlag(UNIT_FIELD_BYTES_1, 3, UNIT_BYTE1_FLAG_ALWAYS_STAND | UNIT_BYTE1_FLAG_UNK_2); + } + } + + void SummonedMovementInform(Creature* pSummoned, uint32 uiMotionType, uint32 uiPointId) override + { + if (uiMotionType != POINT_MOTION_TYPE || pSummoned->GetEntry() != NPC_IMAGE_OF_ARCANAGOS) + return; + + switch (uiPointId) + { + case POINT_ID_INTRO: + StartNextDialogueText(NPC_IMAGE_OF_MEDIVH); + break; + case POINT_ID_DESPAWN: + pSummoned->ForcedDespawn(); + m_creature->ForcedDespawn(10000); + m_creature->GetMotionMaster()->MovePoint(0, afMedivhExitLoc[0], afMedivhExitLoc[1], afMedivhExitLoc[2]); + // complete quest + if (Player* pPlayer = m_creature->GetMap()->GetPlayer(m_eventStarterGuid)) + pPlayer->GroupEventHappens(QUEST_MASTERS_TERRACE, m_creature); + break; + } + } + + void SpellHit(Unit* pCaster, const SpellEntry* pSpell) override + { + if (pSpell->Id == SPELL_FIREBALL_REFLECT && pCaster->GetEntry() == NPC_IMAGE_OF_ARCANAGOS) + { + StartNextDialogueText(SPELL_MANA_SHIELD); + DoCastSpellIfCan(m_creature, SPELL_MANA_SHIELD); + } + } + + void JustDidDialogueStep(int32 iEntry) override + { + if (!m_pInstance) + return; + + switch (iEntry) + { + case SAY_ARCANAGOS_6: + if (Creature* pDragon = m_pInstance->GetSingleCreatureFromStorage(NPC_IMAGE_OF_ARCANAGOS)) + DoCastSpellIfCan(pDragon, SPELL_FIREBALL); + break; + case EMOTE_CAST_SPELL: + DoCastSpellIfCan(m_creature, SPELL_EVOCATION); + break; + case SPELL_CONFLAG_BLAST: + m_creature->RemoveAurasDueToSpell(SPELL_EVOCATION); + if (Creature* pDragon = m_pInstance->GetSingleCreatureFromStorage(NPC_IMAGE_OF_ARCANAGOS)) + { + DoCastSpellIfCan(pDragon, SPELL_CONFLAG_BLAST, CAST_TRIGGERED); + pDragon->GetMotionMaster()->MovePoint(POINT_ID_DESPAWN, afArcanagosFleeLoc[0], afArcanagosFleeLoc[1], afArcanagosFleeLoc[2]); + } + break; + } + } + + void SetEventStarter(ObjectGuid m_starterGuid) { m_eventStarterGuid = m_starterGuid; } + + void UpdateAI(const uint32 uiDiff) { DialogueUpdate(uiDiff); } +}; + +CreatureAI* GetAI_npc_image_of_medivhAI(Creature* pCreature) +{ + return new npc_image_of_medivhAI(pCreature); +} + +/*###### +# npc_image_arcanagos +######*/ + +struct npc_image_arcanagosAI : public ScriptedAI +{ + npc_image_arcanagosAI(Creature* pCreature) : ScriptedAI(pCreature) { Reset(); } + + void Reset() override { } + + void SpellHit(Unit* pCaster, const SpellEntry* pSpell) override + { + if (pSpell->Id == SPELL_FIREBALL && pCaster->GetEntry() == NPC_IMAGE_OF_MEDIVH) + { + // !!!Workaround Alert!!! - the spell should be cast on Medivh without changing the unit_flags! + pCaster->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE); + + DoCastSpellIfCan(pCaster, SPELL_FIREBALL_REFLECT, CAST_TRIGGERED); + DoCastSpellIfCan(m_creature, SPELL_REFLECTION, CAST_TRIGGERED); + + pCaster->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE); + } + } + + void AttackStart(Unit* /*pWho*/) override { } + void MoveInLineOfSight(Unit* /*pWho*/) override { } + void UpdateAI(const uint32 /*uiDiff*/) override { } +}; + +CreatureAI* GetAI_npc_image_arcanagosAI(Creature* pCreature) +{ + return new npc_image_arcanagosAI(pCreature); +} + +/*###### +# event_spell_medivh_journal +######*/ + +bool ProcessEventId_event_spell_medivh_journal(uint32 /*uiEventId*/, Object* pSource, Object* /*pTarget*/, bool bIsStart) +{ + if (bIsStart && pSource->GetTypeId() == TYPEID_PLAYER) + { + // Summon Medivh and Arcanagos + if (Creature* pMedivh = ((Player*)pSource)->SummonCreature(NPC_IMAGE_OF_MEDIVH, afMedivhSpawnLoc[0], afMedivhSpawnLoc[1], afMedivhSpawnLoc[2], afMedivhSpawnLoc[3], TEMPSUMMON_DEAD_DESPAWN, 0)) + { + pMedivh->SummonCreature(NPC_IMAGE_OF_ARCANAGOS, afArcanagosSpawnLoc[0], afArcanagosSpawnLoc[1], afArcanagosSpawnLoc[2], afArcanagosSpawnLoc[3], TEMPSUMMON_DEAD_DESPAWN, 0); + + // store the player who started the event + if (npc_image_of_medivhAI* pMedivhAI = dynamic_cast(pMedivh->AI())) + pMedivhAI->SetEventStarter(pSource->GetObjectGuid()); + } + } + + return true; +} + +void AddSC_karazhan() +{ + Script* pNewScript; + + pNewScript = new Script; + pNewScript->Name = "npc_barnes"; + pNewScript->GetAI = &GetAI_npc_barnesAI; + pNewScript->pGossipHello = &GossipHello_npc_barnes; + pNewScript->pGossipSelect = &GossipSelect_npc_barnes; + pNewScript->RegisterSelf(); + + pNewScript = new Script; + pNewScript->Name = "npc_berthold"; + pNewScript->pGossipHello = &GossipHello_npc_berthold; + pNewScript->pGossipSelect = &GossipSelect_npc_berthold; + pNewScript->RegisterSelf(); + + pNewScript = new Script; + pNewScript->Name = "npc_image_of_medivh"; + pNewScript->GetAI = &GetAI_npc_image_of_medivhAI; + pNewScript->RegisterSelf(); + + pNewScript = new Script; + pNewScript->Name = "npc_image_arcanagos"; + pNewScript->GetAI = &GetAI_npc_image_arcanagosAI; + pNewScript->RegisterSelf(); + + pNewScript = new Script; + pNewScript->Name = "event_spell_medivh_journal"; + pNewScript->pProcessEventId = &ProcessEventId_event_spell_medivh_journal; + pNewScript->RegisterSelf(); +} diff --git a/src/modules/SD2/scripts/eastern_kingdoms/karazhan/karazhan.h b/src/modules/SD2/scripts/eastern_kingdoms/karazhan/karazhan.h new file mode 100644 index 000000000..86da9ba44 --- /dev/null +++ b/src/modules/SD2/scripts/eastern_kingdoms/karazhan/karazhan.h @@ -0,0 +1,205 @@ +/* Copyright (C) 2006 - 2013 ScriptDev2 + * This program is free software licensed under GPL version 2 + * Please see the included DOCS/LICENSE.TXT for more information */ + +#ifndef DEF_KARAZHAN_H +#define DEF_KARAZHAN_H + +enum +{ + MAX_ENCOUNTER = 11, + MAX_OZ_OPERA_MOBS = 4, + + TYPE_ATTUMEN = 0, + TYPE_MOROES = 1, + TYPE_MAIDEN = 2, + TYPE_OPERA = 3, + TYPE_CURATOR = 4, + TYPE_TERESTIAN = 5, + TYPE_ARAN = 6, + TYPE_NETHERSPITE = 7, + TYPE_CHESS = 8, + TYPE_MALCHEZZAR = 9, + TYPE_NIGHTBANE = 10, + TYPE_OPERA_PERFORMANCE = 11, // no regular encounter - just store one random opera event + + NPC_ATTUMEN = 15550, + NPC_MIDNIGHT = 16151, + NPC_MOROES = 15687, + NPC_BARNES = 16812, + // NPC_TERESTIAN = 15688, + NPC_NIGHTBANE = 17225, + NPC_NIGHTBANE_HELPER = 17260, + NPC_NETHERSPITE = 15689, + NPC_ECHO_MEDIVH = 16816, + NPC_INVISIBLE_STALKER = 22519, // placeholder for dead chess npcs + NPC_CHESS_STATUS_BAR = 22520, // npc that controlls the transformation of dead pieces + NPC_CHESS_VICTORY_CONTROLLER = 22524, + // NPC_CHESS_SOUND_BUNNY = 21921, // npc that handles the encounter sounds + // NPC_WAITING_ROOM_STALKER = 17459, // trigger which marks the teleport location of the players; also used to cast some control spells during the game + NPC_SQUARE_WHITE = 17208, // chess white square + NPC_SQUARE_BLACK = 17305, // chess black square + // NPC_SQUARE_OUTSIDE_BLACK = 17316, // outside chess black square + // NPC_SQUARE_OUTSIDE_WHITE = 17317, // outside chess white square + + // Moroes event related + NPC_LADY_KEIRA_BERRYBUCK = 17007, + NPC_LADY_CATRIONA_VON_INDI = 19872, + NPC_LORD_CRISPIN_FERENCE = 19873, + NPC_BARON_RAFE_DREUGER = 19874, + NPC_BARONESS_DOROTHEA_MILLSTIPE = 19875, + NPC_LORD_ROBIN_DARIS = 19876, + + // Opera event + NPC_DOROTHEE = 17535, + NPC_ROAR = 17546, + NPC_TINHEAD = 17547, + NPC_STRAWMAN = 17543, + NPC_CRONE = 18168, + NPC_GRANDMOTHER = 17603, + NPC_JULIANNE = 17534, + NPC_ROMULO = 17533, + + // The Master's Terrace quest related + NPC_IMAGE_OF_MEDIVH = 17651, + NPC_IMAGE_OF_ARCANAGOS = 17652, + + // Chess event + NPC_ORC_GRUNT = 17469, // pawn + NPC_ORC_WOLF = 21748, // knight + NPC_ORC_WARLOCK = 21750, // queen + NPC_ORC_NECROLYTE = 21747, // bishop + NPC_SUMMONED_DAEMON = 21726, // rook + NPC_WARCHIEF_BLACKHAND = 21752, // king + NPC_HUMAN_FOOTMAN = 17211, // pawn + NPC_HUMAN_CHARGER = 21664, // knight + NPC_HUMAN_CONJURER = 21683, // queen + NPC_HUMAN_CLERIC = 21682, // bishop + NPC_CONJURED_WATER_ELEMENTAL = 21160, // rook + NPC_KING_LLANE = 21684, // king + + GO_STAGE_CURTAIN = 183932, + GO_STAGE_DOOR_LEFT = 184278, + GO_STAGE_DOOR_RIGHT = 184279, + GO_PRIVATE_LIBRARY_DOOR = 184517, + GO_MASSIVE_DOOR = 185521, + GO_GAMESMANS_HALL_DOOR = 184276, + GO_GAMESMANS_HALL_EXIT_DOOR = 184277, + GO_NETHERSPACE_DOOR = 185134, + GO_SIDE_ENTRANCE_DOOR = 184275, + GO_DUST_COVERED_CHEST = 185119, + GO_MASTERS_TERRACE_DOOR_1 = 184274, + GO_MASTERS_TERRACE_DOOR_2 = 184280, + + // Opera event stage decoration + GO_OZ_BACKDROP = 183442, + GO_OZ_HAY = 183496, + GO_HOOD_BACKDROP = 183491, + GO_HOOD_TREE = 183492, + GO_HOOD_HOUSE = 183493, + GO_RAJ_BACKDROP = 183443, + GO_RAJ_MOON = 183494, + GO_RAJ_BALCONY = 183495, + + // Chess event spells + SPELL_CLEAR_BOARD = 37366, // spell cast to clear the board at the end of the event + SPELL_GAME_IN_SESSION = 39331, // debuff on players received while the game is in session + SPELL_FORCE_KILL_BUNNY = 45260, // triggers 45259 + SPELL_GAME_OVER = 39401, // cast by Medivh on game end + SPELL_VICTORY_VISUAL = 39395, // cast by the Victory controller on game end + + FACTION_ID_CHESS_HORDE = 1689, + FACTION_ID_CHESS_ALLIANCE = 1690, +}; + +enum OperaEvents +{ + OPERA_EVENT_WIZARD_OZ = 1, + OPERA_EVENT_RED_RIDING_HOOD = 2, + OPERA_EVENT_ROMULO_AND_JUL = 3 +}; + +struct OperaSpawns +{ + uint32 uiEntry; + float fX, fY, fZ, fO; +}; + +static const OperaSpawns aOperaLocOz[MAX_OZ_OPERA_MOBS] = +{ + {NPC_DOROTHEE, -10896.65f, -1757.62f, 90.55f, 4.86f}, + {NPC_ROAR, -10889.53f, -1758.10f, 90.55f, 4.57f}, + {NPC_TINHEAD, -10883.84f, -1758.85f, 90.55f, 4.53f}, + {NPC_STRAWMAN, -10902.11f, -1756.45f, 90.55f, 4.66f}, +}; + +static const OperaSpawns aOperaLocWolf = {NPC_GRANDMOTHER, -10892.01f, -1758.01f, 90.55f, 4.73f}; +static const OperaSpawns aOperaLocJul = {NPC_JULIANNE, -10893.56f, -1760.43f, 90.55f, 4.55f}; + +static const float afChroneSpawnLoc[4] = { -10893.11f, -1757.85f, 90.55f, 4.60f}; + +class instance_karazhan : public ScriptedInstance +{ + public: + instance_karazhan(Map* pMap); + ~instance_karazhan() {} + + void Initialize() override; + bool IsEncounterInProgress() const override; + + void OnPlayerEnter(Player* pPlayer) override; + void OnCreatureCreate(Creature* pCreature) override; + void OnObjectCreate(GameObject* pGo) override; + + void OnCreatureDeath(Creature* pCreature) override; + + void SetData(uint32 uiType, uint32 uiData) override; + uint32 GetData(uint32 uiType) const override; + + void DoPrepareOperaStage(Creature* pOrganizer); + + uint32 GetPlayerTeam() { return m_uiTeam; } + bool IsFriendlyGameReady() { return m_bFriendlyGame; } + void DoMoveChessPieceToSides(uint32 uiSpellId, uint32 uiFaction, bool bGameEnd = false); + void GetChessPiecesByFaction(GuidList& lList, uint32 uiFaction) { lList = uiFaction == FACTION_ID_CHESS_ALLIANCE ? m_lChessPiecesAlliance : m_lChessPiecesHorde; } + + void GetNightbaneTriggers(GuidList& lList, bool bGround) { lList = bGround ? m_lNightbaneGroundTriggers : m_lNightbaneAirTriggers; } + + void Load(const char* chrIn) override; + const char* Save() const override { return m_strInstData.c_str(); } + + void Update(uint32 uiDiff) override; + + private: + void DoPrepareChessEvent(); + + uint32 m_auiEncounter[MAX_ENCOUNTER]; + std::string m_strInstData; + + uint32 m_uiOperaEvent; + uint32 m_uiOzDeathCount; + uint32 m_uiTeam; // Team of first entered player, used for the Chess event + uint32 m_uiChessResetTimer; + + uint8 m_uiAllianceStalkerCount; + uint8 m_uiHordeStalkerCount; + + bool m_bFriendlyGame; + + ObjectGuid m_HordeStatusGuid; + ObjectGuid m_AllianceStatusGuid; + + GuidList m_lOperaTreeGuidList; + GuidList m_lOperaHayGuidList; + GuidList m_lNightbaneGroundTriggers; + GuidList m_lNightbaneAirTriggers; + + GuidList m_lChessHordeStalkerList; + GuidList m_lChessAllianceStalkerList; + GuidList m_lChessPiecesAlliance; + GuidList m_lChessPiecesHorde; + GuidVector m_vHordeStalkers; + GuidVector m_vAllianceStalkers; +}; + +#endif diff --git a/src/modules/SD2/scripts/eastern_kingdoms/loch_modan.cpp b/src/modules/SD2/scripts/eastern_kingdoms/loch_modan.cpp new file mode 100644 index 000000000..00c3fed5e --- /dev/null +++ b/src/modules/SD2/scripts/eastern_kingdoms/loch_modan.cpp @@ -0,0 +1,222 @@ +/** + * ScriptDev2 is an extension for mangos providing enhanced features for + * area triggers, creatures, game objects, instances, items, and spells beyond + * the default database scripting in mangos. + * + * Copyright (C) 2006-2013 ScriptDev2 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * World of Warcraft, and all World of Warcraft or Warcraft art, images, + * and lore are copyrighted by Blizzard Entertainment, Inc. + */ + +/** + * ScriptData + * SDName: Loch_Modan + * SD%Complete: 100 + * SDComment: Quest support: 309, 3181 (only to argue with pebblebitty to get to searing gorge, before quest rewarded). + * SDCategory: Loch Modan + * EndScriptData + */ + +/** + * ContentData + * npc_mountaineer_pebblebitty + * npc_miran + * EndContentData + */ + +#include "precompiled.h" +#include "escort_ai.h" + +/*###### +## npc_mountaineer_pebblebitty +######*/ + +bool GossipHello_npc_mountaineer_pebblebitty(Player* pPlayer, Creature* pCreature) +{ + if (pCreature->IsQuestGiver()) + { + pPlayer->PrepareQuestMenu(pCreature->GetObjectGuid()); + } + + if (!pPlayer->GetQuestRewardStatus(3181) == 1) + { + pPlayer->ADD_GOSSIP_ITEM(GOSSIP_ICON_CHAT, "Open the gate please, i need to get to Searing Gorge", GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 1); + } + + pPlayer->SEND_GOSSIP_MENU(pPlayer->GetGossipTextId(pCreature), pCreature->GetObjectGuid()); + + return true; +} + +bool GossipSelect_npc_mountaineer_pebblebitty(Player* pPlayer, Creature* pCreature, uint32 /*uiSender*/, uint32 uiAction) +{ + switch (uiAction) + { + case GOSSIP_ACTION_INFO_DEF+1: + pPlayer->ADD_GOSSIP_ITEM(GOSSIP_ICON_CHAT, "But i need to get there, now open the gate!", GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 2); + pPlayer->SEND_GOSSIP_MENU(1833, pCreature->GetObjectGuid()); + break; + case GOSSIP_ACTION_INFO_DEF+2: + pPlayer->ADD_GOSSIP_ITEM(GOSSIP_ICON_CHAT, "Ok, so what is this other way?", GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 3); + pPlayer->SEND_GOSSIP_MENU(1834, pCreature->GetObjectGuid()); + break; + case GOSSIP_ACTION_INFO_DEF+3: + pPlayer->ADD_GOSSIP_ITEM(GOSSIP_ICON_CHAT, "Doesn't matter, i'm invulnerable.", GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 4); + pPlayer->SEND_GOSSIP_MENU(1835, pCreature->GetObjectGuid()); + break; + case GOSSIP_ACTION_INFO_DEF+4: + pPlayer->ADD_GOSSIP_ITEM(GOSSIP_ICON_CHAT, "Yes...", GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 5); + pPlayer->SEND_GOSSIP_MENU(1836, pCreature->GetObjectGuid()); + break; + case GOSSIP_ACTION_INFO_DEF+5: + pPlayer->ADD_GOSSIP_ITEM(GOSSIP_ICON_CHAT, "Ok, i'll try to remember that.", GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 6); + pPlayer->SEND_GOSSIP_MENU(1837, pCreature->GetObjectGuid()); + break; + case GOSSIP_ACTION_INFO_DEF+6: + pPlayer->ADD_GOSSIP_ITEM(GOSSIP_ICON_CHAT, "A key? Ok!", GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 7); + pPlayer->SEND_GOSSIP_MENU(1838, pCreature->GetObjectGuid()); + break; + case GOSSIP_ACTION_INFO_DEF+7: + pPlayer->CLOSE_GOSSIP_MENU(); + break; + } + return true; +} + +/*###### +## npc_miran +######*/ + +enum +{ + QUEST_PROTECTING_THE_SHIPMENT = 309, + + SAY_MIRAN_1 = -1000571, + SAY_DARK_IRON_DWARF = -1000572, + SAY_MIRAN_2 = -1000573, + SAY_MIRAN_3 = -1000574, + + NPC_DARK_IRON_DWARF = 2149 +}; + +struct Location +{ + float m_fX, m_fY, m_fZ, m_fO; +}; + +static const Location m_afAmbushSpawn[] = +{ + { -5691.93f, -3745.91f, 319.159f, 2.21f}, + { -5706.98f, -3745.39f, 318.728f, 1.04f} +}; + +struct npc_miranAI: public npc_escortAI +{ + npc_miranAI(Creature* pCreature): npc_escortAI(pCreature) + { + Reset(); + } + + uint8 m_uiDwarves; + + void Reset() override + { + if (!HasEscortState(STATE_ESCORT_ESCORTING)) + { + m_uiDwarves = 0; + } + } + + void WaypointReached(uint32 uiPointId) override + { + switch (uiPointId) + { + case 19: + DoScriptText(SAY_MIRAN_1, m_creature); + m_creature->SummonCreature(NPC_DARK_IRON_DWARF, m_afAmbushSpawn[0].m_fX, m_afAmbushSpawn[0].m_fY, m_afAmbushSpawn[0].m_fZ, m_afAmbushSpawn[0].m_fO, TEMPSUMMON_CORPSE_TIMED_DESPAWN, 25000); + m_creature->SummonCreature(NPC_DARK_IRON_DWARF, m_afAmbushSpawn[1].m_fX, m_afAmbushSpawn[1].m_fY, m_afAmbushSpawn[1].m_fZ, m_afAmbushSpawn[1].m_fO, TEMPSUMMON_CORPSE_TIMED_DESPAWN, 25000); + break; + case 23: + DoScriptText(SAY_MIRAN_3, m_creature); + if (Player* pPlayer = GetPlayerForEscort()) + { + pPlayer->GroupEventHappens(QUEST_PROTECTING_THE_SHIPMENT, m_creature); + } + break; + } + } + + void SummonedCreatureJustDied(Creature* pSummoned) override + { + if (pSummoned->GetEntry() == NPC_DARK_IRON_DWARF) + { + --m_uiDwarves; + if (!m_uiDwarves) + { + DoScriptText(SAY_MIRAN_2, m_creature); + } + } + } + + void JustSummoned(Creature* pSummoned) override + { + if (pSummoned->GetEntry() == NPC_DARK_IRON_DWARF) + { + if (!m_uiDwarves) + { + DoScriptText(SAY_DARK_IRON_DWARF, pSummoned); + } + ++m_uiDwarves; + pSummoned->AI()->AttackStart(m_creature); + } + } +}; + +bool QuestAccept_npc_miran(Player* pPlayer, Creature* pCreature, const Quest* pQuest) +{ + if (pQuest->GetQuestId() == QUEST_PROTECTING_THE_SHIPMENT) + { + if (npc_miranAI* pEscortAI = dynamic_cast(pCreature->AI())) + { + pEscortAI->Start(false, pPlayer, pQuest); + } + } + return true; +} + +CreatureAI* GetAI_npc_miran(Creature* pCreature) +{ + return new npc_miranAI(pCreature); +} + +void AddSC_loch_modan() +{ + Script* pNewScript; + + pNewScript = new Script; + pNewScript->Name = "npc_mountaineer_pebblebitty"; + pNewScript->pGossipHello = &GossipHello_npc_mountaineer_pebblebitty; + pNewScript->pGossipSelect = &GossipSelect_npc_mountaineer_pebblebitty; + pNewScript->RegisterSelf(); + + pNewScript = new Script; + pNewScript->Name = "npc_miran"; + pNewScript->GetAI = &GetAI_npc_miran; + pNewScript->pQuestAcceptNPC = &QuestAccept_npc_miran; + pNewScript->RegisterSelf(); +} diff --git a/src/modules/SD2/scripts/eastern_kingdoms/magisters_terrace/boss_felblood_kaelthas.cpp b/src/modules/SD2/scripts/eastern_kingdoms/magisters_terrace/boss_felblood_kaelthas.cpp new file mode 100644 index 000000000..cb9cca7a2 --- /dev/null +++ b/src/modules/SD2/scripts/eastern_kingdoms/magisters_terrace/boss_felblood_kaelthas.cpp @@ -0,0 +1,676 @@ +/* Copyright (C) 2006 - 2013 ScriptDev2 + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with 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: 90 +SDComment: Minor adjustments required; Timers. +SDCategory: Magisters' Terrace +EndScriptData */ + +#include "precompiled.h" +#include "magisters_terrace.h" + +enum +{ + SAY_INTRO_1 = -1585023, // This yell should be done when the room is cleared. For now, set it as a movelineofsight yell. + SAY_INTRO_2 = -1585030, + SAY_PHOENIX = -1585024, + SAY_FLAMESTRIKE = -1585025, + SAY_GRAVITY_LAPSE = -1585026, + SAY_TIRED = -1585027, + SAY_RECAST_GRAVITY = -1585028, + SAY_DEATH = -1585029, + + // Phase 1 spells + SPELL_FIREBALL = 44189, // Deals 2700-3300 damage at current target + SPELL_FIREBALL_H = 46164, // 4950-6050 + SPELL_PHOENIX = 44194, // Summons a phoenix + SPELL_FLAME_STRIKE = 44192, // Summons the trigger + animation (projectile) + SPELL_SHOCK_BARRIER = 46165, // Heroic only; 10k damage shield, followed by Pyroblast + SPELL_PYROBLAST = 36819, // Heroic only; 45-55k fire damage + + // Phase 2 spells + SPELL_GRAVITY_LAPSE = 44224, // Cast at the beginning of every Gravity Lapse + SPELL_GRAVITY_LAPSE_VISUAL = 44251, // Channeled; blue beam animation to every enemy in range - when removed the Gravity Lapse auras are removed from players + SPELL_TELEPORT_CENTER = 44218, // Teleport the boss in the center. Requires DB entry in spell_target_position. + SPELL_GRAVITY_LAPSE_FLY = 44227, // Hastens flyspeed and allows flying for 1 minute. Requires aura stacking exception for 44226. + SPELL_GRAVITY_LAPSE_DOT = 44226, // Knocks up in the air and applies a 300 DPS DoT. + SPELL_ARCANE_SPHERE_SUMMON = 44265, // Summons 1 arcane sphere + SPELL_POWER_FEEDBACK = 44233, // Stuns him, making him take 50% more damage for 10 seconds. Cast after Gravity Lapse + + // Summoned spells + SPELL_ARCANE_SPHERE_PASSIVE = 44263, // Passive auras on Arcane Spheres + SPELL_FLAME_STRIKE_DUMMY = 44191, // Flamestrike indicator before the damage + SPELL_EMBER_BLAST = 44199, // On Phoenix death + SPELL_PHOENIX_BURN = 44197, // A spell Phoenix uses to damage everything around + SPELL_REBIRTH_DMG = 44196, // DMG if a Phoenix rebirth happen + + // Summoned creatures + NPC_FLAME_STRIKE_TRIGGER = 24666, + NPC_PHOENIX = 24674, + NPC_PHOENIX_EGG = 24675, + NPC_ARCANE_SPHERE = 24708, + + MAX_ARCANE_SPHERES = 3, +}; + +static const DialogueEntry aIntroDialogue[] = +{ + {SAY_INTRO_1, NPC_KAELTHAS, 16000}, + {EMOTE_ONESHOT_LAUGH, 0, 2000}, + {EMOTE_STATE_TALK, 0, 2000}, + {SAY_INTRO_2, NPC_KAELTHAS, 16000}, + {NPC_PHOENIX, 0, 0}, + {SAY_DEATH, NPC_KAELTHAS, 4000}, + {EMOTE_ONESHOT_POINT, 0, 5000}, + {EMOTE_ONESHOT_ROAR, 0, 3000}, + {NPC_PHOENIX_EGG, 0, 0}, + {0, 0, 0}, +}; + +// Spells used to teleport players for Gravity Lapse +static const uint32 aGravityLapseSpells[] = {44219, 44220, 44221, 44222, 44223}; + +/*###### +## boss_felblood_kaelthas +######*/ + +struct boss_felblood_kaelthasAI : public ScriptedAI, private DialogueHelper +{ + boss_felblood_kaelthasAI(Creature* pCreature) : ScriptedAI(pCreature), + DialogueHelper(aIntroDialogue) + { + m_pInstance = (ScriptedInstance*)pCreature->GetInstanceData(); + m_bIsRegularMode = pCreature->GetMap()->IsRegularDifficulty(); + InitializeDialogueHelper(m_pInstance); + m_bHasTaunted = false; + Reset(); + } + + ScriptedInstance* m_pInstance; + bool m_bIsRegularMode; + + uint32 m_uiFireballTimer; + uint32 m_uiPhoenixTimer; + uint32 m_uiFlameStrikeTimer; + + // Heroic only + uint32 m_uiShockBarrierTimer; + uint32 m_uiPyroblastTimer; + + uint32 m_uiGravityLapseTimer; + uint32 m_uiGravityLapseStage; + uint8 m_uiGravityIndex; + + bool m_bIsFirstPhase; + bool m_bFirstGravityLapse; + bool m_bHasTaunted; + + void Reset() override + { + m_uiFireballTimer = 0; + m_uiPhoenixTimer = 10000; + m_uiFlameStrikeTimer = 25000; + + m_uiPyroblastTimer = 0; + m_uiShockBarrierTimer = 60000; + + m_uiGravityLapseTimer = 1000; + m_uiGravityLapseStage = 0; + m_uiGravityIndex = 0; + + m_bFirstGravityLapse = true; + m_bIsFirstPhase = true; + + SetCombatMovement(true); + } + + void JustDied(Unit* /*pKiller*/) override + { + if (m_pInstance) + m_pInstance->SetData(TYPE_KAELTHAS, DONE); + } + + void Aggro(Unit* /*pWho*/) override + { + if (m_pInstance) + m_pInstance->SetData(TYPE_KAELTHAS, IN_PROGRESS); + } + + void JustReachedHome() override + { + if (m_pInstance) + m_pInstance->SetData(TYPE_KAELTHAS, FAIL); + } + + // Boss has an interesting speech before killed, so we need to fake death (without stand state) and allow him to finish his theatre + void DamageTaken(Unit* /*pKiller*/, uint32& uiDamage) override + { + if (uiDamage < m_creature->GetHealth()) + return; + + // Make sure it won't die by accident + if (m_creature->HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE)) + { + uiDamage = 0; + return; + } + + uiDamage = 0; + RemoveGravityLapse(); + StartNextDialogueText(SAY_DEATH); + m_creature->HandleEmote(EMOTE_STATE_TALK); + + m_creature->InterruptNonMeleeSpells(true); + m_creature->SetHealth(1); + m_creature->StopMoving(); + m_creature->ClearComboPointHolders(); + m_creature->RemoveAllAurasOnDeath(); + m_creature->ModifyAuraState(AURA_STATE_HEALTHLESS_20_PERCENT, false); + m_creature->ModifyAuraState(AURA_STATE_HEALTHLESS_35_PERCENT, false); + m_creature->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE); + m_creature->ClearAllReactives(); + m_creature->GetMotionMaster()->Clear(); + m_creature->GetMotionMaster()->MoveIdle(); + } + + void MoveInLineOfSight(Unit* pWho) override + { + if (!m_bHasTaunted && pWho->GetTypeId() == TYPEID_PLAYER && !((Player*)pWho)->isGameMaster() && + m_creature->IsWithinDistInMap(pWho, 40.0) && m_creature->IsWithinLOSInMap(pWho)) + { + StartNextDialogueText(SAY_INTRO_1); + m_creature->HandleEmote(EMOTE_STATE_TALK); + m_creature->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE); + m_bHasTaunted = true; + } + + // Allow him to finish intro + if (m_creature->HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE | UNIT_FLAG_NOT_SELECTABLE)) + return; + + ScriptedAI::MoveInLineOfSight(pWho); + } + + void JustDidDialogueStep(int32 iEntry) override + { + switch (iEntry) + { + case EMOTE_ONESHOT_LAUGH: + m_creature->HandleEmote(EMOTE_ONESHOT_LAUGH); + break; + case EMOTE_STATE_TALK: + m_creature->HandleEmote(EMOTE_STATE_TALK); + break; + case NPC_PHOENIX: + m_creature->HandleEmote(EMOTE_STATE_NONE); + m_creature->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE); + break; + case EMOTE_ONESHOT_POINT: + m_creature->HandleEmote(EMOTE_ONESHOT_POINT); + break; + case EMOTE_ONESHOT_ROAR: + m_creature->HandleEmote(EMOTE_ONESHOT_ROAR); + break; + case NPC_PHOENIX_EGG: + m_creature->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE); + m_creature->DealDamage(m_creature, m_creature->GetHealth(), NULL, DIRECT_DAMAGE, SPELL_SCHOOL_MASK_NORMAL, NULL, false); + break; + } + } + + void AttackStart(Unit* pWho) override + { + if (m_creature->Attack(pWho, true)) + { + m_creature->AddThreat(pWho); + m_creature->SetInCombatWith(pWho); + pWho->SetInCombatWith(m_creature); + DoStartMovement(pWho, 20.0f); + } + } + + void JustSummoned(Creature* pSummoned) override + { + if (pSummoned->GetEntry() == NPC_FLAME_STRIKE_TRIGGER) + pSummoned->CastSpell(pSummoned, SPELL_FLAME_STRIKE_DUMMY, false, NULL, NULL, m_creature->GetObjectGuid()); + else + { + // Attack or follow target + if (Unit* pTarget = m_creature->SelectAttackingTarget(ATTACKING_TARGET_RANDOM, 0)) + { + if (pSummoned->GetEntry() == NPC_ARCANE_SPHERE) + pSummoned->GetMotionMaster()->MoveFollow(pTarget, 0, 0); + else + pSummoned->AI()->AttackStart(pTarget); + } + } + } + + void SpellHitTarget(Unit* pTarget, const SpellEntry* pSpell) override + { + // Handle Gravity Lapse on targets + if (pSpell->Id == SPELL_GRAVITY_LAPSE && pTarget->GetTypeId() == TYPEID_PLAYER) + { + DoCastSpellIfCan(pTarget, aGravityLapseSpells[m_uiGravityIndex], CAST_TRIGGERED); + pTarget->CastSpell(pTarget, SPELL_GRAVITY_LAPSE_FLY, true, 0, 0, m_creature->GetObjectGuid()); + pTarget->CastSpell(pTarget, SPELL_GRAVITY_LAPSE_DOT, true, 0, 0, m_creature->GetObjectGuid()); + ++m_uiGravityIndex; + } + } + + // Wrapper to remove Gravity Lapse - this should be removed on aura 44251 expires + void RemoveGravityLapse() + { + GuidVector vGuids; + m_creature->FillGuidsListFromThreatList(vGuids); + + for (GuidVector::const_iterator itr = vGuids.begin(); itr != vGuids.end(); ++itr) + { + Unit* pUnit = m_creature->GetMap()->GetUnit(*itr); + + if (pUnit && pUnit->GetTypeId() == TYPEID_PLAYER) + { + pUnit->RemoveAurasDueToSpell(SPELL_GRAVITY_LAPSE_FLY); + pUnit->RemoveAurasDueToSpell(SPELL_GRAVITY_LAPSE_DOT); + } + } + } + + void UpdateAI(const uint32 uiDiff) override + { + DialogueUpdate(uiDiff); + + if (!m_creature->SelectHostileTarget() || !m_creature->getVictim()) + return; + + // Don't use spells during the epilogue + if (m_creature->HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE)) + return; + + if (m_bIsFirstPhase) + { + // *Heroic mode only: + if (!m_bIsRegularMode) + { + if (m_uiShockBarrierTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature, SPELL_SHOCK_BARRIER) == CAST_OK) + { + m_uiPyroblastTimer = 1000; + m_uiShockBarrierTimer = 60000; + } + } + else + m_uiShockBarrierTimer -= uiDiff; + + if (m_uiPyroblastTimer) + { + if (m_uiPyroblastTimer <= uiDiff) + { + if (DoCastSpellIfCan(m_creature, SPELL_PYROBLAST) == CAST_OK) + m_uiPyroblastTimer = 0; + } + else + m_uiPyroblastTimer -= uiDiff; + } + } + + if (m_uiFireballTimer < uiDiff) + { + if (Unit* pTarget = m_creature->SelectAttackingTarget(ATTACKING_TARGET_RANDOM, 0)) + { + if (DoCastSpellIfCan(pTarget, m_bIsRegularMode ? SPELL_FIREBALL : SPELL_FIREBALL_H) == CAST_OK) + m_uiFireballTimer = urand(2000, 4000); + } + } + else + m_uiFireballTimer -= uiDiff; + + if (m_uiPhoenixTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature, SPELL_PHOENIX) == CAST_OK) + { + DoScriptText(SAY_PHOENIX, m_creature); + m_uiPhoenixTimer = 45000; + } + } + else + m_uiPhoenixTimer -= uiDiff; + + if (m_uiFlameStrikeTimer < uiDiff) + { + if (Unit* pTarget = m_creature->SelectAttackingTarget(ATTACKING_TARGET_RANDOM, 0)) + { + if (DoCastSpellIfCan(pTarget, SPELL_FLAME_STRIKE) == CAST_OK) + { + DoScriptText(SAY_FLAMESTRIKE, m_creature); + m_uiFlameStrikeTimer = urand(15000, 25000); + } + } + } + else + m_uiFlameStrikeTimer -= uiDiff; + + // Below 50% + if (m_creature->GetHealthPercent() < 50.0f) + { + if (DoCastSpellIfCan(m_creature, SPELL_TELEPORT_CENTER, CAST_INTERRUPT_PREVIOUS) == CAST_OK) + { + SetCombatMovement(false); + m_creature->GetMotionMaster()->Clear(); + m_creature->GetMotionMaster()->MoveIdle(); + + m_bIsFirstPhase = false; + } + } + + DoMeleeAttackIfReady(); + } + else + { + if (m_uiGravityLapseTimer < uiDiff) + { + switch (m_uiGravityLapseStage) + { + case 0: + // Cast Gravity Lapse on Players + if (DoCastSpellIfCan(m_creature, SPELL_GRAVITY_LAPSE) == CAST_OK) + { + if (m_bFirstGravityLapse) + { + DoScriptText(SAY_GRAVITY_LAPSE, m_creature); + m_bFirstGravityLapse = false; + } + else + DoScriptText(SAY_RECAST_GRAVITY, m_creature); + + m_uiGravityLapseTimer = 2000; + m_uiGravityIndex = 0; + ++m_uiGravityLapseStage; + } + break; + case 1: + // Summon spheres and apply the Gravity Lapse visual - upon visual expire, the gravity lapse is removed + if (DoCastSpellIfCan(m_creature, SPELL_GRAVITY_LAPSE_VISUAL) == CAST_OK) + { + for (uint8 i = 0; i < MAX_ARCANE_SPHERES; ++i) + DoCastSpellIfCan(m_creature, SPELL_ARCANE_SPHERE_SUMMON, CAST_TRIGGERED); + + m_uiGravityLapseTimer = 30000; + ++m_uiGravityLapseStage; + } + break; + case 2: + // Cast Power Feedback and stay stunned for 10 secs - also break the statues if they are not broken + if (DoCastSpellIfCan(m_creature, SPELL_POWER_FEEDBACK) == CAST_OK) + { + DoScriptText(SAY_TIRED, m_creature); + RemoveGravityLapse(); + m_uiGravityLapseTimer = 10000; + m_uiGravityLapseStage = 0; + } + break; + } + } + else + m_uiGravityLapseTimer -= uiDiff; + } + } +}; + +/*###### +## mob_felkael_phoenix +######*/ + +struct mob_felkael_phoenixAI : public ScriptedAI +{ + mob_felkael_phoenixAI(Creature* pCreature) : ScriptedAI(pCreature) { Reset(); } + + uint32 m_uiBurnTimer; + + bool m_bFakeDeath; + + void Reset() override + { + m_uiBurnTimer = 2000; + m_bFakeDeath = false; + } + + void Aggro(Unit* /*pWho*/) override + { + DoCastSpellIfCan(m_creature, SPELL_PHOENIX_BURN); + } + + void EnterEvadeMode() override + { + // Don't evade during ember blast + if (m_bFakeDeath) + return; + + ScriptedAI::EnterEvadeMode(); + } + + void DamageTaken(Unit* /*pKiller*/, uint32& uiDamage) override + { + if (uiDamage < m_creature->GetHealth()) + return; + + // Prevent glitch if in fake death + if (m_bFakeDeath) + { + uiDamage = 0; + return; + } + + // prevent death + uiDamage = 0; + DoSetFakeDeath(); + } + + void DoSetFakeDeath() + { + m_bFakeDeath = true; + + m_creature->InterruptNonMeleeSpells(false); + m_creature->SetHealth(1); + m_creature->StopMoving(); + m_creature->ClearComboPointHolders(); + m_creature->RemoveAllAurasOnDeath(); + m_creature->ModifyAuraState(AURA_STATE_HEALTHLESS_20_PERCENT, false); + m_creature->ModifyAuraState(AURA_STATE_HEALTHLESS_35_PERCENT, false); + m_creature->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE); + m_creature->ClearAllReactives(); + m_creature->SetTargetGuid(ObjectGuid()); + m_creature->GetMotionMaster()->Clear(); + m_creature->GetMotionMaster()->MoveIdle(); + m_creature->SetStandState(UNIT_STAND_STATE_DEAD); + + // Spawn egg and make invisible + DoCastSpellIfCan(m_creature, SPELL_EMBER_BLAST, CAST_TRIGGERED); + m_creature->SummonCreature(NPC_PHOENIX_EGG, 0, 0, 0, 0, TEMPSUMMON_TIMED_OOC_OR_DEAD_DESPAWN, 10000); + } + + void SummonedCreatureDespawn(Creature* /*pSummoned*/) override + { + m_creature->RemoveAurasDueToSpell(SPELL_EMBER_BLAST); + m_creature->SetStandState(UNIT_STAND_STATE_STAND); + m_creature->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE); + + // Remove fake death on egg despawn after 10 secs + if (DoCastSpellIfCan(m_creature, SPELL_REBIRTH_DMG) == CAST_OK) + { + m_creature->SetHealth(m_creature->GetMaxHealth()); + m_creature->GetMotionMaster()->Clear(); + DoStartMovement(m_creature->getVictim()); + m_bFakeDeath = false; + + DoCastSpellIfCan(m_creature, SPELL_PHOENIX_BURN, CAST_TRIGGERED); + } + } + + void SummonedCreatureJustDied(Creature* /*pSummoned*/) override + { + // Self kill if the egg is killed + if (m_bFakeDeath) + m_creature->DealDamage(m_creature, m_creature->GetHealth(), NULL, DIRECT_DAMAGE, SPELL_SCHOOL_MASK_NORMAL, NULL, false); + } + + void UpdateAI(const uint32 uiDiff) override + { + if (!m_creature->SelectHostileTarget() || !m_creature->getVictim()) + return; + + if (m_bFakeDeath) + return; + + // ToDo: research if this is correct and how can this be done by spell + if (m_uiBurnTimer < uiDiff) + { + // spell Burn should possible do this, but it doesn't, so do this for now. + uint32 uiDmg = urand(1650, 2050); + if (uiDmg > m_creature->GetHealth()) + DoSetFakeDeath(); + else + m_creature->DealDamage(m_creature, uiDmg, 0, DOT, SPELL_SCHOOL_MASK_FIRE, NULL, false); + + m_uiBurnTimer = 2000; + } + else + m_uiBurnTimer -= uiDiff; + + DoMeleeAttackIfReady(); + } +}; + +/*###### +## mob_felkael_phoenix_egg +######*/ + +// TODO Remove this 'script' when combat movement can be proper prevented from core-side +struct mob_felkael_phoenix_eggAI : public Scripted_NoMovementAI +{ + mob_felkael_phoenix_eggAI(Creature* pCreature) : Scripted_NoMovementAI(pCreature) { Reset(); } + + void Reset() override {} + void MoveInLineOfSight(Unit* /*pWho*/) override {} + void AttackStart(Unit* /*pWho*/) override {} + void UpdateAI(const uint32 /*uiDiff*/) override {} +}; + +/*###### +## mob_arcane_sphere +######*/ + +struct mob_arcane_sphereAI : public ScriptedAI +{ + mob_arcane_sphereAI(Creature* pCreature) : ScriptedAI(pCreature) + { + m_pInstance = (ScriptedInstance*)pCreature->GetInstanceData(); + Reset(); + } + + ScriptedInstance* m_pInstance; + + uint32 m_uiDespawnTimer; + uint32 m_uiChangeTargetTimer; + + void Reset() override + { + m_uiDespawnTimer = 30000; + m_uiChangeTargetTimer = urand(6000, 12000); + + DoCastSpellIfCan(m_creature, SPELL_ARCANE_SPHERE_PASSIVE); + } + + void AttackStart(Unit* /*pWho*/) override { } + void MoveInLineOfSight(Unit* /*pWho*/) override { } + + void UpdateAI(const uint32 uiDiff) override + { + // Should despawn when aura 44251 expires + if (m_uiDespawnTimer < uiDiff) + { + m_creature->DealDamage(m_creature, m_creature->GetHealth(), NULL, DIRECT_DAMAGE, SPELL_SCHOOL_MASK_NORMAL, NULL, false); + m_uiDespawnTimer = 0; + } + else + m_uiDespawnTimer -= uiDiff; + + if (m_uiChangeTargetTimer < uiDiff) + { + if (!m_pInstance) + return; + + // Follow the target - do not attack + if (Creature* pKael = m_pInstance->GetSingleCreatureFromStorage(NPC_KAELTHAS)) + { + if (Unit* pTarget = pKael->SelectAttackingTarget(ATTACKING_TARGET_RANDOM, 0)) + m_creature->GetMotionMaster()->MoveFollow(pTarget, 0, 0); + } + + m_uiChangeTargetTimer = urand(5000, 15000); + } + else + m_uiChangeTargetTimer -= uiDiff; + } +}; + +CreatureAI* GetAI_boss_felblood_kaelthas(Creature* pCreature) +{ + return new boss_felblood_kaelthasAI(pCreature); +} + +CreatureAI* GetAI_mob_arcane_sphere(Creature* pCreature) +{ + return new mob_arcane_sphereAI(pCreature); +} + +CreatureAI* GetAI_mob_felkael_phoenix(Creature* pCreature) +{ + return new mob_felkael_phoenixAI(pCreature); +} + +CreatureAI* GetAI_mob_felkael_phoenix_egg(Creature* pCreature) +{ + return new mob_felkael_phoenix_eggAI(pCreature); +} + +void AddSC_boss_felblood_kaelthas() +{ + Script* pNewScript; + + pNewScript = new Script; + pNewScript->Name = "boss_felblood_kaelthas"; + pNewScript->GetAI = &GetAI_boss_felblood_kaelthas; + pNewScript->RegisterSelf(); + + pNewScript = new Script; + pNewScript->Name = "mob_arcane_sphere"; + pNewScript->GetAI = &GetAI_mob_arcane_sphere; + pNewScript->RegisterSelf(); + + pNewScript = new Script; + pNewScript->Name = "mob_felkael_phoenix"; + pNewScript->GetAI = &GetAI_mob_felkael_phoenix; + pNewScript->RegisterSelf(); + + pNewScript = new Script; + pNewScript->Name = "mob_felkael_phoenix_egg"; + pNewScript->GetAI = &GetAI_mob_felkael_phoenix_egg; + pNewScript->RegisterSelf(); +} diff --git a/src/modules/SD2/scripts/eastern_kingdoms/magisters_terrace/boss_priestess_delrissa.cpp b/src/modules/SD2/scripts/eastern_kingdoms/magisters_terrace/boss_priestess_delrissa.cpp new file mode 100644 index 000000000..adcecdf0d --- /dev/null +++ b/src/modules/SD2/scripts/eastern_kingdoms/magisters_terrace/boss_priestess_delrissa.cpp @@ -0,0 +1,1320 @@ +/* Copyright (C) 2006 - 2013 ScriptDev2 + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with 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: 90 +SDComment: Script handles Delrissa and her companions AI. They need special PvP-like behavior. Timers need adjustments +SDCategory: Magister's Terrace +EndScriptData */ + +#include "precompiled.h" +#include "magisters_terrace.h" + +enum +{ + SAY_AGGRO = -1585012, + SAY_DEATH = -1585022, + + SPELL_HEALING_POTION = 15503, + SPELL_DISPEL_MAGIC = 27609, + SPELL_MEDALLION = 46227, + SPELL_FLASH_HEAL = 17843, + SPELL_SHADOW_WORD_PAIN = 14032, + SPELL_SHADOW_WORD_PAIN_H = 15654, + SPELL_SCREAM = 27610, + SPELL_SHIELD = 44291, // maybe 44175? + SPELL_SHIELD_H = 46193, + SPELL_RENEW = 44174, + SPELL_RENEW_H = 46192, + + MAX_COMPANIONS = 8, +}; + +static const int32 aPlayerDeath[] = { -1585017, -1585018, -1585019, -1585020, -1585021}; +static const uint32 aDelrissaLackeys[MAX_COMPANIONS] = {NPC_KAGANI, NPC_ELLRYS, NPC_ERAMAS, NPC_YAZZAI, NPC_SALARIS, NPC_GARAXXAS, NPC_APOKO, NPC_ZELFAN}; + +static const float aLackeyLocations[MAX_DELRISSA_ADDS][4] = +{ + {123.77f, 17.6007f, -19.921f, 4.98f}, + {131.731f, 15.0827f, -19.921f, 4.98f}, + {121.563f, 15.6213f, -19.921f, 4.98f}, + {129.988f, 17.2355f, -19.921f, 4.98f}, +}; + +/*###### +## boss_priestess_delrissa +######*/ + +struct boss_priestess_delrissaAI : public ScriptedAI +{ + boss_priestess_delrissaAI(Creature* pCreature) : ScriptedAI(pCreature) + { + m_pInstance = (ScriptedInstance*)pCreature->GetInstanceData(); + m_bIsRegularMode = pCreature->GetMap()->IsRegularDifficulty(); + Reset(); + } + + ScriptedInstance* m_pInstance; + bool m_bIsRegularMode; + + std::vector m_vuiLackeyEnties; + + uint32 m_uiHealTimer; + uint32 m_uiRenewTimer; + uint32 m_uiShieldTimer; + uint32 m_uiSWPainTimer; + uint32 m_uiDispelTimer; + uint32 m_uiScreamTimer; + uint32 m_uiMedallionTimer; + uint8 m_uiPlayersKilled; + + void Reset() override + { + m_uiHealTimer = 15000; + m_uiRenewTimer = 10000; + m_uiShieldTimer = 2000; + m_uiSWPainTimer = 5000; + m_uiDispelTimer = 7500; + m_uiScreamTimer = 9000; + m_uiPlayersKilled = 0; + m_uiMedallionTimer = urand(1000, 2000); + + DoInitializeCompanions(); + } + + void JustReachedHome() override + { + if (m_pInstance) + m_pInstance->SetData(TYPE_DELRISSA, FAIL); + } + + void Aggro(Unit* pWho) override + { + if (pWho->GetTypeId() != TYPEID_PLAYER) + return; + + DoScriptText(SAY_AGGRO, m_creature); + + if (m_pInstance) + m_pInstance->SetData(TYPE_DELRISSA, IN_PROGRESS); + } + + // Summon four random adds to help during the fight + void DoInitializeCompanions() + { + // can be called if creature are dead, so avoid + if (!m_creature->IsAlive()) + return; + + // it's empty, so first time + if (m_vuiLackeyEnties.empty()) + { + // pre-allocate size for speed + m_vuiLackeyEnties.resize(MAX_COMPANIONS); + + // fill vector array with entries from creature array + for (uint8 i = 0; i < MAX_COMPANIONS; ++i) + m_vuiLackeyEnties[i] = aDelrissaLackeys[i]; + + std::random_shuffle(m_vuiLackeyEnties.begin(), m_vuiLackeyEnties.end()); + + // Summon the 4 entries + for (uint8 i = 0; i < MAX_DELRISSA_ADDS; ++i) + m_creature->SummonCreature(m_vuiLackeyEnties[i], aLackeyLocations[i][0], aLackeyLocations[i][1], aLackeyLocations[i][2], aLackeyLocations[i][3], TEMPSUMMON_CORPSE_DESPAWN, 0); + } + // Resummon the killed adds + else + { + if (!m_pInstance) + return; + + for (uint8 i = 0; i < MAX_DELRISSA_ADDS; ++i) + { + // If we already have the creature on the map, then don't summon it + if (m_pInstance->GetSingleCreatureFromStorage(m_vuiLackeyEnties[i], true)) + continue; + + m_creature->SummonCreature(m_vuiLackeyEnties[i], aLackeyLocations[i][0], aLackeyLocations[i][1], aLackeyLocations[i][2], aLackeyLocations[i][3], TEMPSUMMON_CORPSE_DESPAWN, 0); + } + } + } + + void KilledUnit(Unit* pVictim) override + { + if (pVictim->GetTypeId() != TYPEID_PLAYER) + return; + + DoScriptText(aPlayerDeath[m_uiPlayersKilled], m_creature); + ++m_uiPlayersKilled; + + // reset counter + if (m_uiPlayersKilled == 5) + m_uiPlayersKilled = 0; + } + + void JustDied(Unit* /*pKiller*/) override + { + DoScriptText(SAY_DEATH, m_creature); + + if (!m_pInstance) + return; + + // Remove lootable flag if the lackeys are not killed + if (m_pInstance->GetData(TYPE_DELRISSA) == SPECIAL) + m_pInstance->SetData(TYPE_DELRISSA, DONE); + else + m_creature->RemoveFlag(UNIT_DYNAMIC_FLAGS, UNIT_DYNFLAG_LOOTABLE); + } + + void UpdateAI(const uint32 uiDiff) override + { + if (!m_creature->SelectHostileTarget() || !m_creature->getVictim()) + return; + + if (m_uiHealTimer < uiDiff) + { + if (Unit* pTarget = DoSelectLowestHpFriendly(50.0f)) + { + if (DoCastSpellIfCan(pTarget, SPELL_FLASH_HEAL) == CAST_OK) + m_uiHealTimer = urand(15000, 20000); + } + } + else + m_uiHealTimer -= uiDiff; + + if (m_uiRenewTimer < uiDiff) + { + if (Unit* pTarget = DoSelectLowestHpFriendly(50.0f)) + { + if (DoCastSpellIfCan(pTarget, m_bIsRegularMode ? SPELL_RENEW : SPELL_RENEW_H) == CAST_OK) + m_uiRenewTimer = urand(5000, 10000); + } + } + else + m_uiRenewTimer -= uiDiff; + + if (m_uiShieldTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature, m_bIsRegularMode ? SPELL_SHIELD : SPELL_SHIELD_H) == CAST_OK) + m_uiShieldTimer = urand(30000, 35000); + } + else + m_uiShieldTimer -= uiDiff; + + if (m_uiDispelTimer < uiDiff) + { + Unit* pTarget = NULL; + std::list lTempList = DoFindFriendlyCC(50.0f); + + if (!lTempList.empty()) + pTarget = *(lTempList.begin()); + else + pTarget = DoSelectLowestHpFriendly(50.0f); + + if (pTarget) + { + if (DoCastSpellIfCan(pTarget, SPELL_DISPEL_MAGIC) == CAST_OK) + m_uiDispelTimer = urand(12000, 15000); + } + } + else + m_uiDispelTimer -= uiDiff; + + // Use the Medallion if CC - only on heroic. Not sure how many times they are allowed to use it. + if (!m_bIsRegularMode && m_uiMedallionTimer) + { + if (m_creature->IsFrozen() || m_creature->hasUnitState(UNIT_STAT_CAN_NOT_REACT)) + { + if (m_uiMedallionTimer <= uiDiff) + { + if (DoCastSpellIfCan(m_creature, SPELL_MEDALLION, CAST_TRIGGERED) == CAST_OK) + m_uiMedallionTimer = 0; + } + else + m_uiMedallionTimer -= uiDiff; + } + } + + if (m_uiSWPainTimer < uiDiff) + { + if (Unit* pTarget = m_creature->SelectAttackingTarget(ATTACKING_TARGET_RANDOM, 0)) + { + if (DoCastSpellIfCan(pTarget, m_bIsRegularMode ? SPELL_SHADOW_WORD_PAIN : SPELL_SHADOW_WORD_PAIN_H) == CAST_OK) + m_uiSWPainTimer = 10000; + } + } + else + m_uiSWPainTimer -= uiDiff; + + if (m_uiScreamTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature, SPELL_SCREAM) == CAST_OK) + m_uiScreamTimer = urand(15000, 20000); + } + else + m_uiScreamTimer -= uiDiff; + + DoMeleeAttackIfReady(); + } +}; + +CreatureAI* GetAI_boss_priestess_delrissa(Creature* pCreature) +{ + return new boss_priestess_delrissaAI(pCreature); +} + +/*###### +## priestess_companion_common +######*/ + +struct priestess_companion_commonAI : public ScriptedAI +{ + priestess_companion_commonAI(Creature* pCreature) : ScriptedAI(pCreature) + { + m_pInstance = (ScriptedInstance*)pCreature->GetInstanceData(); + m_bIsRegularMode = pCreature->GetMap()->IsRegularDifficulty(); + Reset(); + } + + ScriptedInstance* m_pInstance; + bool m_bIsRegularMode; + + uint32 m_uiResetThreatTimer; + uint32 m_uiMedallionTimer; + bool m_bUsedPotion; + + void Reset() override + { + m_bUsedPotion = 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 + m_uiResetThreatTimer = urand(5000, 15000); + m_uiMedallionTimer = urand(1000, 2000); + } + + void KilledUnit(Unit* pVictim) override + { + if (!m_pInstance) + return; + + if (Creature* pDelrissa = m_pInstance->GetSingleCreatureFromStorage(NPC_DELRISSA)) + pDelrissa->AI()->KilledUnit(pVictim); + } + + // Return true to handle shared timers and MeleeAttack + virtual bool UpdateCompanionAI(const uint32 /*uiDiff*/) { return true; } + + void UpdateAI(const uint32 uiDiff) override + { + // Return since we have no target + if (!m_creature->SelectHostileTarget() || !m_creature->getVictim()) + return; + + // Call specific virtual function + if (!UpdateCompanionAI(uiDiff)) + return; + + if (!m_bUsedPotion && m_creature->GetHealthPercent() < 25.0f) + { + if (DoCastSpellIfCan(m_creature, SPELL_HEALING_POTION) == CAST_OK) + m_bUsedPotion = true; + } + + // Change target + if (m_uiResetThreatTimer < uiDiff) + { + if (Unit* pTarget = m_creature->SelectAttackingTarget(ATTACKING_TARGET_RANDOM, 1)) + { + DoResetThreat(); + AttackStart(pTarget); + m_uiResetThreatTimer = urand(5000, 15000); + } + } + else + m_uiResetThreatTimer -= uiDiff; + + // Use the Medallion if CC - only on heroic. Not sure how many times they are allowed to use it. + if (!m_bIsRegularMode && m_uiMedallionTimer) + { + if (m_creature->IsFrozen() || m_creature->hasUnitState(UNIT_STAT_CAN_NOT_REACT)) + { + if (m_uiMedallionTimer <= uiDiff) + { + if (DoCastSpellIfCan(m_creature, SPELL_MEDALLION, CAST_TRIGGERED) == CAST_OK) + m_uiMedallionTimer = 0; + } + else + m_uiMedallionTimer -= uiDiff; + } + } + + DoMeleeAttackIfReady(); + } +}; + +enum +{ + SPELL_KIDNEY_SHOT = 27615, + SPELL_GOUGE = 12540, + SPELL_KICK = 27613, + SPELL_VANISH = 44290, + SPELL_BACKSTAB = 15657, + SPELL_BACKSTAB_H = 15582, + SPELL_EVISCERATE = 27611, + SPELL_EVISCERATE_H = 46189, +}; + +/*###### +## npc_kagani_nightstrike - Rogue +######*/ + +struct npc_kagani_nightstrikeAI : public priestess_companion_commonAI +{ + npc_kagani_nightstrikeAI(Creature* pCreature) : priestess_companion_commonAI(pCreature) { Reset(); } + + uint32 m_uiGougeTimer; + uint32 m_uiKickTimer; + uint32 m_uiVanishTimer; + uint32 m_uiEviscerateTimer; + uint32 m_uiVanishEndTimer; + + void Reset() override + { + m_uiGougeTimer = 5500; + m_uiKickTimer = 7000; + m_uiVanishTimer = 2000; + m_uiEviscerateTimer = 6000; + m_uiVanishEndTimer = 0; + + priestess_companion_commonAI::Reset(); + } + + void EnterEvadeMode() override + { + if (m_uiVanishEndTimer) + return; + + ScriptedAI::EnterEvadeMode(); + } + + bool UpdateCompanionAI(const uint32 uiDiff) + { + if (m_uiVanishEndTimer) + { + if (m_uiVanishEndTimer <= uiDiff) + { + DoCastSpellIfCan(m_creature->getVictim(), SPELL_BACKSTAB, CAST_TRIGGERED); + DoCastSpellIfCan(m_creature->getVictim(), SPELL_KIDNEY_SHOT, CAST_TRIGGERED); + m_uiVanishEndTimer = 0; + } + else + m_uiVanishEndTimer -= uiDiff; + + return false; + } + + if (m_uiVanishTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature, SPELL_VANISH) == CAST_OK) + { + // Prefer targets with mana + if (Unit* pTarget = m_creature->SelectAttackingTarget(ATTACKING_TARGET_RANDOM, 0, uint32(0), SELECT_FLAG_POWER_MANA)) + { + DoResetThreat(); + AttackStart(pTarget); + } + + m_uiVanishTimer = 30000; + m_uiVanishEndTimer = 10000; + } + } + else + m_uiVanishTimer -= uiDiff; + + if (m_uiGougeTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature->getVictim(), SPELL_GOUGE) == CAST_OK) + m_uiGougeTimer = 5500; + } + else + m_uiGougeTimer -= uiDiff; + + if (m_uiKickTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature->getVictim(), SPELL_KICK) == CAST_OK) + m_uiKickTimer = 7000; + } + else + m_uiKickTimer -= uiDiff; + + if (m_uiEviscerateTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature->getVictim(), m_bIsRegularMode ? SPELL_EVISCERATE : SPELL_EVISCERATE_H) == CAST_OK) + m_uiEviscerateTimer = 4000; + } + else + m_uiEviscerateTimer -= uiDiff; + + return true; + } +}; + +CreatureAI* GetAI_npc_kagani_nightstrike(Creature* pCreature) +{ + return new npc_kagani_nightstrikeAI(pCreature); +} + +enum +{ + SPELL_IMMOLATE = 44267, + SPELL_IMMOLATE_H = 46191, + SPELL_SHADOW_BOLT = 12471, + SPELL_SHADOW_BOLT_H = 15232, + SPELL_SEED_OF_CORRUPTION = 44141, + SPELL_CURSE_OF_AGONY = 14875, + SPELL_CURSE_OF_AGONY_H = 46190, + SPELL_FEAR = 38595, + SPELL_DEATH_COIL = 44142, + SPELL_SUMMON_IMP = 44163, + + NPC_FIZZLE = 24656, +}; + +/*###### +## npc_ellris_duskhallow - Warlock +######*/ + +struct npc_ellris_duskhallowAI : public priestess_companion_commonAI +{ + npc_ellris_duskhallowAI(Creature* pCreature) : priestess_companion_commonAI(pCreature) { Reset(); } + + uint32 m_uiImmolateTimer; + uint32 m_uiShadowBoltTimer; + uint32 m_uiSeedCorruptionTimer; + uint32 m_uiCurseAgonyTimer; + uint32 m_uiFearTimer; + uint32 m_uiDeathCoilTimer; + + void Reset() override + { + m_uiImmolateTimer = 6000; + m_uiShadowBoltTimer = 3000; + m_uiSeedCorruptionTimer = 2000; + m_uiCurseAgonyTimer = 1000; + m_uiFearTimer = 10000; + m_uiDeathCoilTimer = 8000; + + priestess_companion_commonAI::Reset(); + + // Check if we already have an imp summoned + if (!GetClosestCreatureWithEntry(m_creature, NPC_FIZZLE, 50.0f)) + DoCastSpellIfCan(m_creature, SPELL_SUMMON_IMP); + } + + void AttackStart(Unit* pWho) override + { + if (m_creature->Attack(pWho, true)) + { + m_creature->AddThreat(pWho); + m_creature->SetInCombatWith(pWho); + pWho->SetInCombatWith(m_creature); + DoStartMovement(pWho, 20.0f); + } + } + + bool UpdateCompanionAI(const uint32 uiDiff) + { + if (m_uiImmolateTimer < uiDiff) + { + if (Unit* pTarget = m_creature->SelectAttackingTarget(ATTACKING_TARGET_RANDOM, 0)) + { + if (DoCastSpellIfCan(pTarget, m_bIsRegularMode ? SPELL_IMMOLATE : SPELL_IMMOLATE_H) == CAST_OK) + m_uiImmolateTimer = 6000; + } + } + else + m_uiImmolateTimer -= uiDiff; + + if (m_uiShadowBoltTimer < uiDiff) + { + if (Unit* pTarget = m_creature->SelectAttackingTarget(ATTACKING_TARGET_RANDOM, 0)) + { + if (DoCastSpellIfCan(pTarget, m_bIsRegularMode ? SPELL_SHADOW_BOLT : SPELL_SHADOW_BOLT_H) == CAST_OK) + m_uiShadowBoltTimer = 5000; + } + } + else + m_uiShadowBoltTimer -= uiDiff; + + if (m_uiSeedCorruptionTimer < uiDiff) + { + if (Unit* pTarget = m_creature->SelectAttackingTarget(ATTACKING_TARGET_RANDOM, 0)) + { + if (DoCastSpellIfCan(pTarget, SPELL_SEED_OF_CORRUPTION) == CAST_OK) + m_uiSeedCorruptionTimer = 10000; + } + } + else + m_uiSeedCorruptionTimer -= uiDiff; + + if (m_uiCurseAgonyTimer < uiDiff) + { + if (Unit* pTarget = m_creature->SelectAttackingTarget(ATTACKING_TARGET_RANDOM, 0)) + { + if (DoCastSpellIfCan(pTarget, m_bIsRegularMode ? SPELL_CURSE_OF_AGONY : SPELL_CURSE_OF_AGONY_H) == CAST_OK) + m_uiCurseAgonyTimer = 13000; + } + } + else + m_uiCurseAgonyTimer -= uiDiff; + + if (m_uiFearTimer < uiDiff) + { + if (Unit* pTarget = m_creature->SelectAttackingTarget(ATTACKING_TARGET_RANDOM, 0)) + { + if (DoCastSpellIfCan(pTarget, SPELL_FEAR) == CAST_OK) + m_uiFearTimer = 10000; + } + } + else + m_uiFearTimer -= uiDiff; + + if (m_uiDeathCoilTimer < uiDiff) + { + if (Unit* pTarget = m_creature->SelectAttackingTarget(ATTACKING_TARGET_RANDOM, 0)) + { + if (DoCastSpellIfCan(pTarget, SPELL_DEATH_COIL) == CAST_OK) + m_uiDeathCoilTimer = urand(8000, 13000); + } + } + else + m_uiDeathCoilTimer -= uiDiff; + + return true; + } +}; + +CreatureAI* GetAI_npc_ellris_duskhallow(Creature* pCreature) +{ + return new npc_ellris_duskhallowAI(pCreature); +} + +enum +{ + SPELL_KNOCKDOWN = 11428, + SPELL_KNOCKDOWN_H = 46183, + SPELL_SNAP_KICK = 46182 +}; + +/*###### +## npc_eramas_brightblaze - Monk +######*/ + +struct npc_eramas_brightblazeAI : public priestess_companion_commonAI +{ + npc_eramas_brightblazeAI(Creature* pCreature) : priestess_companion_commonAI(pCreature) { Reset(); } + + uint32 m_uiKnockdownTimer; + uint32 m_uiSnapKickTimer; + + void Reset() override + { + m_uiKnockdownTimer = 6000; + m_uiSnapKickTimer = 4500; + + priestess_companion_commonAI::Reset(); + } + + bool UpdateCompanionAI(const uint32 uiDiff) + { + if (m_uiKnockdownTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature->getVictim(), m_bIsRegularMode ? SPELL_KNOCKDOWN : SPELL_KNOCKDOWN_H) == CAST_OK) + m_uiKnockdownTimer = 6000; + } + else + m_uiKnockdownTimer -= uiDiff; + + if (m_uiSnapKickTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature->getVictim(), SPELL_SNAP_KICK) == CAST_OK) + m_uiSnapKickTimer = 4500; + } + else + m_uiSnapKickTimer -= uiDiff; + + return true; + } +}; + +CreatureAI* GetAI_npc_eramas_brightblaze(Creature* pCreature) +{ + return new npc_eramas_brightblazeAI(pCreature); +} + +enum +{ + SPELL_POLYMORPH = 13323, + SPELL_ICE_BLOCK = 27619, + SPELL_BLIZZARD = 44178, + SPELL_BLIZZARD_H = 46195, + SPELL_ICE_LANCE = 44176, + SPELL_ICE_LANCE_H = 46194, + SPELL_CONE_OF_COLD = 12611, + SPELL_CONE_OF_COLD_H = 38384, + SPELL_FROSTBOLT = 15043, + SPELL_FROSTBOLT_H = 15530, + SPELL_BLINK = 14514 +}; + +/*###### +## npc_yazzai - Mage +######*/ + +struct npc_yazzaiAI : public priestess_companion_commonAI +{ + npc_yazzaiAI(Creature* pCreature) : priestess_companion_commonAI(pCreature) { Reset(); } + + bool m_bHasIceBlocked; + + uint32 m_uiPolymorphTimer; + uint32 m_uiIceBlockTimer; + uint32 m_uiWait_Timer; + uint32 m_uiBlizzardTimer; + uint32 m_uiIceLanceTimer; + uint32 m_uiConeColdTimer; + uint32 m_uiFrostboltTimer; + uint32 m_uiBlinkTimer; + + void Reset() override + { + m_bHasIceBlocked = false; + + m_uiPolymorphTimer = 1000; + m_uiIceBlockTimer = 20000; + m_uiWait_Timer = 10000; + m_uiBlizzardTimer = 8000; + m_uiIceLanceTimer = 12000; + m_uiConeColdTimer = 10000; + m_uiFrostboltTimer = 3000; + m_uiBlinkTimer = 8000; + + priestess_companion_commonAI::Reset(); + } + + void AttackStart(Unit* pWho) override + { + if (m_creature->Attack(pWho, true)) + { + m_creature->AddThreat(pWho); + m_creature->SetInCombatWith(pWho); + pWho->SetInCombatWith(m_creature); + DoStartMovement(pWho, 20.0f); + } + } + + bool UpdateCompanionAI(const uint32 uiDiff) + { + if (m_uiPolymorphTimer < uiDiff) + { + if (Unit* pTarget = m_creature->SelectAttackingTarget(ATTACKING_TARGET_RANDOM, 0)) + { + if (DoCastSpellIfCan(pTarget, SPELL_POLYMORPH) == CAST_OK) + m_uiPolymorphTimer = 20000; + } + } + else + m_uiPolymorphTimer -= uiDiff; + + if (m_creature->GetHealthPercent() < 35.0f && !m_bHasIceBlocked) + { + if (DoCastSpellIfCan(m_creature, SPELL_ICE_BLOCK) == CAST_OK) + m_bHasIceBlocked = true; + } + + if (m_uiBlizzardTimer < uiDiff) + { + if (Unit* pTarget = m_creature->SelectAttackingTarget(ATTACKING_TARGET_RANDOM, 0)) + { + if (DoCastSpellIfCan(pTarget, m_bIsRegularMode ? SPELL_BLIZZARD : SPELL_BLIZZARD_H) == CAST_OK) + m_uiBlizzardTimer = urand(8000, 15000); + } + } + else + m_uiBlizzardTimer -= uiDiff; + + if (m_uiIceLanceTimer < uiDiff) + { + if (Unit* pTarget = m_creature->SelectAttackingTarget(ATTACKING_TARGET_RANDOM, 0)) + { + if (DoCastSpellIfCan(pTarget, m_bIsRegularMode ? SPELL_ICE_LANCE : SPELL_ICE_LANCE_H) == CAST_OK) + m_uiIceLanceTimer = 12000; + } + } + else + m_uiIceLanceTimer -= uiDiff; + + if (m_uiConeColdTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature, m_bIsRegularMode ? SPELL_CONE_OF_COLD : SPELL_CONE_OF_COLD_H) == CAST_OK) + m_uiConeColdTimer = 10000; + } + else + m_uiConeColdTimer -= uiDiff; + + if (m_uiFrostboltTimer < uiDiff) + { + if (Unit* pTarget = m_creature->SelectAttackingTarget(ATTACKING_TARGET_RANDOM, 0)) + { + if (DoCastSpellIfCan(pTarget, m_bIsRegularMode ? SPELL_FROSTBOLT : SPELL_FROSTBOLT_H) == CAST_OK) + m_uiFrostboltTimer = 8000; + } + } + else + m_uiFrostboltTimer -= uiDiff; + + if (m_uiBlinkTimer < uiDiff) + { + // if anybody is in melee range than escape by blink + if (m_creature->SelectAttackingTarget(ATTACKING_TARGET_RANDOM, 0, uint32(0), SELECT_FLAG_IN_MELEE_RANGE)) + { + if (DoCastSpellIfCan(m_creature, SPELL_BLINK) == CAST_OK) + m_uiBlinkTimer = 8000; + } + else + m_uiBlinkTimer = 2000; + } + else + m_uiBlinkTimer -= uiDiff; + + return true; + } +}; + +CreatureAI* GetAI_npc_yazzai(Creature* pCreature) +{ + return new npc_yazzaiAI(pCreature); +} + +enum +{ + SPELL_INTERCEPT_STUN = 27577, + SPELL_DISARM = 27581, + SPELL_PIERCING_HOWL = 23600, + SPELL_FRIGHTENING_SHOUT = 19134, + SPELL_HAMSTRING = 27584, + SPELL_BATTLE_SHOUT = 27578, + SPELL_MORTAL_STRIKE = 44268 +}; + +/*###### +## npc_warlord_salaris - Warrior +######*/ + +struct npc_warlord_salarisAI : public priestess_companion_commonAI +{ + npc_warlord_salarisAI(Creature* pCreature) : priestess_companion_commonAI(pCreature) { Reset(); } + + uint32 m_uiInterceptStunTimer; + uint32 m_uiDisarmTimer; + uint32 m_uiPiercingHowlTimer; + uint32 m_uiFrighteningShoutTimer; + uint32 m_uiHamstringTimer; + uint32 m_uiMortalStrikeTimer; + + void Reset() override + { + m_uiInterceptStunTimer = 500; + m_uiDisarmTimer = 6000; + m_uiPiercingHowlTimer = 10000; + m_uiFrighteningShoutTimer = 18000; + m_uiHamstringTimer = 4500; + m_uiMortalStrikeTimer = 8000; + + priestess_companion_commonAI::Reset(); + } + + void Aggro(Unit* /*pWho*/) override + { + DoCastSpellIfCan(m_creature, SPELL_BATTLE_SHOUT); + } + + bool UpdateCompanionAI(const uint32 uiDiff) + { + if (m_uiInterceptStunTimer < uiDiff) + { + // if nobody is in melee range than try to use Intercept + if (!m_creature->SelectAttackingTarget(ATTACKING_TARGET_RANDOM, 0, uint32(0), SELECT_FLAG_IN_MELEE_RANGE)) + { + if (Unit* pTarget = m_creature->SelectAttackingTarget(ATTACKING_TARGET_RANDOM, 0, SPELL_INTERCEPT_STUN, SELECT_FLAG_NOT_IN_MELEE_RANGE | SELECT_FLAG_IN_LOS)) + { + if (DoCastSpellIfCan(pTarget, SPELL_INTERCEPT_STUN) == CAST_OK) + m_uiInterceptStunTimer = 10000; + } + } + else + m_uiInterceptStunTimer = 2000; + } + else + m_uiInterceptStunTimer -= uiDiff; + + if (m_uiDisarmTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature->getVictim(), SPELL_DISARM) == CAST_OK) + m_uiDisarmTimer = 6000; + } + else + m_uiDisarmTimer -= uiDiff; + + if (m_uiHamstringTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature->getVictim(), SPELL_HAMSTRING) == CAST_OK) + m_uiHamstringTimer = 4500; + } + else + m_uiHamstringTimer -= uiDiff; + + if (m_uiMortalStrikeTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature->getVictim(), SPELL_MORTAL_STRIKE) == CAST_OK) + m_uiMortalStrikeTimer = 4500; + } + else + m_uiMortalStrikeTimer -= uiDiff; + + if (m_uiPiercingHowlTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature->getVictim(), SPELL_PIERCING_HOWL) == CAST_OK) + m_uiPiercingHowlTimer = 10000; + } + else + m_uiPiercingHowlTimer -= uiDiff; + + if (m_uiFrighteningShoutTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature->getVictim(), SPELL_FRIGHTENING_SHOUT) == CAST_OK) + m_uiFrighteningShoutTimer = 18000; + } + else + m_uiFrighteningShoutTimer -= uiDiff; + + return true; + } +}; + +CreatureAI* GetAI_npc_warlord_salaris(Creature* pCreature) +{ + return new npc_warlord_salarisAI(pCreature); +} + +enum +{ + SPELL_AIMED_SHOT = 44271, + SPELL_SHOOT = 15620, + SPELL_SHOOT_H = 22907, + SPELL_CONCUSSIVE_SHOT = 27634, + SPELL_MULTI_SHOT = 31942, + SPELL_MULTI_SHOT_H = 44285, + SPELL_WING_CLIP = 44286, + SPELL_FREEZING_TRAP = 44136, + + NPC_SLIVER = 24552 +}; + +/*###### +## npc_garaxxas - Hunter +######*/ + +struct npc_garaxxasAI : public priestess_companion_commonAI +{ + npc_garaxxasAI(Creature* pCreature) : priestess_companion_commonAI(pCreature) { Reset(); } + + uint32 m_uiAimedShotTimer; + uint32 m_uiShootTimer; + uint32 m_uiConcussiveShotTimer; + uint32 m_uiMultiShotTimer; + uint32 m_uiWingClipTimer; + uint32 m_uiFreezingTrapTimer; + + void Reset() override + { + m_uiAimedShotTimer = 6000; + m_uiShootTimer = 2500; + m_uiConcussiveShotTimer = 8000; + m_uiMultiShotTimer = 10000; + m_uiWingClipTimer = 4000; + m_uiFreezingTrapTimer = 15000; + + priestess_companion_commonAI::Reset(); + + // Check if the pet was killed + if (!GetClosestCreatureWithEntry(m_creature, NPC_SLIVER, 50.0f)) + m_creature->SummonCreature(NPC_SLIVER, 0, 0, 0, 0, TEMPSUMMON_CORPSE_DESPAWN, 0); + } + + void AttackStart(Unit* pWho) override + { + if (m_creature->Attack(pWho, true)) + { + m_creature->AddThreat(pWho); + m_creature->SetInCombatWith(pWho); + pWho->SetInCombatWith(m_creature); + DoStartMovement(pWho, 20.0f); + } + } + + bool UpdateCompanionAI(const uint32 uiDiff) + { + if (m_creature->CanReachWithMeleeAttack(m_creature->getVictim())) + { + if (m_uiWingClipTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature->getVictim(), SPELL_WING_CLIP) == CAST_OK) + m_uiWingClipTimer = 4000; + } + else + m_uiWingClipTimer -= uiDiff; + + if (m_uiFreezingTrapTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature, SPELL_FREEZING_TRAP) == CAST_OK) + m_uiFreezingTrapTimer = urand(15000, 30000); + } + else + m_uiFreezingTrapTimer -= uiDiff; + } + else + { + if (m_uiConcussiveShotTimer < uiDiff) + { + if (Unit* pTarget = m_creature->SelectAttackingTarget(ATTACKING_TARGET_RANDOM, 0)) + { + if (DoCastSpellIfCan(pTarget, SPELL_CONCUSSIVE_SHOT) == CAST_OK) + m_uiConcussiveShotTimer = 8000; + } + } + else + m_uiConcussiveShotTimer -= uiDiff; + + if (m_uiMultiShotTimer < uiDiff) + { + if (Unit* pTarget = m_creature->SelectAttackingTarget(ATTACKING_TARGET_RANDOM, 0)) + { + if (DoCastSpellIfCan(pTarget, m_bIsRegularMode ? SPELL_MULTI_SHOT : SPELL_MULTI_SHOT_H) == CAST_OK) + m_uiMultiShotTimer = 10000; + } + } + else + m_uiMultiShotTimer -= uiDiff; + + if (m_uiAimedShotTimer < uiDiff) + { + if (Unit* pTarget = m_creature->SelectAttackingTarget(ATTACKING_TARGET_RANDOM, 0)) + { + if (DoCastSpellIfCan(pTarget, SPELL_AIMED_SHOT) == CAST_OK) + m_uiAimedShotTimer = 6000; + } + } + else + m_uiAimedShotTimer -= uiDiff; + + if (m_uiShootTimer < uiDiff) + { + if (Unit* pTarget = m_creature->SelectAttackingTarget(ATTACKING_TARGET_RANDOM, 0)) + { + if (DoCastSpellIfCan(pTarget, m_bIsRegularMode ? SPELL_SHOOT : SPELL_SHOOT_H) == CAST_OK) + m_uiShootTimer = 2500; + } + } + else + m_uiShootTimer -= uiDiff; + } + + return true; + } +}; + +CreatureAI* GetAI_npc_garaxxas(Creature* pCreature) +{ + return new npc_garaxxasAI(pCreature); +} + +enum +{ + SPELL_WAR_STOMP = 46026, + SPELL_PURGE = 27626, + SPELL_LESSER_HEALING_WAVE = 44256, + SPELL_LESSER_HEALING_WAVE_H = 46181, + SPELL_FROST_SHOCK = 21401, + SPELL_FROST_SHOCK_H = 46180, + SPELL_WINDFURY_TOTEM = 27621, + SPELL_FIRE_NOVA_TOTEM = 44257, + SPELL_EARTHBIND_TOTEM = 15786 +}; + +/*###### +## npc_apoko - Shaman +######*/ + +struct npc_apokoAI : public priestess_companion_commonAI +{ + npc_apokoAI(Creature* pCreature) : priestess_companion_commonAI(pCreature) { Reset(); } + + uint32 m_uiTotemTimer; + uint32 m_uiWarStompTimer; + uint32 m_uiPurgeTimer; + uint32 m_uiHealingWaveTimer; + uint32 m_uiFrostShockTimer; + + void Reset() override + { + m_uiTotemTimer = 0; + m_uiWarStompTimer = 10000; + m_uiPurgeTimer = 8000; + m_uiHealingWaveTimer = 5000; + m_uiFrostShockTimer = 7000; + + priestess_companion_commonAI::Reset(); + } + + bool UpdateCompanionAI(const uint32 uiDiff) + { + if (m_uiTotemTimer < uiDiff) + { + // It's not very clear how exactly these spells should be cast + switch (urand(0, 2)) + { + case 0: DoCastSpellIfCan(m_creature, SPELL_WINDFURY_TOTEM); break; + case 1: DoCastSpellIfCan(m_creature, SPELL_FIRE_NOVA_TOTEM); break; + case 2: DoCastSpellIfCan(m_creature, SPELL_EARTHBIND_TOTEM); break; + } + m_uiTotemTimer = urand(2000, 6000); + } + else + m_uiTotemTimer -= uiDiff; + + if (m_uiWarStompTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature, SPELL_WAR_STOMP) == CAST_OK) + m_uiWarStompTimer = 10000; + } + else + m_uiWarStompTimer -= uiDiff; + + if (m_uiPurgeTimer < uiDiff) + { + if (Unit* pTarget = m_creature->SelectAttackingTarget(ATTACKING_TARGET_RANDOM, 0)) + { + if (DoCastSpellIfCan(pTarget, SPELL_PURGE) == CAST_OK) + m_uiPurgeTimer = 15000; + } + } + else + m_uiPurgeTimer -= uiDiff; + + if (m_uiFrostShockTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature->getVictim(), m_bIsRegularMode ? SPELL_FROST_SHOCK : SPELL_FROST_SHOCK_H) == CAST_OK) + m_uiFrostShockTimer = 7000; + } + else + m_uiFrostShockTimer -= uiDiff; + + if (m_uiHealingWaveTimer < uiDiff) + { + if (Unit* pTarget = DoSelectLowestHpFriendly(50.0f)) + { + if (DoCastSpellIfCan(pTarget, m_bIsRegularMode ? SPELL_LESSER_HEALING_WAVE : SPELL_LESSER_HEALING_WAVE_H) == CAST_OK) + m_uiHealingWaveTimer = 5000; + } + } + else + m_uiHealingWaveTimer -= uiDiff; + + return true; + } +}; + +CreatureAI* GetAI_npc_apoko(Creature* pCreature) +{ + return new npc_apokoAI(pCreature); +} + +enum +{ + SPELL_GOBLIN_DRAGON_GUN = 44272, + SPELL_GOBLIN_DRAGON_GUN_H = 46185, + SPELL_ROCKET_LAUNCH = 44137, + SPELL_ROCKET_LAUNCH_H = 46187, + SPELL_RECOMBOBULATE = 44274, + SPELL_HIGH_EXPLOSIVE_SHEEP = 44276, + SPELL_FEL_IRON_BOMB = 46024, + SPELL_FEL_IRON_BOMB_H = 46184, +}; + +/*###### +## npc_zelfan - Engineer +######*/ + +struct npc_zelfanAI : public priestess_companion_commonAI +{ + npc_zelfanAI(Creature* pCreature) : priestess_companion_commonAI(pCreature) { Reset(); } + + uint32 m_uiGoblinDragonGunTimer; + uint32 m_uiRocketLaunchTimer; + uint32 m_uiRecombobulateTimer; + uint32 m_uiHighExplosiveSheepTimer; + uint32 m_uiFelIronBombTimer; + + void Reset() override + { + m_uiGoblinDragonGunTimer = 20000; + m_uiRocketLaunchTimer = 7000; + m_uiRecombobulateTimer = 4000; + m_uiHighExplosiveSheepTimer = 10000; + m_uiFelIronBombTimer = 15000; + + priestess_companion_commonAI::Reset(); + } + + void JustSummoned(Creature* pSummoned) override + { + if (m_creature->getVictim()) + pSummoned->AI()->AttackStart(m_creature->getVictim()); + } + + bool UpdateCompanionAI(const uint32 uiDiff) + { + if (m_uiGoblinDragonGunTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature, m_bIsRegularMode ? SPELL_GOBLIN_DRAGON_GUN : SPELL_GOBLIN_DRAGON_GUN_H) == CAST_OK) + m_uiGoblinDragonGunTimer = urand(10000, 20000); + } + else + m_uiGoblinDragonGunTimer -= uiDiff; + + if (m_uiRocketLaunchTimer < uiDiff) + { + if (Unit* pTarget = m_creature->SelectAttackingTarget(ATTACKING_TARGET_RANDOM, 0)) + { + if (DoCastSpellIfCan(pTarget, m_bIsRegularMode ? SPELL_ROCKET_LAUNCH : SPELL_ROCKET_LAUNCH_H) == CAST_OK) + m_uiRocketLaunchTimer = 9000; + } + } + else + m_uiRocketLaunchTimer -= uiDiff; + + if (m_uiFelIronBombTimer < uiDiff) + { + if (Unit* pTarget = m_creature->SelectAttackingTarget(ATTACKING_TARGET_RANDOM, 0)) + { + if (DoCastSpellIfCan(pTarget, m_bIsRegularMode ? SPELL_FEL_IRON_BOMB : SPELL_FEL_IRON_BOMB_H) == CAST_OK) + m_uiFelIronBombTimer = 15000; + } + } + else + m_uiFelIronBombTimer -= uiDiff; + + if (m_uiRecombobulateTimer < uiDiff) + { + // Note: this should be casted only on Polyformed targets + Unit* pTarget = NULL; + std::list lTempList = DoFindFriendlyCC(50.0f); + + if (!lTempList.empty()) + pTarget = *(lTempList.begin()); + else + pTarget = DoSelectLowestHpFriendly(50.0f); + + if (pTarget) + { + if (DoCastSpellIfCan(pTarget, SPELL_RECOMBOBULATE) == CAST_OK) + m_uiRecombobulateTimer = 2000; + } + } + else + m_uiRecombobulateTimer -= uiDiff; + + if (m_uiHighExplosiveSheepTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature, SPELL_HIGH_EXPLOSIVE_SHEEP) == CAST_OK) + m_uiHighExplosiveSheepTimer = 65000; + } + else + m_uiHighExplosiveSheepTimer -= uiDiff; + + return true; + } +}; + +CreatureAI* GetAI_npc_zelfan(Creature* pCreature) +{ + return new npc_zelfanAI(pCreature); +} + +void AddSC_boss_priestess_delrissa() +{ + Script* pNewScript; + + pNewScript = new Script; + pNewScript->Name = "boss_priestess_delrissa"; + pNewScript->GetAI = &GetAI_boss_priestess_delrissa; + pNewScript->RegisterSelf(); + + pNewScript = new Script; + pNewScript->Name = "npc_kagani_nightstrike"; + pNewScript->GetAI = &GetAI_npc_kagani_nightstrike; + pNewScript->RegisterSelf(); + + pNewScript = new Script; + pNewScript->Name = "npc_ellris_duskhallow"; + pNewScript->GetAI = &GetAI_npc_ellris_duskhallow; + pNewScript->RegisterSelf(); + + pNewScript = new Script; + pNewScript->Name = "npc_eramas_brightblaze"; + pNewScript->GetAI = &GetAI_npc_eramas_brightblaze; + pNewScript->RegisterSelf(); + + pNewScript = new Script; + pNewScript->Name = "npc_yazzai"; + pNewScript->GetAI = &GetAI_npc_yazzai; + pNewScript->RegisterSelf(); + + pNewScript = new Script; + pNewScript->Name = "npc_warlord_salaris"; + pNewScript->GetAI = &GetAI_npc_warlord_salaris; + pNewScript->RegisterSelf(); + + pNewScript = new Script; + pNewScript->Name = "npc_garaxxas"; + pNewScript->GetAI = &GetAI_npc_garaxxas; + pNewScript->RegisterSelf(); + + pNewScript = new Script; + pNewScript->Name = "npc_apoko"; + pNewScript->GetAI = &GetAI_npc_apoko; + pNewScript->RegisterSelf(); + + pNewScript = new Script; + pNewScript->Name = "npc_zelfan"; + pNewScript->GetAI = &GetAI_npc_zelfan; + pNewScript->RegisterSelf(); +} diff --git a/src/modules/SD2/scripts/eastern_kingdoms/magisters_terrace/boss_selin_fireheart.cpp b/src/modules/SD2/scripts/eastern_kingdoms/magisters_terrace/boss_selin_fireheart.cpp new file mode 100644 index 000000000..c14a611d9 --- /dev/null +++ b/src/modules/SD2/scripts/eastern_kingdoms/magisters_terrace/boss_selin_fireheart.cpp @@ -0,0 +1,329 @@ +/* Copyright (C) 2006 - 2013 ScriptDev2 + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with 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: Timers. +SDCategory: Magister's Terrace +EndScriptData */ + +#include "precompiled.h" +#include "magisters_terrace.h" + +enum +{ + SAY_AGGRO = -1585000, + SAY_ENERGY = -1585001, + SAY_EMPOWERED = -1585002, + SAY_KILL_1 = -1585003, + SAY_KILL_2 = -1585004, + SAY_DEATH = -1585005, + EMOTE_CRYSTAL = -1585006, + + // Selin's spells + SPELL_DRAIN_LIFE = 44294, + SPELL_DRAIN_LIFE_H = 46155, + SPELL_FEL_EXPLOSION = 44314, + SPELL_DRAIN_MANA = 46153, // Heroic only + // SPELL_FEL_CRYSTAL_DUMMY = 44329, // used by Selin to select a nearby Crystal - not used in script + SPELL_MANA_RAGE = 44320, // This spell triggers 44321, which changes scale and regens mana Requires an entry in spell_script_target + + // Crystal spells and npcs + SPELL_FEL_CRYSTAL_COSMETIC = 44374, // cosmetic - used by the guys around Selin + SPELL_FEL_CRYSTAL_VISUAL = 44355, // cosmetic + + NPC_HUSK = 24690, + NPC_SKULER = 24688, + NPC_BRUISER = 24689, +}; + +struct boss_selin_fireheartAI : public ScriptedAI +{ + boss_selin_fireheartAI(Creature* pCreature) : ScriptedAI(pCreature) + { + m_pInstance = (instance_magisters_terrace*)pCreature->GetInstanceData(); + m_bIsRegularMode = pCreature->GetMap()->IsRegularDifficulty(); + Reset(); + } + + instance_magisters_terrace* m_pInstance; + bool m_bIsRegularMode; + + uint32 m_uiDrainLifeTimer; + uint32 m_uiDrainManaTimer; + uint32 m_uiFelExplosionTimer; + uint32 m_uiDrainCrystalTimer; + uint32 m_uiManaRageTimer; + + bool m_bDrainingCrystal; + + ObjectGuid m_crystalGuid; + + void Reset() override + { + m_uiDrainLifeTimer = urand(3000, 7000); + m_uiDrainManaTimer = m_uiDrainLifeTimer + 5000; + m_uiFelExplosionTimer = 2100; + m_uiDrainCrystalTimer = m_bIsRegularMode ? urand(20000, 25000) : urand(10000, 15000); + m_uiManaRageTimer = 0; + + m_bDrainingCrystal = false; + } + + // Get the closest alive crystal for draining + bool DoSelectNearestCrystal() + { + // Wait to finish casting + if (m_creature->IsNonMeleeSpellCasted(false)) + return false; + + if (Creature* pCrystal = GetClosestCreatureWithEntry(m_creature, NPC_FEL_CRYSTAL, 60.0f)) + { + m_crystalGuid = pCrystal->GetObjectGuid(); + DoScriptText(SAY_ENERGY, m_creature); + DoScriptText(EMOTE_CRYSTAL, m_creature); + m_creature->InterruptNonMeleeSpells(false); + + float fX, fY, fZ; + SetCombatMovement(false); + m_creature->GetContactPoint(pCrystal, fX, fY, fZ, INTERACTION_DISTANCE); + m_creature->GetMotionMaster()->MovePoint(1, fX, fY, fZ); + m_bDrainingCrystal = true; + + return true; + } + + return false; + } + + void Aggro(Unit* /*pWho*/) override + { + DoScriptText(SAY_AGGRO, m_creature); + + if (m_pInstance) + m_pInstance->SetData(TYPE_SELIN, IN_PROGRESS); + } + + void JustReachedHome() override + { + if (m_pInstance) + m_pInstance->SetData(TYPE_SELIN, FAIL); + } + + void KilledUnit(Unit* /*pVictim*/) override + { + DoScriptText(urand(0, 1) ? SAY_KILL_1 : SAY_KILL_2, m_creature); + } + + void MovementInform(uint32 uiType, uint32 uiPointId) override + { + if (uiType != POINT_MOTION_TYPE || !uiPointId) + return; + + Creature* pCrystal = m_creature->GetMap()->GetCreature(m_crystalGuid); + if (pCrystal && pCrystal->IsAlive()) + { + if (DoCastSpellIfCan(pCrystal, SPELL_MANA_RAGE) == CAST_OK) + { + // Allow the crystal to be killed + pCrystal->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE); + m_uiManaRageTimer = 10000; + } + } + else + { + // Make an error message in case something weird happened here + script_error_log("Selin Fireheart unable to drain crystal as the crystal is either dead or deleted.."); + m_bDrainingCrystal = false; + } + } + + void JustDied(Unit* /*pKiller*/) override + { + DoScriptText(SAY_DEATH, m_creature); + + if (m_pInstance) + m_pInstance->SetData(TYPE_SELIN, DONE); + } + + // Mark the Mana Rage as complete + void ManaRageComplete() + { + m_bDrainingCrystal = false; + SetCombatMovement(true); + m_creature->GetMotionMaster()->Clear(); + DoStartMovement(m_creature->getVictim()); + } + + void UpdateAI(const uint32 uiDiff) override + { + if (!m_creature->SelectHostileTarget() || !m_creature->getVictim()) + return; + + if (!m_bDrainingCrystal) + { + if (m_creature->GetPower(POWER_MANA) * 100 / m_creature->GetMaxPower(POWER_MANA) < 10) + { + if (m_uiDrainLifeTimer < uiDiff) + { + if (Unit* pTarget = m_creature->SelectAttackingTarget(ATTACKING_TARGET_RANDOM, 0)) + { + if (DoCastSpellIfCan(pTarget, m_bIsRegularMode ? SPELL_DRAIN_LIFE : SPELL_DRAIN_LIFE_H) == CAST_OK) + m_uiDrainLifeTimer = 10000; + } + } + else + m_uiDrainLifeTimer -= uiDiff; + + // Heroic only + if (!m_bIsRegularMode) + { + if (m_uiDrainManaTimer < uiDiff) + { + if (Unit* pTarget = m_creature->SelectAttackingTarget(ATTACKING_TARGET_RANDOM, 0, SPELL_DRAIN_MANA, SELECT_FLAG_POWER_MANA)) + { + if (DoCastSpellIfCan(pTarget, SPELL_DRAIN_MANA) == CAST_OK) + m_uiDrainManaTimer = 10000; + } + } + else + m_uiDrainManaTimer -= uiDiff; + } + + if (m_uiDrainCrystalTimer < uiDiff) + { + if (DoSelectNearestCrystal()) + m_uiDrainCrystalTimer = m_bIsRegularMode ? urand(20000, 25000) : urand(10000, 15000); + } + else + m_uiDrainCrystalTimer -= uiDiff; + } + + if (m_uiFelExplosionTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature, SPELL_FEL_EXPLOSION) == CAST_OK) + m_uiFelExplosionTimer = 2000; + } + else + m_uiFelExplosionTimer -= uiDiff; + + DoMeleeAttackIfReady(); + } + else + { + if (m_uiManaRageTimer) + { + if (m_uiManaRageTimer <= uiDiff) + { + DoScriptText(SAY_EMPOWERED, m_creature); + ManaRageComplete(); + + // Kill the drained crystal + Creature* pCrystalChosen = m_creature->GetMap()->GetCreature(m_crystalGuid); + if (pCrystalChosen && pCrystalChosen->IsAlive()) + pCrystalChosen->DealDamage(pCrystalChosen, pCrystalChosen->GetHealth(), NULL, DIRECT_DAMAGE, SPELL_SCHOOL_MASK_NORMAL, NULL, false); + + m_uiManaRageTimer = 0; + } + else + m_uiManaRageTimer -= uiDiff; + } + } + } +}; + +CreatureAI* GetAI_boss_selin_fireheart(Creature* pCreature) +{ + return new boss_selin_fireheartAI(pCreature); +}; + +struct mob_fel_crystalAI : public ScriptedAI +{ + mob_fel_crystalAI(Creature* pCreature) : ScriptedAI(pCreature) { Reset(); } + + GuidSet m_sWretchedGuids; + + uint32 m_uiVisualTimer; + + void Reset() override + { + m_uiVisualTimer = 1000; + m_sWretchedGuids.clear(); + } + + void AttackStart(Unit* /*pWho*/) override {} + + void MoveInLineOfSight(Unit* pWho) override + { + // Cosmetic spell + if (m_sWretchedGuids.find(pWho->GetObjectGuid()) == m_sWretchedGuids.end() && pWho->IsWithinDist(m_creature, 5.0f) && pWho->IsAlive() && + (pWho->GetEntry() == NPC_SKULER || pWho->GetEntry() == NPC_BRUISER || pWho->GetEntry() == NPC_HUSK)) + { + pWho->CastSpell(m_creature, SPELL_FEL_CRYSTAL_COSMETIC, false); + m_sWretchedGuids.insert(pWho->GetObjectGuid()); + } + } + + void JustDied(Unit* /*pKiller*/) override + { + if (ScriptedInstance* pInstance = (ScriptedInstance*)m_creature->GetInstanceData()) + { + Creature* pSelin = pInstance->GetSingleCreatureFromStorage(NPC_SELIN_FIREHEART); + if (!pSelin || !pSelin->IsAlive()) + return; + + // Mark Mana rage as completed + pSelin->InterruptNonMeleeSpells(false); + if (boss_selin_fireheartAI* pBossAI = dynamic_cast(pSelin->AI())) + pBossAI->ManaRageComplete(); + } + } + + void UpdateAI(const uint32 uiDiff) override + { + if (m_uiVisualTimer) + { + if (m_uiVisualTimer <= uiDiff) + { + if (DoCastSpellIfCan(m_creature, SPELL_FEL_CRYSTAL_VISUAL) == CAST_OK) + m_uiVisualTimer = 0; + } + else + m_uiVisualTimer -= uiDiff; + } + } +}; + +CreatureAI* GetAI_mob_fel_crystal(Creature* pCreature) +{ + return new mob_fel_crystalAI(pCreature); +}; + +void AddSC_boss_selin_fireheart() +{ + Script* pNewScript; + + pNewScript = new Script; + pNewScript->Name = "boss_selin_fireheart"; + pNewScript->GetAI = &GetAI_boss_selin_fireheart; + pNewScript->RegisterSelf(); + + pNewScript = new Script; + pNewScript->Name = "mob_fel_crystal"; + pNewScript->GetAI = &GetAI_mob_fel_crystal; + pNewScript->RegisterSelf(); +} diff --git a/src/modules/SD2/scripts/eastern_kingdoms/magisters_terrace/boss_vexallus.cpp b/src/modules/SD2/scripts/eastern_kingdoms/magisters_terrace/boss_vexallus.cpp new file mode 100644 index 000000000..aa4b635c3 --- /dev/null +++ b/src/modules/SD2/scripts/eastern_kingdoms/magisters_terrace/boss_vexallus.cpp @@ -0,0 +1,239 @@ +/* Copyright (C) 2006 - 2013 ScriptDev2 + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with 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: Timers. +SDCategory: Magister's Terrace +EndScriptData */ + +#include "precompiled.h" +#include "magisters_terrace.h" +#include "TemporarySummon.h" + +enum +{ + SAY_AGGRO = -1585007, + SAY_ENERGY = -1585008, + 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, + SPELL_ENERGY_PASSIVE = 44326, + + // Vexallus spell info + SPELL_CHAIN_LIGHTNING = 44318, + SPELL_CHAIN_LIGHTNING_H = 46380, // heroic spell + SPELL_OVERLOAD = 44353, + SPELL_ARCANE_SHOCK = 44319, + SPELL_ARCANE_SHOCK_H = 46381, // heroic spell + + SPELL_SUMMON_PURE_ENERGY = 44322, // mod scale -10 + SPELL_SUMMON_PURE_ENERGY1_H = 46154, // mod scale -5 + SPELL_SUMMON_PURE_ENERGY2_H = 46159, // mod scale -5 + + // Creatures + NPC_PURE_ENERGY = 24745, +}; + +struct boss_vexallusAI : public ScriptedAI +{ + boss_vexallusAI(Creature* pCreature) : ScriptedAI(pCreature) + { + m_pInstance = (ScriptedInstance*)pCreature->GetInstanceData(); + m_bIsRegularMode = pCreature->GetMap()->IsRegularDifficulty(); + Reset(); + } + + ScriptedInstance* m_pInstance; + bool m_bIsRegularMode; + + uint32 m_uiChainLightningTimer; + uint32 m_uiArcaneShockTimer; + uint32 m_uiOverloadTimer; + uint32 m_uiIntervalHealthAmount; + bool m_bEnraged; + + void Reset() override + { + m_uiChainLightningTimer = 8000; + m_uiArcaneShockTimer = 5000; + m_uiOverloadTimer = 1200; + m_uiIntervalHealthAmount = 1; + m_bEnraged = false; + } + + void KilledUnit(Unit* /*pVictim*/) override + { + DoScriptText(SAY_KILL, m_creature); + } + + void JustReachedHome() override + { + if (m_pInstance) + m_pInstance->SetData(TYPE_VEXALLUS, FAIL); + } + + void JustDied(Unit* /*pKiller*/) override + { + if (m_pInstance) + m_pInstance->SetData(TYPE_VEXALLUS, DONE); + } + + void Aggro(Unit* /*pWho*/) override + { + DoScriptText(SAY_AGGRO, m_creature); + + if (m_pInstance) + m_pInstance->SetData(TYPE_VEXALLUS, IN_PROGRESS); + } + + void JustSummoned(Creature* pSummoned) override + { + if (Unit* pTarget = m_creature->SelectAttackingTarget(ATTACKING_TARGET_RANDOM, 0)) + pSummoned->GetMotionMaster()->MoveFollow(pTarget, 0.0f, 0.0f); + + pSummoned->CastSpell(pSummoned, SPELL_ENERGY_PASSIVE, true, NULL, NULL, m_creature->GetObjectGuid()); + pSummoned->CastSpell(pSummoned, SPELL_ENERGY_BOLT, true, NULL, NULL, m_creature->GetObjectGuid()); + } + + void UpdateAI(const uint32 uiDiff) override + { + if (!m_creature->SelectHostileTarget() || !m_creature->getVictim()) + return; + + if (!m_bEnraged) + { + // Enrage at 20% hp + if (m_creature->GetHealthPercent() < 20.0f) + { + m_bEnraged = true; + return; + } + + // used for check, when Vexallus cast adds 85%, 70%, 55%, 40%, 25% + if (m_creature->GetHealthPercent() <= float(100.0f - 15.0f * m_uiIntervalHealthAmount)) + { + DoScriptText(SAY_ENERGY, m_creature); + DoScriptText(EMOTE_DISCHARGE_ENERGY, m_creature); + ++m_uiIntervalHealthAmount; + + if (m_bIsRegularMode) + DoCastSpellIfCan(m_creature, SPELL_SUMMON_PURE_ENERGY); + else + { + DoCastSpellIfCan(m_creature, SPELL_SUMMON_PURE_ENERGY1_H, CAST_TRIGGERED); + DoCastSpellIfCan(m_creature, SPELL_SUMMON_PURE_ENERGY2_H, CAST_TRIGGERED); + } + } + + if (m_uiChainLightningTimer < uiDiff) + { + if (Unit* pTarget = m_creature->SelectAttackingTarget(ATTACKING_TARGET_RANDOM, 0)) + { + if (DoCastSpellIfCan(pTarget, m_bIsRegularMode ? SPELL_CHAIN_LIGHTNING : SPELL_CHAIN_LIGHTNING_H) == CAST_OK) + m_uiChainLightningTimer = 8000; + } + } + else + m_uiChainLightningTimer -= uiDiff; + + if (m_uiArcaneShockTimer < uiDiff) + { + if (Unit* pTarget = m_creature->SelectAttackingTarget(ATTACKING_TARGET_RANDOM, 0)) + { + if (DoCastSpellIfCan(pTarget, m_bIsRegularMode ? SPELL_ARCANE_SHOCK : SPELL_ARCANE_SHOCK_H) == CAST_OK) + m_uiArcaneShockTimer = 8000; + } + } + else + m_uiArcaneShockTimer -= uiDiff; + } + else + { + if (m_uiOverloadTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature, SPELL_OVERLOAD) == CAST_OK) + m_uiOverloadTimer = 2000; + } + else + m_uiOverloadTimer -= uiDiff; + } + + DoMeleeAttackIfReady(); + } +}; + +CreatureAI* GetAI_boss_vexallus(Creature* pCreature) +{ + return new boss_vexallusAI(pCreature); +}; + +struct mob_pure_energyAI : public ScriptedAI +{ + mob_pure_energyAI(Creature* pCreature) : ScriptedAI(pCreature) {Reset();} + + void Reset() override { } + + void JustDied(Unit* pKiller) override + { + if (m_creature->IsTemporarySummon()) + { + TemporarySummon* pTemporary = (TemporarySummon*)m_creature; + + if (pTemporary->GetSummonerGuid().IsCreature()) + { + Creature* pVex = m_creature->GetMap()->GetCreature(pTemporary->GetSummonerGuid()); + + if (!pVex || !pVex->IsAlive()) + return; + + if (Player* pPlayer = pKiller->GetCharmerOrOwnerPlayerOrPlayerItself()) + pPlayer->CastSpell(pPlayer, SPELL_ENERGY_FEEDBACK, true, NULL, NULL, pVex->GetObjectGuid()); + } + } + } + + void MoveInLineOfSight(Unit* /*pWho*/) override {} + void AttackStart(Unit* /*pWho*/) override {} +}; + +CreatureAI* GetAI_mob_pure_energy(Creature* pCreature) +{ + return new mob_pure_energyAI(pCreature); +}; + +void AddSC_boss_vexallus() +{ + Script* pNewScript; + + pNewScript = new Script; + pNewScript->Name = "boss_vexallus"; + pNewScript->GetAI = &GetAI_boss_vexallus; + pNewScript->RegisterSelf(); + + pNewScript = new Script; + pNewScript->Name = "mob_pure_energy"; + pNewScript->GetAI = &GetAI_mob_pure_energy; + pNewScript->RegisterSelf(); +} diff --git a/src/modules/SD2/scripts/eastern_kingdoms/magisters_terrace/instance_magisters_terrace.cpp b/src/modules/SD2/scripts/eastern_kingdoms/magisters_terrace/instance_magisters_terrace.cpp new file mode 100644 index 000000000..673e4384a --- /dev/null +++ b/src/modules/SD2/scripts/eastern_kingdoms/magisters_terrace/instance_magisters_terrace.cpp @@ -0,0 +1,238 @@ +/* Copyright (C) 2006 - 2013 ScriptDev2 + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along 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: 80 +SDComment: +SDCategory: Magister's Terrace +EndScriptData */ + +#include "precompiled.h" +#include "magisters_terrace.h" + +/* +0 - Selin Fireheart +1 - Vexallus +2 - Priestess Delrissa +3 - Kael'thas Sunstrider +*/ + +instance_magisters_terrace::instance_magisters_terrace(Map* pMap) : ScriptedInstance(pMap), + m_uiDelrissaDeathCount(0) +{ + Initialize(); +} + +void instance_magisters_terrace::Initialize() +{ + memset(&m_auiEncounter, 0, sizeof(m_auiEncounter)); +} + +void instance_magisters_terrace::OnCreatureCreate(Creature* pCreature) +{ + switch (pCreature->GetEntry()) + { + case NPC_SELIN_FIREHEART: + case NPC_DELRISSA: + case NPC_KALECGOS_DRAGON: + case NPC_KAELTHAS: + // insert Delrissa adds here, for better handling + case NPC_KAGANI: + case NPC_ELLRYS: + case NPC_ERAMAS: + case NPC_YAZZAI: + case NPC_SALARIS: + case NPC_GARAXXAS: + case NPC_APOKO: + case NPC_ZELFAN: + m_mNpcEntryGuidStore[pCreature->GetEntry()] = pCreature->GetObjectGuid(); + break; + case NPC_FEL_CRYSTAL: + m_lFelCrystalGuid.push_back(pCreature->GetObjectGuid()); + break; + } +} + +void instance_magisters_terrace::OnObjectCreate(GameObject* pGo) +{ + switch (pGo->GetEntry()) + { + case GO_VEXALLUS_DOOR: + if (m_auiEncounter[TYPE_VEXALLUS] == DONE) + pGo->SetGoState(GO_STATE_ACTIVE); + break; + case GO_SELIN_DOOR: + if (m_auiEncounter[TYPE_SELIN] == DONE) + pGo->SetGoState(GO_STATE_ACTIVE); + break; + case GO_DELRISSA_DOOR: + if (m_auiEncounter[TYPE_DELRISSA] == DONE) + pGo->SetGoState(GO_STATE_ACTIVE); + break; + case GO_SELIN_ENCOUNTER_DOOR: + case GO_KAEL_DOOR: + case GO_ESCAPE_QUEL_DANAS: + break; + + default: + return; + } + m_mGoEntryGuidStore[pGo->GetEntry()] = pGo->GetObjectGuid(); +} + +void instance_magisters_terrace::OnCreatureDeath(Creature* pCreature) +{ + switch (pCreature->GetEntry()) + { + case NPC_KAGANI: + case NPC_ELLRYS: + case NPC_ERAMAS: + case NPC_YAZZAI: + case NPC_SALARIS: + case NPC_GARAXXAS: + case NPC_APOKO: + case NPC_ZELFAN: + ++m_uiDelrissaDeathCount; + if (m_uiDelrissaDeathCount == MAX_DELRISSA_ADDS) + SetData(TYPE_DELRISSA, SPECIAL); + // yell on summoned death + if (Creature* pDelrissa = GetSingleCreatureFromStorage(NPC_DELRISSA)) + { + if (pDelrissa->IsAlive()) + DoScriptText(aDelrissaAddDeath[m_uiDelrissaDeathCount - 1], pDelrissa); + else if (GetData(TYPE_DELRISSA) == SPECIAL) + { + SetData(TYPE_DELRISSA, DONE); + pDelrissa->SetFlag(UNIT_DYNAMIC_FLAGS, UNIT_DYNFLAG_LOOTABLE); + } + } + break; + } +} + +void instance_magisters_terrace::SetData(uint32 uiType, uint32 uiData) +{ + switch (uiType) + { + case TYPE_SELIN: + if (uiData == DONE) + DoUseDoorOrButton(GO_SELIN_DOOR); + if (uiData == FAIL) + { + // Reset crystals - respawn and kill is handled by creature linking + for (GuidList::const_iterator itr = m_lFelCrystalGuid.begin(); itr != m_lFelCrystalGuid.end(); ++itr) + { + if (Creature* pTemp = instance->GetCreature(*itr)) + { + if (!pTemp->HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE)) + pTemp->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE); + + if (pTemp->IsAlive()) + pTemp->AI()->EnterEvadeMode(); + } + } + } + if (uiData == IN_PROGRESS) + { + // Stop channeling when the fight starts + for (GuidList::const_iterator itr = m_lFelCrystalGuid.begin(); itr != m_lFelCrystalGuid.end(); ++itr) + { + if (Creature* pTemp = instance->GetCreature(*itr)) + pTemp->InterruptNonMeleeSpells(false); + } + } + DoUseDoorOrButton(GO_SELIN_ENCOUNTER_DOOR); + m_auiEncounter[uiType] = uiData; + break; + case TYPE_VEXALLUS: + if (uiData == DONE) + DoUseDoorOrButton(GO_VEXALLUS_DOOR); + m_auiEncounter[uiType] = uiData; + break; + case TYPE_DELRISSA: + if (uiData == DONE) + DoUseDoorOrButton(GO_DELRISSA_DOOR); + if (uiData == IN_PROGRESS) + m_uiDelrissaDeathCount = 0; + m_auiEncounter[uiType] = uiData; + break; + case TYPE_KAELTHAS: + DoUseDoorOrButton(GO_KAEL_DOOR); + if (uiData == DONE) + DoToggleGameObjectFlags(GO_ESCAPE_QUEL_DANAS, GO_FLAG_NO_INTERACT, false); + m_auiEncounter[uiType] = uiData; + 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_strInstData = saveStream.str(); + + SaveToDB(); + OUT_SAVE_INST_DATA_COMPLETE; + } +} + +void instance_magisters_terrace::Load(const char* chrIn) +{ + if (!chrIn) + { + 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 < MAX_ENCOUNTER; ++i) + { + if (m_auiEncounter[i] == IN_PROGRESS) + m_auiEncounter[i] = NOT_STARTED; + } + + OUT_LOAD_INST_DATA_COMPLETE; +} + +uint32 instance_magisters_terrace::GetData(uint32 uiType) const +{ + if (uiType < MAX_ENCOUNTER) + return m_auiEncounter[uiType]; + + return 0; +} + +InstanceData* GetInstanceData_instance_magisters_terrace(Map* pMap) +{ + return new instance_magisters_terrace(pMap); +} + +void AddSC_instance_magisters_terrace() +{ + Script* pNewScript; + + pNewScript = new Script; + pNewScript->Name = "instance_magisters_terrace"; + pNewScript->GetInstanceData = &GetInstanceData_instance_magisters_terrace; + pNewScript->RegisterSelf(); +} diff --git a/src/modules/SD2/scripts/eastern_kingdoms/magisters_terrace/magisters_terrace.cpp b/src/modules/SD2/scripts/eastern_kingdoms/magisters_terrace/magisters_terrace.cpp new file mode 100644 index 000000000..edce2c120 --- /dev/null +++ b/src/modules/SD2/scripts/eastern_kingdoms/magisters_terrace/magisters_terrace.cpp @@ -0,0 +1,132 @@ +/* Copyright (C) 2006 - 2013 ScriptDev2 + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along 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" +#include "magisters_terrace.h" + +/*###### +## npc_kalecgos +######*/ + +enum +{ + SPELL_TRANSFORM_TO_KAEL = 44670, + SPELL_ORB_KILL_CREDIT = 46307, + NPC_KALECGOS = 24848, // human form entry + + MAP_ID_MAGISTER = 585, +}; + +static const float afKaelLandPoint[4] = {200.36f, -270.77f, -8.73f, 0.01f}; + +// 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 npc_kalecgosAI : public ScriptedAI +{ + npc_kalecgosAI(Creature* pCreature) : ScriptedAI(pCreature) { Reset(); } + + uint32 m_uiTransformTimer; + + void Reset() override + { + // Check the map id because the same creature entry is involved in other scripted event in other instance + if (m_creature->GetMapId() != MAP_ID_MAGISTER) + return; + + m_uiTransformTimer = 0; + + // Move the dragon to landing point + m_creature->GetMotionMaster()->MovePoint(1, afKaelLandPoint[0], afKaelLandPoint[1], afKaelLandPoint[2]); + } + + void MovementInform(uint32 uiType, uint32 uiPointId) override + { + if (uiType != POINT_MOTION_TYPE) + return; + + if (uiPointId) + { + m_creature->SetLevitate(false); + m_creature->SetFacingTo(afKaelLandPoint[3]); + m_uiTransformTimer = MINUTE * IN_MILLISECONDS; + } + } + + void UpdateAI(const uint32 uiDiff) override + { + if (m_uiTransformTimer) + { + if (m_uiTransformTimer <= uiDiff) + { + // Transform and update entry, now ready for quest/read gossip + if (DoCastSpellIfCan(m_creature, SPELL_TRANSFORM_TO_KAEL) == CAST_OK) + { + DoCastSpellIfCan(m_creature, SPELL_ORB_KILL_CREDIT, CAST_TRIGGERED); + m_creature->UpdateEntry(NPC_KALECGOS); + + m_uiTransformTimer = 0; + } + } + else + m_uiTransformTimer -= uiDiff; + } + } +}; + +CreatureAI* GetAI_npc_kalecgos(Creature* pCreature) +{ + return new npc_kalecgosAI(pCreature); +} + +bool ProcessEventId_event_go_scrying_orb(uint32 /*uiEventId*/, Object* pSource, Object* /*pTarget*/, bool bIsStart) +{ + if (bIsStart && pSource->GetTypeId() == TYPEID_PLAYER) + { + if (instance_magisters_terrace* pInstance = (instance_magisters_terrace*)((Player*)pSource)->GetInstanceData()) + { + // Check if the Dragon is already spawned and don't allow it to spawn it multiple times + if (pInstance->GetSingleCreatureFromStorage(NPC_KALECGOS_DRAGON, true)) + return true; + } + } + return false; +} + +void AddSC_magisters_terrace() +{ + Script* pNewScript; + + pNewScript = new Script; + pNewScript->Name = "npc_kalecgos"; + pNewScript->GetAI = &GetAI_npc_kalecgos; + pNewScript->RegisterSelf(); + + pNewScript = new Script; + pNewScript->Name = "event_go_scrying_orb"; + pNewScript->pProcessEventId = &ProcessEventId_event_go_scrying_orb; + pNewScript->RegisterSelf(); +} diff --git a/src/modules/SD2/scripts/eastern_kingdoms/magisters_terrace/magisters_terrace.h b/src/modules/SD2/scripts/eastern_kingdoms/magisters_terrace/magisters_terrace.h new file mode 100644 index 000000000..138a01498 --- /dev/null +++ b/src/modules/SD2/scripts/eastern_kingdoms/magisters_terrace/magisters_terrace.h @@ -0,0 +1,74 @@ +/* Copyright (C) 2006 - 2013 ScriptDev2 + * This program is free software licensed under GPL version 2 + * Please see the included DOCS/LICENSE.TXT for more information */ + +#ifndef DEF_MAGISTERS_TERRACE_H +#define DEF_MAGISTERS_TERRACE_H + +enum +{ + MAX_ENCOUNTER = 4, + MAX_DELRISSA_ADDS = 4, + + TYPE_SELIN = 0, + TYPE_VEXALLUS = 1, + TYPE_DELRISSA = 2, + TYPE_KAELTHAS = 3, + + NPC_SELIN_FIREHEART = 24723, + NPC_DELRISSA = 24560, + NPC_FEL_CRYSTAL = 24722, + NPC_KALECGOS_DRAGON = 24844, + NPC_KAELTHAS = 24664, + + // Delrissa adds + NPC_KAGANI = 24557, + NPC_ELLRYS = 24558, + NPC_ERAMAS = 24554, + NPC_YAZZAI = 24561, + NPC_SALARIS = 24559, + NPC_GARAXXAS = 24555, + NPC_APOKO = 24553, + NPC_ZELFAN = 24556, + + GO_VEXALLUS_DOOR = 187896, + GO_SELIN_DOOR = 187979, // SunwellRaid Gate 02 + GO_DELRISSA_DOOR = 187770, + GO_SELIN_ENCOUNTER_DOOR = 188065, // Assembly Chamber Door + + GO_KAEL_DOOR = 188064, + // GO_KAEL_STATUE_LEFT = 188165, // animation statues - they do not reset on fail + // GO_KAEL_STATUE_RIGHT = 188166, + GO_ESCAPE_QUEL_DANAS = 188173, +}; + +static const int32 aDelrissaAddDeath[MAX_DELRISSA_ADDS] = { -1585013, -1585014, -1585015, -1585016}; + +class instance_magisters_terrace : public ScriptedInstance +{ + public: + instance_magisters_terrace(Map* pMap); + + void Initialize() override; + + void OnCreatureCreate(Creature* pCreature) override; + void OnObjectCreate(GameObject* pGo) override; + + void OnCreatureDeath(Creature* pCreature) override; + + uint32 GetData(uint32 uiType) const override; + void SetData(uint32 uiType, uint32 uiData) override; + + const char* Save() const override { return m_strInstData.c_str(); } + void Load(const char* chrIn) override; + + private: + uint32 m_auiEncounter[MAX_ENCOUNTER]; + std::string m_strInstData; + + uint32 m_uiDelrissaDeathCount; + + GuidList m_lFelCrystalGuid; +}; + +#endif diff --git a/src/modules/SD2/scripts/eastern_kingdoms/redridge_mountains.cpp b/src/modules/SD2/scripts/eastern_kingdoms/redridge_mountains.cpp new file mode 100644 index 000000000..73328fbef --- /dev/null +++ b/src/modules/SD2/scripts/eastern_kingdoms/redridge_mountains.cpp @@ -0,0 +1,168 @@ +/** + * ScriptDev2 is an extension for mangos providing enhanced features for + * area triggers, creatures, game objects, instances, items, and spells beyond + * the default database scripting in mangos. + * + * Copyright (C) 2006-2013 ScriptDev2 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * World of Warcraft, and all World of Warcraft or Warcraft art, images, + * and lore are copyrighted by Blizzard Entertainment, Inc. + */ + +/** + * ScriptData + * SDName: redridge_mountains + * SD%Complete: 100 + * SDComment: Quest support: 219. + * SDCategory: Redridge Mountains + * EndScriptData + */ + +/** + * ContentData + * npc_corporal_keeshan + * EndContentData + */ + +#include "precompiled.h" +#include "escort_ai.h" + +/*###### +## npc_corporal_leehsan +######*/ + +enum +{ + QUEST_MISSING_IN_ACTION = 219, + + SPELL_MOCKING_BLOW = 21008, + SPELL_SHIELD_BASH = 11972, + + SAY_CORPORAL_KEESHAN_1 = -1000561, + SAY_CORPORAL_KEESHAN_2 = -1000562, + SAY_CORPORAL_KEESHAN_3 = -1000563, + SAY_CORPORAL_KEESHAN_4 = -1000564, + SAY_CORPORAL_KEESHAN_5 = -1000565, +}; + +struct npc_corporal_keeshan_escortAI : public npc_escortAI +{ + npc_corporal_keeshan_escortAI(Creature* pCreature) : npc_escortAI(pCreature) { Reset(); } + + uint32 m_uiMockingBlowTimer; + uint32 m_uiShieldBashTimer; + + void Reset() override + { + m_uiMockingBlowTimer = 5000; + m_uiShieldBashTimer = 8000; + } + + void ReceiveAIEvent(AIEventType eventType, Creature* /*pSender*/, Unit* pInvoker, uint32 uiMiscValue) override + { + if (eventType == AI_EVENT_START_ESCORT && pInvoker->GetTypeId() == TYPEID_PLAYER) + { + DoScriptText(SAY_CORPORAL_KEESHAN_1, m_creature); + Start(false, (Player*)pInvoker, GetQuestTemplateStore(uiMiscValue)); + } + } + + void WaypointStart(uint32 uiWP) override + { + switch (uiWP) + { + case 27: // break outside + DoScriptText(SAY_CORPORAL_KEESHAN_3, m_creature); + m_creature->SetStandState(UNIT_STAND_STATE_STAND); + break; + case 54: // say goodbye + DoScriptText(SAY_CORPORAL_KEESHAN_5, m_creature); + break; + } + } + + void WaypointReached(uint32 uiWP) override + { + switch (uiWP) + { + case 26: // break outside + m_creature->SetStandState(UNIT_STAND_STATE_SIT); + DoScriptText(SAY_CORPORAL_KEESHAN_2, m_creature); + break; + case 53: // quest_complete + DoScriptText(SAY_CORPORAL_KEESHAN_4, m_creature); + if (Player* pPlayer = GetPlayerForEscort()) + { + pPlayer->GroupEventHappens(QUEST_MISSING_IN_ACTION, m_creature); + } + break; + } + } + + void UpdateEscortAI(const uint32 uiDiff) override + { + // Combat check + if (!m_creature->SelectHostileTarget() || !m_creature->getVictim()) + { + return; + } + + if (m_uiMockingBlowTimer < uiDiff) + { + DoCastSpellIfCan(m_creature->getVictim(), SPELL_MOCKING_BLOW); + m_uiMockingBlowTimer = 5000; + } + else + { m_uiMockingBlowTimer -= uiDiff; } + + if (m_uiShieldBashTimer < uiDiff) + { + DoCastSpellIfCan(m_creature->getVictim(), SPELL_SHIELD_BASH); + m_uiShieldBashTimer = 8000; + } + else + { m_uiShieldBashTimer -= uiDiff; } + + DoMeleeAttackIfReady(); + } +}; + +CreatureAI* GetAI_npc_corporal_keeshan(Creature* pCreature) +{ + return new npc_corporal_keeshan_escortAI(pCreature); +} + +bool QuestAccept_npc_corporal_keeshan(Player* pPlayer, Creature* pCreature, const Quest* pQuest) +{ + if (pQuest->GetQuestId() == QUEST_MISSING_IN_ACTION) + { + pCreature->AI()->SendAIEvent(AI_EVENT_START_ESCORT, pPlayer, pCreature, pQuest->GetQuestId()); + } + + return true; +} + +void AddSC_redridge_mountains() +{ + Script* pNewScript; + + pNewScript = new Script; + pNewScript->Name = "npc_corporal_keeshan"; + pNewScript->GetAI = &GetAI_npc_corporal_keeshan; + pNewScript->pQuestAcceptNPC = &QuestAccept_npc_corporal_keeshan; + pNewScript->RegisterSelf(); +} diff --git a/src/modules/SD2/scripts/eastern_kingdoms/scarlet_enclave/ebon_hold.cpp b/src/modules/SD2/scripts/eastern_kingdoms/scarlet_enclave/ebon_hold.cpp new file mode 100644 index 000000000..28209a28a --- /dev/null +++ b/src/modules/SD2/scripts/eastern_kingdoms/scarlet_enclave/ebon_hold.cpp @@ -0,0 +1,2978 @@ +/* Copyright (C) 2006 - 2013 ScriptDev2 + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +/* ScriptData +SDName: Ebon_Hold +SD%Complete: 95 +SDComment: Quest support: 12641, 12687, 12698, 12727, 12733, 12739(and 12742 to 12750), 12801, 12848 +SDCategory: Ebon Hold +EndScriptData */ + +/* ContentData +npc_a_special_surprise +npc_death_knight_initiate +npc_unworthy_initiate_anchor +npc_unworthy_initiate +go_acherus_soul_prison +npc_eye_of_acherus +npc_scarlet_ghoul +npc_highlord_darion_mograine +npc_fellow_death_knight +npc_acherus_deathcharger +EndContentData */ + +#include "precompiled.h" +#include "escort_ai.h" +#include "world_map_ebon_hold.h" +#include "pet_ai.h" + +/*###### +## npc_a_special_surprise +######*/ + +enum SpecialSurprise +{ + SAY_EXEC_START_1 = -1609025, // speech for all + SAY_EXEC_START_2 = -1609026, + SAY_EXEC_START_3 = -1609027, + SAY_EXEC_PROG_1 = -1609028, + SAY_EXEC_PROG_2 = -1609029, + SAY_EXEC_PROG_3 = -1609030, + SAY_EXEC_PROG_4 = -1609031, + SAY_EXEC_PROG_5 = -1609032, + SAY_EXEC_PROG_6 = -1609033, + SAY_EXEC_PROG_7 = -1609034, + SAY_EXEC_NAME_1 = -1609035, + SAY_EXEC_NAME_2 = -1609036, + SAY_EXEC_RECOG_1 = -1609037, + SAY_EXEC_RECOG_2 = -1609038, + SAY_EXEC_RECOG_3 = -1609039, + SAY_EXEC_RECOG_4 = -1609040, + SAY_EXEC_RECOG_5 = -1609041, + SAY_EXEC_RECOG_6 = -1609042, + SAY_EXEC_NOREM_1 = -1609043, + SAY_EXEC_NOREM_2 = -1609044, + SAY_EXEC_NOREM_3 = -1609045, + SAY_EXEC_NOREM_4 = -1609046, + SAY_EXEC_NOREM_5 = -1609047, + SAY_EXEC_NOREM_6 = -1609048, + SAY_EXEC_NOREM_7 = -1609049, + SAY_EXEC_NOREM_8 = -1609050, + SAY_EXEC_NOREM_9 = -1609051, + SAY_EXEC_THINK_1 = -1609052, + SAY_EXEC_THINK_2 = -1609053, + SAY_EXEC_THINK_3 = -1609054, + SAY_EXEC_THINK_4 = -1609055, + SAY_EXEC_THINK_5 = -1609056, + SAY_EXEC_THINK_6 = -1609057, + SAY_EXEC_THINK_7 = -1609058, + SAY_EXEC_THINK_8 = -1609059, + SAY_EXEC_THINK_9 = -1609060, + SAY_EXEC_THINK_10 = -1609061, + SAY_EXEC_LISTEN_1 = -1609062, + SAY_EXEC_LISTEN_2 = -1609063, + SAY_EXEC_LISTEN_3 = -1609064, + SAY_EXEC_LISTEN_4 = -1609065, + SAY_PLAGUEFIST = -1609066, + SAY_EXEC_TIME_1 = -1609067, + SAY_EXEC_TIME_2 = -1609068, + SAY_EXEC_TIME_3 = -1609069, + SAY_EXEC_TIME_4 = -1609070, + SAY_EXEC_TIME_5 = -1609071, + SAY_EXEC_TIME_6 = -1609072, + SAY_EXEC_TIME_7 = -1609073, + SAY_EXEC_TIME_8 = -1609074, + SAY_EXEC_TIME_9 = -1609075, + SAY_EXEC_TIME_10 = -1609076, + SAY_EXEC_WAITING = -1609077, + EMOTE_DIES = -1609078, + + NPC_PLAGUEFIST = 29053 +}; + +struct npc_a_special_surpriseAI : public ScriptedAI +{ + npc_a_special_surpriseAI(Creature* pCreature) : ScriptedAI(pCreature) { Reset(); } + + uint32 m_uiExecuteSpeech_Timer; + uint32 m_uiExecuteSpeech_Counter; + ObjectGuid m_playerGuid; + + void Reset() override + { + m_uiExecuteSpeech_Timer = 0; + m_uiExecuteSpeech_Counter = 0; + m_playerGuid.Clear(); + } + + bool MeetQuestCondition(Player* pPlayer) + { + switch (m_creature->GetEntry()) + { + case 29061: // Ellen Stanbridge + if (pPlayer->GetQuestStatus(12742) == QUEST_STATUS_INCOMPLETE) + return true; + break; + case 29072: // Kug Ironjaw + if (pPlayer->GetQuestStatus(12748) == QUEST_STATUS_INCOMPLETE) + return true; + break; + case 29067: // Donovan Pulfrost + if (pPlayer->GetQuestStatus(12744) == QUEST_STATUS_INCOMPLETE) + return true; + break; + case 29065: // Yazmina Oakenthorn + if (pPlayer->GetQuestStatus(12743) == QUEST_STATUS_INCOMPLETE) + return true; + break; + case 29071: // Antoine Brack + if (pPlayer->GetQuestStatus(12750) == QUEST_STATUS_INCOMPLETE) + return true; + break; + case 29032: // Malar Bravehorn + if (pPlayer->GetQuestStatus(12739) == QUEST_STATUS_INCOMPLETE) + return true; + break; + case 29068: // Goby Blastenheimer + if (pPlayer->GetQuestStatus(12745) == QUEST_STATUS_INCOMPLETE) + return true; + break; + case 29073: // Iggy Darktusk + if (pPlayer->GetQuestStatus(12749) == QUEST_STATUS_INCOMPLETE) + return true; + break; + case 29074: // Lady Eonys + if (pPlayer->GetQuestStatus(12747) == QUEST_STATUS_INCOMPLETE) + return true; + break; + case 29070: // Valok the Righteous + if (pPlayer->GetQuestStatus(12746) == QUEST_STATUS_INCOMPLETE) + return true; + break; + } + + return false; + } + + void MoveInLineOfSight(Unit* pWho) override + { + if (m_playerGuid || pWho->GetTypeId() != TYPEID_PLAYER || !pWho->IsWithinDist(m_creature, INTERACTION_DISTANCE)) + return; + + if (MeetQuestCondition((Player*)pWho)) + m_playerGuid = pWho->GetObjectGuid(); + } + + void UpdateAI(const uint32 uiDiff) override + { + if (m_playerGuid && !m_creature->getVictim() && m_creature->IsAlive()) + { + if (m_uiExecuteSpeech_Timer < uiDiff) + { + Player* pPlayer = m_creature->GetMap()->GetPlayer(m_playerGuid); + + if (!pPlayer) + { + Reset(); + return; + } + + // TODO: simplify text's selection + + switch (pPlayer->getRace()) + { + case RACE_HUMAN: + switch (m_uiExecuteSpeech_Counter) + { + case 0: DoScriptText(SAY_EXEC_START_1, m_creature, pPlayer); break; + case 1: m_creature->SetStandState(UNIT_STAND_STATE_STAND); break; + case 2: DoScriptText(SAY_EXEC_PROG_5, m_creature, pPlayer); break; + case 3: DoScriptText(SAY_EXEC_NAME_1, m_creature, pPlayer); break; + case 4: DoScriptText(SAY_EXEC_RECOG_1, m_creature, pPlayer); break; + case 5: DoScriptText(SAY_EXEC_NOREM_5, m_creature, pPlayer); break; + case 6: DoScriptText(SAY_EXEC_THINK_7, m_creature, pPlayer); break; + case 7: DoScriptText(SAY_EXEC_LISTEN_1, m_creature, pPlayer); break; + case 8: + if (Creature* pPlaguefist = GetClosestCreatureWithEntry(m_creature, NPC_PLAGUEFIST, 85.0f)) + DoScriptText(SAY_PLAGUEFIST, pPlaguefist, pPlayer); + break; + case 9: + DoScriptText(SAY_EXEC_TIME_6, m_creature, pPlayer); + m_creature->SetStandState(UNIT_STAND_STATE_KNEEL); + m_creature->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_OOC_NOT_ATTACKABLE); + break; + case 10: DoScriptText(SAY_EXEC_WAITING, m_creature, pPlayer); break; + case 11: + DoScriptText(EMOTE_DIES, m_creature); + m_creature->SetDeathState(JUST_DIED); + m_creature->SetHealth(0); + return; + } + break; + case RACE_ORC: + switch (m_uiExecuteSpeech_Counter) + { + case 0: DoScriptText(SAY_EXEC_START_1, m_creature, pPlayer); break; + case 1: m_creature->SetStandState(UNIT_STAND_STATE_STAND); break; + case 2: DoScriptText(SAY_EXEC_PROG_6, m_creature, pPlayer); break; + case 3: DoScriptText(SAY_EXEC_NAME_1, m_creature, pPlayer); break; + case 4: DoScriptText(SAY_EXEC_RECOG_1, m_creature, pPlayer); break; + case 5: DoScriptText(SAY_EXEC_NOREM_7, m_creature, pPlayer); break; + case 6: DoScriptText(SAY_EXEC_THINK_8, m_creature, pPlayer); break; + case 7: DoScriptText(SAY_EXEC_LISTEN_1, m_creature, pPlayer); break; + case 8: + if (Creature* pPlaguefist = GetClosestCreatureWithEntry(m_creature, NPC_PLAGUEFIST, 85.0f)) + DoScriptText(SAY_PLAGUEFIST, pPlaguefist, pPlayer); + break; + case 9: + DoScriptText(SAY_EXEC_TIME_8, m_creature, pPlayer); + m_creature->SetStandState(UNIT_STAND_STATE_KNEEL); + m_creature->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_OOC_NOT_ATTACKABLE); + break; + case 10: DoScriptText(SAY_EXEC_WAITING, m_creature, pPlayer); break; + case 11: + DoScriptText(EMOTE_DIES, m_creature); + m_creature->SetDeathState(JUST_DIED); + m_creature->SetHealth(0); + return; + } + break; + case RACE_DWARF: + switch (m_uiExecuteSpeech_Counter) + { + case 0: DoScriptText(SAY_EXEC_START_2, m_creature, pPlayer); break; + case 1: m_creature->SetStandState(UNIT_STAND_STATE_STAND); break; + case 2: DoScriptText(SAY_EXEC_PROG_2, m_creature, pPlayer); break; + case 3: DoScriptText(SAY_EXEC_NAME_1, m_creature, pPlayer); break; + case 4: DoScriptText(SAY_EXEC_RECOG_3, m_creature, pPlayer); break; + case 5: DoScriptText(SAY_EXEC_NOREM_2, m_creature, pPlayer); break; + case 6: DoScriptText(SAY_EXEC_THINK_5, m_creature, pPlayer); break; + case 7: DoScriptText(SAY_EXEC_LISTEN_2, m_creature, pPlayer); break; + case 8: + if (Creature* pPlaguefist = GetClosestCreatureWithEntry(m_creature, NPC_PLAGUEFIST, 85.0f)) + DoScriptText(SAY_PLAGUEFIST, pPlaguefist, pPlayer); + break; + case 9: + DoScriptText(SAY_EXEC_TIME_3, m_creature, pPlayer); + m_creature->SetStandState(UNIT_STAND_STATE_KNEEL); + m_creature->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_OOC_NOT_ATTACKABLE); + break; + case 10: DoScriptText(SAY_EXEC_WAITING, m_creature, pPlayer); break; + case 11: + DoScriptText(EMOTE_DIES, m_creature); + m_creature->SetDeathState(JUST_DIED); + m_creature->SetHealth(0); + return; + } + break; + case RACE_NIGHTELF: + switch (m_uiExecuteSpeech_Counter) + { + case 0: DoScriptText(SAY_EXEC_START_1, m_creature, pPlayer); break; + case 1: m_creature->SetStandState(UNIT_STAND_STATE_STAND); break; + case 2: DoScriptText(SAY_EXEC_PROG_5, m_creature, pPlayer); break; + case 3: DoScriptText(SAY_EXEC_NAME_1, m_creature, pPlayer); break; + case 4: DoScriptText(SAY_EXEC_RECOG_1, m_creature, pPlayer); break; + case 5: DoScriptText(SAY_EXEC_NOREM_6, m_creature, pPlayer); break; + case 6: DoScriptText(SAY_EXEC_THINK_2, m_creature, pPlayer); break; + case 7: DoScriptText(SAY_EXEC_LISTEN_1, m_creature, pPlayer); break; + case 8: + if (Creature* pPlaguefist = GetClosestCreatureWithEntry(m_creature, NPC_PLAGUEFIST, 85.0f)) + DoScriptText(SAY_PLAGUEFIST, pPlaguefist, pPlayer); + break; + case 9: + DoScriptText(SAY_EXEC_TIME_7, m_creature, pPlayer); + m_creature->SetStandState(UNIT_STAND_STATE_KNEEL); + m_creature->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_OOC_NOT_ATTACKABLE); + break; + case 10: DoScriptText(SAY_EXEC_WAITING, m_creature, pPlayer); break; + case 11: + DoScriptText(EMOTE_DIES, m_creature); + m_creature->SetDeathState(JUST_DIED); + m_creature->SetHealth(0); + return; + } + break; + case RACE_UNDEAD: + switch (m_uiExecuteSpeech_Counter) + { + case 0: DoScriptText(SAY_EXEC_START_1, m_creature, pPlayer); break; + case 1: m_creature->SetStandState(UNIT_STAND_STATE_STAND); break; + case 2: DoScriptText(SAY_EXEC_PROG_3, m_creature, pPlayer); break; + case 3: DoScriptText(SAY_EXEC_NAME_1, m_creature, pPlayer); break; + case 4: DoScriptText(SAY_EXEC_RECOG_4, m_creature, pPlayer); break; + case 5: DoScriptText(SAY_EXEC_NOREM_3, m_creature, pPlayer); break; + case 6: DoScriptText(SAY_EXEC_THINK_1, m_creature, pPlayer); break; + case 7: DoScriptText(SAY_EXEC_LISTEN_3, m_creature, pPlayer); break; + case 8: + if (Creature* pPlaguefist = GetClosestCreatureWithEntry(m_creature, NPC_PLAGUEFIST, 85.0f)) + DoScriptText(SAY_PLAGUEFIST, pPlaguefist, pPlayer); + break; + case 9: + DoScriptText(SAY_EXEC_TIME_4, m_creature, pPlayer); + m_creature->SetStandState(UNIT_STAND_STATE_KNEEL); + m_creature->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_OOC_NOT_ATTACKABLE); + break; + case 10: DoScriptText(SAY_EXEC_WAITING, m_creature, pPlayer); break; + case 11: + DoScriptText(EMOTE_DIES, m_creature); + m_creature->SetDeathState(JUST_DIED); + m_creature->SetHealth(0); + return; + } + break; + case RACE_TAUREN: + switch (m_uiExecuteSpeech_Counter) + { + case 0: DoScriptText(SAY_EXEC_START_1, m_creature, pPlayer); break; + case 1: m_creature->SetStandState(UNIT_STAND_STATE_STAND); break; + case 2: DoScriptText(SAY_EXEC_PROG_1, m_creature, pPlayer); break; + case 3: DoScriptText(SAY_EXEC_NAME_1, m_creature, pPlayer); break; + case 4: DoScriptText(SAY_EXEC_RECOG_5, m_creature, pPlayer); break; + case 5: DoScriptText(SAY_EXEC_NOREM_8, m_creature, pPlayer); break; + case 6: DoScriptText(SAY_EXEC_THINK_9, m_creature, pPlayer); break; + case 7: DoScriptText(SAY_EXEC_LISTEN_1, m_creature, pPlayer); break; + case 8: + if (Creature* pPlaguefist = GetClosestCreatureWithEntry(m_creature, NPC_PLAGUEFIST, 85.0f)) + DoScriptText(SAY_PLAGUEFIST, pPlaguefist, pPlayer); + break; + case 9: + DoScriptText(SAY_EXEC_TIME_9, m_creature, pPlayer); + m_creature->SetStandState(UNIT_STAND_STATE_KNEEL); + m_creature->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_OOC_NOT_ATTACKABLE); + break; + case 10: DoScriptText(SAY_EXEC_WAITING, m_creature, pPlayer); break; + case 11: + DoScriptText(EMOTE_DIES, m_creature); + m_creature->SetDeathState(JUST_DIED); + m_creature->SetHealth(0); + return; + } + break; + case RACE_GNOME: + switch (m_uiExecuteSpeech_Counter) + { + case 0: DoScriptText(SAY_EXEC_START_1, m_creature, pPlayer); break; + case 1: m_creature->SetStandState(UNIT_STAND_STATE_STAND); break; + case 2: DoScriptText(SAY_EXEC_PROG_4, m_creature, pPlayer); break; + case 3: DoScriptText(SAY_EXEC_NAME_1, m_creature, pPlayer); break; + case 4: DoScriptText(SAY_EXEC_RECOG_1, m_creature, pPlayer); break; + case 5: DoScriptText(SAY_EXEC_NOREM_4, m_creature, pPlayer); break; + case 6: DoScriptText(SAY_EXEC_THINK_6, m_creature, pPlayer); break; + case 7: DoScriptText(SAY_EXEC_LISTEN_1, m_creature, pPlayer); break; + case 8: + if (Creature* pPlaguefist = GetClosestCreatureWithEntry(m_creature, NPC_PLAGUEFIST, 85.0f)) + DoScriptText(SAY_PLAGUEFIST, pPlaguefist, pPlayer); + break; + case 9: + DoScriptText(SAY_EXEC_TIME_5, m_creature, pPlayer); + m_creature->SetStandState(UNIT_STAND_STATE_KNEEL); + m_creature->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_OOC_NOT_ATTACKABLE); + break; + case 10: DoScriptText(SAY_EXEC_WAITING, m_creature, pPlayer); break; + case 11: + DoScriptText(EMOTE_DIES, m_creature); + m_creature->SetDeathState(JUST_DIED); + m_creature->SetHealth(0); + return; + } + break; + case RACE_TROLL: + switch (m_uiExecuteSpeech_Counter) + { + case 0: DoScriptText(SAY_EXEC_START_3, m_creature, pPlayer); break; + case 1: m_creature->SetStandState(UNIT_STAND_STATE_STAND); break; + case 2: DoScriptText(SAY_EXEC_PROG_7, m_creature, pPlayer); break; + case 3: DoScriptText(SAY_EXEC_NAME_2, m_creature, pPlayer); break; + case 4: DoScriptText(SAY_EXEC_RECOG_6, m_creature, pPlayer); break; + case 5: DoScriptText(SAY_EXEC_NOREM_9, m_creature, pPlayer); break; + case 6: DoScriptText(SAY_EXEC_THINK_10, m_creature, pPlayer); break; + case 7: DoScriptText(SAY_EXEC_LISTEN_4, m_creature, pPlayer); break; + case 8: + if (Creature* pPlaguefist = GetClosestCreatureWithEntry(m_creature, NPC_PLAGUEFIST, 85.0f)) + DoScriptText(SAY_PLAGUEFIST, pPlaguefist, pPlayer); + break; + case 9: + DoScriptText(SAY_EXEC_TIME_10, m_creature, pPlayer); + m_creature->SetStandState(UNIT_STAND_STATE_KNEEL); + m_creature->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_OOC_NOT_ATTACKABLE); + break; + case 10: DoScriptText(SAY_EXEC_WAITING, m_creature, pPlayer); break; + case 11: + DoScriptText(EMOTE_DIES, m_creature); + m_creature->SetDeathState(JUST_DIED); + m_creature->SetHealth(0); + return; + } + break; + case RACE_BLOODELF: + switch (m_uiExecuteSpeech_Counter) + { + case 0: DoScriptText(SAY_EXEC_START_1, m_creature, pPlayer); break; + case 1: m_creature->SetStandState(UNIT_STAND_STATE_STAND); break; + case 2: DoScriptText(SAY_EXEC_PROG_1, m_creature, pPlayer); break; + case 3: DoScriptText(SAY_EXEC_NAME_1, m_creature, pPlayer); break; + case 4: DoScriptText(SAY_EXEC_RECOG_1, m_creature, pPlayer); break; + // case 5: // unknown + case 6: DoScriptText(SAY_EXEC_THINK_3, m_creature, pPlayer); break; + case 7: DoScriptText(SAY_EXEC_LISTEN_1, m_creature, pPlayer); break; + case 8: + if (Creature* pPlaguefist = GetClosestCreatureWithEntry(m_creature, NPC_PLAGUEFIST, 85.0f)) + DoScriptText(SAY_PLAGUEFIST, pPlaguefist, pPlayer); + break; + case 9: + DoScriptText(SAY_EXEC_TIME_1, m_creature, pPlayer); + m_creature->SetStandState(UNIT_STAND_STATE_KNEEL); + m_creature->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_OOC_NOT_ATTACKABLE); + break; + case 10: DoScriptText(SAY_EXEC_WAITING, m_creature, pPlayer); break; + case 11: + DoScriptText(EMOTE_DIES, m_creature); + m_creature->SetDeathState(JUST_DIED); + m_creature->SetHealth(0); + return; + } + break; + case RACE_DRAENEI: + switch (m_uiExecuteSpeech_Counter) + { + case 0: DoScriptText(SAY_EXEC_START_1, m_creature, pPlayer); break; + case 1: m_creature->SetStandState(UNIT_STAND_STATE_STAND); break; + case 2: DoScriptText(SAY_EXEC_PROG_1, m_creature, pPlayer); break; + case 3: DoScriptText(SAY_EXEC_NAME_1, m_creature, pPlayer); break; + case 4: DoScriptText(SAY_EXEC_RECOG_2, m_creature, pPlayer); break; + case 5: DoScriptText(SAY_EXEC_NOREM_1, m_creature, pPlayer); break; + case 6: DoScriptText(SAY_EXEC_THINK_4, m_creature, pPlayer); break; + case 7: DoScriptText(SAY_EXEC_LISTEN_1, m_creature, pPlayer); break; + case 8: + if (Creature* pPlaguefist = GetClosestCreatureWithEntry(m_creature, NPC_PLAGUEFIST, 85.0f)) + DoScriptText(SAY_PLAGUEFIST, pPlaguefist, pPlayer); + break; + case 9: + DoScriptText(SAY_EXEC_TIME_2, m_creature, pPlayer); + m_creature->SetStandState(UNIT_STAND_STATE_KNEEL); + m_creature->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_OOC_NOT_ATTACKABLE); + break; + case 10: DoScriptText(SAY_EXEC_WAITING, m_creature, pPlayer); break; + case 11: + DoScriptText(EMOTE_DIES, m_creature); + m_creature->SetDeathState(JUST_DIED); + m_creature->SetHealth(0); + return; + } + break; + } + + if (m_uiExecuteSpeech_Counter >= 9) + m_uiExecuteSpeech_Timer = 15000; + else + m_uiExecuteSpeech_Timer = 7000; + + ++m_uiExecuteSpeech_Counter; + } + else + m_uiExecuteSpeech_Timer -= uiDiff; + } + } +}; + +CreatureAI* GetAI_npc_a_special_surprise(Creature* pCreature) +{ + return new npc_a_special_surpriseAI(pCreature); +} + +/*###### +## npc_death_knight_initiate +######*/ + +enum +{ + SAY_DUEL_A = -1609016, + SAY_DUEL_B = -1609017, + SAY_DUEL_C = -1609018, + SAY_DUEL_D = -1609019, + SAY_DUEL_E = -1609020, + SAY_DUEL_F = -1609021, + SAY_DUEL_G = -1609022, + SAY_DUEL_H = -1609023, + SAY_DUEL_I = -1609024, + + SPELL_DUEL = 52996, + SPELL_DUEL_TRIGGERED = 52990, + SPELL_DUEL_VICTORY = 52994, + SPELL_DUEL_FLAG = 52991, + + GOSSIP_ITEM_ACCEPT_DUEL = -3609000, + GOSSIP_TEXT_ID_DUEL = 13433, + + 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 npc_death_knight_initiateAI : public ScriptedAI +{ + npc_death_knight_initiateAI(Creature* pCreature) : ScriptedAI(pCreature) { Reset(); } + + ObjectGuid m_duelerGuid; + uint32 m_uiDuelTimer; + bool m_bIsDuelInProgress; + + void Reset() override + { + m_creature->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_UNK_15); + + m_duelerGuid.Clear(); + m_uiDuelTimer = 5000; + m_bIsDuelInProgress = false; + } + + void AttackedBy(Unit* pAttacker) override + { + if (m_creature->getVictim()) + return; + + if (m_creature->IsFriendlyTo(pAttacker)) + return; + + AttackStart(pAttacker); + } + + void SpellHit(Unit* pCaster, const SpellEntry* pSpell) override + { + if (!m_bIsDuelInProgress && pSpell->Id == SPELL_DUEL_TRIGGERED && pCaster->GetTypeId() == TYPEID_PLAYER) + { + m_duelerGuid = pCaster->GetObjectGuid(); + m_bIsDuelInProgress = true; + } + } + + void DamageTaken(Unit* /*pDoneBy*/, uint32& uiDamage) override + { + if (m_bIsDuelInProgress && uiDamage > m_creature->GetHealth()) + { + uiDamage = 0; + + if (Player* pPlayer = m_creature->GetMap()->GetPlayer(m_duelerGuid)) + m_creature->CastSpell(pPlayer, SPELL_DUEL_VICTORY, true); + + // possibly not evade, but instead have end sequenze + EnterEvadeMode(); + } + } + + void UpdateAI(const uint32 uiDiff) override + { + if (!m_creature->SelectHostileTarget() || !m_creature->getVictim()) + { + if (m_bIsDuelInProgress) + { + if (m_uiDuelTimer < uiDiff) + { + m_creature->SetFactionTemporary(FACTION_HOSTILE, TEMPFACTION_RESTORE_COMBAT_STOP | TEMPFACTION_RESTORE_RESPAWN); + + if (Player* pPlayer = m_creature->GetMap()->GetPlayer(m_duelerGuid)) + AttackStart(pPlayer); + } + else + m_uiDuelTimer -= uiDiff; + } + return; + } + + // TODO: spells + + DoMeleeAttackIfReady(); + } +}; + +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) + { + pPlayer->ADD_GOSSIP_ITEM_ID(GOSSIP_ICON_CHAT, GOSSIP_ITEM_ACCEPT_DUEL, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 1); + pPlayer->SEND_GOSSIP_MENU(GOSSIP_TEXT_ID_DUEL, pCreature->GetObjectGuid()); + return true; + } + return false; +} + +bool GossipSelect_npc_death_knight_initiate(Player* pPlayer, Creature* pCreature, uint32 /*uiSender*/, uint32 uiAction) +{ + if (uiAction == GOSSIP_ACTION_INFO_DEF + 1) + { + pPlayer->CLOSE_GOSSIP_MENU(); + + if (npc_death_knight_initiateAI* pInitiateAI = dynamic_cast(pCreature->AI())) + { + if (pInitiateAI->m_bIsDuelInProgress) + return true; + } + + pCreature->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_UNK_15); + + DoScriptText(m_auiRandomSay[urand(0, countof(m_auiRandomSay) - 1)], pCreature, pPlayer); + + pCreature->CastSpell(pPlayer, SPELL_DUEL, false); + pCreature->CastSpell(pPlayer, SPELL_DUEL_FLAG, true); + } + return true; +} + +/*###### +## npc_koltira_deathweaver +######*/ + +enum eKoltira +{ + SAY_BREAKOUT1 = -1609079, + SAY_BREAKOUT2 = -1609080, + SAY_BREAKOUT3 = -1609081, + SAY_BREAKOUT4 = -1609082, + SAY_BREAKOUT5 = -1609083, + SAY_BREAKOUT6 = -1609084, + SAY_BREAKOUT7 = -1609085, + SAY_BREAKOUT8 = -1609086, + SAY_BREAKOUT9 = -1609087, + SAY_BREAKOUT10 = -1609088, + + 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 npc_koltira_deathweaverAI : public npc_escortAI +{ + npc_koltira_deathweaverAI(Creature* pCreature) : npc_escortAI(pCreature) { Reset(); } + + uint32 m_uiWave; + uint32 m_uiWave_Timer; + ObjectGuid m_valrothGuid; + + void Reset() override + { + if (!HasEscortState(STATE_ESCORT_ESCORTING)) + { + m_uiWave = 0; + m_uiWave_Timer = 3000; + m_valrothGuid.Clear(); + } + } + + void WaypointReached(uint32 uiPointId) override + { + switch (uiPointId) + { + case 0: + DoScriptText(SAY_BREAKOUT1, m_creature); + break; + case 1: + m_creature->SetStandState(UNIT_STAND_STATE_KNEEL); + break; + case 2: + m_creature->SetStandState(UNIT_STAND_STATE_STAND); + // m_creature->UpdateEntry(NPC_KOLTIRA_ALT);// unclear if we must update or not + DoCastSpellIfCan(m_creature, SPELL_KOLTIRA_TRANSFORM); + break; + case 3: + SetEscortPaused(true); + m_creature->SetStandState(UNIT_STAND_STATE_KNEEL); + DoScriptText(SAY_BREAKOUT2, m_creature); + DoCastSpellIfCan(m_creature, SPELL_ANTI_MAGIC_ZONE); // cast again that makes bubble up + break; + case 4: + SetRun(true); + break; + case 9: + m_creature->Mount(MODEL_DEATH_KNIGHT_MOUNT); + break; + case 10: + m_creature->Unmount(); + break; + } + } + + void JustSummoned(Creature* pSummoned) override + { + if (Player* pPlayer = GetPlayerForEscort()) + pSummoned->AI()->AttackStart(pPlayer); + + if (pSummoned->GetEntry() == NPC_HIGH_INQUISITOR_VALROTH) + m_valrothGuid = pSummoned->GetObjectGuid(); + } + + void SummonAcolyte(uint32 uiAmount) + { + for (uint32 i = 0; i < uiAmount; ++i) + m_creature->SummonCreature(NPC_CRIMSON_ACOLYTE, 1642.329f, -6045.818f, 127.583f, 0.0f, TEMPSUMMON_TIMED_OOC_DESPAWN, 5000); + } + + void UpdateEscortAI(const uint32 uiDiff) override + { + if (HasEscortState(STATE_ESCORT_PAUSED)) + { + if (m_uiWave_Timer < uiDiff) + { + switch (m_uiWave) + { + case 0: + DoScriptText(SAY_BREAKOUT3, m_creature); + SummonAcolyte(3); + m_uiWave_Timer = 20000; + break; + case 1: + DoScriptText(SAY_BREAKOUT4, m_creature); + SummonAcolyte(3); + m_uiWave_Timer = 20000; + break; + case 2: + DoScriptText(SAY_BREAKOUT5, m_creature); + SummonAcolyte(4); + m_uiWave_Timer = 20000; + break; + case 3: + DoScriptText(SAY_BREAKOUT6, m_creature); + m_creature->SummonCreature(NPC_HIGH_INQUISITOR_VALROTH, 1642.329f, -6045.818f, 127.583f, 0.0f, TEMPSUMMON_TIMED_OOC_DESPAWN, 1000); + m_uiWave_Timer = 1000; + break; + case 4: + { + Creature* pTemp = m_creature->GetMap()->GetCreature(m_valrothGuid); + + if (!pTemp || !pTemp->IsAlive()) + { + DoScriptText(SAY_BREAKOUT8, m_creature); + m_uiWave_Timer = 5000; + } + else + { + m_uiWave_Timer = 2500; + return; // return, we don't want m_uiWave to increment now + } + break; + } + case 5: + DoScriptText(SAY_BREAKOUT9, m_creature); + m_creature->RemoveAurasDueToSpell(SPELL_ANTI_MAGIC_ZONE); + m_uiWave_Timer = 2500; + break; + case 6: + DoScriptText(SAY_BREAKOUT10, m_creature); + SetEscortPaused(false); + break; + } + + ++m_uiWave; + } + else + m_uiWave_Timer -= uiDiff; + } + + if (!m_creature->SelectHostileTarget() || !m_creature->getVictim()) + return; + + DoMeleeAttackIfReady(); + } +}; + +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_koltira_deathweaverAI* pEscortAI = dynamic_cast(pCreature->AI())) + pEscortAI->Start(false, pPlayer, pQuest); + } + return true; +} + +/*###### +## +######*/ + +enum +{ + SAY_START = -1609000, // 8 texts in total, GetTextId() generates random with this as base + SAY_AGGRO = -1609008, // 8 texts in total, GetTextId() generates random with this as base + + // SPELL_CHAINED_PESANT_LH = 54602, // not used. possible it determine side, where to go get "weapon" + // SPELL_CHAINED_PESANT_RH = 54610, + SPELL_CHAINED_PESANT_CHEST = 54612, + SPELL_CHAINED_PESANT_BREATH = 54613, + SPELL_INITIATE_VISUAL = 51519, + + SPELL_BLOOD_STRIKE = 52374, + SPELL_DEATH_COIL = 52375, + SPELL_ICY_TOUCH = 52372, + SPELL_PLAGUE_STRIKE = 52373, + + NPC_ANCHOR = 29521, + FACTION_MONSTER = 16, + + PHASE_INACTIVE_OR_COMBAT = 0, + PHASE_DRESSUP = 1, + PHASE_ACTIVATE = 2 +}; + +/*###### +## npc_unworthy_initiate_anchor +######*/ + +struct npc_unworthy_initiate_anchorAI : public ScriptedAI +{ + npc_unworthy_initiate_anchorAI(Creature* pCreature) : ScriptedAI(pCreature) { Reset(); } + + ObjectGuid m_myInitiateGuid; + ObjectGuid m_myPrisonGuid; + + void Reset() override {} + + void NotifyMe(Unit* pSource, GameObject* pGo) + { + m_myPrisonGuid = pGo->GetObjectGuid(); + Creature* pInitiate = m_creature->GetMap()->GetCreature(m_myInitiateGuid); + + if (pInitiate && pSource) + { + pInitiate->SetLootRecipient(pSource); + m_creature->CastSpell(pInitiate, SPELL_CHAINED_PESANT_BREATH, true); + } + } + + void RegisterCloseInitiate(Creature* pCreature) + { + m_myInitiateGuid = pCreature->GetObjectGuid(); + } + + void ResetPrison() + { + if (GameObject* pPrison = m_creature->GetMap()->GetGameObject(m_myPrisonGuid)) + pPrison->ResetDoorOrButton(); + } +}; + +CreatureAI* GetAI_npc_unworthy_initiate_anchor(Creature* pCreature) +{ + return new npc_unworthy_initiate_anchorAI(pCreature); +} + +/*###### +## npc_unworthy_initiate +######*/ + +struct npc_unworthy_initiateAI : public ScriptedAI +{ + npc_unworthy_initiateAI(Creature* pCreature) : ScriptedAI(pCreature) + { + Reset(); + } + + ObjectGuid m_myAnchorGuid; + uint32 m_uiAnchorCheckTimer; + uint32 m_uiPhase; + uint32 m_uiPhaseTimer; + uint32 m_uiBloodStrike_Timer; + uint32 m_uiDeathCoil_Timer; + uint32 m_uiIcyTouch_Timer; + uint32 m_uiPlagueStrike_Timer; + + void Reset() override + { + m_uiAnchorCheckTimer = 5000; + m_uiPhase = PHASE_INACTIVE_OR_COMBAT; + m_uiPhaseTimer = 7500; + m_uiBloodStrike_Timer = 4000; + m_uiDeathCoil_Timer = 6000; + m_uiIcyTouch_Timer = 2000; + m_uiPlagueStrike_Timer = 5000; + } + + void JustReachedHome() override + { + SetAnchor(); + + if (Creature* pAnchor = GetAnchor()) + { + if (npc_unworthy_initiate_anchorAI* pAnchorAI = dynamic_cast(pAnchor->AI())) + pAnchorAI->ResetPrison(); + } + } + + void JustRespawned() override + { + if (Creature* pAnchor = GetAnchor()) + { + if (npc_unworthy_initiate_anchorAI* pAnchorAI = dynamic_cast(pAnchor->AI())) + pAnchorAI->ResetPrison(); + } + + Reset(); + } + + int32 GetTextId() + { + return m_uiPhase == PHASE_DRESSUP ? SAY_START - urand(0, 7) : SAY_AGGRO - urand(0, 7); + } + + Creature* GetAnchor() + { + if (m_myAnchorGuid) + return m_creature->GetMap()->GetCreature(m_myAnchorGuid); + else + return GetClosestCreatureWithEntry(m_creature, NPC_ANCHOR, INTERACTION_DISTANCE * 2); + } + + void SetAnchor() + { + if (Creature* pAnchor = GetAnchor()) + { + if (npc_unworthy_initiate_anchorAI* pAnchorAI = dynamic_cast(pAnchor->AI())) + pAnchorAI->RegisterCloseInitiate(m_creature); + + pAnchor->CastSpell(m_creature, SPELL_CHAINED_PESANT_CHEST, false); + m_myAnchorGuid = pAnchor->GetObjectGuid(); + + m_uiAnchorCheckTimer = 0; + return; + } + + m_uiAnchorCheckTimer = 5000; + } + + void SpellHit(Unit* pCaster, const SpellEntry* pSpell) override + { + if (pSpell->Id == SPELL_CHAINED_PESANT_BREATH) + { + pCaster->InterruptNonMeleeSpells(true); + m_creature->SetStandState(UNIT_STAND_STATE_STAND); + + m_uiPhase = PHASE_DRESSUP; + + if (Player* pSource = m_creature->GetLootRecipient()) + DoScriptText(GetTextId(), m_creature, pSource); + } + } + + void UpdateAI(const uint32 uiDiff) override + { + if (m_uiAnchorCheckTimer) + { + if (m_uiAnchorCheckTimer <= uiDiff) + SetAnchor(); + else + m_uiAnchorCheckTimer -= uiDiff; + } + + if (m_uiPhase == PHASE_INACTIVE_OR_COMBAT) + { + if (!m_creature->SelectHostileTarget() || !m_creature->getVictim()) + return; + + if (m_uiBloodStrike_Timer < uiDiff) + { + DoCastSpellIfCan(m_creature->getVictim(), SPELL_BLOOD_STRIKE); + m_uiBloodStrike_Timer = 9000; + } + else + m_uiBloodStrike_Timer -= uiDiff; + + if (m_uiDeathCoil_Timer < uiDiff) + { + DoCastSpellIfCan(m_creature->getVictim(), SPELL_DEATH_COIL); + m_uiDeathCoil_Timer = 8000; + } + else + m_uiDeathCoil_Timer -= uiDiff; + + if (m_uiIcyTouch_Timer < uiDiff) + { + DoCastSpellIfCan(m_creature->getVictim(), SPELL_ICY_TOUCH); + m_uiIcyTouch_Timer = 8000; + } + else + m_uiIcyTouch_Timer -= uiDiff; + + if (m_uiPlagueStrike_Timer < uiDiff) + { + DoCastSpellIfCan(m_creature->getVictim(), SPELL_PLAGUE_STRIKE); + m_uiPlagueStrike_Timer = 8000; + } + else + m_uiPlagueStrike_Timer -= uiDiff; + + DoMeleeAttackIfReady(); + } + else + { + if (m_uiPhaseTimer < uiDiff) + { + if (m_uiPhase == PHASE_DRESSUP) + { + m_creature->CastSpell(m_creature, SPELL_INITIATE_VISUAL, false); + + m_uiPhase = PHASE_ACTIVATE; + } + else + { + m_creature->SetFactionTemporary(FACTION_MONSTER, TEMPFACTION_RESTORE_COMBAT_STOP | TEMPFACTION_RESTORE_RESPAWN); + + m_uiPhase = PHASE_INACTIVE_OR_COMBAT; + + if (Player* pTarget = m_creature->GetLootRecipient()) + { + DoScriptText(GetTextId(), m_creature, pTarget); + AttackStart(pTarget); + } + } + + m_uiPhaseTimer = 5000; + } + else + m_uiPhaseTimer -= uiDiff; + } + } +}; + +CreatureAI* GetAI_npc_unworthy_initiate(Creature* pCreature) +{ + return new npc_unworthy_initiateAI(pCreature); +} + +/*###### +## go_acherus_soul_prison +######*/ + +bool GOUse_go_acherus_soul_prison(Player* pPlayer, GameObject* pGo) +{ + if (Creature* pAnchor = GetClosestCreatureWithEntry(pGo, NPC_ANCHOR, INTERACTION_DISTANCE)) + { + if (npc_unworthy_initiate_anchorAI* pAnchorAI = dynamic_cast(pAnchor->AI())) + pAnchorAI->NotifyMe(pPlayer, pGo); + } + + return false; +} + +/*###### +## npc_eye_of_acherus +######*/ + +enum +{ + SPELL_EYE_CONTROL = 51852, // player control aura + SPELL_EYE_VISUAL = 51892, + SPELL_EYE_FLIGHT = 51890, // player flight control + SPELL_EYE_FLIGHT_BOOST = 51923, // flight boost to reach new avalon + + EMOTE_DESTIANTION = -1609089, + EMOTE_CONTROL = -1609090, + + POINT_EYE_DESTINATION = 0 +}; + +// movement destination coords +static const float aEyeDestination[3] = {1750.8276f, -5873.788f, 147.2266f}; + +struct npc_eye_of_acherusAI : public ScriptedAI +{ + npc_eye_of_acherusAI(Creature* pCreature) : ScriptedAI(pCreature) + { + m_bIsInitialized = false; + m_creature->SetPhaseMask(3, true); // HACK as mangos cannot handle auras proberly, also HACK below + Reset(); + } + + bool m_bIsInitialized; + + void Reset() override {} + + void JustDied(Unit* /*pKiller*/) override + { + m_creature->CastSpell(m_creature, 52694, true); // HACK - Remove this when mangos supports proper spell casting + } + + void MovementInform(uint32 uiType, uint32 uiPointId) override + { + if (uiType != POINT_MOTION_TYPE || uiPointId != POINT_EYE_DESTINATION) + return; + + if (Player* pPlayer = m_creature->GetCharmerOrOwnerPlayerOrPlayerItself()) + DoScriptText(EMOTE_CONTROL, m_creature, pPlayer); + + DoCastSpellIfCan(m_creature, SPELL_EYE_FLIGHT, CAST_TRIGGERED); + } + + void AttackStart(Unit* /*pWho*/) override {} + + void UpdateAI(const uint32 /*uiDiff*/) override + { + if (m_bIsInitialized) + return; + + if (Player* pPlayer = m_creature->GetCharmerOrOwnerPlayerOrPlayerItself()) + { + m_creature->SetPhaseMask(2, true); // HACK remove when summon spells and auras are implemented properly in mangos + + DoScriptText(EMOTE_DESTIANTION, m_creature, pPlayer); + + DoCastSpellIfCan(m_creature, SPELL_EYE_VISUAL, CAST_TRIGGERED); + DoCastSpellIfCan(m_creature, SPELL_EYE_FLIGHT_BOOST, CAST_TRIGGERED); + // Update Speed for Eye + m_creature->UpdateSpeed(MOVE_FLIGHT, true, pPlayer->GetSpeed(MOVE_FLIGHT)); + + //m_creature->RemoveSplineFlag(SPLINEFLAG_WALKMODE); + m_creature->GetMotionMaster()->MovePoint(POINT_EYE_DESTINATION, aEyeDestination[0], aEyeDestination[1], aEyeDestination[2]); + + m_bIsInitialized = true; + } + else + m_creature->ForcedDespawn(); + } +}; + +CreatureAI* GetAI_npc_eye_of_acherus(Creature* pCreature) +{ + return new npc_eye_of_acherusAI(pCreature); +} + +/*###### +## npc_scarlet_ghoul +######*/ + +enum +{ + SAY_GHUL_SPAWN_1 = -1609091, + SAY_GHUL_SPAWN_2 = -1609092, + SAY_GHUL_SPAWN_3 = -1609093, + SAY_GHUL_SPAWN_4 = -1609094, + SAY_GHUL_SPAWN_5 = -1609095, + SAY_GOTHIK_THROW_IN_PIT = -1609096, // TODO: Unclear if there exist more texts + + SPELL_GHOUL_SUMMONED = 52500, + SPELL_GOTHIK_GHOUL_PING = 52514, + SPELL_QUEST_CREDIT = 52517, + SPELL_GHOUL_UNSUMMON = 52555, + + NPC_GOTHIK = 28658, +}; + +static const float aPitPosition[3] = {2380.13f, -5783.06f, 151.367f}; + +struct npc_scarlet_ghoulAI : public ScriptedPetAI +{ + npc_scarlet_ghoulAI(Creature* pCreature) : ScriptedPetAI(pCreature) + { + m_bGotHit = false; + m_bIsJumping = false; + m_bDidInitText = false; + m_uiUnsummonTimer = 0; + DoCastSpellIfCan(m_creature, SPELL_GHOUL_SUMMONED); + Reset(); + } + + bool m_bGotHit; + bool m_bIsJumping; + bool m_bDidInitText; + uint32 m_uiUnsummonTimer; + + void Reset() override {} + + void MovementInform(uint32 uiMotionType, uint32 uiPointId) override + { + if (uiMotionType == EFFECT_MOTION_TYPE && uiPointId == 1) + { + m_uiUnsummonTimer = 1000; + DoCastSpellIfCan(m_creature, SPELL_GHOUL_UNSUMMON); + m_creature->GetMotionMaster()->MoveIdle(); + } + } + + void JustDied(Unit* /*pKiller*/) override + { + DoCastSpellIfCan(m_creature, SPELL_GHOUL_UNSUMMON, CAST_TRIGGERED); + } + + void UpdateAI(const uint32 uiDiff) override + { + if (!m_bDidInitText) + { + Unit* pOwner = m_creature->GetCharmerOrOwner(); + DoScriptText(SAY_GHUL_SPAWN_1 - urand(0, 4), m_creature, pOwner); + + m_bDidInitText = true; + } + + if (m_uiUnsummonTimer) + { + if (m_uiUnsummonTimer <= uiDiff) + { + m_creature->DealDamage(m_creature, m_creature->GetHealth(), NULL, DIRECT_DAMAGE, SPELL_SCHOOL_MASK_NORMAL, NULL, false); + if (m_creature->IsPet()) + ((Pet*)m_creature)->Unsummon(PET_SAVE_AS_DELETED); + return; + } + else + m_uiUnsummonTimer -= uiDiff; + } + + if (m_bIsJumping) + return; + + ScriptedPetAI::UpdateAI(uiDiff); + } +}; + +bool EffectDummyCreature_npc_scarlet_ghoul(Unit* pCaster, uint32 uiSpellId, SpellEffectIndex uiEffIndex, Creature* pCreatureTarget, ObjectGuid /*originalCasterGuid*/) +{ + if (uiSpellId == SPELL_GOTHIK_GHOUL_PING && uiEffIndex == EFFECT_INDEX_0) + { + if (npc_scarlet_ghoulAI* pGhoulAi = dynamic_cast(pCreatureTarget->AI())) + { + if (!pGhoulAi->m_bGotHit) // First hit + { + pCreatureTarget->CastSpell(pCreatureTarget, 52517, false); + pGhoulAi->m_bGotHit = true; + } + else // Second hit + { + world_map_ebon_hold* pInstance = static_cast(pCreatureTarget->GetInstanceData()); + if (pCaster && pInstance && pInstance->CanAndToggleGothikYell()) + DoScriptText(SAY_GOTHIK_THROW_IN_PIT, pCaster); + + float fX, fY, fZ; + pCreatureTarget->GetRandomPoint(aPitPosition[0], aPitPosition[1], aPitPosition[2], 10.0f, fX, fY, fZ); + pGhoulAi->m_bIsJumping = true; + pCreatureTarget->GetMotionMaster()->MoveJump(fX, fY, fZ, 24.21229f, 6.0f, 1); + } + } + return true; + } + + return false; +} + +CreatureAI* GetAI_npc_scarlet_ghoul(Creature* pCreature) +{ + return new npc_scarlet_ghoulAI(pCreature); +} + +/*###### +## npc_highlord_darion_mograine +######*/ + +enum LightOfDawn +{ + // yells + SAY_LIGHT_OF_DAWN_INTRO_1 = -1609201, // Highlord Darion Mograine + SAY_LIGHT_OF_DAWN_INTRO_2 = -1609202, + + SAY_LIGHT_OF_DAWN_PREPARE_1 = -1609203, + SAY_LIGHT_OF_DAWN_PREPARE_2 = -1609204, + SAY_LIGHT_OF_DAWN_PREPARE_3 = -1609205, + SAY_LIGHT_OF_DAWN_PREPARE_4 = -1609206, + + SAY_LIGHT_OF_DAWN_STAND_1 = -1609207, // Korfax + SAY_LIGHT_OF_DAWN_STAND_2 = -1609208, // Lord Maxwell Tyrosus + + SAY_LIGHT_OF_DAWN_BATTLE_1 = -1609209, // Highlord Darion Mograine + SAY_LIGHT_OF_DAWN_BATTLE_2 = -1609210, + SAY_LIGHT_OF_DAWN_BATTLE_3 = -1609211, + SAY_LIGHT_OF_DAWN_BATTLE_4 = -1609212, + SAY_LIGHT_OF_DAWN_BATTLE_5 = -1609213, + SAY_LIGHT_OF_DAWN_BATTLE_6 = -1609214, + SAY_LIGHT_OF_DAWN_BATTLE_7 = -1609215, + SAY_LIGHT_OF_DAWN_BATTLE_8 = -1609216, + SAY_LIGHT_OF_DAWN_BATTLE_9 = -1609224, + + SAY_LIGHT_OF_DAWN_BATTLE_10 = -1609217, // Battle end yells + SAY_LIGHT_OF_DAWN_BATTLE_11 = -1609218, + SAY_LIGHT_OF_DAWN_BATTLE_12 = -1609219, + SAY_LIGHT_OF_DAWN_BATTLE_13 = -1609220, + SAY_LIGHT_OF_DAWN_BATTLE_14 = -1609221, + SAY_LIGHT_OF_DAWN_BATTLE_15 = -1609222, + SAY_LIGHT_OF_DAWN_BATTLE_16 = -1609223, + + SAY_LIGHT_OF_DAWN_OUTRO_1 = -1609225, // Highlord Tirion Fordring + SAY_LIGHT_OF_DAWN_OUTRO_2 = -1609226, + SAY_LIGHT_OF_DAWN_OUTRO_3 = -1609227, // Highlord Darion Mograine + SAY_LIGHT_OF_DAWN_OUTRO_4 = -1609228, // Highlord Tirion Fordring + SAY_LIGHT_OF_DAWN_OUTRO_5 = -1609229, + SAY_LIGHT_OF_DAWN_OUTRO_6 = -1609230, + SAY_LIGHT_OF_DAWN_OUTRO_7 = -1609231, // Highlord Darion Mograine + + SAY_LIGHT_OF_DAWN_VISION_1 = -1609232, // Highlord Alexandros Mograine + SAY_LIGHT_OF_DAWN_VISION_2 = -1609233, // Highlord Darion Mograine + SAY_LIGHT_OF_DAWN_VISION_3 = -1609234, + SAY_LIGHT_OF_DAWN_VISION_4 = -1609235, // Darion Mograine + SAY_LIGHT_OF_DAWN_VISION_5 = -1609236, + SAY_LIGHT_OF_DAWN_VISION_6 = -1609237, // Highlord Alexandros Mograine + SAY_LIGHT_OF_DAWN_VISION_7 = -1609238, // Darion Mograine + SAY_LIGHT_OF_DAWN_VISION_8 = -1609239, // Highlord Alexandros Mograine + SAY_LIGHT_OF_DAWN_VISION_9 = -1609240, // Darion Mograine + SAY_LIGHT_OF_DAWN_VISION_10 = -1609241, // Highlord Alexandros Mograine + SAY_LIGHT_OF_DAWN_VISION_11 = -1609242, + + SAY_LIGHT_OF_DAWN_KING_VISIT_1 = -1609243, // The Lich King + SAY_LIGHT_OF_DAWN_KING_VISIT_2 = -1609245, + SAY_LIGHT_OF_DAWN_KING_VISIT_3 = -1609244, // Highlord Darion Mograine + SAY_LIGHT_OF_DAWN_KING_VISIT_4 = -1609246, // The Lich King + SAY_LIGHT_OF_DAWN_KING_VISIT_5 = -1609247, // Highlord Tirion Fordring + SAY_LIGHT_OF_DAWN_KING_VISIT_6 = -1609248, // The Lich King + SAY_LIGHT_OF_DAWN_KING_VISIT_7 = -1609249, + SAY_LIGHT_OF_DAWN_KING_VISIT_8 = -1609250, // Lord Maxwell Tyrosus + SAY_LIGHT_OF_DAWN_KING_VISIT_9 = -1609251, // The Lich King + SAY_LIGHT_OF_DAWN_KING_VISIT_10 = -1609252, // Highlord Darion Mograine + SAY_LIGHT_OF_DAWN_KING_VISIT_11 = -1609253, + SAY_LIGHT_OF_DAWN_KING_VISIT_12 = -1609254, // Highlord Tirion Fordring + SAY_LIGHT_OF_DAWN_KING_VISIT_13 = -1609255, // The Lich King + SAY_LIGHT_OF_DAWN_KING_VISIT_14 = -1609256, // Highlord Tirion Fordring + SAY_LIGHT_OF_DAWN_KING_VISIT_15 = -1609257, // The Lich King + SAY_LIGHT_OF_DAWN_KING_VISIT_16 = -1609258, + SAY_LIGHT_OF_DAWN_KING_VISIT_17 = -1609259, + + SAY_LIGHT_OF_DAWN_EPILOGUE_1 = -1609260, // Highlord Tirion Fordring + SAY_LIGHT_OF_DAWN_EPILOGUE_2 = -1609261, + SAY_LIGHT_OF_DAWN_EPILOGUE_3 = -1609262, + SAY_LIGHT_OF_DAWN_EPILOGUE_4 = -1609263, + SAY_LIGHT_OF_DAWN_EPILOGUE_5 = -1609264, + SAY_LIGHT_OF_DAWN_EPILOGUE_6 = -1609265, + SAY_LIGHT_OF_DAWN_EPILOGUE_7 = -1609266, + SAY_LIGHT_OF_DAWN_EPILOGUE_8 = -1609267, + SAY_LIGHT_OF_DAWN_EPILOGUE_9 = -1609268, // Highlord Darion Mograine + + // Emotes + EMOTE_LIGHT_OF_DAWN_ARMY_RISE = -1609269, // Emotes + EMOTE_LIGHT_OF_DAWN_ARMY_MARCH = -1609270, + EMOTE_LIGHT_OF_DAWN_TIRION = -1609271, + EMOTE_LIGHT_OF_DAWN_FLEE = -1609272, + EMOTE_LIGHT_OF_DAWN_KNEEL = -1609273, + EMOTE_LIGHT_OF_DAWN_ALEXANDROS = -1609274, + EMOTE_LIGHT_OF_DAWN_SHADE = -1609275, + EMOTE_LIGHT_OF_DAWN_HUG = -1609276, + EMOTE_LIGHT_OF_DAWN_LICH_KING = -1609277, + EMOTE_LIGHT_OF_DAWN_ANGRY = -1609278, + EMOTE_LIGHT_OF_DAWN_CAST_SPELL = -1609279, + EMOTE_LIGHT_OF_DAWN_GRASP = -1609280, + EMOTE_LIGHT_OF_DAWN_POWERFULL = -1609281, + EMOTE_LIGHT_OF_DAWN_ASHBRINGER = -1609282, + EMOTE_LIGHT_OF_DAWN_COLAPSE = -1609283, + EMOTE_LIGHT_OF_DAWN_CHARGE = -1609284, + EMOTE_LIGHT_OF_DAWN_KING_LEAVE = -1609285, + EMOTE_LIGHT_OF_DAWN_LIGHT = -1609286, + + // Spells + // Highlord Darion Mograine + SPELL_HERO_AGGRO_AURA = 53627, + SPELL_SCOURGE_AGGRO_AURA = 53624, + SPELL_ANTI_MAGIC_ZONE_DARION = 52893, + SPELL_DEATH_STRIKE = 53639, + SPELL_DEATH_EMBRACE = 53635, + SPELL_ICY_TOUCH_DARION = 49723, + SPELL_PLAGUE_STRIKE_KNIGHTS = 50688, + SPELL_THE_MIGHT_OF_MOGRAINE = 53642, // on players when battle begins + SPELL_UNHOLY_BLIGHT = 53640, + + SPELL_BIRTH = 53603, // ground shake + SPELL_THE_LIGHT_OF_DAWN_DUMMY = 53658, // light globe + SPELL_THE_LIGHT_OF_DAWN_DAMAGE_LOSS = 53645, // cast by the scourge units + SPELL_ALEXANDROS_MOGRAINE_SPAWN = 53667, // spawn effect for Alexandros + SPELL_MOGRAINE_CHARGE = 53679, // charge to the Lich King + SPELL_ASHBRINGER = 53701, // throw Ashbringer to Tirion + SPELL_THE_LIGHT_OF_DAWN_CREDIT = 53606, // quest credit + + // Lich King spells + SPELL_APOCALYPSE = 53210, // knocks back all enemies + SPELL_APOCALYPSE_STUN = 53745, // stuns all enemies + SPELL_POST_APOCALYPSE = 53211, // after apocalypse - not sure where to use it + SPELL_TELEPORT_VISUAL = 52233, // on leave + SPELL_SOUL_FEAST_ALEX = 53677, // on Alexandros + SPELL_SOUL_FEAST_TIRION = 53685, // on Tirion + SPELL_ICEBOUND_VISAGE = 53274, // ice effect + SPELL_REBUKE = 53680, // knockback + + // Highlord Tirion Fordring + //EQUIP_HIGHLORD_TIRION_FORDRING = 13262, + SPELL_LAY_ON_HANDS = 53778, // heal effect + SPELL_REBIRTH_OF_THE_ASHBRINGER = 53702, // globe sphere + SPELL_TIRION_CHARGE = 53705, // on the lich king + + POINT_MOVE_CHAPEL = 100, // Use high entries to not conflict with escortAI waypoints + POINT_MOVE_OTHER = 101, + POINT_MOVE_RETURN_BATTLE = 102, + + // others + QUEST_ID_LIGHT_OF_DAWN = 12801, + + GOSSIP_ITEM_READY = -3609001, + GOSSIP_TEXT_ID_READY = 13485, +}; + +struct npc_highlord_darion_mograineAI : public npc_escortAI +{ + npc_highlord_darion_mograineAI(Creature* pCreature) : npc_escortAI(pCreature) + { + m_pInstance = (world_map_ebon_hold*)pCreature->GetInstanceData(); + Reset(); + } + + world_map_ebon_hold* m_pInstance; + + // event timers + uint8 m_uiIntroYell; + uint32 m_uiPrepareTimer; + + uint32 m_uiEventStep; + uint32 m_uiEventTimer; + uint32 m_uiFightTimer; + + bool m_bIsBattleEnd; + + uint8 m_uiLightWarriorsDead; + uint8 m_uiScourgeWarriorsDead; + + // spell timers + uint32 m_uiAntimagicZoneTimer; + uint32 m_uiDeathStrikeTimer; + uint32 m_uiDeathEmbraceTimer; + uint32 m_uiIcyTouchTimer; + uint32 m_uiUnholyBlightTimer; + uint32 m_uiFightSpeechTimer; + + uint32 m_uiSpawncheck; + uint32 m_uiTargetcheck; + + // others + GuidList m_lDefendersGUIDs; // light of dawn defenders + GuidList m_lAttackersGUIDs; // scourge attackers + + void Reset() override + { + // reset only when event is not in progress + if (!HasEscortState(STATE_ESCORT_ESCORTING)) + { + m_uiIntroYell = 0; + m_uiPrepareTimer = 5 * MINUTE * IN_MILLISECONDS; + + m_uiEventStep = 0; + m_uiEventTimer = 3000; + m_uiFightTimer = 0; + + m_bIsBattleEnd = false; + + m_uiLightWarriorsDead = 0; + m_uiScourgeWarriorsDead = 0; + + m_uiAntimagicZoneTimer = urand(1000, 5000); + m_uiDeathStrikeTimer = urand(5000, 10000); + m_uiDeathEmbraceTimer = urand(5000, 10000); + m_uiIcyTouchTimer = urand(5000, 10000); + m_uiUnholyBlightTimer = urand(5000, 10000); + m_uiFightSpeechTimer = 15000; + } + } + + void GetAIInformation(ChatHandler& reader) override + { + npc_escortAI::GetAIInformation(reader); + + if (m_pInstance) + reader.PSendSysMessage("Current state for TYPE_BATTLE: %u", m_pInstance->GetData(TYPE_BATTLE)); + + reader.PSendSysMessage("Current Event step: %u (%s)", m_uiEventStep, m_uiEventStep == 0 ? "Not-Started" : m_uiEventStep < 7 ? "Intro" : m_uiEventStep < 10 ? "Battle" : "Outro"); + reader.PSendSysMessage("Event-processing is %s, Fighting is %s", reader.GetOnOffStr(m_uiEventTimer), reader.GetOnOffStr(m_uiFightTimer)); + } + + void Aggro(Unit* /*pWho*/) override + { + // cast aggro aura + DoCastSpellIfCan(m_creature, SPELL_HERO_AGGRO_AURA); + } + + void JustSummoned(Creature* pSummoned) override + { + // store summoned guid for easy handle + switch (pSummoned->GetEntry()) + { + case NPC_VOLATILE_GHOUL: + pSummoned->CastSpell(pSummoned, SPELL_BIRTH, true); + // no break; + case NPC_WARRIOR_OF_THE_FROZEN_WASTES: + m_lAttackersGUIDs.push_back(pSummoned->GetObjectGuid()); + // make the scourge attack only during the battle + if (m_creature->IsInCombat()) + { + if (Unit* pTarget = m_creature->SelectAttackingTarget(ATTACKING_TARGET_RANDOM, 0)) + pSummoned->AI()->AttackStart(pTarget); + } + break; + case NPC_DEFENDER_OF_THE_LIGHT: + m_lDefendersGUIDs.push_back(pSummoned->GetObjectGuid()); + break; + } + + // set respawn delay + pSummoned->SetRespawnDelay(DAY); + } + + void SummonedCreatureJustDied(Creature* pSummoned) override + { + // if battle has ended return + if (m_pInstance->GetData(TYPE_BATTLE) != IN_PROGRESS) + return; + + // should we count the 2 behemots and 5 abominations as well? + switch (pSummoned->GetEntry()) + { + case NPC_VOLATILE_GHOUL: + case NPC_WARRIOR_OF_THE_FROZEN_WASTES: + ++m_uiScourgeWarriorsDead; + m_lAttackersGUIDs.remove(pSummoned->GetObjectGuid()); + + if (m_pInstance) + m_pInstance->DoUpdateBattleWorldState(WORLD_STATE_FORCES_SCOURGE, MAX_FORCES_SCOURGE - m_uiScourgeWarriorsDead); + + // if 5 soldiers are dead summon others + if (m_uiScourgeWarriorsDead % MAX_WARRIORS_SUMMONED_PER_TURN == 0) + { + float fX, fY, fZ; + // Actually this is some sort of cheat - but so many scourge numbers fall (currently), that I think it is ok to increase the summon amount + for (uint8 i = 0; i < MAX_WARRIORS_SUMMONED_PER_TURN + 1; ++i) + { + uint32 uiSummonEntry = urand(0, 1) ? NPC_VOLATILE_GHOUL : NPC_WARRIOR_OF_THE_FROZEN_WASTES; + m_creature->GetRandomPoint(aEventLocations[1].m_fX, aEventLocations[1].m_fY, aEventLocations[1].m_fZ, 30.0f, fX, fY, fZ); + m_creature->SummonCreature(uiSummonEntry, fX, fY, fZ, 0.0f, TEMPSUMMON_CORPSE_DESPAWN, 5000); + } + } + break; + case NPC_DEFENDER_OF_THE_LIGHT: + ++m_uiLightWarriorsDead; + m_lDefendersGUIDs.remove(pSummoned->GetObjectGuid()); + + if (m_pInstance) + m_pInstance->DoUpdateBattleWorldState(WORLD_STATE_FORCES_LIGHT, MAX_FORCES_LIGHT - m_uiLightWarriorsDead); + + // if 5 light soldiers are dead summon others + if (m_uiLightWarriorsDead % MAX_WARRIORS_SUMMONED_PER_TURN == 0) + { + float fX, fY, fZ; + for (uint8 i = 0; i < MAX_WARRIORS_SUMMONED_PER_TURN; i++) + { + m_creature->GetRandomPoint(aEventLocations[1].m_fX, aEventLocations[1].m_fY, aEventLocations[1].m_fZ, 30.0f, fX, fY, fZ); + m_creature->SummonCreature(NPC_DEFENDER_OF_THE_LIGHT, fX, fY, fZ, 0.0f, TEMPSUMMON_CORPSE_DESPAWN, 5000); + } + } + break; + } + } + + void SummonedMovementInform(Creature* pSummoned, uint32 uiType, uint32 uiPointId) override + { + if (uiType != POINT_MOTION_TYPE || uiPointId != POINT_MOVE_CHAPEL) + return; + + if (!m_pInstance) + return; + + switch (pSummoned->GetEntry()) + { + // hug father + case NPC_DARION_MOGRAINE: + if (Creature* pAlexandros = m_pInstance->GetSingleCreatureFromStorage(NPC_HIGHLORD_ALEXANDROS_MOGRAINE)) + DoScriptText(EMOTE_LIGHT_OF_DAWN_HUG, pSummoned, pAlexandros); + break; + case NPC_HIGHLORD_TIRION_FORDRING: + // tirions stops the battle and brings the DK in front of the chapel + DoScriptText(SAY_LIGHT_OF_DAWN_OUTRO_2, pSummoned); + m_pInstance->SetData(TYPE_BATTLE, DONE); + + // scourge fighters die, if not already dead + for (GuidList::const_iterator itr = m_lAttackersGUIDs.begin(); itr != m_lAttackersGUIDs.end(); ++itr) + { + if (Creature* pTemp = m_creature->GetMap()->GetCreature(*itr)) + pTemp->DealDamage(pTemp, pTemp->GetHealth(), NULL, DIRECT_DAMAGE, SPELL_SCHOOL_MASK_NORMAL, NULL, false); + } + + // light fighters despawn + for (GuidList::const_iterator itr = m_lDefendersGUIDs.begin(); itr != m_lDefendersGUIDs.end(); ++itr) + { + if (Creature* pTemp = m_creature->GetMap()->GetCreature(*itr)) + pTemp->ForcedDespawn(); + } + + // despawn big units + m_pInstance->DoDespawnArmy(); + + // facing and mount + pSummoned->Unmount(); + pSummoned->SetFacingTo(aEventLocations[1].m_fO); + + m_creature->Unmount(); + m_bIsBattleEnd = false; + + if (!HasEscortState(STATE_ESCORT_PAUSED)) + { + SetEscortPaused(true); // In case something didn't go as expected + SetCurrentWaypoint(5); + m_uiEventTimer = 60000; // Another failsafe + } + + SetEscortPaused(false); + m_creature->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_PASSIVE); + SetRun(false); + m_creature->AI()->EnterEvadeMode(); + + DoCastSpellIfCan(m_creature, SPELL_THE_LIGHT_OF_DAWN_DUMMY); + + // death knights are defeated + if (Creature* pKoltira = m_pInstance->GetSingleCreatureFromStorage(NPC_KOLTIRA_DEATHWEAVER)) + pKoltira->AI()->EnterEvadeMode(); + if (Creature* pThassarian = m_pInstance->GetSingleCreatureFromStorage(NPC_THASSARIAN)) + pThassarian->AI()->EnterEvadeMode(); + // Orbaz flees -> despawn + if (Creature* pOrbaz = m_pInstance->GetSingleCreatureFromStorage(NPC_ORBAZ_BLOODBANE)) + { + DoScriptText(EMOTE_LIGHT_OF_DAWN_FLEE, pOrbaz); + pOrbaz->AI()->EnterEvadeMode(); + pOrbaz->ForcedDespawn(30000); + } + + // ligth champs evade to their summon points + for (uint8 i = 0; i < MAX_LIGHT_CHAMPIONS; i++) + { + if (Creature* pTemp = m_pInstance->GetSingleCreatureFromStorage(aLightArmySpawnLoc[i].m_uiEntry)) + { + // normally it shouldn't happen + if (!pTemp->IsAlive()) + pTemp->Respawn(); + else + pTemp->AI()->EnterEvadeMode(); + + pTemp->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_PASSIVE); + pTemp->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE); + } + } + + // clear defenders list + m_lDefendersGUIDs.clear(); + + // spawn soldiers + for (uint8 i = 0; i < MAX_LIGHT_GUARDS; ++i) + { + if (Creature* pGuard = m_creature->SummonCreature(NPC_DEFENDER_OF_THE_LIGHT, aGuardsSpawnLoc[i].m_fX, aGuardsSpawnLoc[i].m_fY, aGuardsSpawnLoc[i].m_fZ, aGuardsSpawnLoc[i].m_fO, TEMPSUMMON_CORPSE_DESPAWN, 0)) + { + // make guard passive and with weapon + pGuard->SetFacingToObject(m_creature); + // should be 2 handed when the DB data is correct + pGuard->HandleEmoteCommand(EMOTE_STATE_READY2H); + pGuard->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE); + pGuard->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_PASSIVE); + } + } + + break; + } + } + + void MovementInform(uint32 uiMotionType, uint32 uiPointId) + { + if (uiPointId < POINT_MOVE_CHAPEL || uiPointId > 10 * POINT_MOVE_RETURN_BATTLE) + { + npc_escortAI::MovementInform(uiMotionType, uiPointId); + return; + } + + if (uiMotionType == POINT_MOTION_TYPE && uiPointId == POINT_MOVE_RETURN_BATTLE) + { + SetCombatMovement(false); + DoStartMovement(m_creature->getVictim()); + } + } + + void JustRespawned() override + { + m_creature->SetActiveObjectState(false); + + if (m_pInstance) + m_pInstance->SetData(TYPE_BATTLE, NOT_STARTED); + + npc_escortAI::JustRespawned(); + } + + void WaypointReached(uint32 uiPoint) override + { + if (!m_pInstance) + return; + + switch (uiPoint) + { + case 0: + // summon light champions + for (uint8 i = 0; i < MAX_LIGHT_CHAMPIONS; i++) + m_creature->SummonCreature(aLightArmySpawnLoc[i].m_uiEntry, aLightArmySpawnLoc[i].m_fX, aLightArmySpawnLoc[i].m_fY, aLightArmySpawnLoc[i].m_fZ, aLightArmySpawnLoc[i].m_fO, TEMPSUMMON_CORPSE_TIMED_DESPAWN, 5 * MINUTE * IN_MILLISECONDS); + + // summon light soldiers + float fX, fY, fZ; + for (uint8 i = 0; i < 5 * MAX_WARRIORS_SUMMONED_PER_TURN; ++i) + { + m_creature->GetRandomPoint(aEventLocations[1].m_fX, aEventLocations[1].m_fY, aEventLocations[1].m_fZ, 30.0f, fX, fY, fZ); + m_creature->SummonCreature(NPC_DEFENDER_OF_THE_LIGHT, fX, fY, fZ, 0.0f, TEMPSUMMON_CORPSE_DESPAWN, 5000); + } + break; + case 2: + // yell dawn 1 + if (Creature* pKorfax = m_pInstance->GetSingleCreatureFromStorage(NPC_KORFAX_CHAMPION_OF_THE_LIGHT)) + DoScriptText(SAY_LIGHT_OF_DAWN_STAND_1, pKorfax); + break; + case 3: + // yell dawn 2 + if (Creature* pMaxwell = m_pInstance->GetSingleCreatureFromStorage(NPC_LORD_MAXWELL_TYROSUS)) + DoScriptText(SAY_LIGHT_OF_DAWN_STAND_2, pMaxwell); + + DoCastSpellIfCan(m_creature, SPELL_THE_MIGHT_OF_MOGRAINE); + // max fight timer + m_uiFightTimer = 5 * MINUTE * IN_MILLISECONDS; + break; + case 4: + // start the battle + SetEscortPaused(true); + + // start attacking someone + if (Creature* pChamp = m_pInstance->GetSingleCreatureFromStorage(aLightArmySpawnLoc[urand(0, MAX_LIGHT_CHAMPIONS - 1)].m_uiEntry)) + m_creature->AI()->AttackStart(pChamp); + + // make army attack + for (GuidList::const_iterator itr = m_lAttackersGUIDs.begin(); itr != m_lAttackersGUIDs.end(); ++itr) + { + Creature* pAttacker = m_creature->GetMap()->GetCreature(*itr); + Creature* pChamp = m_pInstance->GetSingleCreatureFromStorage(aLightArmySpawnLoc[urand(0, MAX_LIGHT_CHAMPIONS - 1)].m_uiEntry); + if (pAttacker && pChamp) + pAttacker->AI()->AttackStart(pChamp); + } + + // need to make sure that all defenders attack + for (GuidList::const_iterator itr = m_lDefendersGUIDs.begin(); itr != m_lDefendersGUIDs.end(); ++itr) + { + if (Creature* pDefender = m_creature->GetMap()->GetCreature(*itr)) + pDefender->AI()->AttackStart(m_creature); + } + break; + case 5: + // battle finished - remove light of dawn aura + DoScriptText(EMOTE_LIGHT_OF_DAWN_KNEEL, m_creature); + DoScriptText(SAY_LIGHT_OF_DAWN_OUTRO_3, m_creature); + + if (m_creature->HasAura(SPELL_THE_LIGHT_OF_DAWN_DUMMY)) + m_creature->RemoveAurasDueToSpell(SPELL_THE_LIGHT_OF_DAWN_DUMMY); + + m_creature->SetStandState(UNIT_STAND_STATE_KNEEL); + + if (Creature* pTirion = m_pInstance->GetSingleCreatureFromStorage(NPC_HIGHLORD_TIRION_FORDRING)) + m_creature->SetFacingToObject(pTirion); + + // update guards facing + for (GuidList::const_iterator itr = m_lDefendersGUIDs.begin(); itr != m_lDefendersGUIDs.end(); ++itr) + { + if (Creature* pTemp = m_creature->GetMap()->GetCreature(*itr)) + pTemp->SetFacingToObject(m_creature); + } + + // escort paused and start cinematic + m_uiEventTimer = 10000; + SetEscortPaused(true); + break; + } + } + + // override evade function to always check for targets while in battle + void EnterEvadeMode() override + { + if (!m_pInstance) + return; + + // if evade while the battle is in progress start attacking another target + if (m_pInstance->GetData(TYPE_BATTLE) == IN_PROGRESS) + { + // attack random champion + if (Creature* pChamp = m_pInstance->GetSingleCreatureFromStorage(aLightArmySpawnLoc[urand(0, MAX_LIGHT_CHAMPIONS - 1)].m_uiEntry)) + m_creature->AI()->AttackStart(pChamp); + } + else + npc_escortAI::EnterEvadeMode(); + } + + void DoSendQuestCredit() + { + Map::PlayerList const& PlayerList = m_creature->GetMap()->GetPlayers(); + + for (Map::PlayerList::const_iterator itr = PlayerList.begin(); itr != PlayerList.end(); ++itr) + { + Player* pPlayer = itr->getSource(); + if (pPlayer && pPlayer->GetQuestStatus(QUEST_ID_LIGHT_OF_DAWN) == QUEST_STATUS_INCOMPLETE && pPlayer->IsAlive() && m_creature->IsWithinDistInMap(pPlayer, 50.0f)) + pPlayer->CastSpell(pPlayer, SPELL_THE_LIGHT_OF_DAWN_CREDIT, true); + } + } + + void UpdateEscortAI(const uint32 uiDiff) override + { + if (m_pInstance->GetData(TYPE_BATTLE) == SPECIAL) + { + // intro event and battle timer + if (m_uiIntroYell == 0 && m_uiPrepareTimer < 3 * MINUTE * IN_MILLISECONDS) + { + DoScriptText(SAY_LIGHT_OF_DAWN_INTRO_1, m_creature); + ++m_uiIntroYell; + } + else if (m_uiIntroYell == 1 && m_uiPrepareTimer < 2 * MINUTE * IN_MILLISECONDS) + { + DoScriptText(SAY_LIGHT_OF_DAWN_INTRO_2, m_creature); + ++m_uiIntroYell; + } + + // battle prepare timer + if (m_uiPrepareTimer < uiDiff) + { + if (m_pInstance) + m_pInstance->SetData(TYPE_BATTLE, IN_PROGRESS); + } + else + { + m_uiPrepareTimer -= uiDiff; + + if (m_uiPrepareTimer / IN_MILLISECONDS % 60 == 0) + { + if (m_pInstance) + m_pInstance->DoUpdateBattleWorldState(WORLD_STATE_BATTLE_TIMER_TIME, m_uiPrepareTimer / (MINUTE * IN_MILLISECONDS)); + } + } + } + else if (m_pInstance->GetData(TYPE_BATTLE) == IN_PROGRESS || m_pInstance->GetData(TYPE_BATTLE) == DONE) + { + if (m_uiEventTimer) + { + if (m_uiEventTimer <= uiDiff) + { + if (!m_pInstance) + return; + + switch (m_uiEventStep) + { + case 0: + DoScriptText(SAY_LIGHT_OF_DAWN_PREPARE_1, m_creature); + m_uiEventTimer = 5000; + break; + case 1: + DoScriptText(SAY_LIGHT_OF_DAWN_PREPARE_2, m_creature); + m_uiEventTimer = 10000; + break; + case 2: + DoScriptText(SAY_LIGHT_OF_DAWN_PREPARE_3, m_creature); + m_uiEventTimer = 3000; + break; + case 3: + DoScriptText(EMOTE_LIGHT_OF_DAWN_ARMY_RISE, m_creature); + case 4: + case 5: + { + // summon army takes about 20 secs and it's done on a few stages; no break between them + float fX, fY, fZ; + for (uint8 i = 0; i < MAX_WARRIORS_SUMMONED_PER_TURN; ++i) + { + uint32 uiSummonEntry = urand(0, 1) ? NPC_VOLATILE_GHOUL : NPC_WARRIOR_OF_THE_FROZEN_WASTES; + m_creature->GetRandomPoint(m_creature->GetPositionX(), m_creature->GetPositionY(), m_creature->GetPositionZ(), 50.0f, fX, fY, fZ); + m_creature->SummonCreature(uiSummonEntry, fX, fY, fZ, 4.7f, TEMPSUMMON_CORPSE_DESPAWN, 0); + } + m_uiEventTimer = 6000; + break; + } + case 6: + DoScriptText(SAY_LIGHT_OF_DAWN_PREPARE_4, m_creature); + m_uiEventTimer = 2000; + break; + case 7: + // send army emote + for (GuidList::const_iterator itr = m_lAttackersGUIDs.begin(); itr != m_lAttackersGUIDs.end(); ++itr) + { + if (Creature* pTemp = m_creature->GetMap()->GetCreature(*itr)) + pTemp->HandleEmoteCommand(EMOTE_ONESHOT_BATTLEROAR); + } + m_uiEventTimer = 6000; + break; + case 8: + // start attack (escort) + DoScriptText(EMOTE_LIGHT_OF_DAWN_ARMY_MARCH, m_creature); + m_creature->SetActiveObjectState(true); + Start(true); + + // move the companions as well + float fX, fY, fZ; + if (Creature* pKoltira = m_pInstance->GetSingleCreatureFromStorage(NPC_KOLTIRA_DEATHWEAVER)) + { + pKoltira->SetWalk(false); + m_creature->GetRandomPoint(aEventLocations[1].m_fX, aEventLocations[1].m_fY, aEventLocations[1].m_fZ, 30.0f, fX, fY, fZ); + pKoltira->GetMotionMaster()->MovePoint(0, fX, fY, fZ); + } + if (Creature* pThassarian = m_pInstance->GetSingleCreatureFromStorage(NPC_THASSARIAN)) + { + pThassarian->SetWalk(false); + m_creature->GetRandomPoint(aEventLocations[1].m_fX, aEventLocations[1].m_fY, aEventLocations[1].m_fZ, 30.0f, fX, fY, fZ); + pThassarian->GetMotionMaster()->MovePoint(0, fX, fY, fZ); + } + if (Creature* pOrbaz = m_pInstance->GetSingleCreatureFromStorage(NPC_ORBAZ_BLOODBANE)) + { + pOrbaz->SetWalk(false); + m_creature->GetRandomPoint(aEventLocations[1].m_fX, aEventLocations[1].m_fY, aEventLocations[1].m_fZ, 30.0f, fX, fY, fZ); + pOrbaz->GetMotionMaster()->MovePoint(0, fX, fY, fZ); + } + + // move army + for (GuidList::const_iterator itr = m_lAttackersGUIDs.begin(); itr != m_lAttackersGUIDs.end(); ++itr) + { + if (Creature* pTemp = m_creature->GetMap()->GetCreature(*itr)) + { + pTemp->SetWalk(false); + m_creature->GetRandomPoint(aEventLocations[1].m_fX, aEventLocations[1].m_fY, aEventLocations[1].m_fZ, 30.0f, fX, fY, fZ); + pTemp->GetMotionMaster()->MovePoint(0, fX, fY, fZ); + } + } + // move big units + m_pInstance->DoMoveArmy(); + m_uiEventTimer = 0; + break; + case 9: + // after the battle + if (Creature* pTirion = m_pInstance->GetSingleCreatureFromStorage(NPC_HIGHLORD_TIRION_FORDRING)) + DoScriptText(SAY_LIGHT_OF_DAWN_OUTRO_4, pTirion); + m_uiEventTimer = 21000; + break; + case 10: + if (Creature* pTirion = m_pInstance->GetSingleCreatureFromStorage(NPC_HIGHLORD_TIRION_FORDRING)) + DoScriptText(SAY_LIGHT_OF_DAWN_OUTRO_5, pTirion); + m_uiEventTimer = 13000; + break; + case 11: + if (Creature* pTirion = m_pInstance->GetSingleCreatureFromStorage(NPC_HIGHLORD_TIRION_FORDRING)) + DoScriptText(SAY_LIGHT_OF_DAWN_OUTRO_6, pTirion); + m_uiEventTimer = 13000; + break; + case 12: + m_creature->SetStandState(UNIT_STAND_STATE_STAND); + DoScriptText(SAY_LIGHT_OF_DAWN_OUTRO_7, m_creature); + m_uiEventTimer = 7000; + break; + case 13: + // start Alexandros vision + if (Creature* pAlexandros = m_creature->SummonCreature(NPC_HIGHLORD_ALEXANDROS_MOGRAINE, aEventLocations[4].m_fX, aEventLocations[4].m_fY, aEventLocations[4].m_fZ, aEventLocations[4].m_fO, TEMPSUMMON_CORPSE_TIMED_DESPAWN, 3000)) + { + DoScriptText(EMOTE_LIGHT_OF_DAWN_ALEXANDROS, pAlexandros); + pAlexandros->CastSpell(pAlexandros, SPELL_ALEXANDROS_MOGRAINE_SPAWN, true); + } + m_uiEventTimer = 4000; + break; + case 14: + if (Creature* pAlexandros = m_pInstance->GetSingleCreatureFromStorage(NPC_HIGHLORD_ALEXANDROS_MOGRAINE)) + { + pAlexandros->GetMotionMaster()->MovePoint(POINT_MOVE_OTHER, aEventLocations[5].m_fX, aEventLocations[5].m_fY, aEventLocations[5].m_fZ); + DoScriptText(SAY_LIGHT_OF_DAWN_VISION_1, pAlexandros); + m_creature->SetFacingToObject(pAlexandros); + } + m_uiEventTimer = 2000; + break; + case 15: + DoScriptText(SAY_LIGHT_OF_DAWN_VISION_2, m_creature); + m_uiEventTimer = 4000; + break; + case 16: + DoScriptText(SAY_LIGHT_OF_DAWN_VISION_3, m_creature); + m_creature->SetStandState(UNIT_STAND_STATE_KNEEL); + // summon young Darion for 1 min + if (Creature* pDarion = m_creature->SummonCreature(NPC_DARION_MOGRAINE, aEventLocations[6].m_fX, aEventLocations[6].m_fY, aEventLocations[6].m_fZ, aEventLocations[6].m_fO, TEMPSUMMON_TIMED_DESPAWN, 1 * MINUTE * IN_MILLISECONDS)) + DoScriptText(EMOTE_LIGHT_OF_DAWN_SHADE, pDarion); + m_uiEventTimer = 3000; + break; + case 17: + if (Creature* pDarion = m_pInstance->GetSingleCreatureFromStorage(NPC_DARION_MOGRAINE)) + DoScriptText(SAY_LIGHT_OF_DAWN_VISION_4, pDarion); + m_uiEventTimer = 3000; + break; + case 18: + // young darion runs to father + if (Creature* pDarion = m_pInstance->GetSingleCreatureFromStorage(NPC_DARION_MOGRAINE)) + { + pDarion->SetWalk(false); + pDarion->GetMotionMaster()->MovePoint(POINT_MOVE_CHAPEL, aEventLocations[7].m_fX, aEventLocations[7].m_fY, aEventLocations[7].m_fZ); + } + m_uiEventTimer = 5000; + break; + case 19: + if (Creature* pDarion = m_pInstance->GetSingleCreatureFromStorage(NPC_DARION_MOGRAINE)) + DoScriptText(SAY_LIGHT_OF_DAWN_VISION_5, pDarion); + m_uiEventTimer = 5000; + break; + case 20: + if (Creature* pAlexandros = m_pInstance->GetSingleCreatureFromStorage(NPC_HIGHLORD_ALEXANDROS_MOGRAINE)) + DoScriptText(SAY_LIGHT_OF_DAWN_VISION_6, pAlexandros); + m_uiEventTimer = 8000; + break; + case 21: + if (Creature* pDarion = m_pInstance->GetSingleCreatureFromStorage(NPC_DARION_MOGRAINE)) + DoScriptText(SAY_LIGHT_OF_DAWN_VISION_7, pDarion); + m_uiEventTimer = 8000; + break; + case 22: + if (Creature* pAlexandros = m_pInstance->GetSingleCreatureFromStorage(NPC_HIGHLORD_ALEXANDROS_MOGRAINE)) + DoScriptText(SAY_LIGHT_OF_DAWN_VISION_8, pAlexandros); + + // move Tirion to the point where the light of dawn is + if (Creature* pTirion = m_pInstance->GetSingleCreatureFromStorage(NPC_HIGHLORD_TIRION_FORDRING)) + { + pTirion->SetWalk(true); + if (GameObject* pLight = m_pInstance->GetSingleGameObjectFromStorage(GO_LIGHT_OF_DAWN)) + pTirion->GetMotionMaster()->MovePoint(POINT_MOVE_OTHER, pLight->GetPositionX(), pLight->GetPositionY(), pLight->GetPositionZ()); + } + m_uiEventTimer = 15000; + break; + case 23: + if (Creature* pDarion = m_pInstance->GetSingleCreatureFromStorage(NPC_DARION_MOGRAINE)) + DoScriptText(SAY_LIGHT_OF_DAWN_VISION_9, pDarion); + m_uiEventTimer = 11000; + break; + case 24: + if (Creature* pAlexandros = m_pInstance->GetSingleCreatureFromStorage(NPC_HIGHLORD_ALEXANDROS_MOGRAINE)) + DoScriptText(SAY_LIGHT_OF_DAWN_VISION_10, pAlexandros); + m_uiEventTimer = 29000; + break; + case 25: + if (Creature* pAlexandros = m_pInstance->GetSingleCreatureFromStorage(NPC_HIGHLORD_ALEXANDROS_MOGRAINE)) + DoScriptText(SAY_LIGHT_OF_DAWN_VISION_11, pAlexandros); + m_uiEventTimer = 6000; + break; + case 26: + // Lich king visit + if (Creature* pLichKing = m_creature->SummonCreature(NPC_THE_LICH_KING, aEventLocations[8].m_fX, aEventLocations[8].m_fY, aEventLocations[8].m_fZ, aEventLocations[8].m_fO, TEMPSUMMON_CORPSE_DESPAWN, 5000)) + DoScriptText(SAY_LIGHT_OF_DAWN_KING_VISIT_1, pLichKing); + if (Creature* pAlexandros = m_pInstance->GetSingleCreatureFromStorage(NPC_HIGHLORD_ALEXANDROS_MOGRAINE)) + DoScriptText(EMOTE_LIGHT_OF_DAWN_LICH_KING, pAlexandros); + m_uiEventTimer = 2000; + break; + case 27: + // the LK feasts on Alexandros + if (Creature* pLichKing = m_pInstance->GetSingleCreatureFromStorage(NPC_THE_LICH_KING)) + { + DoScriptText(SAY_LIGHT_OF_DAWN_KING_VISIT_2, pLichKing); + if (Creature* pAlexandros = m_pInstance->GetSingleCreatureFromStorage(NPC_HIGHLORD_ALEXANDROS_MOGRAINE)) + pLichKing->CastSpell(pAlexandros, SPELL_SOUL_FEAST_ALEX, false); + } + m_uiEventTimer = 2000; + break; + case 28: + if (Creature* pAlexandros = m_pInstance->GetSingleCreatureFromStorage(NPC_HIGHLORD_ALEXANDROS_MOGRAINE)) + pAlexandros->ForcedDespawn(); + m_uiEventTimer = 2000; + break; + case 29: + m_creature->SetStandState(UNIT_STAND_STATE_STAND); + DoScriptText(EMOTE_LIGHT_OF_DAWN_ANGRY, m_creature); + DoScriptText(SAY_LIGHT_OF_DAWN_KING_VISIT_3, m_creature); + m_uiEventTimer = 3000; + break; + case 30: + // the LK moves forward + if (Creature* pLichKing = m_pInstance->GetSingleCreatureFromStorage(NPC_THE_LICH_KING)) + { + pLichKing->CastSpell(pLichKing, SPELL_ICEBOUND_VISAGE, true); + pLichKing->GetMotionMaster()->MovePoint(POINT_MOVE_CHAPEL, aEventLocations[9].m_fX, aEventLocations[9].m_fY, aEventLocations[9].m_fZ); + } + m_uiEventTimer = 5000; + break; + case 31: + // darion charges + DoCastSpellIfCan(m_creature, SPELL_MOGRAINE_CHARGE); + m_uiEventTimer = 3000; + break; + case 32: + // the LK kicks darion + if (Creature* pLichKing = m_pInstance->GetSingleCreatureFromStorage(NPC_THE_LICH_KING)) + { + DoScriptText(SAY_LIGHT_OF_DAWN_KING_VISIT_4, pLichKing); + // Note: this should be cast by the LK - spell bug + m_creature->CastSpell(m_creature, SPELL_REBUKE, true); + } + m_creature->SetStandState(UNIT_STAND_STATE_DEAD); + m_uiEventTimer = 4000; + break; + case 33: + m_creature->SetStandState(UNIT_STAND_STATE_KNEEL); + if (Creature* pLichKing = m_pInstance->GetSingleCreatureFromStorage(NPC_THE_LICH_KING)) + m_creature->SetFacingToObject(pLichKing); + if (Creature* pTirion = m_pInstance->GetSingleCreatureFromStorage(NPC_HIGHLORD_TIRION_FORDRING)) + DoScriptText(SAY_LIGHT_OF_DAWN_KING_VISIT_5, pTirion); + m_uiEventTimer = 8000; + break; + case 34: + if (Creature* pLichKing = m_pInstance->GetSingleCreatureFromStorage(NPC_THE_LICH_KING)) + DoScriptText(SAY_LIGHT_OF_DAWN_KING_VISIT_6, pLichKing); + m_uiEventTimer = 15000; + break; + case 35: + if (Creature* pLichKing = m_pInstance->GetSingleCreatureFromStorage(NPC_THE_LICH_KING)) + DoScriptText(SAY_LIGHT_OF_DAWN_KING_VISIT_7, pLichKing); + m_uiEventTimer = 17000; + break; + case 36: + // the LK feasts on tirion + if (Creature* pLichKing = m_pInstance->GetSingleCreatureFromStorage(NPC_THE_LICH_KING)) + { + DoScriptText(EMOTE_LIGHT_OF_DAWN_CAST_SPELL, pLichKing); + if (Creature* pTirion = m_pInstance->GetSingleCreatureFromStorage(NPC_HIGHLORD_TIRION_FORDRING)) + { + DoScriptText(EMOTE_LIGHT_OF_DAWN_GRASP, pTirion); + pLichKing->CastSpell(pTirion, SPELL_SOUL_FEAST_TIRION, false); + pLichKing->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_PASSIVE); + } + } + m_uiEventTimer = 2000; + break; + case 37: + // the light champions attack the LK + if (Creature* pMaxwell = m_pInstance->GetSingleCreatureFromStorage(NPC_LORD_MAXWELL_TYROSUS)) + DoScriptText(SAY_LIGHT_OF_DAWN_KING_VISIT_8, pMaxwell); + if (Creature* pLichKing = m_pInstance->GetSingleCreatureFromStorage(NPC_THE_LICH_KING)) + { + float fX, fY, fZ; + pLichKing->GetContactPoint(m_creature, fX, fY, fZ); + for (GuidList::const_iterator itr = m_lDefendersGUIDs.begin(); itr != m_lDefendersGUIDs.end(); ++itr) + { + if (Creature* pTemp = m_creature->GetMap()->GetCreature(*itr)) + { + pTemp->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_PASSIVE); + pTemp->SetWalk(false); + pTemp->GetMotionMaster()->MovePoint(0, fX, fY, fZ); + // attack gives us some issues + //pTemp->AI()->AttackStart(pLichKing); + } + } + for (uint8 i = 0; i < MAX_LIGHT_CHAMPIONS; i++) + { + if (Creature* pTemp = m_pInstance->GetSingleCreatureFromStorage(aLightArmySpawnLoc[i].m_uiEntry)) + { + pTemp->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_PASSIVE); + pTemp->SetWalk(false); + pTemp->GetMotionMaster()->MovePoint(0, fX, fY, fZ); + // attack gives us some issues + //pTemp->AI()->AttackStart(pLichKing); + } + } + } + m_uiEventTimer = 6000; + break; + case 38: + // the LK throws away all the attackers + if (Creature* pLichKing = m_pInstance->GetSingleCreatureFromStorage(NPC_THE_LICH_KING)) + { + DoScriptText(EMOTE_LIGHT_OF_DAWN_POWERFULL, pLichKing); + DoScriptText(SAY_LIGHT_OF_DAWN_KING_VISIT_9, pLichKing); + pLichKing->CastSpell(pLichKing, SPELL_APOCALYPSE, true); + } + m_uiEventTimer = 1000; + break; + case 39: + if (Creature* pLichKing = m_pInstance->GetSingleCreatureFromStorage(NPC_THE_LICH_KING)) + { + pLichKing->CastSpell(pLichKing, SPELL_POST_APOCALYPSE, true); + + // despawn guards + for (GuidList::const_iterator itr = m_lDefendersGUIDs.begin(); itr != m_lDefendersGUIDs.end(); ++itr) + { + if (Creature* pTemp = m_creature->GetMap()->GetCreature(*itr)) + pTemp->DealDamage(pTemp, pTemp->GetHealth(), NULL, DIRECT_DAMAGE, SPELL_SCHOOL_MASK_NORMAL, NULL, false); + } + // workaround for the light champions - spell doesn't work right + for (uint8 i = 0; i < MAX_LIGHT_CHAMPIONS; i++) + { + if (Creature* pTemp = m_pInstance->GetSingleCreatureFromStorage(aLightArmySpawnLoc[i].m_uiEntry)) + { + pTemp->SetStandState(UNIT_STAND_STATE_DEAD); + pTemp->KnockBackFrom(pLichKing, 50, float(urand(44, 87)) / 10); + } + } + } + m_uiEventTimer = 5000; + break; + case 40: + DoScriptText(SAY_LIGHT_OF_DAWN_KING_VISIT_10, m_creature); + m_uiEventTimer = 5000; + break; + case 41: + // darion throws the ashbringer to tirion + DoScriptText(SAY_LIGHT_OF_DAWN_KING_VISIT_11, m_creature); + m_creature->SetStandState(UNIT_STAND_STATE_STAND); + m_uiEventTimer = 1000; + break; + case 42: + DoScriptText(EMOTE_LIGHT_OF_DAWN_ASHBRINGER, m_creature); + DoCastSpellIfCan(m_creature, SPELL_ASHBRINGER); + SetEquipmentSlots(false, EQUIP_UNEQUIP, EQUIP_UNEQUIP, EQUIP_NO_CHANGE); + m_uiEventTimer = 5000; + break; + case 43: + // darion colapses while tirion is engulfed in light + DoScriptText(EMOTE_LIGHT_OF_DAWN_COLAPSE, m_creature); + m_creature->SetStandState(UNIT_STAND_STATE_DEAD); + if (Creature* pTirion = m_pInstance->GetSingleCreatureFromStorage(NPC_HIGHLORD_TIRION_FORDRING)) + pTirion->CastSpell(pTirion, SPELL_REBIRTH_OF_THE_ASHBRINGER, true); + m_pInstance->DoRespawnGameObject(GO_LIGHT_OF_DAWN, 5 * MINUTE); + if (Creature* pLichKing = m_pInstance->GetSingleCreatureFromStorage(NPC_THE_LICH_KING)) + pLichKing->InterruptNonMeleeSpells(false); + m_uiEventTimer = 2000; + break; + case 44: + // rebirth of the ashbringer + if (Creature* pTirion = m_pInstance->GetSingleCreatureFromStorage(NPC_HIGHLORD_TIRION_FORDRING)) + { + if (pTirion->HasAura(SPELL_REBIRTH_OF_THE_ASHBRINGER)) + pTirion->RemoveAurasDueToSpell(SPELL_REBIRTH_OF_THE_ASHBRINGER); + pTirion->HandleEmoteCommand(EMOTE_ONESHOT_ROAR); + } + m_uiEventTimer = 2500; + break; + case 45: + if (Creature* pTirion = m_pInstance->GetSingleCreatureFromStorage(NPC_HIGHLORD_TIRION_FORDRING)) + DoScriptText(SAY_LIGHT_OF_DAWN_KING_VISIT_12, pTirion); + m_uiEventTimer = 4000; + break; + case 46: + if (Creature* pLichKing = m_pInstance->GetSingleCreatureFromStorage(NPC_THE_LICH_KING)) + DoScriptText(SAY_LIGHT_OF_DAWN_KING_VISIT_13, pLichKing); + m_uiEventTimer = 5000; + break; + case 47: + if (Creature* pTirion = m_pInstance->GetSingleCreatureFromStorage(NPC_HIGHLORD_TIRION_FORDRING)) + DoScriptText(SAY_LIGHT_OF_DAWN_KING_VISIT_14, pTirion); + m_uiEventTimer = 1000; + break; + case 48: + // tirion charges to the LK + if (Creature* pTirion = m_pInstance->GetSingleCreatureFromStorage(NPC_HIGHLORD_TIRION_FORDRING)) + { + DoScriptText(EMOTE_LIGHT_OF_DAWN_CHARGE, pTirion); + pTirion->CastSpell(pTirion, SPELL_TIRION_CHARGE, true); + } + m_uiEventTimer = 2000; + break; + case 49: + // move the LK back in front of tirion; + if (Creature* pLichKing = m_pInstance->GetSingleCreatureFromStorage(NPC_THE_LICH_KING)) + { + DoScriptText(SAY_LIGHT_OF_DAWN_KING_VISIT_15, pLichKing); + pLichKing->GetMotionMaster()->MovePoint(POINT_MOVE_CHAPEL, aEventLocations[8].m_fX, aEventLocations[8].m_fY, aEventLocations[8].m_fZ); + } + if (Creature* pTirion = m_pInstance->GetSingleCreatureFromStorage(NPC_HIGHLORD_TIRION_FORDRING)) + pTirion->DeleteThreatList(); + m_uiEventTimer = 1000; + break; + case 50: + if (Creature* pLichKing = m_pInstance->GetSingleCreatureFromStorage(NPC_THE_LICH_KING)) + pLichKing->HandleEmoteCommand(EMOTE_ONESHOT_KNEEL); + m_uiEventTimer = 3000; + break; + case 51: + if (Creature* pLichKing = m_pInstance->GetSingleCreatureFromStorage(NPC_THE_LICH_KING)) + DoScriptText(SAY_LIGHT_OF_DAWN_KING_VISIT_16, pLichKing); + m_uiEventTimer = 10000; + break; + case 52: + if (Creature* pLichKing = m_pInstance->GetSingleCreatureFromStorage(NPC_THE_LICH_KING)) + DoScriptText(SAY_LIGHT_OF_DAWN_KING_VISIT_17, pLichKing); + m_uiEventTimer = 10000; + break; + case 53: + // the lich king teleports to leave + if (Creature* pLichKing = m_pInstance->GetSingleCreatureFromStorage(NPC_THE_LICH_KING)) + pLichKing->CastSpell(pLichKing, SPELL_TELEPORT_VISUAL, false); + if (Creature* pTirion = m_pInstance->GetSingleCreatureFromStorage(NPC_HIGHLORD_TIRION_FORDRING)) + { + float fX, fY, fZ; + pTirion->SetWalk(false); + m_creature->GetContactPoint(pTirion, fX, fY, fZ, INTERACTION_DISTANCE); + pTirion->GetMotionMaster()->MovePoint(POINT_MOVE_OTHER, fX, fY, fZ); + } + // make champions stand + for (uint8 i = 0; i < MAX_LIGHT_CHAMPIONS; i++) + { + if (Creature* pTemp = m_pInstance->GetSingleCreatureFromStorage(aLightArmySpawnLoc[i].m_uiEntry)) + { + pTemp->SetStandState(UNIT_STAND_STATE_STAND); + pTemp->SetFacingToObject(m_creature); + } + } + m_uiEventTimer = 2000; + break; + case 54: + // the lich king leaves + if (Creature* pLichKing = m_pInstance->GetSingleCreatureFromStorage(NPC_THE_LICH_KING)) + { + DoScriptText(EMOTE_LIGHT_OF_DAWN_KING_LEAVE, pLichKing); + pLichKing->ForcedDespawn(); + } + m_uiEventTimer = 7000; + break; + case 55: + // tirion reaches darion and starts the epilogue + if (Creature* pTirion = m_pInstance->GetSingleCreatureFromStorage(NPC_HIGHLORD_TIRION_FORDRING)) + { + pTirion->CastSpell(m_creature, SPELL_LAY_ON_HANDS, true); + DoScriptText(SAY_LIGHT_OF_DAWN_EPILOGUE_1, pTirion); + } + m_creature->SetStandState(UNIT_STAND_STATE_KNEEL); + m_uiEventTimer = 3000; + break; + case 56: + // tirion moves near the light of dawn object + if (Creature* pTirion = m_pInstance->GetSingleCreatureFromStorage(NPC_HIGHLORD_TIRION_FORDRING)) + { + pTirion->SetWalk(true); + pTirion->GetMotionMaster()->MovePoint(POINT_MOVE_OTHER, aEventLocations[10].m_fX, aEventLocations[10].m_fY, aEventLocations[10].m_fZ); + } + m_uiEventTimer = 5000; + break; + case 57: + if (Creature* pTirion = m_pInstance->GetSingleCreatureFromStorage(NPC_HIGHLORD_TIRION_FORDRING)) + { + pTirion->SetFacingToObject(m_creature); + m_creature->SetFacingToObject(pTirion); + DoScriptText(SAY_LIGHT_OF_DAWN_EPILOGUE_2, pTirion); + } + m_uiEventTimer = 15000; + break; + case 58: + if (Creature* pTirion = m_pInstance->GetSingleCreatureFromStorage(NPC_HIGHLORD_TIRION_FORDRING)) + DoScriptText(SAY_LIGHT_OF_DAWN_EPILOGUE_3, pTirion); + m_uiEventTimer = 7000; + break; + case 59: + if (Creature* pTirion = m_pInstance->GetSingleCreatureFromStorage(NPC_HIGHLORD_TIRION_FORDRING)) + DoScriptText(SAY_LIGHT_OF_DAWN_EPILOGUE_4, pTirion); + m_uiEventTimer = 10000; + break; + case 60: + if (Creature* pTirion = m_pInstance->GetSingleCreatureFromStorage(NPC_HIGHLORD_TIRION_FORDRING)) + DoScriptText(SAY_LIGHT_OF_DAWN_EPILOGUE_5, pTirion); + m_uiEventTimer = 11000; + break; + case 61: + if (Creature* pTirion = m_pInstance->GetSingleCreatureFromStorage(NPC_HIGHLORD_TIRION_FORDRING)) + DoScriptText(SAY_LIGHT_OF_DAWN_EPILOGUE_6, pTirion); + m_uiEventTimer = 10000; + break; + case 62: + if (Creature* pTirion = m_pInstance->GetSingleCreatureFromStorage(NPC_HIGHLORD_TIRION_FORDRING)) + DoScriptText(SAY_LIGHT_OF_DAWN_EPILOGUE_7, pTirion); + m_uiEventTimer = 8000; + break; + case 63: + if (Creature* pTirion = m_pInstance->GetSingleCreatureFromStorage(NPC_HIGHLORD_TIRION_FORDRING)) + DoScriptText(SAY_LIGHT_OF_DAWN_EPILOGUE_8, pTirion); + m_uiEventTimer = 10000; + break; + case 64: + m_creature->SetStandState(UNIT_STAND_STATE_STAND); + DoScriptText(SAY_LIGHT_OF_DAWN_EPILOGUE_9, m_creature); + m_uiEventTimer = 10000; + break; + case 65: + // send credit then in 5 min reset + DoSendQuestCredit(); + m_creature->SetFlag(UNIT_NPC_FLAGS, UNIT_NPC_FLAG_QUESTGIVER); + m_uiEventTimer = 5 * MINUTE * IN_MILLISECONDS; + break; + case 66: + m_pInstance->SetData(TYPE_BATTLE, NOT_STARTED); + if (Creature* pKoltira = m_pInstance->GetSingleCreatureFromStorage(NPC_KOLTIRA_DEATHWEAVER)) + pKoltira->ForcedDespawn(); + if (Creature* pThassarian = m_pInstance->GetSingleCreatureFromStorage(NPC_THASSARIAN)) + pThassarian->ForcedDespawn(); + if (Creature* pTirion = m_pInstance->GetSingleCreatureFromStorage(NPC_HIGHLORD_TIRION_FORDRING)) + pTirion->ForcedDespawn(); + for (uint8 i = 0; i < MAX_LIGHT_CHAMPIONS; i++) + { + if (Creature* pTemp = m_pInstance->GetSingleCreatureFromStorage(aLightArmySpawnLoc[i].m_uiEntry)) + pTemp->ForcedDespawn(); + } + SetEscortPaused(false); + m_uiEventTimer = 0; + break; + } + + ++m_uiEventStep; + } + else + m_uiEventTimer -= uiDiff; + } + + // Battle end yells + if (m_bIsBattleEnd) + { + if (m_uiFightSpeechTimer < uiDiff) + { + switch (urand(0, 6)) + { + case 0: DoScriptText(SAY_LIGHT_OF_DAWN_BATTLE_10, m_creature); break; + case 1: DoScriptText(SAY_LIGHT_OF_DAWN_BATTLE_11, m_creature); break; + case 2: DoScriptText(SAY_LIGHT_OF_DAWN_BATTLE_12, m_creature); break; + case 3: DoScriptText(SAY_LIGHT_OF_DAWN_BATTLE_13, m_creature); break; + case 4: DoScriptText(SAY_LIGHT_OF_DAWN_BATTLE_14, m_creature); break; + case 5: DoScriptText(SAY_LIGHT_OF_DAWN_BATTLE_15, m_creature); break; + case 6: DoScriptText(SAY_LIGHT_OF_DAWN_BATTLE_16, m_creature); break; + } + m_uiFightSpeechTimer = urand(5000, 7000); + } + else + m_uiFightSpeechTimer -= uiDiff; + } + + // Handle battle events + if (m_uiFightTimer) + { + // on blizz the battle takes about 4 min, time in which about 100 light warriors die + if (m_uiFightTimer <= uiDiff || m_uiLightWarriorsDead >= 100) + { + // summon Tirion and move him to the chapel + if (Creature* pTirion = m_creature->SummonCreature(NPC_HIGHLORD_TIRION_FORDRING, aEventLocations[0].m_fX, aEventLocations[0].m_fY, aEventLocations[0].m_fZ, aEventLocations[0].m_fO, TEMPSUMMON_CORPSE_DESPAWN, 5000, true)) + { + // decrease Darion's damage + DoCastSpellIfCan(m_creature, SPELL_THE_LIGHT_OF_DAWN_DAMAGE_LOSS, CAST_TRIGGERED); + + // Damage the scourge army + if (m_pInstance) + m_pInstance->DoEnableHolyTraps(); + + DoScriptText(SAY_LIGHT_OF_DAWN_OUTRO_1, pTirion); + DoScriptText(EMOTE_LIGHT_OF_DAWN_TIRION, pTirion); + + pTirion->SetWalk(false); + pTirion->GetMotionMaster()->MovePoint(POINT_MOVE_CHAPEL, aEventLocations[1].m_fX, aEventLocations[1].m_fY, aEventLocations[1].m_fZ); + + m_uiFightTimer = 0; + m_uiFightSpeechTimer = 1000; + m_bIsBattleEnd = true; + } + } + else + m_uiFightTimer -= uiDiff; + + if (!m_creature->SelectHostileTarget() || !m_creature->getVictim()) + return; + + // battle sounds + if (m_uiFightSpeechTimer < uiDiff) + { + switch (urand(0, 8)) + { + case 0: DoScriptText(SAY_LIGHT_OF_DAWN_BATTLE_1, m_creature); break; + case 1: DoScriptText(SAY_LIGHT_OF_DAWN_BATTLE_2, m_creature); break; + case 2: DoScriptText(SAY_LIGHT_OF_DAWN_BATTLE_3, m_creature); break; + case 3: DoScriptText(SAY_LIGHT_OF_DAWN_BATTLE_4, m_creature); break; + case 4: DoScriptText(SAY_LIGHT_OF_DAWN_BATTLE_5, m_creature); break; + case 5: DoScriptText(SAY_LIGHT_OF_DAWN_BATTLE_6, m_creature); break; + case 6: DoScriptText(SAY_LIGHT_OF_DAWN_BATTLE_7, m_creature); break; + case 7: DoScriptText(SAY_LIGHT_OF_DAWN_BATTLE_8, m_creature); break; + case 8: DoScriptText(SAY_LIGHT_OF_DAWN_BATTLE_9, m_creature); break; + } + m_uiFightSpeechTimer = urand(15000, 20000); + } + else + m_uiFightSpeechTimer -= uiDiff; + + // make sure that darion always stays in the area + if (!m_creature->IsWithinDist2d(aEventLocations[1].m_fX, aEventLocations[1].m_fY, 50.0f)) + { + SetCombatMovement(false); + m_creature->GetMotionMaster()->MovePoint(POINT_MOVE_RETURN_BATTLE, aEventLocations[1].m_fX, aEventLocations[1].m_fY, aEventLocations[1].m_fZ); + } + + // Darion spells + if (m_uiAntimagicZoneTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature, SPELL_ANTI_MAGIC_ZONE_DARION) == CAST_OK) + m_uiAntimagicZoneTimer = urand(85000, 90000); + } + else + m_uiAntimagicZoneTimer -= uiDiff; + + if (m_uiDeathStrikeTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature->getVictim(), SPELL_DEATH_STRIKE) == CAST_OK) + m_uiDeathStrikeTimer = urand(5000, 10000); + } + else + m_uiDeathStrikeTimer -= uiDiff; + + if (m_uiDeathEmbraceTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature->getVictim(), SPELL_DEATH_EMBRACE) == CAST_OK) + m_uiDeathEmbraceTimer = urand(5000, 10000); + } + else + m_uiDeathEmbraceTimer -= uiDiff; + + if (m_uiIcyTouchTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature->getVictim(), SPELL_ICY_TOUCH_DARION) == CAST_OK) + m_uiIcyTouchTimer = urand(5000, 10000); + } + else + m_uiIcyTouchTimer -= uiDiff; + + if (m_uiUnholyBlightTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature->getVictim(), SPELL_UNHOLY_BLIGHT) == CAST_OK) + m_uiUnholyBlightTimer = urand(5000, 10000); + } + else + m_uiUnholyBlightTimer -= uiDiff; + + DoMeleeAttackIfReady(); + } + } + } +}; + +bool GossipHello_npc_highlord_darion_mograine(Player* pPlayer, Creature* pCreature) +{ + if (pCreature->IsQuestGiver()) + pPlayer->PrepareQuestMenu(pCreature->GetObjectGuid()); + + // Only allow start battle after reset + if (world_map_ebon_hold* pInstance = (world_map_ebon_hold*)pCreature->GetInstanceData()) + { + if (pPlayer->GetQuestStatus(QUEST_ID_LIGHT_OF_DAWN) == QUEST_STATUS_INCOMPLETE && pInstance->GetData(TYPE_BATTLE) == NOT_STARTED) + pPlayer->ADD_GOSSIP_ITEM_ID(GOSSIP_ICON_CHAT, GOSSIP_ITEM_READY, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 1); + } + + pPlayer->SEND_GOSSIP_MENU(GOSSIP_TEXT_ID_READY, pCreature->GetObjectGuid()); + + return true; +} + +bool GossipSelect_npc_highlord_darion_mograine(Player* pPlayer, Creature* pCreature, uint32 /*uiSender*/, uint32 uiAction) +{ + if (uiAction == GOSSIP_ACTION_INFO_DEF + 1) + { + if (world_map_ebon_hold* pInstance = (world_map_ebon_hold*)pCreature->GetInstanceData()) + { + // set data to special in order to start the event + pInstance->SetData(TYPE_BATTLE, SPECIAL); + pPlayer->CLOSE_GOSSIP_MENU(); + + return true; + } + } + pPlayer->CLOSE_GOSSIP_MENU(); + + return false; +} + +CreatureAI* GetAI_npc_highlord_darion_mograine(Creature* pCreature) +{ + return new npc_highlord_darion_mograineAI(pCreature); +} + +struct npc_fellow_death_knightAI : public ScriptedAI +{ + npc_fellow_death_knightAI(Creature* pCreature) : ScriptedAI(pCreature) + { + m_pInstance = (world_map_ebon_hold*)pCreature->GetInstanceData(); + Reset(); + } + + world_map_ebon_hold* m_pInstance; + + uint32 m_uiIcyTouchTimer; + uint32 m_uiBloodStrikeTimer; + uint32 m_uiPlagueStrikeTimer; + + void Reset() override + { + m_uiBloodStrikeTimer = urand(5000, 10000); + m_uiIcyTouchTimer = urand(5000, 10000); + m_uiPlagueStrikeTimer = urand(5000, 10000); + } + + void Aggro(Unit* /*pWho*/) override + { + DoCastSpellIfCan(m_creature, SPELL_HERO_AGGRO_AURA); + } + + void MovementInform(uint32 uiType, uint32 uiPointId) override + { + if (uiType != POINT_MOTION_TYPE || uiPointId != POINT_MOVE_CHAPEL) + return; + + // make the death knights kneel + if (m_creature->HasAura(SPELL_THE_LIGHT_OF_DAWN_DUMMY)) + m_creature->RemoveAurasDueToSpell(SPELL_THE_LIGHT_OF_DAWN_DUMMY); + + m_creature->SetStandState(UNIT_STAND_STATE_KNEEL); + + if (Creature* pTirion = m_pInstance->GetSingleCreatureFromStorage(NPC_HIGHLORD_TIRION_FORDRING)) + m_creature->SetFacingToObject(pTirion); + } + + void EnterEvadeMode() override + { + if (!m_creature->IsAlive()) + return; + + if (!m_pInstance) + return; + + // if evade while the battle is in progress start attacking another target + if (m_pInstance->GetData(TYPE_BATTLE) == IN_PROGRESS) + { + if (Creature* pDarion = m_pInstance->GetSingleCreatureFromStorage(NPC_HIGHLORD_DARION_MOGRAINE)) + { + if (Unit* pTarget = pDarion->SelectAttackingTarget(ATTACKING_TARGET_RANDOM, 0)) + m_creature->AI()->AttackStart(pTarget); + } + } + else if (m_pInstance->GetData(TYPE_BATTLE) == DONE) + { + m_creature->RemoveAllAurasOnEvade(); + m_creature->DeleteThreatList(); + m_creature->CombatStop(true); + m_creature->LoadCreatureAddon(true); + m_creature->SetLootRecipient(NULL); + + Reset(); + + if (m_creature->GetEntry() != NPC_ORBAZ_BLOODBANE) + { + // cast light of dawn + if (DoCastSpellIfCan(m_creature, SPELL_THE_LIGHT_OF_DAWN_DUMMY, CAST_TRIGGERED) == CAST_OK) + { + m_creature->Unmount(); + m_creature->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_PASSIVE); + } + } + + // move to chapel points + switch (m_creature->GetEntry()) + { + case NPC_THASSARIAN: + m_creature->GetMotionMaster()->MovePoint(POINT_MOVE_CHAPEL, aEventLocations[3].m_fX, aEventLocations[3].m_fY, aEventLocations[3].m_fZ); + break; + case NPC_KOLTIRA_DEATHWEAVER: + m_creature->GetMotionMaster()->MovePoint(POINT_MOVE_CHAPEL, aEventLocations[2].m_fX, aEventLocations[2].m_fY, aEventLocations[2].m_fZ); + break; + case NPC_ORBAZ_BLOODBANE: + m_creature->GetMotionMaster()->MoveTargetedHome(); + break; + } + } + else + ScriptedAI::EnterEvadeMode(); + } + + void UpdateAI(const uint32 uiDiff) override + { + if (!m_creature->SelectHostileTarget() || !m_creature->getVictim()) + return; + + if (m_uiPlagueStrikeTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature->getVictim(), SPELL_PLAGUE_STRIKE_KNIGHTS) == CAST_OK) + m_uiPlagueStrikeTimer = urand(5000, 10000); + } + else + m_uiPlagueStrikeTimer -= uiDiff; + + if (m_uiIcyTouchTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature->getVictim(), SPELL_ICY_TOUCH_DARION) == CAST_OK) + m_uiIcyTouchTimer = urand(5000, 10000); + } + else + m_uiIcyTouchTimer -= uiDiff; + + if (m_uiBloodStrikeTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature->getVictim(), SPELL_BLOOD_STRIKE) == CAST_OK) + m_uiBloodStrikeTimer = urand(5000, 10000); + } + else + m_uiBloodStrikeTimer -= uiDiff; + + DoMeleeAttackIfReady(); + } +}; + +CreatureAI* GetAI_npc_fellow_death_knight(Creature* pCreature) +{ + return new npc_fellow_death_knightAI(pCreature); +} + +// TODO Remove this 'script' when combat can be proper prevented from core-side +struct npc_lich_king_light_dawnAI : public ScriptedAI +{ + npc_lich_king_light_dawnAI(Creature* pCreature) : ScriptedAI(pCreature) { Reset(); } + + void Reset() override { } + void MoveInLineOfSight(Unit* /*pWho*/) override { } + void AttackStart(Unit* /*pWho*/) override { } + void UpdateAI(const uint32 /*uiDiff*/) override { } +}; + +CreatureAI* GetAI_npc_lich_king_light_dawn(Creature* pCreature) +{ + return new npc_lich_king_light_dawnAI(pCreature); +} + +/*###### +## npc_acherus_deathcharger +######*/ + +enum +{ + EMOTE_HORSE_READY = -1609097, + SAY_RACE_FINISHED = -1609098, + + SPELL_HORSEMAN_SLAIN = 52692, + SPELL_RACE_COMPLETE = 52361, + + NPC_DARK_RIDER_OF_ACHERUS = 28768, + NPC_SALANAR_THE_HORSEMAN = 28788, + + FACTION_FRIENDLY = 35, +}; + +struct npc_acherus_deathchargerAI : public ScriptedAI +{ + npc_acherus_deathchargerAI(Creature* pCreature) : ScriptedAI(pCreature) { Reset(); } + + bool m_bIsRiderDead; + + uint8 m_uiQuestEndStage; + uint32 m_uiQuestEndTimer; + + ObjectGuid m_salaranGuid; + + void Reset() override + { + m_bIsRiderDead = false; + m_uiQuestEndStage = 0; + m_uiQuestEndTimer = 0; + + SetCombatMovement(true); + m_creature->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE); + } + + void EnterEvadeMode() override + { + if (m_bIsRiderDead) + { + m_creature->RemoveAllAurasOnEvade(); + m_creature->DeleteThreatList(); + m_creature->CombatStop(true); + m_creature->LoadCreatureAddon(true); + m_creature->SetLootRecipient(NULL); + + // Stop movemnet + m_creature->GetMotionMaster()->Clear(); + m_creature->GetMotionMaster()->MoveIdle(); + + // Prepare to be mounted + SetCombatMovement(false); + DoScriptText(EMOTE_HORSE_READY, m_creature); + m_creature->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE); + m_creature->SetFactionTemporary(FACTION_FRIENDLY, TEMPFACTION_RESTORE_RESPAWN); + } + else + ScriptedAI::EnterEvadeMode(); + } + + void JustSummoned(Creature* pSummoned) override + { + if (pSummoned->GetEntry() == NPC_SALANAR_THE_HORSEMAN) + { + float fX, fY, fZ; + m_creature->GetContactPoint(pSummoned, fX, fY, fZ, INTERACTION_DISTANCE); + pSummoned->GetMotionMaster()->MovePoint(1, fX, fY, fZ); + + m_salaranGuid = pSummoned->GetObjectGuid(); + m_uiQuestEndTimer = 4000; + } + } + + void SummonedCreatureJustDied(Creature* pSummoned) override + { + // Initial vehicle rider - handled in DB + if (pSummoned->GetEntry() == NPC_DARK_RIDER_OF_ACHERUS) + { + m_bIsRiderDead = true; + DoCastSpellIfCan(m_creature, SPELL_HORSEMAN_SLAIN, CAST_TRIGGERED); + } + } + + void UpdateAI(const uint32 uiDiff) override + { + if (m_uiQuestEndTimer) + { + if (m_uiQuestEndTimer <= uiDiff) + { + switch (m_uiQuestEndStage) + { + case 0: + if (Creature* pSalaran = m_creature->GetMap()->GetCreature(m_salaranGuid)) + DoScriptText(SAY_RACE_FINISHED, pSalaran); + + m_uiQuestEndTimer = 5000; + break; + case 1: + // Cast completion spell on player + Creature* pSalaran = m_creature->GetMap()->GetCreature(m_salaranGuid); + Player* pPlayer = m_creature->GetCharmerOrOwnerPlayerOrPlayerItself(); + if (!pPlayer || !pSalaran) + return; + + pSalaran->CastSpell(pPlayer, SPELL_RACE_COMPLETE, true); + pSalaran->ForcedDespawn(1000); + m_creature->ForcedDespawn(1000); + m_uiQuestEndTimer = 0; + break; + } + ++m_uiQuestEndStage; + } + else + m_uiQuestEndTimer -= uiDiff; + } + } +}; + +CreatureAI* GetAI_npc_acherus_deathcharger(Creature* pCreature) +{ + return new npc_acherus_deathchargerAI(pCreature); +} + +bool EffectDummyCreature_npc_acherus_deathcharger(Unit* /*pCaster*/, uint32 uiSpellId, SpellEffectIndex uiEffIndex, Creature* pCreatureTarget, ObjectGuid /*originalCasterGuid*/) +{ + // always check spellid and effectindex + if (uiSpellId == SPELL_HORSEMAN_SLAIN && uiEffIndex == EFFECT_INDEX_0) + { + // Make horse evade + pCreatureTarget->AI()->EnterEvadeMode(); + + // always return true when we are handling this spell and effect + return true; + } + + return false; +} + +void AddSC_ebon_hold() +{ + Script* pNewScript; + + pNewScript = new Script; + pNewScript->Name = "npc_a_special_surprise"; + pNewScript->GetAI = &GetAI_npc_a_special_surprise; + pNewScript->RegisterSelf(); + + pNewScript = new Script; + pNewScript->Name = "npc_death_knight_initiate"; + pNewScript->GetAI = &GetAI_npc_death_knight_initiate; + pNewScript->pGossipHello = &GossipHello_npc_death_knight_initiate; + pNewScript->pGossipSelect = &GossipSelect_npc_death_knight_initiate; + pNewScript->RegisterSelf(); + + pNewScript = new Script; + pNewScript->Name = "npc_koltira_deathweaver"; + pNewScript->GetAI = &GetAI_npc_koltira_deathweaver; + pNewScript->pQuestAcceptNPC = &QuestAccept_npc_koltira_deathweaver; + pNewScript->RegisterSelf(); + + pNewScript = new Script; + pNewScript->Name = "npc_unworthy_initiate"; + pNewScript->GetAI = &GetAI_npc_unworthy_initiate; + pNewScript->RegisterSelf(); + + pNewScript = new Script; + pNewScript->Name = "npc_unworthy_initiate_anchor"; + pNewScript->GetAI = &GetAI_npc_unworthy_initiate_anchor; + pNewScript->RegisterSelf(); + + pNewScript = new Script; + pNewScript->Name = "go_acherus_soul_prison"; + pNewScript->pGOUse = &GOUse_go_acherus_soul_prison; + pNewScript->RegisterSelf(); + + pNewScript = new Script; + pNewScript->Name = "npc_eye_of_acherus"; + pNewScript->GetAI = &GetAI_npc_eye_of_acherus; + pNewScript->RegisterSelf(); + + pNewScript = new Script; + pNewScript->Name = "npc_scarlet_ghoul"; + pNewScript->GetAI = &GetAI_npc_scarlet_ghoul; + pNewScript->pEffectDummyNPC = &EffectDummyCreature_npc_scarlet_ghoul; + pNewScript->RegisterSelf(); + + pNewScript = new Script; + pNewScript->Name = "npc_highlord_darion_mograine"; + pNewScript->GetAI = &GetAI_npc_highlord_darion_mograine; + pNewScript->pGossipHello = &GossipHello_npc_highlord_darion_mograine; + pNewScript->pGossipSelect = &GossipSelect_npc_highlord_darion_mograine; + pNewScript->RegisterSelf(); + + pNewScript = new Script; + pNewScript->Name = "npc_fellow_death_knight"; + pNewScript->GetAI = &GetAI_npc_fellow_death_knight; + pNewScript->RegisterSelf(); + + pNewScript = new Script; + pNewScript->Name = "npc_lich_king_light_dawn"; + pNewScript->GetAI = &GetAI_npc_lich_king_light_dawn; + pNewScript->RegisterSelf(); + + pNewScript = new Script; + pNewScript->Name = "npc_acherus_deathcharger"; + pNewScript->GetAI = &GetAI_npc_acherus_deathcharger; + pNewScript->pEffectDummyNPC = &EffectDummyCreature_npc_acherus_deathcharger; + pNewScript->RegisterSelf(); +} diff --git a/src/modules/SD2/scripts/eastern_kingdoms/scarlet_enclave/world_map_ebon_hold.cpp b/src/modules/SD2/scripts/eastern_kingdoms/scarlet_enclave/world_map_ebon_hold.cpp new file mode 100644 index 000000000..649530861 --- /dev/null +++ b/src/modules/SD2/scripts/eastern_kingdoms/scarlet_enclave/world_map_ebon_hold.cpp @@ -0,0 +1,294 @@ +/* Copyright (C) 2006 - 2013 ScriptDev2 + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +/* ScriptData +SDName: world_map_ebon_hold +SD%Complete: 0 +SDComment: +SDCategory: Ebon Hold +EndScriptData */ + +#include "precompiled.h" +#include "world_map_ebon_hold.h" + +world_map_ebon_hold::world_map_ebon_hold(Map* pMap) : ScriptedInstance(pMap), + m_uiGothikYellTimer(0), + m_uiBattleEncounter(0) +{ + Initialize(); +} + +void world_map_ebon_hold::Initialize() {} + +void world_map_ebon_hold::OnCreatureCreate(Creature* pCreature) +{ + switch (pCreature->GetEntry()) + { + case NPC_HIGHLORD_DARION_MOGRAINE: + case NPC_KOLTIRA_DEATHWEAVER: + case NPC_ORBAZ_BLOODBANE: + case NPC_THASSARIAN: + + case NPC_HIGHLORD_TIRION_FORDRING: + case NPC_KORFAX_CHAMPION_OF_THE_LIGHT: + case NPC_LORD_MAXWELL_TYROSUS: + case NPC_LEONID_BARTHALOMEW_THE_REVERED: + case NPC_DUKE_NICHOLAS_ZVERENHOFF: + case NPC_COMMANDER_ELIGOR_DAWNBRINGER: + case NPC_RIMBLAT_EARTHSHATTER: + case NPC_RAYNE: + + case NPC_THE_LICH_KING: + case NPC_HIGHLORD_ALEXANDROS_MOGRAINE: + case NPC_DARION_MOGRAINE: + m_mNpcEntryGuidStore[pCreature->GetEntry()] = pCreature->GetObjectGuid(); + break; + + // Behemots and abominations are spawned by default on the map so they need to be handled here + case NPC_FLESH_BEHEMOTH: + case NPC_RAMPAGING_ABOMINATION: + m_lArmyGuids.push_back(pCreature->GetObjectGuid()); + break; + } +} + +void world_map_ebon_hold::OnCreatureDeath(Creature* pCreature) +{ + if (GetData(TYPE_BATTLE) != IN_PROGRESS) + return; + + switch (pCreature->GetEntry()) + { + // resummon the behemots or abominations if they die + case NPC_FLESH_BEHEMOTH: + case NPC_RAMPAGING_ABOMINATION: + m_lArmyGuids.remove(pCreature->GetObjectGuid());// if remove respawning on reset won't work! (are there any spawned by default?) ?? - unclear related to ResetBattle() + if (Creature* pTemp = pCreature->SummonCreature(pCreature->GetEntry(), pCreature->GetPositionX(), pCreature->GetPositionY(), pCreature->GetPositionZ(), pCreature->GetOrientation(), TEMPSUMMON_CORPSE_DESPAWN, 0)) + { + // the new summoned mob should attack + Creature* pDarion = GetSingleCreatureFromStorage(NPC_HIGHLORD_DARION_MOGRAINE); + if (pDarion && pDarion->getVictim()) + pTemp->AI()->AttackStart(pDarion->getVictim()); + } + pCreature->ForcedDespawn(1000); + break; + } +} + +void world_map_ebon_hold::OnCreatureEvade(Creature* pCreature) +{ + if (GetData(TYPE_BATTLE) != IN_PROGRESS) + return; + + switch (pCreature->GetEntry()) + { + // don't let the scourge evade while the battle is running + case NPC_FLESH_BEHEMOTH: + case NPC_RAMPAGING_ABOMINATION: + case NPC_VOLATILE_GHOUL: + case NPC_WARRIOR_OF_THE_FROZEN_WASTES: + if (Creature* pDarion = GetSingleCreatureFromStorage(NPC_HIGHLORD_DARION_MOGRAINE)) + { + if (!pDarion->IsInCombat()) + return; + + if (Unit* pTarget = pDarion->SelectAttackingTarget(ATTACKING_TARGET_RANDOM, 0)) + pCreature->AI()->AttackStart(pTarget); + } + case NPC_KORFAX_CHAMPION_OF_THE_LIGHT: + case NPC_LORD_MAXWELL_TYROSUS: + case NPC_COMMANDER_ELIGOR_DAWNBRINGER: + case NPC_LEONID_BARTHALOMEW_THE_REVERED: + case NPC_DUKE_NICHOLAS_ZVERENHOFF: + case NPC_RIMBLAT_EARTHSHATTER: + case NPC_RAYNE: + case NPC_DEFENDER_OF_THE_LIGHT: + if (Creature* pDarion = GetSingleCreatureFromStorage(NPC_HIGHLORD_DARION_MOGRAINE)) + pCreature->AI()->AttackStart(pDarion); + break; + } +} + +void world_map_ebon_hold::OnObjectCreate(GameObject* pGo) +{ + switch (pGo->GetEntry()) + { + case GO_LIGHT_OF_DAWN: + m_mGoEntryGuidStore[pGo->GetEntry()] = pGo->GetObjectGuid(); + break; + case GO_HOLY_LIGHTNING_1: + case GO_HOLY_LIGHTNING_2: + m_lLightTrapsGuids.push_back(pGo->GetObjectGuid()); + break; + } +} + +void world_map_ebon_hold::SetData(uint32 uiType, uint32 uiData) +{ + if (uiType == TYPE_BATTLE) + { + switch (uiData) + { + case NOT_STARTED: + // update world states to default + DoUpdateBattleWorldState(WORLD_STATE_FORCES_SHOW, 1); + DoUpdateBattleWorldState(WORLD_STATE_FORCES_LIGHT, MAX_FORCES_LIGHT); + DoUpdateBattleWorldState(WORLD_STATE_FORCES_SCOURGE, MAX_FORCES_SCOURGE); + + DoUpdateBattleWorldState(WORLD_STATE_BATTLE_TIMER_SHOW, 0); + DoUpdateBattleWorldState(WORLD_STATE_BATTLE_BEGIN, 0); + + DoResetBattle(); + break; + case SPECIAL: + // display timer + DoUpdateBattleWorldState(WORLD_STATE_BATTLE_TIMER_SHOW, 1); + DoUpdateBattleWorldState(WORLD_STATE_BATTLE_TIMER_TIME, MAX_BATTLE_INTRO_TIMER); + + // update world states to also show the army + DoUpdateBattleWorldState(WORLD_STATE_FORCES_SHOW, 1); + DoUpdateBattleWorldState(WORLD_STATE_FORCES_LIGHT, MAX_FORCES_LIGHT); + DoUpdateBattleWorldState(WORLD_STATE_FORCES_SCOURGE, MAX_FORCES_SCOURGE); + break; + case IN_PROGRESS: + DoUpdateBattleWorldState(WORLD_STATE_BATTLE_TIMER_SHOW, 0); + DoUpdateBattleWorldState(WORLD_STATE_BATTLE_BEGIN, 1); + break; + } + + m_uiBattleEncounter = uiData; + } +} + +uint32 world_map_ebon_hold::GetData(uint32 uiType) const +{ + if (uiType == TYPE_BATTLE) + return m_uiBattleEncounter; + + return 0; +} + +void world_map_ebon_hold::DoUpdateBattleWorldState(uint32 uiStateId, uint32 uiStateData) +{ + Map::PlayerList const& lPlayers = instance->GetPlayers(); + + for (Map::PlayerList::const_iterator itr = lPlayers.begin(); itr != lPlayers.end(); ++itr) + { + if (Player* pPlayer = itr->getSource()) + { + // we need to manually check the phase mask because the value from DBC is not used yet + if (pPlayer->HasAura(SPELL_CHAPTER_IV) || pPlayer->isGameMaster()) + pPlayer->SendUpdateWorldState(uiStateId, uiStateData); + } + } +} + +void world_map_ebon_hold::DoResetBattle() +{ + // reset all npcs to the original state + if (Creature* pKoltira = GetSingleCreatureFromStorage(NPC_KOLTIRA_DEATHWEAVER)) + pKoltira->Respawn(); + if (Creature* pThassarian = GetSingleCreatureFromStorage(NPC_THASSARIAN)) + pThassarian->Respawn(); + if (Creature* pOrbaz = GetSingleCreatureFromStorage(NPC_ORBAZ_BLOODBANE)) + pOrbaz->Respawn(); + + // respawn all abominations + for (GuidList::const_iterator itr = m_lArmyGuids.begin(); itr != m_lArmyGuids.end(); ++itr) + { + if (Creature* pTemp = instance->GetCreature(*itr)) + pTemp->Respawn(); + } + + // despawn the argent dawn + for (uint8 i = 0; i < MAX_LIGHT_CHAMPIONS; i++) + { + if (Creature* pTemp = GetSingleCreatureFromStorage(aLightArmySpawnLoc[i].m_uiEntry)) + pTemp->ForcedDespawn(); + } + + if (Creature* pTirion = GetSingleCreatureFromStorage(NPC_HIGHLORD_TIRION_FORDRING)) + pTirion->ForcedDespawn(); +} + +void world_map_ebon_hold::DoMoveArmy() +{ + // move all the army to the chapel + float fX, fY, fZ; + for (GuidList::const_iterator itr = m_lArmyGuids.begin(); itr != m_lArmyGuids.end(); ++itr) + { + if (Creature* pTemp = instance->GetCreature(*itr)) + { + pTemp->SetWalk(false); + pTemp->GetRandomPoint(aEventLocations[1].m_fX, aEventLocations[1].m_fY, aEventLocations[1].m_fZ, 30.0f, fX, fY, fZ); + pTemp->GetMotionMaster()->MovePoint(0, fX, fY, fZ); + } + } +} + +void world_map_ebon_hold::DoDespawnArmy() +{ + // despawn all army units when the battle is finished + for (GuidList::const_iterator itr = m_lArmyGuids.begin(); itr != m_lArmyGuids.end(); ++itr) + { + if (Creature* pTemp = instance->GetCreature(*itr)) + { + if (pTemp->IsAlive()) + pTemp->DealDamage(pTemp, pTemp->GetHealth(), NULL, DIRECT_DAMAGE, SPELL_SCHOOL_MASK_NORMAL, NULL, false); + } + } +} + +void world_map_ebon_hold::DoEnableHolyTraps() +{ + for (GuidList::const_iterator itr = m_lLightTrapsGuids.begin(); itr != m_lLightTrapsGuids.end(); ++itr) + DoRespawnGameObject(*itr, 25); +} + +void world_map_ebon_hold::Update(uint32 uiDiff) +{ + if (m_uiGothikYellTimer) + { + if (m_uiGothikYellTimer <= uiDiff) + m_uiGothikYellTimer = 0; + else + m_uiGothikYellTimer -= uiDiff; + } +} + +bool world_map_ebon_hold::CanAndToggleGothikYell() +{ + if (m_uiGothikYellTimer) + return false; + + m_uiGothikYellTimer = 2000; + return true; +} + +InstanceData* GetInstance_world_map_ebon_hold(Map* pMap) +{ + return new world_map_ebon_hold(pMap); +} + +void AddSC_world_map_ebon_hold() +{ + Script* pNewScript; + + pNewScript = new Script; + pNewScript->Name = "world_map_ebon_hold"; + pNewScript->GetInstanceData = &GetInstance_world_map_ebon_hold; + pNewScript->RegisterSelf(); +} diff --git a/src/modules/SD2/scripts/eastern_kingdoms/scarlet_enclave/world_map_ebon_hold.h b/src/modules/SD2/scripts/eastern_kingdoms/scarlet_enclave/world_map_ebon_hold.h new file mode 100644 index 000000000..7ff632f0b --- /dev/null +++ b/src/modules/SD2/scripts/eastern_kingdoms/scarlet_enclave/world_map_ebon_hold.h @@ -0,0 +1,153 @@ +/* Copyright (C) 2006 - 2013 ScriptDev2 + * This program is free software licensed under GPL version 2 + * Please see the included DOCS/LICENSE.TXT for more information */ + +#ifndef DEF_EBON_HOLD_H +#define DEF_EBON_HOLD_H + +enum +{ + TYPE_BATTLE = 0, + + // npcs + // death knights + NPC_HIGHLORD_DARION_MOGRAINE = 29173, + NPC_KOLTIRA_DEATHWEAVER = 29199, + NPC_ORBAZ_BLOODBANE = 29204, + NPC_THASSARIAN = 29200, + + // scourge warriors - summond during the event + NPC_FLESH_BEHEMOTH = 29190, + NPC_RAMPAGING_ABOMINATION = 29186, + NPC_VOLATILE_GHOUL = 29219, + NPC_WARRIOR_OF_THE_FROZEN_WASTES = 29206, + + // argent dawn commanders + NPC_HIGHLORD_TIRION_FORDRING = 29175, + NPC_KORFAX_CHAMPION_OF_THE_LIGHT = 29176, + NPC_LORD_MAXWELL_TYROSUS = 29178, + NPC_COMMANDER_ELIGOR_DAWNBRINGER = 29177, + NPC_LEONID_BARTHALOMEW_THE_REVERED = 29179, + NPC_DUKE_NICHOLAS_ZVERENHOFF = 29180, + NPC_RIMBLAT_EARTHSHATTER = 29182, + NPC_RAYNE = 29181, + + // argent warriors + NPC_DEFENDER_OF_THE_LIGHT = 29174, + + // cinematic + NPC_THE_LICH_KING = 29183, + NPC_HIGHLORD_ALEXANDROS_MOGRAINE = 29227, + NPC_DARION_MOGRAINE = 29228, + + // object + GO_LIGHT_OF_DAWN = 191330, + GO_HOLY_LIGHTNING_1 = 191301, + GO_HOLY_LIGHTNING_2 = 191302, + + // spells + SPELL_CHAPTER_IV = 53405, // phase aura + + // variables + MAX_LIGHT_CHAMPIONS = 7, // the number of the light champions + MAX_WARRIORS_SUMMONED_PER_TURN = 5, // summoned warriors (light and death) per turn + MAX_LIGHT_GUARDS = 4, // guards summond for the outro + + // event variables + MAX_BATTLE_INTRO_TIMER = 5, + MAX_FORCES_LIGHT = 300, + MAX_FORCES_SCOURGE = 10000, + + // world states + // basically world states should be shown to all players with phase mask = 128 as stated in DBC + // because we don't have the possibility to do that we'll just iterate through the players and set the phase mask manually based on the battle status + WORLD_STATE_FORCES_SHOW = 3592, // show the remaining units + WORLD_STATE_FORCES_SCOURGE = 3591, + WORLD_STATE_FORCES_LIGHT = 3590, + WORLD_STATE_BATTLE_TIMER_SHOW = 3603, // countdown timer + WORLD_STATE_BATTLE_TIMER_TIME = 3604, + WORLD_STATE_BATTLE_BEGIN = 3605, // battle has begun +}; + +struct sSpawnLocation +{ + float m_fX, m_fY, m_fZ, m_fO; + uint32 m_uiEntry; +}; + +// light champions +static sSpawnLocation aLightArmySpawnLoc[MAX_LIGHT_CHAMPIONS] = +{ + {2285.80f, -5308.82f, 87.04f, 1.67f, NPC_KORFAX_CHAMPION_OF_THE_LIGHT}, + {2276.96f, -5309.36f, 86.66f, 1.61f, NPC_LORD_MAXWELL_TYROSUS}, + {2279.82f, -5322.61f, 88.95f, 1.54f, NPC_LEONID_BARTHALOMEW_THE_REVERED}, + {2287.96f, -5313.96f, 88.27f, 1.63f, NPC_DUKE_NICHOLAS_ZVERENHOFF}, + {2276.84f, -5313.78f, 87.62f, 1.61f, NPC_COMMANDER_ELIGOR_DAWNBRINGER}, + {2275.80f, -5322.51f, 88.62f, 1.68f, NPC_RAYNE}, + {2282.47f, -5319.84f, 88.83f, 1.74f, NPC_RIMBLAT_EARTHSHATTER} +}; + +// four guards spawned for the outro +static sSpawnLocation aGuardsSpawnLoc[MAX_LIGHT_GUARDS] = +{ + {2287.581f, -5284.991f, 82.535f, 2.60f}, + {2287.856f, -5281.127f, 82.225f, 3.44f}, + {2275.964f, -5282.389f, 82.301f, 5.80f}, + {2275.471f, -5277.668f, 82.058f, 5.79f} +}; + +// Tirion is spawned at the edge of the battle and runs toward the chapel +// When he reach the chapel he cast some powerfull light spell and the battle ends +static sSpawnLocation aEventLocations[] = +{ + {2165.711f, -5266.124f, 95.50f, 0.13f}, // 0 Tirion spawn location + {2281.390f, -5299.98f, 85.07f, 1.61f}, // 1 Tirion move location + {2289.259f, -5280.350f, 82.11f, 0.0f}, // 2 Koltira chapel loc + {2273.289f, -5273.675f, 81.70f, 0.0f}, // 3 Thassarian chapel loc + {2280.159f, -5263.561f, 81.15f, 4.70f}, // 4 Alexandros summon location + {2279.927f, -5265.84f, 81.39f, 0.0f}, // 5 Alexandros move loc + {2280.538f, -5280.103f, 82.41f, 1.60f}, // 6 Young Darion spawn + {2279.895f, -5269.334f, 81.73f, 0.0f}, // 7 Young Darion move + {2280.304f, -5257.205f, 80.09f, 4.62f}, // 8 Lich King spawn + {2281.523f, -5261.058f, 80.87f, 0.0f}, // 9 Lich King move + {2273.071f, -5293.428f, 83.06f, 0.0f}, // 10 Tirion final point +}; + +class world_map_ebon_hold : public ScriptedInstance +{ + public: + world_map_ebon_hold(Map* pMap); + + void Initialize() override; + + void OnCreatureCreate(Creature* pCreature) override; + void OnCreatureDeath(Creature* pCreature) override; + void OnCreatureEvade(Creature* pCreature) override; + void OnObjectCreate(GameObject* pGo) override; + + void SetData(uint32 uiType, uint32 uiData) override; + uint32 GetData(uint32 uiType) const override; + + void Update(uint32 uiDiff) override; + + bool CanAndToggleGothikYell(); + + void DoUpdateBattleWorldState(uint32 uiStateId, uint32 uiStateData); + + void DoEnableHolyTraps(); + + // Move the behemots and abominations and make them attack + void DoMoveArmy(); + void DoDespawnArmy(); + + protected: + void DoResetBattle(); + + uint32 m_uiGothikYellTimer; // Timer to check if Gothik can yell (related q 12698) + uint32 m_uiBattleEncounter; // Store state of the battle around "The Light of Dawn" + + GuidList m_lArmyGuids; + GuidList m_lLightTrapsGuids; +}; + +#endif diff --git a/src/modules/SD2/scripts/eastern_kingdoms/scarlet_monastery/boss_arcanist_doan.cpp b/src/modules/SD2/scripts/eastern_kingdoms/scarlet_monastery/boss_arcanist_doan.cpp new file mode 100644 index 000000000..e698f7fa7 --- /dev/null +++ b/src/modules/SD2/scripts/eastern_kingdoms/scarlet_monastery/boss_arcanist_doan.cpp @@ -0,0 +1,141 @@ +/* Copyright (C) 2006 - 2013 ScriptDev2 + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with 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 +{ + SAY_AGGRO = -1189019, + SAY_SPECIALAE = -1189020, + + SPELL_POLYMORPH = 13323, + SPELL_SILENCE = 8988, + SPELL_ARCANE_EXPLOSION = 9433, + SPELL_DETONATION = 9435, + SPELL_ARCANE_BUBBLE = 9438, +}; + +struct boss_arcanist_doanAI : public ScriptedAI +{ + boss_arcanist_doanAI(Creature* pCreature) : ScriptedAI(pCreature) {Reset();} + + uint32 m_uiPolymorphTimer; + uint32 m_uiSilenceTimer; + uint32 m_uiArcaneExplosionTimer; + uint32 m_uiDetonationTimer; + bool bShielded; + + void Reset() override + { + m_uiPolymorphTimer = 15000; + m_uiSilenceTimer = 7500; + m_uiArcaneExplosionTimer = urand(1000, 3000); + m_uiDetonationTimer = 0; + bShielded = false; + } + + void Aggro(Unit* /*pWho*/) override + { + DoScriptText(SAY_AGGRO, m_creature); + } + + void UpdateAI(const uint32 uiDiff) override + { + if (!m_creature->SelectHostileTarget() || !m_creature->getVictim()) + return; + + if (m_uiDetonationTimer) + { + if (m_uiDetonationTimer <= uiDiff) + { + if (DoCastSpellIfCan(m_creature, SPELL_DETONATION) == CAST_OK) + { + DoScriptText(SAY_SPECIALAE, m_creature); + m_uiDetonationTimer = 0; + } + } + else + m_uiDetonationTimer -= uiDiff; + } + + // Do not attack while having the bubble active + if (m_creature->HasAura(SPELL_ARCANE_BUBBLE)) + return; + + // If we are <50% hp cast Arcane Bubble + if (!bShielded && m_creature->GetHealthPercent() <= 50.0f) + { + if (DoCastSpellIfCan(m_creature, SPELL_ARCANE_BUBBLE) == CAST_OK) + { + m_uiDetonationTimer = 1000; + bShielded = true; + } + } + + if (m_uiPolymorphTimer < uiDiff) + { + if (Unit* pTarget = m_creature->SelectAttackingTarget(ATTACKING_TARGET_RANDOM, 1)) + { + if (DoCastSpellIfCan(pTarget, SPELL_POLYMORPH) == CAST_OK) + m_uiPolymorphTimer = 20000; + } + } + else + m_uiPolymorphTimer -= uiDiff; + + // Silence_Timer + if (m_uiSilenceTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature, SPELL_SILENCE) == CAST_OK) + m_uiSilenceTimer = urand(15000, 22000); + } + else + m_uiSilenceTimer -= uiDiff; + + // ArcaneExplosion_Timer + if (m_uiArcaneExplosionTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature, SPELL_ARCANE_EXPLOSION) == CAST_OK) + m_uiArcaneExplosionTimer = urand(2500, 8500); + } + else + m_uiArcaneExplosionTimer -= uiDiff; + + DoMeleeAttackIfReady(); + } +}; + +CreatureAI* GetAI_boss_arcanist_doan(Creature* pCreature) +{ + return new boss_arcanist_doanAI(pCreature); +} + +void AddSC_boss_arcanist_doan() +{ + Script* pNewScript; + + pNewScript = new Script; + pNewScript->Name = "boss_arcanist_doan"; + pNewScript->GetAI = &GetAI_boss_arcanist_doan; + pNewScript->RegisterSelf(); +} diff --git a/src/modules/SD2/scripts/eastern_kingdoms/scarlet_monastery/boss_headless_horseman.cpp b/src/modules/SD2/scripts/eastern_kingdoms/scarlet_monastery/boss_headless_horseman.cpp new file mode 100644 index 000000000..cc9bb1473 --- /dev/null +++ b/src/modules/SD2/scripts/eastern_kingdoms/scarlet_monastery/boss_headless_horseman.cpp @@ -0,0 +1,90 @@ +/* Copyright (C) 2006 - 2013 ScriptDev2 +* This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with 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: 0 +SDComment: Place Holder +SDCategory: Scarlet Monastery +EndScriptData */ + +#include "precompiled.h" + +enum +{ + SAY_ENTRANCE = -1189022, + SAY_REJOINED = -1189023, + SAY_BODY_DEFEAT = -1189024, + SAY_LOST_HEAD = -1189025, + SAY_CONFLAGRATION = -1189026, + SAY_SPROUTING_PUMPKINS = -1189027, + SAY_SLAY = -1189028, + SAY_DEATH = -1189029, + + EMOTE_LAUGH = -1189030, + + SAY_PLAYER1 = -1189031, + SAY_PLAYER2 = -1189032, + SAY_PLAYER3 = -1189033, + SAY_PLAYER4 = -1189034 +}; + +struct boss_headless_horsemanAI : public ScriptedAI +{ + boss_headless_horsemanAI(Creature* pCreature) : ScriptedAI(pCreature) {Reset();} + + void Reset() override + { + } + + void Aggro(Unit* /*pWho*/) override + { + m_creature->SetInCombatWithZone(); + } + + void KilledUnit(Unit* /*pVictim*/) override + { + DoScriptText(SAY_SLAY, m_creature); + } + + void JustDied(Unit* /*pKiller*/) override + { + DoScriptText(SAY_DEATH, m_creature); + } + + void UpdateAI(const uint32 /*uiDiff*/) override + { + if (!m_creature->SelectHostileTarget() || !m_creature->getVictim()) + return; + + DoMeleeAttackIfReady(); + } +}; + +CreatureAI* GetAI_boss_headless_horseman(Creature* pCreature) +{ + return new boss_headless_horsemanAI(pCreature); +} + +void AddSC_boss_headless_horseman() +{ + Script* pNewScript; + + pNewScript = new Script; + pNewScript->Name = "boss_headless_horseman"; + pNewScript->GetAI = GetAI_boss_headless_horseman; + pNewScript->RegisterSelf(); +} diff --git a/src/modules/SD2/scripts/eastern_kingdoms/scarlet_monastery/boss_herod.cpp b/src/modules/SD2/scripts/eastern_kingdoms/scarlet_monastery/boss_herod.cpp new file mode 100644 index 000000000..4abe94148 --- /dev/null +++ b/src/modules/SD2/scripts/eastern_kingdoms/scarlet_monastery/boss_herod.cpp @@ -0,0 +1,187 @@ +/* Copyright (C) 2006 - 2013 ScriptDev2 + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with 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" + +enum +{ + SAY_AGGRO = -1189000, + SAY_WHIRLWIND = -1189001, + SAY_ENRAGE = -1189002, + SAY_KILL = -1189003, + EMOTE_GENERIC_ENRAGED = -1000003, + + SAY_TRAINEE_SPAWN = -1189035, + + SPELL_RUSHINGCHARGE = 8260, + SPELL_CLEAVE = 15496, + SPELL_WHIRLWIND = 8989, + SPELL_FRENZY = 8269, + + NPC_SCARLET_TRAINEE = 6575 +}; + +struct boss_herodAI : public ScriptedAI +{ + boss_herodAI(Creature* pCreature) : ScriptedAI(pCreature) {Reset();} + + bool m_bEnrage; + bool m_bTraineeSay; + + uint32 m_uiCleaveTimer; + uint32 m_uiWhirlwindTimer; + + void Reset() override + { + m_bTraineeSay = false; + m_bEnrage = false; + + m_uiCleaveTimer = 7500; + m_uiWhirlwindTimer = 14500; + } + + void Aggro(Unit* /*pWho*/) override + { + DoScriptText(SAY_AGGRO, m_creature); + DoCastSpellIfCan(m_creature, SPELL_RUSHINGCHARGE); + } + + void SummonedCreature(Creature* pSummoned) + { + // make first Scarlet Trainee say text + if (pSummoned->GetEntry() == NPC_SCARLET_TRAINEE && !m_bTraineeSay) + { + DoScriptText(SAY_TRAINEE_SPAWN, pSummoned); + m_bTraineeSay = true; + } + } + + void KilledUnit(Unit* /*pVictim*/) override + { + DoScriptText(SAY_KILL, m_creature); + } + + void JustDied(Unit* /*pKiller*/) override + { + for (uint8 i = 0; i < 20; ++i) + m_creature->SummonCreature(NPC_SCARLET_TRAINEE, 1939.18f, -431.58f, 17.09f, 6.22f, TEMPSUMMON_TIMED_OOC_OR_DEAD_DESPAWN, 600000); + } + + void UpdateAI(const uint32 uiDiff) override + { + if (!m_creature->SelectHostileTarget() || !m_creature->getVictim()) + return; + + // If we are < 30% hp enrage + if (!m_bEnrage && m_creature->GetHealthPercent() <= 30.0f && !m_creature->IsNonMeleeSpellCasted(false)) + { + if (DoCastSpellIfCan(m_creature, SPELL_FRENZY) == CAST_OK) + { + DoScriptText(EMOTE_GENERIC_ENRAGED, m_creature); + DoScriptText(SAY_ENRAGE, m_creature); + m_bEnrage = true; + } + } + + // Cleave + if (m_uiCleaveTimer < uiDiff) + { + DoCastSpellIfCan(m_creature->getVictim(), SPELL_CLEAVE); + m_uiCleaveTimer = urand(7500, 17500); + } + else + m_uiCleaveTimer -= uiDiff; + + if (m_uiWhirlwindTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature->getVictim(), SPELL_WHIRLWIND) == CAST_OK) + { + DoScriptText(SAY_WHIRLWIND, m_creature); + m_uiWhirlwindTimer = urand(15000, 25000); + } + } + else + m_uiWhirlwindTimer -= uiDiff; + + DoMeleeAttackIfReady(); + } +}; + +CreatureAI* GetAI_boss_herod(Creature* pCreature) +{ + return new boss_herodAI(pCreature); +} + +struct mob_scarlet_traineeAI : public npc_escortAI +{ + mob_scarlet_traineeAI(Creature* pCreature) : npc_escortAI(pCreature) + { + m_uiStartTimer = urand(1000, 6000); + Reset(); + } + + uint32 m_uiStartTimer; + + void Reset() override { } + void WaypointReached(uint32 /*uiPointId*/) override {} + + void UpdateEscortAI(const uint32 uiDiff) override + { + if (m_uiStartTimer) + { + if (m_uiStartTimer <= uiDiff) + { + Start(true); + m_uiStartTimer = 0; + } + else + m_uiStartTimer -= uiDiff; + } + + if (!m_creature->SelectHostileTarget() || !m_creature->getVictim()) + return; + + DoMeleeAttackIfReady(); + } +}; + +CreatureAI* GetAI_mob_scarlet_trainee(Creature* pCreature) +{ + return new mob_scarlet_traineeAI(pCreature); +} + +void AddSC_boss_herod() +{ + Script* pNewScript; + + pNewScript = new Script; + pNewScript->Name = "boss_herod"; + pNewScript->GetAI = &GetAI_boss_herod; + pNewScript->RegisterSelf(); + + pNewScript = new Script; + pNewScript->Name = "mob_scarlet_trainee"; + pNewScript->GetAI = &GetAI_mob_scarlet_trainee; + pNewScript->RegisterSelf(); +} diff --git a/src/modules/SD2/scripts/eastern_kingdoms/scarlet_monastery/boss_mograine_and_whitemane.cpp b/src/modules/SD2/scripts/eastern_kingdoms/scarlet_monastery/boss_mograine_and_whitemane.cpp new file mode 100644 index 000000000..a5337362f --- /dev/null +++ b/src/modules/SD2/scripts/eastern_kingdoms/scarlet_monastery/boss_mograine_and_whitemane.cpp @@ -0,0 +1,378 @@ +/* Copyright (C) 2006 - 2013 ScriptDev2 + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with 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 "scarlet_monastery.h" + +enum +{ + // 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, + SPELL_DOMINATEMIND = 14515, + SPELL_HOLYSMITE = 9481, + SPELL_HEAL = 12039, + SPELL_POWERWORDSHIELD = 22187 +}; + +struct boss_scarlet_commander_mograineAI : public ScriptedAI +{ + boss_scarlet_commander_mograineAI(Creature* pCreature) : ScriptedAI(pCreature) + { + m_pInstance = (ScriptedInstance*)pCreature->GetInstanceData(); + Reset(); + } + + ScriptedInstance* m_pInstance; + + uint32 m_uiCrusaderStrike_Timer; + uint32 m_uiHammerOfJustice_Timer; + + bool m_bHasDied; + bool m_bHeal; + bool m_bFakeDeath; + + void Reset() override + { + m_uiCrusaderStrike_Timer = 8400; + m_uiHammerOfJustice_Timer = 9600; + + m_bHasDied = false; + m_bHeal = false; + m_bFakeDeath = false; + + // 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); + } + + void Aggro(Unit* /*pWho*/) override + { + DoScriptText(SAY_MO_AGGRO, m_creature); + DoCastSpellIfCan(m_creature, SPELL_RETRIBUTIONAURA); + + m_creature->CallForHelp(VISIBLE_RANGE); + } + + void KilledUnit(Unit* /*pVictim*/) override + { + DoScriptText(SAY_MO_KILL, m_creature); + } + + void JustReachedHome() override + { + if (!m_pInstance) + return; + + Creature* pWhitemane = m_pInstance->GetSingleCreatureFromStorage(NPC_WHITEMANE); + if (pWhitemane && !pWhitemane->IsAlive()) + pWhitemane->Respawn(); + } + + void DamageTaken(Unit* /*pDoneBy*/, uint32& uiDamage) override + { + if (uiDamage < m_creature->GetHealth() || m_bHasDied) + return; + + if (!m_pInstance) + return; + + // On first death, fake death and open door, as well as initiate whitemane if exist + if (Creature* pWhitemane = m_pInstance->GetSingleCreatureFromStorage(NPC_WHITEMANE)) + { + m_pInstance->SetData(TYPE_MOGRAINE_AND_WHITE_EVENT, IN_PROGRESS); + + pWhitemane->GetMotionMaster()->MovePoint(1, 1163.113370f, 1398.856812f, 32.527786f); + + 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->RemoveAllAurasOnDeath(); + m_creature->ClearAllReactives(); + + m_creature->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE); + m_creature->SetStandState(UNIT_STAND_STATE_DEAD); + + m_bHasDied = true; + m_bFakeDeath = true; + + uiDamage = 0; + } + } + + void SpellHit(Unit* /*pWho*/, const SpellEntry* pSpell) override + { + // When hit with ressurection say text + if (pSpell->Id == SPELL_SCARLETRESURRECTION) + { + 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) override + { + if (!m_creature->SelectHostileTarget() || !m_creature->getVictim()) + 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 + m_creature->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE); + m_creature->SetStandState(UNIT_STAND_STATE_STAND); + // spell has script target on Whitemane + DoCastSpellIfCan(m_creature, SPELL_LAYONHANDS); + + m_uiCrusaderStrike_Timer = 8400; + m_uiHammerOfJustice_Timer = 9600; + + 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) + { + if (DoCastSpellIfCan(m_creature->getVictim(), SPELL_CRUSADERSTRIKE) == CAST_OK) + m_uiCrusaderStrike_Timer = urand(6000, 15000); + } + else + m_uiCrusaderStrike_Timer -= uiDiff; + + // m_uiHammerOfJustice_Timer + if (m_uiHammerOfJustice_Timer < uiDiff) + { + if (DoCastSpellIfCan(m_creature->getVictim(), SPELL_HAMMEROFJUSTICE) == CAST_OK) + m_uiHammerOfJustice_Timer = urand(7000, 18500); + } + else + m_uiHammerOfJustice_Timer -= uiDiff; + + DoMeleeAttackIfReady(); + } +}; + +struct boss_high_inquisitor_whitemaneAI : public ScriptedAI +{ + boss_high_inquisitor_whitemaneAI(Creature* pCreature) : ScriptedAI(pCreature) + { + m_pInstance = (ScriptedInstance*)pCreature->GetInstanceData(); + Reset(); + } + + 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() override + { + m_uiWait_Timer = 7000; + m_uiHeal_Timer = 10000; + m_uiPowerWordShield_Timer = 15000; + m_uiHolySmite_Timer = 4000; + + m_bCanResurrectCheck = false; + m_bCanResurrect = false; + + if (!m_pInstance) + return; + + if (Creature* pMograine = m_pInstance->GetSingleCreatureFromStorage(NPC_MOGRAINE)) + { + if (m_creature->IsAlive() && !pMograine->IsAlive()) + pMograine->Respawn(); + } + } + + void JustReachedHome() override + { + if (m_pInstance) + { + if (!(m_pInstance->GetData(TYPE_MOGRAINE_AND_WHITE_EVENT) == NOT_STARTED) || !(m_pInstance->GetData(TYPE_MOGRAINE_AND_WHITE_EVENT) == FAIL)) + m_pInstance->SetData(TYPE_MOGRAINE_AND_WHITE_EVENT, FAIL); + } + } + + void MoveInLineOfSight(Unit* /*pWho*/) override + { + // This needs to be empty because Whitemane should NOT aggro while fighting Mograine. Mograine will give us a target. + } + + void DamageTaken(Unit* /*pDoneBy*/, uint32& uiDamage) override + { + if (uiDamage < m_creature->GetHealth()) + return; + + if (!m_bCanResurrectCheck || m_bCanResurrect) + { + // prevent killing blow before rezzing commander + m_creature->SetHealth(uiDamage + 1); + } + } + + void AttackStart(Unit* pWho) override + { + if (m_pInstance && (m_pInstance->GetData(TYPE_MOGRAINE_AND_WHITE_EVENT) == NOT_STARTED || m_pInstance->GetData(TYPE_MOGRAINE_AND_WHITE_EVENT) == FAIL)) + return; + + ScriptedAI::AttackStart(pWho); + } + + void Aggro(Unit* /*pWho*/) override + { + DoScriptText(SAY_WH_INTRO, m_creature); + } + + void KilledUnit(Unit* /*pVictim*/) override + { + DoScriptText(SAY_WH_KILL, m_creature); + } + + void UpdateAI(const uint32 uiDiff) override + { + if (!m_creature->SelectHostileTarget() || !m_creature->getVictim()) + return; + + if (m_bCanResurrect) + { + // When casting resuruction make sure to delay so on rez when reinstate battle deepsleep runs out + if (m_uiWait_Timer < uiDiff) + { + // spell has script target on Mograine + DoCastSpellIfCan(m_creature, SPELL_SCARLETRESURRECTION); + DoScriptText(SAY_WH_RESSURECT, m_creature); + m_bCanResurrect = false; + } + else + m_uiWait_Timer -= uiDiff; + } + + // Cast Deep sleep when health is less than 50% + if (!m_bCanResurrectCheck && m_creature->GetHealthPercent() <= 50.0f) + { + DoCastSpellIfCan(m_creature, SPELL_DEEPSLEEP, CAST_INTERRUPT_PREVIOUS); + 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) + { + if (Unit* pTarget = DoSelectLowestHpFriendly(50.0f)) + { + if (DoCastSpellIfCan(pTarget, SPELL_HEAL) == CAST_OK) + m_uiHeal_Timer = 13000; + } + } + else + m_uiHeal_Timer -= uiDiff; + + // m_uiPowerWordShield_Timer + if (m_uiPowerWordShield_Timer < uiDiff) + { + if (DoCastSpellIfCan(m_creature, SPELL_POWERWORDSHIELD) == CAST_OK) + m_uiPowerWordShield_Timer = urand(22000, 45000); + } + else + m_uiPowerWordShield_Timer -= uiDiff; + + // m_uiHolySmite_Timer + if (m_uiHolySmite_Timer < uiDiff) + { + if (DoCastSpellIfCan(m_creature->getVictim(), SPELL_HOLYSMITE) == CAST_OK) + m_uiHolySmite_Timer = urand(3500, 5000); + } + 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* pNewScript; + + pNewScript = new Script; + pNewScript->Name = "boss_scarlet_commander_mograine"; + pNewScript->GetAI = &GetAI_boss_scarlet_commander_mograine; + pNewScript->RegisterSelf(); + + pNewScript = new Script; + pNewScript->Name = "boss_high_inquisitor_whitemane"; + pNewScript->GetAI = &GetAI_boss_high_inquisitor_whitemane; + pNewScript->RegisterSelf(); +} diff --git a/src/modules/SD2/scripts/eastern_kingdoms/scarlet_monastery/instance_scarlet_monastery.cpp b/src/modules/SD2/scripts/eastern_kingdoms/scarlet_monastery/instance_scarlet_monastery.cpp new file mode 100644 index 000000000..71f709ea7 --- /dev/null +++ b/src/modules/SD2/scripts/eastern_kingdoms/scarlet_monastery/instance_scarlet_monastery.cpp @@ -0,0 +1,99 @@ +/* Copyright (C) 2006 - 2013 ScriptDev2 + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along 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 "scarlet_monastery.h" + +instance_scarlet_monastery::instance_scarlet_monastery(Map* pMap) : ScriptedInstance(pMap) +{ + Initialize(); +} + +void instance_scarlet_monastery::Initialize() +{ + memset(&m_auiEncounter, 0, sizeof(m_auiEncounter)); +} + +void instance_scarlet_monastery::OnCreatureCreate(Creature* pCreature) +{ + switch (pCreature->GetEntry()) + { + case NPC_MOGRAINE: + case NPC_WHITEMANE: + case NPC_VORREL: + m_mNpcEntryGuidStore[pCreature->GetEntry()] = pCreature->GetObjectGuid(); + break; + } +} + +void instance_scarlet_monastery::OnCreatureDeath(Creature* pCreature) +{ + if (pCreature->GetEntry() == NPC_INTERROGATOR_VISHAS) + { + // Any other actions to do with Vorrel? setStandState? + if (Creature* pVorrel = GetSingleCreatureFromStorage(NPC_VORREL)) + DoScriptText(SAY_TRIGGER_VORREL, pVorrel); + } +} + +void instance_scarlet_monastery::OnObjectCreate(GameObject* pGo) +{ + if (pGo->GetEntry() == GO_WHITEMANE_DOOR) + m_mGoEntryGuidStore[GO_WHITEMANE_DOOR] = pGo->GetObjectGuid(); +} + +void instance_scarlet_monastery::SetData(uint32 uiType, uint32 uiData) +{ + if (uiType == TYPE_MOGRAINE_AND_WHITE_EVENT) + { + if (uiData == IN_PROGRESS) + DoUseDoorOrButton(GO_WHITEMANE_DOOR); + if (uiData == FAIL) + DoUseDoorOrButton(GO_WHITEMANE_DOOR); + + m_auiEncounter[0] = uiData; + } +} + +uint32 instance_scarlet_monastery::GetData(uint32 uiData) const +{ + if (uiData == 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* pNewScript; + + pNewScript = new Script; + pNewScript->Name = "instance_scarlet_monastery"; + pNewScript->GetInstanceData = &GetInstanceData_instance_scarlet_monastery; + pNewScript->RegisterSelf(); +} diff --git a/src/modules/SD2/scripts/eastern_kingdoms/scarlet_monastery/scarlet_monastery.h b/src/modules/SD2/scripts/eastern_kingdoms/scarlet_monastery/scarlet_monastery.h new file mode 100644 index 000000000..6e2f35630 --- /dev/null +++ b/src/modules/SD2/scripts/eastern_kingdoms/scarlet_monastery/scarlet_monastery.h @@ -0,0 +1,42 @@ +/* Copyright (C) 2006 - 2013 ScriptDev2 + * This program is free software licensed under GPL version 2 + * Please see the included DOCS/LICENSE.TXT for more information */ + +#ifndef DEF_SCARLETM_H +#define DEF_SCARLETM_H + +enum +{ + MAX_ENCOUNTER = 1, + + TYPE_MOGRAINE_AND_WHITE_EVENT = 1, + + NPC_MOGRAINE = 3976, + NPC_WHITEMANE = 3977, + NPC_VORREL = 3981, + NPC_INTERROGATOR_VISHAS = 3983, + + GO_WHITEMANE_DOOR = 104600, + + SAY_TRIGGER_VORREL = -1189015, +}; + +class instance_scarlet_monastery : public ScriptedInstance +{ + public: + instance_scarlet_monastery(Map* pMap); + + void Initialize() override; + + void OnCreatureCreate(Creature* pCreature) override; + void OnCreatureDeath(Creature* pCreature) override; + void OnObjectCreate(GameObject* pGo) override; + + void SetData(uint32 uiType, uint32 uiData) override; + uint32 GetData(uint32 uiData) const override; + + private: + uint32 m_auiEncounter[MAX_ENCOUNTER]; +}; + +#endif diff --git a/src/modules/SD2/scripts/eastern_kingdoms/scholomance/boss_darkmaster_gandling.cpp b/src/modules/SD2/scripts/eastern_kingdoms/scholomance/boss_darkmaster_gandling.cpp new file mode 100644 index 000000000..25f59c643 --- /dev/null +++ b/src/modules/SD2/scripts/eastern_kingdoms/scholomance/boss_darkmaster_gandling.cpp @@ -0,0 +1,129 @@ +/* Copyright (C) 2006 - 2013 ScriptDev2 + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with 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: 100 +SDComment: +SDCategory: Scholomance +EndScriptData */ + +#include "precompiled.h" +#include "scholomance.h" + +enum +{ + SPELL_ARCANE_MISSILES = 15790, + SPELL_SHADOW_SHIELD = 12040, + SPELL_CURSE = 18702, + SPELL_SHADOW_PORTAL = 17950 +}; + +struct boss_darkmaster_gandlingAI : public ScriptedAI +{ + boss_darkmaster_gandlingAI(Creature* pCreature) : ScriptedAI(pCreature) + { + m_pInstance = (ScriptedInstance*)pCreature->GetInstanceData(); + Reset(); + } + + ScriptedInstance* m_pInstance; + + uint32 m_uiArcaneMissilesTimer; + uint32 m_uiShadowShieldTimer; + uint32 m_uiCurseTimer; + uint32 m_uiTeleportTimer; + + void Reset() override + { + m_uiArcaneMissilesTimer = 4500; + m_uiShadowShieldTimer = 12000; + m_uiCurseTimer = 2000; + m_uiTeleportTimer = 16000; + } + + void UpdateAI(const uint32 uiDiff) override + { + if (!m_creature->SelectHostileTarget() || !m_creature->getVictim()) + return; + + // Arcane Missiles Timer + if (m_uiArcaneMissilesTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature->getVictim(), SPELL_ARCANE_MISSILES) == CAST_OK) + m_uiArcaneMissilesTimer = 8000; + } + else + m_uiArcaneMissilesTimer -= uiDiff; + + // Shadow Shield Timer + if (m_uiShadowShieldTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature, SPELL_SHADOW_SHIELD) == CAST_OK) + m_uiShadowShieldTimer = urand(14000, 28000); + } + else + m_uiShadowShieldTimer -= uiDiff; + + // Curse Timer + if (m_uiCurseTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature->getVictim(), SPELL_CURSE) == CAST_OK) + m_uiCurseTimer = urand(15000, 27000); + } + else + m_uiCurseTimer -= uiDiff; + + // 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->GetHealthPercent() > 3.0f) + { + if (m_uiTeleportTimer < uiDiff) + { + if (Unit* pTarget = m_creature->SelectAttackingTarget(ATTACKING_TARGET_RANDOM, 0, SPELL_SHADOW_PORTAL, SELECT_FLAG_PLAYER)) + { + if (DoCastSpellIfCan(pTarget, SPELL_SHADOW_PORTAL) == CAST_OK) + { + // remove threat + if (m_creature->GetThreatManager().getThreat(pTarget)) + m_creature->GetThreatManager().modifyThreatPercent(pTarget, -100); + + m_uiTeleportTimer = urand(20000, 35000); + } + } + } + else + m_uiTeleportTimer -= uiDiff; + } + + DoMeleeAttackIfReady(); + } +}; + +CreatureAI* GetAI_boss_darkmaster_gandling(Creature* pCreature) +{ + return new boss_darkmaster_gandlingAI(pCreature); +} + +void AddSC_boss_darkmaster_gandling() +{ + Script* pNewScript; + + pNewScript = new Script; + pNewScript->Name = "boss_darkmaster_gandling"; + pNewScript->GetAI = &GetAI_boss_darkmaster_gandling; + pNewScript->RegisterSelf(); +} diff --git a/src/modules/SD2/scripts/eastern_kingdoms/scholomance/boss_jandice_barov.cpp b/src/modules/SD2/scripts/eastern_kingdoms/scholomance/boss_jandice_barov.cpp new file mode 100644 index 000000000..d3dcf4ee0 --- /dev/null +++ b/src/modules/SD2/scripts/eastern_kingdoms/scholomance/boss_jandice_barov.cpp @@ -0,0 +1,109 @@ +/* Copyright (C) 2006 - 2013 ScriptDev2 +* This program is free software; you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation; either version 2 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with 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" + +enum +{ + SPELL_CURSE_OF_BLOOD = 16098, + SPELL_SUMMON_ILLUSIONS = 17773, + SPELL_BANISH = 8994, +}; + +struct boss_jandicebarovAI : public ScriptedAI +{ + boss_jandicebarovAI(Creature* pCreature) : ScriptedAI(pCreature) {Reset();} + + uint32 m_uiCurseOfBloodTimer; + uint32 m_uiIllusionTimer; + uint32 m_uiBanishTimer; + + void Reset() override + { + m_uiCurseOfBloodTimer = 5000; + m_uiIllusionTimer = 15000; + m_uiBanishTimer = urand(9000, 13000); + } + + void JustSummoned(Creature* pSummoned) override + { + pSummoned->ApplySpellImmune(0, IMMUNITY_DAMAGE, SPELL_SCHOOL_MASK_MAGIC, true); + + if (Unit* pTarget = m_creature->SelectAttackingTarget(ATTACKING_TARGET_RANDOM, 0)) + pSummoned->AI()->AttackStart(pTarget); + } + + void UpdateAI(const uint32 uiDiff) override + { + // Return since we have no target + if (!m_creature->SelectHostileTarget() || !m_creature->getVictim()) + return; + + // CurseOfBlood_Timer + if (m_uiCurseOfBloodTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature, SPELL_CURSE_OF_BLOOD) == CAST_OK) + m_uiCurseOfBloodTimer = urand(30000, 35000); + } + else + m_uiCurseOfBloodTimer -= uiDiff; + + // Banish + if (m_uiBanishTimer < uiDiff) + { + if (Unit* pTarget = m_creature->SelectAttackingTarget(ATTACKING_TARGET_RANDOM, 1)) + { + if (DoCastSpellIfCan(pTarget, SPELL_BANISH) == CAST_OK) + m_uiBanishTimer = urand(17000, 21000); + } + } + else + m_uiBanishTimer -= uiDiff; + + // Illusion_Timer + if (m_uiIllusionTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature, SPELL_SUMMON_ILLUSIONS) == CAST_OK) + m_uiIllusionTimer = 25000; + } + else + m_uiIllusionTimer -= uiDiff; + + DoMeleeAttackIfReady(); + } +}; + +CreatureAI* GetAI_boss_jandicebarov(Creature* pCreature) +{ + return new boss_jandicebarovAI(pCreature); +} + +void AddSC_boss_jandicebarov() +{ + Script* pNewScript; + + pNewScript = new Script; + pNewScript->Name = "boss_jandice_barov"; + pNewScript->GetAI = &GetAI_boss_jandicebarov; + pNewScript->RegisterSelf(); +} diff --git a/src/modules/SD2/scripts/eastern_kingdoms/scholomance/instance_scholomance.cpp b/src/modules/SD2/scripts/eastern_kingdoms/scholomance/instance_scholomance.cpp new file mode 100644 index 000000000..615cbbeea --- /dev/null +++ b/src/modules/SD2/scripts/eastern_kingdoms/scholomance/instance_scholomance.cpp @@ -0,0 +1,387 @@ +/* Copyright (C) 2006 - 2013 ScriptDev2 + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along 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: 99 +SDComment: Possible some D2 or other exotic missing +SDCategory: Scholomance +EndScriptData */ + +#include "precompiled.h" +#include "scholomance.h" + +instance_scholomance::instance_scholomance(Map* pMap) : ScriptedInstance(pMap), + m_uiGandlingEvent(0) +{ + Initialize(); +} + +void instance_scholomance::Initialize() +{ + memset(&m_auiEncounter, 0, sizeof(m_auiEncounter)); + + for (uint8 i = 0; i < MAX_EVENTS; ++i) + m_mGandlingData[aGandlingEvents[i]] = GandlingEventData(); +} + +void instance_scholomance::OnPlayerEnter(Player* /*pPlayer*/) +{ + // Summon Gandling if can + DoSpawnGandlingIfCan(true); +} + +void instance_scholomance::OnCreatureCreate(Creature* pCreature) +{ + switch (pCreature->GetEntry()) + { + case NPC_DARKMASTER_GANDLING: + m_mNpcEntryGuidStore[NPC_DARKMASTER_GANDLING] = pCreature->GetObjectGuid(); + break; + case NPC_BONE_MINION: + GandlingEventMap::iterator find = m_mGandlingData.find(m_uiGandlingEvent); + if (find != m_mGandlingData.end()) + find->second.m_sAddGuids.insert(pCreature->GetGUIDLow()); + break; + } +} + +void instance_scholomance::OnObjectCreate(GameObject* pGo) +{ + switch (pGo->GetEntry()) + { + case GO_GATE_KIRTONOS: + case GO_GATE_RAS: + case GO_GATE_GANDLING: + m_mGoEntryGuidStore[pGo->GetEntry()] = pGo->GetObjectGuid(); + break; + + case GO_GATE_MALICIA: m_mGandlingData[EVENT_ID_MALICIA].m_doorGuid = pGo->GetObjectGuid(); break; + case GO_GATE_THEOLEN: m_mGandlingData[EVENT_ID_THEOLEN].m_doorGuid = pGo->GetObjectGuid(); break; + case GO_GATE_POLKELT: m_mGandlingData[EVENT_ID_POLKELT].m_doorGuid = pGo->GetObjectGuid(); break; + case GO_GATE_RAVENIAN: m_mGandlingData[EVENT_ID_RAVENIAN].m_doorGuid = pGo->GetObjectGuid(); break; + case GO_GATE_BAROV: m_mGandlingData[EVENT_ID_BAROV].m_doorGuid = pGo->GetObjectGuid(); break; + case GO_GATE_ILLUCIA: m_mGandlingData[EVENT_ID_ILLUCIA].m_doorGuid = pGo->GetObjectGuid(); break; + + case GO_VIEWING_ROOM_DOOR: + // In normal flow of the instance, this door is opened by a dropped key + if (m_auiEncounter[TYPE_RATTLEGORE] == DONE) + pGo->SetGoState(GO_STATE_ACTIVE); + break; + } +} + +void instance_scholomance::SetData(uint32 uiType, uint32 uiData) +{ + switch (uiType) + { + case TYPE_KIRTONOS: + // This door is initially closed by DB-scripts, so only use it in case of FAIL, DONE, or on aggro after wipe + if (m_auiEncounter[uiType] != FAIL && uiData == IN_PROGRESS) + return; + m_auiEncounter[uiType] = uiData; + DoUseDoorOrButton(GO_GATE_KIRTONOS); + break; + case TYPE_RATTLEGORE: + m_auiEncounter[uiType] = uiData; + break; + case TYPE_RAS_FROSTWHISPER: + m_auiEncounter[uiType] = uiData; + DoUseDoorOrButton(GO_GATE_RAS); + break; + case TYPE_MALICIA: // TODO this code can be simplified, when it is known which event-ids correspond to which room + m_auiEncounter[uiType] = uiData; + DoUseDoorOrButton(m_mGandlingData[EVENT_ID_MALICIA].m_doorGuid); + break; + case TYPE_THEOLEN: + m_auiEncounter[uiType] = uiData; + DoUseDoorOrButton(m_mGandlingData[EVENT_ID_THEOLEN].m_doorGuid); + break; + case TYPE_POLKELT: + m_auiEncounter[uiType] = uiData; + DoUseDoorOrButton(m_mGandlingData[EVENT_ID_POLKELT].m_doorGuid); + break; + case TYPE_RAVENIAN: + m_auiEncounter[uiType] = uiData; + DoUseDoorOrButton(m_mGandlingData[EVENT_ID_RAVENIAN].m_doorGuid); + break; + case TYPE_ALEXEI_BAROV: + m_auiEncounter[uiType] = uiData; + DoUseDoorOrButton(m_mGandlingData[EVENT_ID_BAROV].m_doorGuid); + break; + case TYPE_ILLUCIA_BAROV: + m_auiEncounter[uiType] = uiData; + DoUseDoorOrButton(m_mGandlingData[EVENT_ID_ILLUCIA].m_doorGuid); + break; + case TYPE_GANDLING: + m_auiEncounter[uiType] = uiData; + // Close the door to main room, because the encounter will take place only in the main hall and random around all the 6 rooms + DoUseDoorOrButton(GO_GATE_GANDLING); + break; + } + + // Summon Gandling + if (uiData == DONE) + DoSpawnGandlingIfCan(false); + + 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_strInstData = saveStream.str(); + + SaveToDB(); + OUT_SAVE_INST_DATA_COMPLETE; + } +} + +void instance_scholomance::DoSpawnGandlingIfCan(bool bByPlayerEnter) +{ + // Do not summon, if event finished + if (m_auiEncounter[TYPE_GANDLING] == DONE) + return; + + // Summon only once + if (GetSingleCreatureFromStorage(NPC_DARKMASTER_GANDLING)) + return; + + Player* pPlayer = GetPlayerInMap(); + if (!pPlayer) + return; + + // Check if all the six bosses are done first + if (m_auiEncounter[TYPE_MALICIA] == DONE && m_auiEncounter[TYPE_THEOLEN] == DONE && m_auiEncounter[TYPE_POLKELT] == DONE && + m_auiEncounter[TYPE_RAVENIAN] == DONE && m_auiEncounter[TYPE_ALEXEI_BAROV] == DONE && m_auiEncounter[TYPE_ILLUCIA_BAROV] == DONE) + { + if (Creature* pGandling = pPlayer->SummonCreature(NPC_DARKMASTER_GANDLING, aGandlingSpawnLocs[0].m_fX, aGandlingSpawnLocs[0].m_fY, aGandlingSpawnLocs[0].m_fZ, aGandlingSpawnLocs[0].m_fO, TEMPSUMMON_DEAD_DESPAWN, 0)) + { + if (!bByPlayerEnter) + DoScriptText(SAY_GANDLING_SPAWN, pGandling); + } + } +} + +void instance_scholomance::HandlePortalEvent(uint32 uiEventId, uint32 uiData) +{ + GandlingEventMap::iterator find = m_mGandlingData.find(uiEventId); + if (find == m_mGandlingData.end()) + return; + + if (uiData == SPECIAL) + { + // Set current Event index + m_uiGandlingEvent = uiEventId; + + // Close door if needed + if (!find->second.m_bIsActive) + { + find->second.m_bIsActive = true; + DoUseDoorOrButton(find->second.m_doorGuid); + } + } + // Toggle door and event state in case of state-switch + else + { + if ((uiData == IN_PROGRESS && !find->second.m_bIsActive) || + (uiData == FAIL && find->second.m_bIsActive) || + (uiData == DONE && find->second.m_bIsActive)) + { + find->second.m_bIsActive = !find->second.m_bIsActive; + DoUseDoorOrButton(find->second.m_doorGuid); + } + } +} + +uint32 instance_scholomance::GetData(uint32 uiType) const +{ + if (uiType < MAX_ENCOUNTER) + return m_auiEncounter[uiType]; + + return 0; +} + +void instance_scholomance::Load(const char* chrIn) +{ + if (!chrIn) + { + 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]; + + for (uint8 i = 0; i < MAX_ENCOUNTER; ++i) + if (m_auiEncounter[i] == IN_PROGRESS) + m_auiEncounter[i] = NOT_STARTED; + + OUT_LOAD_INST_DATA_COMPLETE; +} + +void instance_scholomance::OnCreatureEnterCombat(Creature* pCreature) +{ + switch (pCreature->GetEntry()) + { + case NPC_KIRTONOS: SetData(TYPE_KIRTONOS, IN_PROGRESS); break; + case NPC_RATTLEGORE: SetData(TYPE_RATTLEGORE, IN_PROGRESS); break; + case NPC_RAS_FROSTWHISPER: SetData(TYPE_RAS_FROSTWHISPER, IN_PROGRESS); break; + case NPC_THEOLEN_KRASTINOV: SetData(TYPE_THEOLEN, IN_PROGRESS); break; + case NPC_LOREKEEPER_POLKELT: SetData(TYPE_POLKELT, IN_PROGRESS); break; + case NPC_RAVENIAN: SetData(TYPE_RAVENIAN, IN_PROGRESS); break; + case NPC_ILLUCIA_BAROV: SetData(TYPE_ILLUCIA_BAROV, IN_PROGRESS); break; + case NPC_ALEXEI_BAROV: SetData(TYPE_ALEXEI_BAROV, IN_PROGRESS); break; + case NPC_INSTRUCTOR_MALICIA: SetData(TYPE_MALICIA, IN_PROGRESS); break; + case NPC_DARKMASTER_GANDLING: SetData(TYPE_GANDLING, IN_PROGRESS); break; + + case NPC_BONE_MINION: + for (GandlingEventMap::iterator itr = m_mGandlingData.begin(); itr != m_mGandlingData.end(); ++itr) + { + // if there are no minions for a room, skip it + if (!itr->second.m_sAddGuids.empty()) + { + // set data to fail in case of player death + if (itr->second.m_sAddGuids.find(pCreature->GetGUIDLow()) != itr->second.m_sAddGuids.end()) + { + HandlePortalEvent(itr->first, IN_PROGRESS); + break; + } + } + } + break; + } +} + +void instance_scholomance::OnCreatureEvade(Creature* pCreature) +{ + switch (pCreature->GetEntry()) + { + case NPC_KIRTONOS: SetData(TYPE_KIRTONOS, FAIL); break; + case NPC_RATTLEGORE: SetData(TYPE_RATTLEGORE, FAIL); break; + case NPC_RAS_FROSTWHISPER: SetData(TYPE_RAS_FROSTWHISPER, FAIL); break; + case NPC_THEOLEN_KRASTINOV: SetData(TYPE_THEOLEN, FAIL); break; + case NPC_LOREKEEPER_POLKELT: SetData(TYPE_POLKELT, FAIL); break; + case NPC_RAVENIAN: SetData(TYPE_RAVENIAN, FAIL); break; + case NPC_ILLUCIA_BAROV: SetData(TYPE_ILLUCIA_BAROV, FAIL); break; + case NPC_ALEXEI_BAROV: SetData(TYPE_ALEXEI_BAROV, FAIL); break; + case NPC_INSTRUCTOR_MALICIA: SetData(TYPE_MALICIA, FAIL); break; + case NPC_DARKMASTER_GANDLING: SetData(TYPE_GANDLING, FAIL); break; + + case NPC_BONE_MINION: + for (GandlingEventMap::iterator itr = m_mGandlingData.begin(); itr != m_mGandlingData.end(); ++itr) + { + // if there are no minions for a room, skip it + if (!itr->second.m_sAddGuids.empty()) + { + // set data to fail in case of player death + if (itr->second.m_sAddGuids.find(pCreature->GetGUIDLow()) != itr->second.m_sAddGuids.end()) + { + HandlePortalEvent(itr->first, FAIL); + break; + } + } + } + break; + } +} + +void instance_scholomance::OnCreatureDeath(Creature* pCreature) +{ + switch (pCreature->GetEntry()) + { + case NPC_KIRTONOS: SetData(TYPE_KIRTONOS, DONE); break; + case NPC_RATTLEGORE: SetData(TYPE_RATTLEGORE, DONE); break; + case NPC_RAS_FROSTWHISPER: SetData(TYPE_RAS_FROSTWHISPER, DONE); break; + case NPC_THEOLEN_KRASTINOV: SetData(TYPE_THEOLEN, DONE); break; + case NPC_LOREKEEPER_POLKELT: SetData(TYPE_POLKELT, DONE); break; + case NPC_RAVENIAN: SetData(TYPE_RAVENIAN, DONE); break; + case NPC_ILLUCIA_BAROV: SetData(TYPE_ILLUCIA_BAROV, DONE); break; + case NPC_ALEXEI_BAROV: SetData(TYPE_ALEXEI_BAROV, DONE); break; + case NPC_INSTRUCTOR_MALICIA: SetData(TYPE_MALICIA, DONE); break; + case NPC_DARKMASTER_GANDLING: SetData(TYPE_GANDLING, DONE); break; + + case NPC_BONE_MINION: + for (GandlingEventMap::iterator itr = m_mGandlingData.begin(); itr != m_mGandlingData.end(); ++itr) + { + // if there are no minions for a room, skip it + if (!itr->second.m_sAddGuids.empty()) + { + // search for the dead minion and erase it + if (itr->second.m_sAddGuids.find(pCreature->GetGUIDLow()) != itr->second.m_sAddGuids.end()) + { + itr->second.m_sAddGuids.erase(pCreature->GetGUIDLow()); + + // if the current list is empty; set event id as done + if (itr->second.m_sAddGuids.empty()) + { + HandlePortalEvent(itr->first, DONE); + break; + } + } + } + } + break; + } +} + +InstanceData* GetInstanceData_instance_scholomance(Map* pMap) +{ + return new instance_scholomance(pMap); +} + +bool ProcessEventId_event_spell_gandling_shadow_portal(uint32 uiEventId, Object* pSource, Object* /*pTarget*/, bool /*bIsStart*/) +{ + if (pSource->GetTypeId() == TYPEID_UNIT) + { + if (instance_scholomance* pInstance = (instance_scholomance*)((Creature*)pSource)->GetInstanceData()) + { + // Check if we are handling an event associated with the room events of gandling + for (uint8 i = 0; i < MAX_EVENTS; ++i) + { + if (uiEventId == aGandlingEvents[i]) + { + // Set data in progress for the current event and store current event + pInstance->HandlePortalEvent(uiEventId, SPECIAL); + // return false, to allow the DB-scripts to summon some NPCSs + return false; + } + } + } + } + return false; +} + +void AddSC_instance_scholomance() +{ + Script* pNewScript; + + pNewScript = new Script; + pNewScript->Name = "instance_scholomance"; + pNewScript->GetInstanceData = &GetInstanceData_instance_scholomance; + pNewScript->RegisterSelf(); + + pNewScript = new Script; + pNewScript->Name = "event_spell_gandling_shadow_portal"; + pNewScript->pProcessEventId = &ProcessEventId_event_spell_gandling_shadow_portal; + pNewScript->RegisterSelf(); +} diff --git a/src/modules/SD2/scripts/eastern_kingdoms/scholomance/scholomance.h b/src/modules/SD2/scripts/eastern_kingdoms/scholomance/scholomance.h new file mode 100644 index 000000000..52f649f71 --- /dev/null +++ b/src/modules/SD2/scripts/eastern_kingdoms/scholomance/scholomance.h @@ -0,0 +1,115 @@ +/* Copyright (C) 2006 - 2013 ScriptDev2 + * This program is free software licensed under GPL version 2 + * Please see the included DOCS/LICENSE.TXT for more information */ + +#ifndef DEF_SCHOLOMANCE_H +#define DEF_SCHOLOMANCE_H + +enum +{ + MAX_ENCOUNTER = 10, + MAX_EVENTS = 6, + + TYPE_KIRTONOS = 0, + TYPE_RATTLEGORE = 1, + TYPE_RAS_FROSTWHISPER = 2, + TYPE_MALICIA = 3, + TYPE_THEOLEN = 4, + TYPE_POLKELT = 5, + TYPE_RAVENIAN = 6, + TYPE_ALEXEI_BAROV = 7, + TYPE_ILLUCIA_BAROV = 8, + TYPE_GANDLING = 9, + + NPC_KIRTONOS = 10506, + NPC_RATTLEGORE = 11622, + NPC_RAS_FROSTWHISPER = 10508, + NPC_THEOLEN_KRASTINOV = 11261, + NPC_LOREKEEPER_POLKELT = 10901, + NPC_RAVENIAN = 10507, + NPC_ILLUCIA_BAROV = 10502, + NPC_ALEXEI_BAROV = 10504, + NPC_INSTRUCTOR_MALICIA = 10505, + NPC_DARKMASTER_GANDLING = 1853, + NPC_BONE_MINION = 16119, // summoned in random rooms by gandling + + GO_GATE_KIRTONOS = 175570, + GO_VIEWING_ROOM_DOOR = 175167, // Must be opened in reload case + GO_GATE_RAS = 177370, + GO_GATE_MALICIA = 177375, + GO_GATE_THEOLEN = 177377, + GO_GATE_POLKELT = 177376, + GO_GATE_RAVENIAN = 177372, + GO_GATE_BAROV = 177373, + GO_GATE_ILLUCIA = 177371, + GO_GATE_GANDLING = 177374, + + // Because the shadow portal teleport coordinates are guesswork (taken from old script) these IDs might be randomized + // TODO Syncronise with correct DB coordinates when they will be known + EVENT_ID_POLKELT = 5618, + EVENT_ID_THEOLEN = 5619, + EVENT_ID_MALICIA = 5620, + EVENT_ID_ILLUCIA = 5621, + EVENT_ID_BAROV = 5622, + EVENT_ID_RAVENIAN = 5623, + + SAY_GANDLING_SPAWN = -1289000, +}; + +struct SpawnLocation +{ + float m_fX, m_fY, m_fZ, m_fO; +}; + +static const SpawnLocation aGandlingSpawnLocs[1] = +{ + {180.771f, -5.4286f, 75.5702f, 1.29154f} +}; + +struct GandlingEventData +{ + GandlingEventData() : m_bIsActive(false) {} + bool m_bIsActive; + ObjectGuid m_doorGuid; + std::set m_sAddGuids; +}; + +static const uint32 aGandlingEvents[MAX_EVENTS] = {EVENT_ID_POLKELT, EVENT_ID_THEOLEN, EVENT_ID_MALICIA, EVENT_ID_ILLUCIA, EVENT_ID_BAROV, EVENT_ID_RAVENIAN}; + +typedef std::map GandlingEventMap; + +class instance_scholomance : public ScriptedInstance +{ + public: + instance_scholomance(Map* pMap); + ~instance_scholomance() {} + + void Initialize() override; + + void OnCreatureEnterCombat(Creature* pCreature) override; + void OnCreatureEvade(Creature* pCreature); + void OnCreatureDeath(Creature* pCreature) override; + + void OnCreatureCreate(Creature* pCreature) override; + void OnObjectCreate(GameObject* pGo) override; + void OnPlayerEnter(Player* pPlayer) override; + + void HandlePortalEvent(uint32 uiEventId, uint32 uiData); + + void SetData(uint32 uiType, uint32 uiData) override; + uint32 GetData(uint32 uiType) const override; + + const char* Save() const override { return m_strInstData.c_str(); } + void Load(const char* chrIn) override; + + private: + void DoSpawnGandlingIfCan(bool bByPlayerEnter); + + uint32 m_auiEncounter[MAX_ENCOUNTER]; + std::string m_strInstData; + + uint32 m_uiGandlingEvent; + GandlingEventMap m_mGandlingData; +}; + +#endif diff --git a/src/modules/SD2/scripts/eastern_kingdoms/searing_gorge.cpp b/src/modules/SD2/scripts/eastern_kingdoms/searing_gorge.cpp new file mode 100644 index 000000000..ee4a392c2 --- /dev/null +++ b/src/modules/SD2/scripts/eastern_kingdoms/searing_gorge.cpp @@ -0,0 +1,44 @@ +/** + * ScriptDev2 is an extension for mangos providing enhanced features for + * area triggers, creatures, game objects, instances, items, and spells beyond + * the default database scripting in mangos. + * + * Copyright (C) 2006-2013 ScriptDev2 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * World of Warcraft, and all World of Warcraft or Warcraft art, images, + * and lore are copyrighted by Blizzard Entertainment, Inc. + */ + +/* ScriptData +SDName: Searing_Gorge +SD%Complete: 0 +SDComment: Placeholder +SDCategory: Searing Gorge +EndScriptData */ + +/* ContentData +EndContentData */ + +#include "precompiled.h" + +/*###### +## +######*/ + +void AddSC_searing_gorge() +{ +} diff --git a/src/modules/SD2/scripts/eastern_kingdoms/shadowfang_keep/boss_hummel.cpp b/src/modules/SD2/scripts/eastern_kingdoms/shadowfang_keep/boss_hummel.cpp new file mode 100644 index 000000000..0bc66c462 --- /dev/null +++ b/src/modules/SD2/scripts/eastern_kingdoms/shadowfang_keep/boss_hummel.cpp @@ -0,0 +1,273 @@ +/* Copyright (C) 2006 - 2013 ScriptDev2 + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +/* ScriptData +SDName: boss_hummel +SD%Complete: 50 +SDComment: The bosses are handled in eventAI; The event needs more research; only the basics are implemented; Check crazed apothecary timer +SDCategory: Shadowfang Keep +EndScriptData */ + +#include "precompiled.h" +#include "shadowfang_keep.h" + +enum +{ + SAY_INTRO_1 = -1033020, + SAY_INTRO_2 = -1033021, + SAY_INTRO_3 = -1033022, + SAY_CALL_BAXTER = -1033023, + SAY_CALL_FRYE = -1033024, + + SPELL_SUMMON_VALENTINE_ADD = 68610, // summons the crazy apothecary + + // It's unk who is handling the summoning of the crazed apothecary. This may get the following npcs involved: 36212 + NCP_CRAZED_APOTHECARY = 36568, // casts spell 68957 on range check + + QUEST_BEEN_SERVED = 14488, + + FACTION_HOSTILE = 14, +}; + +static const DialogueEntry aIntroDialogue[] = +{ + {QUEST_BEEN_SERVED, 0, 1000}, + {SAY_INTRO_1, NPC_HUMMEL, 4000}, + {SAY_INTRO_2, NPC_HUMMEL, 4000}, + {SAY_INTRO_3, NPC_HUMMEL, 3000}, + {NPC_HUMMEL, 0, 8000}, + {NPC_BAXTER, 0, 8000}, + {NPC_FRYE, 0, 0}, + {0, 0, 0}, +}; + +struct npc_valentine_boss_managerAI : public ScriptedAI, private DialogueHelper +{ + npc_valentine_boss_managerAI(Creature* pCreature) : ScriptedAI(pCreature), + DialogueHelper(aIntroDialogue) + { + m_pInstance = (instance_shadowfang_keep*)pCreature->GetInstanceData(); + InitializeDialogueHelper(m_pInstance); + Reset(); + } + + instance_shadowfang_keep* m_pInstance; + + uint32 m_uiCrazedApothecaryTimer; + + ObjectGuid m_EventStarterGuid; + + void Reset() override + { + m_uiCrazedApothecaryTimer = 30000; + } + + void JustDidDialogueStep(int32 iEntry) override + { + if (!m_pInstance) + return; + + switch (iEntry) + { + case NPC_HUMMEL: + { + if (Creature* pHummel = m_pInstance->GetSingleCreatureFromStorage(NPC_HUMMEL)) + { + // WARNING: workaround -> faction should be set on event start + pHummel->SetFactionTemporary(FACTION_HOSTILE, TEMPFACTION_RESTORE_REACH_HOME | TEMPFACTION_RESTORE_RESPAWN); + pHummel->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_PASSIVE | UNIT_FLAG_NON_ATTACKABLE); + + if (Player* pPlayer = m_creature->GetMap()->GetPlayer(m_EventStarterGuid)) + pHummel->AI()->AttackStart(pPlayer); + } + + m_pInstance->SetData(TYPE_APOTHECARY, IN_PROGRESS); + break; + } + case NPC_BAXTER: + { + Creature* pHummel = m_pInstance->GetSingleCreatureFromStorage(NPC_HUMMEL); + if (!pHummel) + return; + + if (Creature* pBaxter = m_pInstance->GetSingleCreatureFromStorage(NPC_BAXTER)) + { + DoScriptText(SAY_CALL_BAXTER, pHummel); + + // WARNING: workaround -> faction should be set on event start + pBaxter->SetFactionTemporary(FACTION_HOSTILE, TEMPFACTION_RESTORE_REACH_HOME | TEMPFACTION_RESTORE_RESPAWN); + pBaxter->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_PASSIVE | UNIT_FLAG_NON_ATTACKABLE); + if (pHummel->getVictim()) + pBaxter->AI()->AttackStart(pHummel->getVictim()); + } + break; + } + case NPC_FRYE: + { + Creature* pHummel = m_pInstance->GetSingleCreatureFromStorage(NPC_HUMMEL); + if (!pHummel) + return; + + if (Creature* pFrye = m_pInstance->GetSingleCreatureFromStorage(NPC_FRYE)) + { + DoScriptText(SAY_CALL_FRYE, pHummel); + + // WARNING: workaround -> faction should be set on event start + pFrye->SetFactionTemporary(FACTION_HOSTILE, TEMPFACTION_RESTORE_REACH_HOME | TEMPFACTION_RESTORE_RESPAWN); + pFrye->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_PASSIVE | UNIT_FLAG_NON_ATTACKABLE); + if (pHummel->getVictim()) + pFrye->AI()->AttackStart(pHummel->getVictim()); + } + break; + } + } + } + + void JustSummoned(Creature* pSummoned) override + { + if (pSummoned->GetEntry() == NCP_CRAZED_APOTHECARY) + { + if (!m_pInstance) + return; + + // Make it attack a random Target + Creature* pBoss = m_pInstance->GetSingleCreatureFromStorage(NPC_HUMMEL); + if (!pBoss || !pBoss->IsAlive()) + pBoss = m_pInstance->GetSingleCreatureFromStorage(NPC_BAXTER); + if (!pBoss || !pBoss->IsAlive()) + pBoss = m_pInstance->GetSingleCreatureFromStorage(NPC_FRYE); + if (!pBoss || !pBoss->IsAlive()) + return; + + // Attack a random target + if (Unit* pTarget = pBoss->SelectAttackingTarget(ATTACKING_TARGET_RANDOM, 0)) + pSummoned->AI()->AttackStart(pTarget); + } + } + + // Wrapper to get the event started + void DoStartValentineEvent(ObjectGuid starterGuid) + { + if (!m_pInstance) + return; + + m_EventStarterGuid = starterGuid; + + if (Creature* pHummel = m_pInstance->GetSingleCreatureFromStorage(NPC_HUMMEL)) + { + // I'm not sure if this unit flag should be used, but it's clear that the NPC shouldn't be attacked until the dialogue is finished + pHummel->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE); + // WARNING: workaround -> faction should be set here - FIX THIS after the aura bug in core is fixed + // pHummel->SetFactionTemporary(FACTION_HOSTILE, TEMPFACTION_RESTORE_REACH_HOME | TEMPFACTION_RESTORE_RESPAWN); + } + + StartNextDialogueText(QUEST_BEEN_SERVED); + + // Move Baxter to position + if (Creature* pBaxter = m_pInstance->GetSingleCreatureFromStorage(NPC_BAXTER)) + { + pBaxter->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE); + // WARNING: workaround -> faction should be set here - FIX THIS after the aura bug in core is fixed + // pBaxter->SetFactionTemporary(FACTION_HOSTILE, TEMPFACTION_RESTORE_REACH_HOME | TEMPFACTION_RESTORE_RESPAWN); + + if (GameObject* pVials = m_pInstance->GetSingleGameObjectFromStorage(GO_APOTHECARE_VIALS)) + { + float fX, fY, fZ; + pVials->GetContactPoint(pBaxter, fX, fY, fZ, CONTACT_DISTANCE); + pBaxter->SetWalk(false); + pBaxter->GetMotionMaster()->MovePoint(1, fX, fY, fZ); + } + else + script_error_log("Gameobject %u couldn't be found or something really bad happened.", GO_APOTHECARE_VIALS); + } + + // Move Frye to position + if (Creature* pFrye = m_pInstance->GetSingleCreatureFromStorage(NPC_FRYE)) + { + pFrye->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE); + // WARNING: workaround -> faction should be set here - FIX THIS after the aura bug in core is fixed + // pFrye->SetFactionTemporary(FACTION_HOSTILE, TEMPFACTION_RESTORE_REACH_HOME | TEMPFACTION_RESTORE_RESPAWN); + + if (GameObject* pChemistry = m_pInstance->GetSingleGameObjectFromStorage(GO_CHEMISTRY_SET)) + { + float fX, fY, fZ; + pChemistry->GetContactPoint(pFrye, fX, fY, fZ, CONTACT_DISTANCE); + pFrye->SetWalk(false); + pFrye->GetMotionMaster()->MovePoint(1, fX, fY, fZ); + } + else + script_error_log("Gameobject %u couldn't be found or something really bad happened.", GO_CHEMISTRY_SET); + } + } + + void UpdateAI(const uint32 uiDiff) override + { + DialogueUpdate(uiDiff); + + if (!m_pInstance) + return; + + if (m_pInstance->GetData(TYPE_APOTHECARY) != IN_PROGRESS) + return; + + if (m_uiCrazedApothecaryTimer < uiDiff) + { + if (Creature* pGenerator = m_pInstance->GetSingleCreatureFromStorage(NPC_APOTHECARY_GENERATOR)) + pGenerator->CastSpell(pGenerator, SPELL_SUMMON_VALENTINE_ADD, true, NULL, NULL, m_creature->GetObjectGuid()); + + m_uiCrazedApothecaryTimer = 30000; + } + else + m_uiCrazedApothecaryTimer -= uiDiff; + } +}; + +CreatureAI* GetAI_npc_valentine_boss_manager(Creature* pCreature) +{ + return new npc_valentine_boss_managerAI(pCreature); +} + +bool QuestRewarded_npc_apothecary_hummel(Player* pPlayer, Creature* pCreature, Quest const* pQuest) +{ + if (pQuest->GetQuestId() == QUEST_BEEN_SERVED) + { + if (instance_shadowfang_keep* pInstance = (instance_shadowfang_keep*)pCreature->GetInstanceData()) + { + if (Creature* pValentineMgr = pInstance->GetSingleCreatureFromStorage(NPC_VALENTINE_BOSS_MGR)) + { + if (npc_valentine_boss_managerAI* pManagerAI = dynamic_cast(pValentineMgr->AI())) + pManagerAI->DoStartValentineEvent(pPlayer->GetObjectGuid()); + } + } + } + + return true; +} + +void AddSC_boss_hummel() +{ + Script* pNewScript; + + pNewScript = new Script; + pNewScript->Name = "npc_valentine_boss_manager"; + pNewScript->GetAI = GetAI_npc_valentine_boss_manager; + pNewScript->RegisterSelf(); + + pNewScript = new Script; + pNewScript->Name = "npc_apothecary_hummel"; + pNewScript->pQuestRewardedNPC = &QuestRewarded_npc_apothecary_hummel; + pNewScript->RegisterSelf(); +} diff --git a/src/modules/SD2/scripts/eastern_kingdoms/shadowfang_keep/instance_shadowfang_keep.cpp b/src/modules/SD2/scripts/eastern_kingdoms/shadowfang_keep/instance_shadowfang_keep.cpp new file mode 100644 index 000000000..2b0084529 --- /dev/null +++ b/src/modules/SD2/scripts/eastern_kingdoms/shadowfang_keep/instance_shadowfang_keep.cpp @@ -0,0 +1,261 @@ +/* Copyright (C) 2006 - 2013 ScriptDev2 + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along 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 "shadowfang_keep.h" + +instance_shadowfang_keep::instance_shadowfang_keep(Map* pMap) : ScriptedInstance(pMap), + m_uiApothecaryDead(0) +{ + Initialize(); +} + +void instance_shadowfang_keep::Initialize() +{ + memset(&m_auiEncounter, 0, sizeof(m_auiEncounter)); +} + +void instance_shadowfang_keep::OnCreatureCreate(Creature* pCreature) +{ + switch (pCreature->GetEntry()) + { + case NPC_ASH: + case NPC_ADA: + case NPC_FENRUS: + case NPC_HUMMEL: + case NPC_FRYE: + case NPC_BAXTER: + case NPC_APOTHECARY_GENERATOR: + case NPC_VALENTINE_BOSS_MGR: + break; + case NPC_VINCENT: + // If Arugal has done the intro, make Vincent dead! + if (m_auiEncounter[4] == DONE) + pCreature->SetStandState(UNIT_STAND_STATE_DEAD); + break; + + default: + return; + } + m_mNpcEntryGuidStore[pCreature->GetEntry()] = pCreature->GetObjectGuid(); +} + +void instance_shadowfang_keep::OnObjectCreate(GameObject* pGo) +{ + switch (pGo->GetEntry()) + { + case GO_COURTYARD_DOOR: + if (m_auiEncounter[0] == DONE) + pGo->SetGoState(GO_STATE_ACTIVE); + break; + // For this we ignore voidwalkers, because if the server restarts + // They won't be there, but Fenrus is dead so the door can't be opened! + case GO_SORCERER_DOOR: + if (m_auiEncounter[2] == DONE) + pGo->SetGoState(GO_STATE_ACTIVE); + break; + case GO_ARUGAL_DOOR: + if (m_auiEncounter[3] == DONE) + pGo->SetGoState(GO_STATE_ACTIVE); + break; + case GO_ARUGAL_FOCUS: + case GO_APOTHECARE_VIALS: + case GO_CHEMISTRY_SET: + break; + + default: + return; + } + m_mGoEntryGuidStore[pGo->GetEntry()] = pGo->GetObjectGuid(); +} + +void instance_shadowfang_keep::OnCreatureDeath(Creature* pCreature) +{ + switch (pCreature->GetEntry()) + { + // Remove lootable flag from Hummel + // Instance data is set to SPECIAL because the encounter depends on multiple bosses + case NPC_HUMMEL: + pCreature->RemoveFlag(UNIT_DYNAMIC_FLAGS, UNIT_DYNFLAG_LOOTABLE); + DoScriptText(SAY_HUMMEL_DEATH, pCreature); + // no break; + case NPC_FRYE: + case NPC_BAXTER: + SetData(TYPE_APOTHECARY, SPECIAL); + break; + } +} + +void instance_shadowfang_keep::OnCreatureEvade(Creature* pCreature) +{ + switch (pCreature->GetEntry()) + { + case NPC_HUMMEL: + case NPC_FRYE: + case NPC_BAXTER: + SetData(TYPE_APOTHECARY, FAIL); + break; + } +} + +void instance_shadowfang_keep::DoSpeech() +{ + Creature* pAda = GetSingleCreatureFromStorage(NPC_ADA); + Creature* pAsh = GetSingleCreatureFromStorage(NPC_ASH); + + if (pAda && pAda->IsAlive() && pAsh && pAsh->IsAlive()) + { + DoScriptText(SAY_BOSS_DIE_AD, pAda); + DoScriptText(SAY_BOSS_DIE_AS, pAsh); + } +} + +void instance_shadowfang_keep::SetData(uint32 uiType, uint32 uiData) +{ + switch (uiType) + { + case TYPE_FREE_NPC: + if (uiData == DONE) + DoUseDoorOrButton(GO_COURTYARD_DOOR); + m_auiEncounter[0] = uiData; + break; + case TYPE_RETHILGORE: + if (uiData == DONE) + DoSpeech(); + m_auiEncounter[1] = uiData; + break; + case TYPE_FENRUS: + if (uiData == DONE) + { + if (Creature* pFenrus = GetSingleCreatureFromStorage(NPC_FENRUS)) + pFenrus->SummonCreature(NPC_ARCHMAGE_ARUGAL, -136.89f, 2169.17f, 136.58f, 2.794f, TEMPSUMMON_TIMED_DESPAWN, 30000); + } + m_auiEncounter[2] = uiData; + break; + case TYPE_NANDOS: + if (uiData == DONE) + DoUseDoorOrButton(GO_ARUGAL_DOOR); + m_auiEncounter[3] = uiData; + break; + case TYPE_INTRO: + m_auiEncounter[4] = uiData; + break; + case TYPE_VOIDWALKER: + if (uiData == DONE) + { + m_auiEncounter[5]++; + if (m_auiEncounter[5] > 3) + DoUseDoorOrButton(GO_SORCERER_DOOR); + } + break; + case TYPE_APOTHECARY: + // Reset apothecary counter on fail + if (uiData == IN_PROGRESS) + m_uiApothecaryDead = 0; + if (uiData == SPECIAL) + { + ++m_uiApothecaryDead; + + // Set Hummel as lootable only when the others are dead + if (m_uiApothecaryDead == MAX_APOTHECARY) + { + if (Creature* pHummel = GetSingleCreatureFromStorage(NPC_HUMMEL)) + pHummel->SetFlag(UNIT_DYNAMIC_FLAGS, UNIT_DYNFLAG_LOOTABLE); + + SetData(TYPE_APOTHECARY, DONE); + } + } + // We don't want to store the SPECIAL data + else + m_auiEncounter[6] = uiData; + 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_strInstData = saveStream.str(); + + SaveToDB(); + OUT_SAVE_INST_DATA_COMPLETE; + } +} + +uint32 instance_shadowfang_keep::GetData(uint32 uiType) const +{ + switch (uiType) + { + case TYPE_FREE_NPC: return m_auiEncounter[0]; + case TYPE_RETHILGORE: return m_auiEncounter[1]; + case TYPE_FENRUS: return m_auiEncounter[2]; + case TYPE_NANDOS: return m_auiEncounter[3]; + case TYPE_INTRO: return m_auiEncounter[4]; + case TYPE_APOTHECARY: return m_auiEncounter[6]; + + default: + return 0; + } +} + +void instance_shadowfang_keep::Load(const char* chrIn) +{ + if (!chrIn) + { + 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]; + + 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* pNewScript; + + pNewScript = new Script; + pNewScript->Name = "instance_shadowfang_keep"; + pNewScript->GetInstanceData = &GetInstanceData_instance_shadowfang_keep; + pNewScript->RegisterSelf(); +} diff --git a/src/modules/SD2/scripts/eastern_kingdoms/shadowfang_keep/shadowfang_keep.cpp b/src/modules/SD2/scripts/eastern_kingdoms/shadowfang_keep/shadowfang_keep.cpp new file mode 100644 index 000000000..44c88a757 --- /dev/null +++ b/src/modules/SD2/scripts/eastern_kingdoms/shadowfang_keep/shadowfang_keep.cpp @@ -0,0 +1,932 @@ +/* Copyright (C) 2006 - 2013 ScriptDev2 + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along 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. +SDCategory: Shadowfang Keep +EndScriptData */ + +/* ContentData +npc_shadowfang_prisoner +mob_arugal_voidwalker +npc_arugal +boss_arugal +npc_deathstalker_vincent +EndContentData */ + +#include "precompiled.h" +#include "escort_ai.h" +#include "shadowfang_keep.h" + +/*###### +## npc_shadowfang_prisoner +######*/ + +enum +{ + SAY_FREE_AS = -1033000, + SAY_OPEN_DOOR_AS = -1033001, + SAY_POST_DOOR_AS = -1033002, + EMOTE_VANISH_AS = -1033014, + SAY_FREE_AD = -1033003, + SAY_OPEN_DOOR_AD = -1033004, + SAY_POST1_DOOR_AD = -1033005, + SAY_POST2_DOOR_AD = -1033006, + EMOTE_UNLOCK_DOOR_AD = -1033015, + + SPELL_UNLOCK = 6421, + SPELL_FIRE = 6422, + + GOSSIP_ITEM_DOOR = -3033000 +}; + +struct npc_shadowfang_prisonerAI : public npc_escortAI +{ + npc_shadowfang_prisonerAI(Creature* pCreature) : npc_escortAI(pCreature) + { + m_pInstance = (ScriptedInstance*)pCreature->GetInstanceData(); + m_uiNpcEntry = pCreature->GetEntry(); + Reset(); + } + + ScriptedInstance* m_pInstance; + uint32 m_uiNpcEntry; + + void WaypointReached(uint32 uiPoint) override + { + switch (uiPoint) + { + case 0: + if (m_uiNpcEntry == NPC_ASH) + DoScriptText(SAY_FREE_AS, m_creature); + else + DoScriptText(SAY_FREE_AD, m_creature); + break; + case 10: + if (m_uiNpcEntry == NPC_ASH) + DoScriptText(SAY_OPEN_DOOR_AS, m_creature); + else + DoScriptText(SAY_OPEN_DOOR_AD, m_creature); + break; + case 11: + if (m_uiNpcEntry == NPC_ASH) + DoCastSpellIfCan(m_creature, SPELL_UNLOCK); + else + DoScriptText(EMOTE_UNLOCK_DOOR_AD, m_creature); + break; + case 12: + if (m_uiNpcEntry != NPC_ASH) + m_creature->HandleEmote(EMOTE_ONESHOT_USESTANDING); + break; + case 13: + if (m_uiNpcEntry == NPC_ASH) + DoScriptText(SAY_POST_DOOR_AS, m_creature); + else + DoScriptText(SAY_POST1_DOOR_AD, m_creature); + + if (m_pInstance) + m_pInstance->SetData(TYPE_FREE_NPC, DONE); + break; + case 14: + if (m_uiNpcEntry == NPC_ASH) + DoCastSpellIfCan(m_creature, SPELL_FIRE); + else + { + DoScriptText(SAY_POST2_DOOR_AD, m_creature); + SetRun(); + } + break; + case 15: + if (m_uiNpcEntry == NPC_ASH) + DoScriptText(EMOTE_VANISH_AS, m_creature); + break; + } + } + + void Reset() override {} + + // Let's prevent Adamant from charging into Ashcrombe's cell + // And beating the crap out of him and vice versa XD + void AttackStart(Unit* pWho) override + { + if (pWho) + { + if (pWho->GetEntry() == NPC_ASH || pWho->GetEntry() == NPC_ADA) + return; + else + ScriptedAI::AttackStart(pWho); + } + } +}; + +CreatureAI* GetAI_npc_shadowfang_prisoner(Creature* pCreature) +{ + return new npc_shadowfang_prisonerAI(pCreature); +} + +bool GossipHello_npc_shadowfang_prisoner(Player* pPlayer, Creature* pCreature) +{ + ScriptedInstance* pInstance = (ScriptedInstance*)pCreature->GetInstanceData(); + + if (pInstance && pInstance->GetData(TYPE_FREE_NPC) != DONE && pInstance->GetData(TYPE_RETHILGORE) == DONE) + pPlayer->ADD_GOSSIP_ITEM_ID(GOSSIP_ICON_CHAT, GOSSIP_ITEM_DOOR, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 1); + + pPlayer->SEND_GOSSIP_MENU(pPlayer->GetGossipTextId(pCreature), pCreature->GetObjectGuid()); + 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_shadowfang_prisonerAI* pEscortAI = dynamic_cast(pCreature->AI())) + pEscortAI->Start(); + } + return true; +} + +/*###### +## mob_arugal_voidwalker +######*/ + +struct Waypoint +{ + float fX, fY, fZ; +}; + +// Cordinates for voidwalker spawns +static const Waypoint VWWaypoints[] = +{ + { -146.06f, 2172.84f, 127.953f}, // This is the initial location, in the middle of the room + { -159.547f, 2178.11f, 128.944f}, // When they come back up, they hit this point then walk back down + { -171.113f, 2182.69f, 129.255f}, + { -177.613f, 2175.59f, 128.161f}, + { -185.396f, 2178.35f, 126.413f}, + { -184.004f, 2188.31f, 124.122f}, + { -172.781f, 2188.71f, 121.611f}, + { -173.245f, 2176.93f, 119.085f}, + { -183.145f, 2176.04f, 116.995f}, + { -185.551f, 2185.77f, 114.784f}, + { -177.502f, 2190.75f, 112.681f}, + { -171.218f, 2182.61f, 110.314f}, + { -173.857f, 2175.1f, 109.255f} +}; + +enum +{ + LAST_WAYPOINT = 12, + + NPC_VOIDWALKER = 4627, + + SPELL_DARK_OFFERING = 7154 +}; + +struct mob_arugal_voidwalkerAI : public ScriptedAI +{ + mob_arugal_voidwalkerAI(Creature* pCreature) : ScriptedAI(pCreature) + { + m_pInstance = (ScriptedInstance*)pCreature->GetInstanceData(); + m_bIsLeader = false; + m_uiCurrentPoint = 0; + m_bReverse = false; + } + + uint32 m_uiResetTimer, m_uiDarkOffering; + uint8 m_uiCurrentPoint, m_uiPosition; // 0 - leader, 1 - behind-right, 2 - behind, 3 - behind-left + ObjectGuid m_leaderGuid; + ScriptedInstance* m_pInstance; + bool m_bIsLeader, m_bReverse, m_bWPDone; + + void Reset() override + { + m_creature->SetWalk(true); + m_uiDarkOffering = urand(4400, 12500); + m_bWPDone = true; + + Creature* pLeader = m_creature->GetMap()->GetCreature(m_leaderGuid); + if (pLeader && pLeader->IsAlive()) + { + m_creature->GetMotionMaster()->MoveFollow(pLeader, 1.0f, M_PI / 2 * m_uiPosition); + } + else + { + std::list lVoidwalkerList; + Creature* pNewLeader = NULL; + uint8 uiHighestPosition = 0; + GetCreatureListWithEntryInGrid(lVoidwalkerList, m_creature, NPC_VOIDWALKER, 50.0f); + for (std::list::iterator itr = lVoidwalkerList.begin(); itr != lVoidwalkerList.end(); ++itr) + { + if ((*itr)->IsAlive()) + { + if (mob_arugal_voidwalkerAI* pVoidwalkerAI = dynamic_cast((*itr)->AI())) + { + uint8 uiPosition = pVoidwalkerAI->GetPosition(); + if (uiPosition > uiHighestPosition) + { + pNewLeader = (*itr); + uiHighestPosition = uiPosition; + } + } + } + } + + if (pNewLeader) + { + m_leaderGuid = pNewLeader->GetObjectGuid(); + if (pNewLeader == m_creature) + { + m_bIsLeader = true; + m_bWPDone = true; + } + else + m_creature->GetMotionMaster()->MoveFollow(pNewLeader, 1.0f, M_PI / 2 * m_uiPosition); + } + else + { + pNewLeader = m_creature; + m_bIsLeader = true; + m_bWPDone = true; + } + } + } + + void UpdateAI(const uint32 uiDiff) override + { + if (m_bIsLeader && m_bWPDone) + { + m_creature->GetMotionMaster()->MovePoint(m_uiCurrentPoint, VWWaypoints[m_uiCurrentPoint].fX, + VWWaypoints[m_uiCurrentPoint].fY, VWWaypoints[m_uiCurrentPoint].fZ); + m_bWPDone = false; + } + + if (!m_creature->IsInCombat()) + return; + + if (m_uiDarkOffering < uiDiff) + { + m_uiDarkOffering = urand(4400, 12500); + + if (Unit* pUnit = DoSelectLowestHpFriendly(10.0f, 290)) + DoCastSpellIfCan(pUnit, SPELL_DARK_OFFERING); + } + else + m_uiDarkOffering -= uiDiff; + + // Check if we have a current target + if (!m_creature->SelectHostileTarget() || !m_creature->getVictim()) + return; + + DoMeleeAttackIfReady(); + } + + void MovementInform(uint32 uiMoveType, uint32 uiPointId) override + { + if (uiMoveType != POINT_MOTION_TYPE || !m_bIsLeader) + return; + + switch (uiPointId) + { + case 1: + if (m_bReverse) + m_bReverse = false; + break; + case LAST_WAYPOINT: + if (!m_bReverse) + m_bReverse = true; + break; + } + + if (m_bReverse) + --m_uiCurrentPoint; + else + ++m_uiCurrentPoint; + + m_bWPDone = true; + + SendWaypoint(); + } + + void JustDied(Unit* /*pKiller*/) override + { + if (m_pInstance) + m_pInstance->SetData(TYPE_VOIDWALKER, DONE); + } + + void SetPosition(uint8 uiPosition, Creature* pLeader) + { + m_uiPosition = uiPosition; + + if (!uiPosition) + m_bIsLeader = true; + else + { + if (pLeader) + m_leaderGuid = pLeader->GetObjectGuid(); + else + m_leaderGuid.Clear(); + } + + Reset(); + } + + uint8 GetPosition() + { + return m_uiPosition; + } + + void SendWaypoint() + { + std::list lVoidwalkerList; + GetCreatureListWithEntryInGrid(lVoidwalkerList, m_creature, NPC_VOIDWALKER, 50.0f); + for (std::list::iterator itr = lVoidwalkerList.begin(); itr != lVoidwalkerList.end(); ++itr) + { + if ((*itr)->IsAlive()) + if (mob_arugal_voidwalkerAI* pVoidwalkerAI = dynamic_cast((*itr)->AI())) + pVoidwalkerAI->ReceiveWaypoint(m_uiCurrentPoint, m_bReverse); + } + } + + void ReceiveWaypoint(uint32 uiNewPoint, bool bReverse) + { + m_uiCurrentPoint = uiNewPoint; + m_bReverse = bReverse; + } + + void EnterEvadeMode() override + { + m_creature->RemoveAllAurasOnEvade(); + m_creature->DeleteThreatList(); + m_creature->CombatStop(true); + m_creature->LoadCreatureAddon(true); + + m_creature->SetLootRecipient(NULL); + + Reset(); + } +}; + +CreatureAI* GetAI_mob_arugal_voidwalker(Creature* pCreature) +{ + return new mob_arugal_voidwalkerAI(pCreature); +} + +/*###### +## boss_arugal +######*/ + +enum +{ + SPELL_VOID_BOLT = 7588, + SPELL_SHADOW_PORT_UPPER_LEDGE = 7587, + SPELL_SHADOW_PORT_SPAWN_LEDGE = 7586, + SPELL_SHADOW_PORT_STAIRS = 7136, + SPELL_ARUGALS_CURSE = 7621, + SPELL_THUNDERSHOCK = 7803, + + YELL_AGGRO = -1033017, + YELL_KILLED_PLAYER = -1033018, + YELL_COMBAT = -1033019, + YELL_FENRUS = -1033013 +}; + +enum ArugalPosition +{ + POSITION_SPAWN_LEDGE = 0, + POSITION_UPPER_LEDGE = 1, + POSITION_STAIRS = 2 +}; + +struct SpawnPoint +{ + float fX, fY, fZ, fO; +}; + +// Cordinates for voidwalker spawns +static const SpawnPoint VWSpawns[] = +{ + // fX fY fZ fO + { -155.352f, 2172.780f, 128.448f, 4.679f}, + { -147.059f, 2163.193f, 128.696f, 0.128f}, + { -148.869f, 2180.859f, 128.448f, 1.814f}, + { -140.203f, 2175.263f, 128.448f, 0.373f} +}; + +// Roughly the height of Fenrus' room, +// Used to tell how he should behave +const float HEIGHT_FENRUS_ROOM = 140.0f; + +struct boss_arugalAI : public ScriptedAI +{ + boss_arugalAI(Creature* pCreature) : ScriptedAI(pCreature) + { + m_pInstance = (ScriptedInstance*)pCreature->GetInstanceData(); + + if (pCreature->GetPositionZ() < HEIGHT_FENRUS_ROOM) + { + m_creature->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE); + m_creature->SetVisibility(VISIBILITY_OFF); + m_bEventMode = true; + } + else + m_bEventMode = false; + + Reset(); + } + + ScriptedInstance* m_pInstance; + ArugalPosition m_posPosition; + uint32 m_uiTeleportTimer, m_uiCurseTimer, m_uiVoidboltTimer, m_uiThundershockTimer, m_uiYellTimer, m_uiSpeechTimer; + uint8 m_uiSpeechStep; + bool m_bAttacking, m_bEventMode; + + void Reset() override + { + m_uiTeleportTimer = urand(22000, 26000); + m_uiCurseTimer = urand(20000, 30000); + m_uiVoidboltTimer = m_uiThundershockTimer = m_uiSpeechTimer = 0; + m_uiYellTimer = urand(32000, 46000); + m_bAttacking = true; + m_posPosition = POSITION_SPAWN_LEDGE; + m_uiSpeechStep = 1; + } + + void Aggro(Unit* pWho) override + { + DoScriptText(YELL_AGGRO, m_creature); + DoCastSpellIfCan(pWho, SPELL_VOID_BOLT); + } + + void KilledUnit(Unit* pVictim) override + { + if (pVictim->GetTypeId() == TYPEID_PLAYER) + DoScriptText(YELL_KILLED_PLAYER, m_creature); + } + + void UpdateAI(const uint32 uiDiff) override + { + if (m_bEventMode) + { + if (!m_uiSpeechStep) + return; + + if (m_uiSpeechTimer < uiDiff) + { + switch (m_uiSpeechStep) + { + case 1: + DoScriptText(YELL_FENRUS, m_creature); + m_creature->SetVisibility(VISIBILITY_ON); + m_uiSpeechTimer = 2000; + break; + case 2: + DoCastSpellIfCan(m_creature, SPELL_FIRE); + m_uiSpeechTimer = 5000; + break; + case 3: + if (m_pInstance) + if (GameObject* pLightning = m_pInstance->GetSingleGameObjectFromStorage(GO_ARUGAL_FOCUS)) + pLightning->Use(m_creature); + + m_uiSpeechTimer = 5000; + break; + case 4: + m_creature->SetVisibility(VISIBILITY_OFF); + m_uiSpeechTimer = 500; + break; + case 5: + { + Creature* pVoidwalker = NULL; + Creature* pLeader = NULL; + + for (uint8 i = 0; i < 4; ++i) + { + pVoidwalker = m_creature->SummonCreature(NPC_VOIDWALKER, VWSpawns[i].fX, + VWSpawns[i].fY, VWSpawns[i].fZ, VWSpawns[i].fO, TEMPSUMMON_DEAD_DESPAWN, 1); + + if (!pVoidwalker) + continue; + + if (!i) + pLeader = pVoidwalker; + + if (mob_arugal_voidwalkerAI* pVoidwalkerAI = dynamic_cast(pVoidwalker->AI())) + pVoidwalkerAI->SetPosition(i, pLeader); + + pVoidwalker = NULL; + } + m_uiSpeechStep = 0; + return; + } + default: + m_uiSpeechStep = 0; + return; + } + ++m_uiSpeechStep; + } + else + m_uiSpeechTimer -= uiDiff; + + return; + } + + // Check if we have a current target + if (!m_creature->SelectHostileTarget() || !m_creature->getVictim()) + return; + + if (GetManaPercent() < 6.0f && !m_bAttacking) + { + if (m_posPosition != POSITION_UPPER_LEDGE) + StartAttacking(); + else if (m_uiTeleportTimer > 2000) + m_uiTeleportTimer = 2000; + + m_bAttacking = true; + } + else if (GetManaPercent() > 12.0f && m_bAttacking) + { + StopAttacking(); + m_bAttacking = false; + } + + if (m_uiYellTimer < uiDiff) + { + DoScriptText(YELL_COMBAT, m_creature); + m_uiYellTimer = urand(34000, 68000); + } + else + m_uiYellTimer -= uiDiff; + + if (m_uiCurseTimer < uiDiff) + { + if (Unit* pTarget = m_creature->SelectAttackingTarget(ATTACKING_TARGET_RANDOM, 1)) + DoCastSpellIfCan(pTarget, SPELL_ARUGALS_CURSE); + + m_uiCurseTimer = urand(20000, 35000); + } + else + m_uiCurseTimer -= uiDiff; + + if (m_uiThundershockTimer < uiDiff) + { + if (GetVictimDistance() < 5.0f) + { + DoCastSpellIfCan(m_creature->getVictim(), SPELL_THUNDERSHOCK); + m_uiThundershockTimer = urand(30200, 38500); + } + } + else + m_uiThundershockTimer -= uiDiff; + + if (m_uiVoidboltTimer < uiDiff) + { + if (!m_bAttacking) + { + DoCastSpellIfCan(m_creature->getVictim(), SPELL_VOID_BOLT); + m_uiVoidboltTimer = urand(2900, 4800); + } + } + else + m_uiVoidboltTimer -= uiDiff; + + if (m_uiTeleportTimer < uiDiff) + { + ArugalPosition posNewPosition; + + if (m_posPosition == POSITION_SPAWN_LEDGE) + posNewPosition = (ArugalPosition)urand(1, 2); + else + { + posNewPosition = (ArugalPosition)urand(0, 1); + + if (m_posPosition == posNewPosition) + posNewPosition = POSITION_STAIRS; + } + + if (m_creature->IsNonMeleeSpellCasted(false)) + m_creature->InterruptNonMeleeSpells(false); + + switch (posNewPosition) + { + case POSITION_SPAWN_LEDGE: + DoCastSpellIfCan(m_creature, SPELL_SHADOW_PORT_SPAWN_LEDGE, true); + break; + case POSITION_UPPER_LEDGE: + DoCastSpellIfCan(m_creature, SPELL_SHADOW_PORT_UPPER_LEDGE, true); + break; + case POSITION_STAIRS: + DoCastSpellIfCan(m_creature, SPELL_SHADOW_PORT_STAIRS, true); + break; + } + + if (GetManaPercent() < 6.0f) + { + if (posNewPosition == POSITION_UPPER_LEDGE) + { + StopAttacking(); + m_uiTeleportTimer = urand(2000, 2200); + } + else + { + StartAttacking(); + m_uiTeleportTimer = urand(48000, 55000); + } + } + else + m_uiTeleportTimer = urand(48000, 55000); + + m_posPosition = posNewPosition; + } + else + m_uiTeleportTimer -= uiDiff; + + if (m_bAttacking) + DoMeleeAttackIfReady(); + } + + void AttackStart(Unit* pWho) override + { + if (!m_bEventMode) + ScriptedAI::AttackStart(pWho); + } + + // Make the code nice and pleasing to the eye + inline float GetManaPercent() + { + return (((float)m_creature->GetPower(POWER_MANA) / (float)m_creature->GetMaxPower(POWER_MANA)) * 100); + } + + inline float GetVictimDistance() + { + return (m_creature->getVictim() ? m_creature->GetDistance2d(m_creature->getVictim()) : 999.9f); + } + + void StopAttacking() + { + if (Unit* victim = m_creature->getVictim()) + m_creature->SendMeleeAttackStop(victim); + + if (m_creature->GetMotionMaster()->GetCurrentMovementGeneratorType() == CHASE_MOTION_TYPE) + { + m_creature->GetMotionMaster()->Clear(false); + m_creature->GetMotionMaster()->MoveIdle(); + m_creature->StopMoving(); + } + } + + void StartAttacking() + { + if (Unit* victim = m_creature->getVictim()) + m_creature->SendMeleeAttackStart(victim); + + if (m_creature->GetMotionMaster()->GetCurrentMovementGeneratorType() == IDLE_MOTION_TYPE) + { + m_creature->GetMotionMaster()->Clear(false); + m_creature->GetMotionMaster()->MoveChase(m_creature->getVictim(), 0.0f, 0.0f); + } + } +}; + +CreatureAI* GetAI_boss_arugal(Creature* pCreature) +{ + return new boss_arugalAI(pCreature); +} + +/*###### +## npc_arugal +######*/ + +enum +{ + SAY_INTRO_1 = -1033009, + SAY_INTRO_2 = -1033010, + SAY_INTRO_3 = -1033011, + SAY_INTRO_4 = -1033012, + + SPELL_SPAWN = 7741, +}; + +struct npc_arugalAI : public ScriptedAI +{ + npc_arugalAI(Creature* pCreature) : ScriptedAI(pCreature) + { + m_pInstance = (ScriptedInstance*)pCreature->GetInstanceData(); + Reset(); + } + + uint32 m_uiSpeechTimer; + uint8 m_uiSpeechStep; + ScriptedInstance* m_pInstance; + + void Reset() override + { + m_uiSpeechTimer = 0; + m_uiSpeechStep = 0; + + m_creature->SetVisibility(VISIBILITY_OFF); + + if (m_pInstance && m_pInstance->GetData(TYPE_INTRO) == NOT_STARTED) + m_uiSpeechStep = 1; + } + + void UpdateAI(const uint32 uiDiff) override + { + if (!m_uiSpeechStep) + return; + + if (m_uiSpeechTimer < uiDiff) + { + switch (m_uiSpeechStep) + { + case 1: + m_creature->SetVisibility(VISIBILITY_ON); + m_uiSpeechTimer = 500; + break; + case 2: + DoCastSpellIfCan(m_creature, SPELL_SPAWN); + m_uiSpeechTimer = 2000; + break; + case 3: + // Make him die + if (Creature* pVincent = GetClosestCreatureWithEntry(m_creature, NPC_VINCENT, 20.0f)) + pVincent->SetStandState(UNIT_STAND_STATE_DEAD); + + m_uiSpeechTimer = 10000; + break; + case 4: + DoScriptText(SAY_INTRO_1, m_creature); + // m_creature->HandleEmote(EMOTE_ONESHOT_TALK); + m_uiSpeechTimer = 1750; + break; + case 5: + m_creature->HandleEmote(EMOTE_ONESHOT_POINT); + m_uiSpeechTimer = 1750; + break; + case 6: + DoScriptText(SAY_INTRO_2, m_creature); + m_uiSpeechTimer = 1750; + break; + case 7: + m_creature->HandleEmote(EMOTE_ONESHOT_EXCLAMATION); + m_uiSpeechTimer = 1750; + break; + case 8: + // m_creature->HandleEmote(EMOTE_ONESHOT_TALK); + DoScriptText(SAY_INTRO_3, m_creature); + m_uiSpeechTimer = 1750; + break; + case 9: + m_creature->HandleEmote(EMOTE_ONESHOT_LAUGH); + m_uiSpeechTimer = 1750; + break; + case 10: + DoScriptText(SAY_INTRO_4, m_creature); + m_uiSpeechTimer = 2000; + break; + case 11: + DoCastSpellIfCan(m_creature, SPELL_SPAWN); + m_uiSpeechTimer = 500; + break; + case 12: + if (m_pInstance) + m_pInstance->SetData(TYPE_INTRO, DONE); + + m_creature->SetVisibility(VISIBILITY_OFF); + m_uiSpeechStep = 0; + return; + default: + m_uiSpeechStep = 0; + return; + } + ++m_uiSpeechStep; + } + else + m_uiSpeechTimer -= uiDiff; + } + + void AttackStart(Unit* /*who*/) override { } +}; + +CreatureAI* GetAI_npc_arugal(Creature* pCreature) +{ + return new npc_arugalAI(pCreature); +} + +/*###### +## npc_deathstalker_vincent +######*/ + +enum +{ + SAY_VINCENT_DIE = -1033016, + + FACTION_FRIENDLY = 35 +}; + +struct npc_deathstalker_vincentAI : public ScriptedAI +{ + npc_deathstalker_vincentAI(Creature* pCreature) : ScriptedAI(pCreature) + { + m_pInstance = (ScriptedInstance*)pCreature->GetInstanceData(); + Reset(); + } + + ScriptedInstance* m_pInstance; + + void Reset() override + { + if (m_pInstance && m_pInstance->GetData(TYPE_INTRO) == DONE && !m_creature->GetByteValue(UNIT_FIELD_BYTES_1, 0)) + m_creature->SetStandState(UNIT_STAND_STATE_DEAD); + } + + void DamageTaken(Unit* pDoneBy, uint32& uiDamage) override + { + if (pDoneBy) + { + m_creature->SetInCombatWith(pDoneBy); + pDoneBy->SetInCombatWith(m_creature); + } + + if (m_creature->getStandState()) + m_creature->SetStandState(UNIT_STAND_STATE_STAND); + + if (uiDamage >= m_creature->GetHealth()) + { + m_creature->GetHealth() > 1 ? uiDamage = m_creature->GetHealth() - 1 : uiDamage = 0; + m_creature->SetFactionTemporary(FACTION_FRIENDLY, TEMPFACTION_NONE); + EnterEvadeMode(); + DoScriptText(SAY_VINCENT_DIE, m_creature); + } + } + + void UpdateAI(const uint32 uiDiff) override + { + if (m_creature->IsInCombat() && m_creature->getFaction() == FACTION_FRIENDLY) + EnterEvadeMode(); + + ScriptedAI::UpdateAI(uiDiff); + } + + void EnterEvadeMode() override + { + m_creature->RemoveAllAuras(); + m_creature->DeleteThreatList(); + m_creature->CombatStop(true); + m_creature->LoadCreatureAddon(true); + m_creature->SetLootRecipient(NULL); + Reset(); + } +}; + +CreatureAI* GetAI_npc_deathstalker_vincent(Creature* pCreature) +{ + return new npc_deathstalker_vincentAI(pCreature); +} + +void AddSC_shadowfang_keep() +{ + Script* pNewScript; + + pNewScript = new Script; + pNewScript->Name = "npc_shadowfang_prisoner"; + pNewScript->pGossipHello = &GossipHello_npc_shadowfang_prisoner; + pNewScript->pGossipSelect = &GossipSelect_npc_shadowfang_prisoner; + pNewScript->GetAI = &GetAI_npc_shadowfang_prisoner; + pNewScript->RegisterSelf(); + + pNewScript = new Script; + pNewScript->Name = "mob_arugal_voidwalker"; + pNewScript->GetAI = &GetAI_mob_arugal_voidwalker; + pNewScript->RegisterSelf(); + + pNewScript = new Script; + pNewScript->Name = "npc_arugal"; + pNewScript->GetAI = &GetAI_npc_arugal; + pNewScript->RegisterSelf(); + + pNewScript = new Script; + pNewScript->Name = "boss_arugal"; + pNewScript->GetAI = &GetAI_boss_arugal; + pNewScript->RegisterSelf(); + + pNewScript = new Script; + pNewScript->Name = "npc_deathstalker_vincent"; + pNewScript->GetAI = &GetAI_npc_deathstalker_vincent; + pNewScript->RegisterSelf(); +} diff --git a/src/modules/SD2/scripts/eastern_kingdoms/shadowfang_keep/shadowfang_keep.h b/src/modules/SD2/scripts/eastern_kingdoms/shadowfang_keep/shadowfang_keep.h new file mode 100644 index 000000000..0f500950e --- /dev/null +++ b/src/modules/SD2/scripts/eastern_kingdoms/shadowfang_keep/shadowfang_keep.h @@ -0,0 +1,76 @@ +/* Copyright (C) 2006 - 2013 ScriptDev2 + * This program is free software licensed under GPL version 2 + * Please see the included DOCS/LICENSE.TXT for more information */ + +#ifndef DEF_SHADOWFANG_H +#define DEF_SHADOWFANG_H + +enum +{ + MAX_ENCOUNTER = 7, + + TYPE_FREE_NPC = 1, + TYPE_RETHILGORE = 2, + TYPE_FENRUS = 3, + TYPE_NANDOS = 4, + TYPE_INTRO = 5, + TYPE_VOIDWALKER = 6, + TYPE_APOTHECARY = 7, + + SAY_BOSS_DIE_AD = -1033007, + SAY_BOSS_DIE_AS = -1033008, + + NPC_ASH = 3850, + NPC_ADA = 3849, + // NPC_ARUGAL = 10000, //"Arugal" says intro text, not used + NPC_ARCHMAGE_ARUGAL = 4275, //"Archmage Arugal" does Fenrus event + NPC_FENRUS = 4274, // used to summon Arugal in Fenrus event + NPC_VINCENT = 4444, // Vincent should be "dead" is Arugal is done the intro already + + NPC_HUMMEL = 36296, // Love is in the Air event + NPC_FRYE = 36272, + NPC_BAXTER = 36565, + NPC_VALENTINE_BOSS_MGR = 36643, // controller npc for the apothecary event + NPC_APOTHECARY_GENERATOR = 36212, // the npc which summons the crazed apothecary + + 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 + GO_ARUGAL_FOCUS = 18973, // this generates the lightning visual in the Fenrus event + + GO_APOTHECARE_VIALS = 190678, // move position for Baxter + GO_CHEMISTRY_SET = 200335, // move position for Frye + + SAY_HUMMEL_DEATH = -1033025, + + MAX_APOTHECARY = 3, +}; + +class instance_shadowfang_keep : public ScriptedInstance +{ + public: + instance_shadowfang_keep(Map* pMap); + + void Initialize() override; + + void OnCreatureCreate(Creature* pCreature) override; + void OnObjectCreate(GameObject* pGo) override; + void DoSpeech(); + + void OnCreatureDeath(Creature* pCreature) override; + void OnCreatureEvade(Creature* pCreature); + + void SetData(uint32 uiType, uint32 uiData) override; + uint32 GetData(uint32 uiType) const override; + + const char* Save() const override { return m_strInstData.c_str(); } + void Load(const char* chrIn) override; + + private: + uint32 m_auiEncounter[MAX_ENCOUNTER]; + std::string m_strInstData; + + uint8 m_uiApothecaryDead; +}; + +#endif diff --git a/src/modules/SD2/scripts/eastern_kingdoms/silvermoon_city.cpp b/src/modules/SD2/scripts/eastern_kingdoms/silvermoon_city.cpp new file mode 100644 index 000000000..692860a70 --- /dev/null +++ b/src/modules/SD2/scripts/eastern_kingdoms/silvermoon_city.cpp @@ -0,0 +1,40 @@ +/** + * ScriptDev2 is an extension for mangos providing enhanced features for + * area triggers, creatures, game objects, instances, items, and spells beyond + * the default database scripting in mangos. + * + * Copyright (C) 2006-2013 ScriptDev2 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * World of Warcraft, and all World of Warcraft or Warcraft art, images, + * and lore are copyrighted by Blizzard Entertainment, Inc. + */ + +/* ScriptData +SDName: Silvermoon_City +SD%Complete: 0 +SDComment: Placeholder +SDCategory: Silvermoon City +EndScriptData */ + +/* ContentData +EndContentData */ + +#include "precompiled.h" + +void AddSC_silvermoon_city() +{ +} diff --git a/src/modules/SD2/scripts/eastern_kingdoms/silverpine_forest.cpp b/src/modules/SD2/scripts/eastern_kingdoms/silverpine_forest.cpp new file mode 100644 index 000000000..5ba2bc6c5 --- /dev/null +++ b/src/modules/SD2/scripts/eastern_kingdoms/silverpine_forest.cpp @@ -0,0 +1,344 @@ +/** + * ScriptDev2 is an extension for mangos providing enhanced features for + * area triggers, creatures, game objects, instances, items, and spells beyond + * the default database scripting in mangos. + * + * Copyright (C) 2006-2013 ScriptDev2 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * World of Warcraft, and all World of Warcraft or Warcraft art, images, + * and lore are copyrighted by Blizzard Entertainment, Inc. + */ + +/** + * ScriptData + * SDName: Silverpine_Forest + * SD%Complete: 100 + * SDComment: Quest support: 435, 452. + * SDCategory: Silverpine Forest + * EndScriptData + */ + +/** + * ContentData + * npc_deathstalker_erland + * npc_deathstalker_faerleia + * EndContentData + */ + +#include "precompiled.h" +#include "escort_ai.h" + +/*##### +## npc_deathstalker_erland +#####*/ + +enum +{ + SAY_START_1 = -1000306, + SAY_START_2 = -1000307, + SAY_AGGRO_1 = -1000308, + SAY_AGGRO_2 = -1000309, + SAY_AGGRO_3 = -1000310, + SAY_PROGRESS = -1000311, + SAY_END = -1000312, + SAY_RANE = -1000313, + SAY_RANE_REPLY = -1000314, + SAY_CHECK_NEXT = -1000315, + SAY_QUINN = -1000316, + SAY_QUINN_REPLY = -1000317, + SAY_BYE = -1000318, + + QUEST_ERLAND = 435, + NPC_RANE = 1950, + NPC_QUINN = 1951 +}; + +struct npc_deathstalker_erlandAI : public npc_escortAI +{ + npc_deathstalker_erlandAI(Creature* pCreature) : npc_escortAI(pCreature) { Reset(); } + + void WaypointReached(uint32 i) override + { + Player* pPlayer = GetPlayerForEscort(); + + if (!pPlayer) + return; + + switch (i) + { + case 0: + DoScriptText(SAY_START_2, m_creature, pPlayer); + break; + case 13: + DoScriptText(SAY_END, m_creature, pPlayer); + pPlayer->GroupEventHappens(QUEST_ERLAND, m_creature); + break; + case 14: + if (Creature* pRane = GetClosestCreatureWithEntry(m_creature, NPC_RANE, 45.0f)) + DoScriptText(SAY_RANE, pRane, m_creature); + break; + case 15: + DoScriptText(SAY_RANE_REPLY, m_creature); + break; + case 16: + DoScriptText(SAY_CHECK_NEXT, m_creature); + break; + case 24: + DoScriptText(SAY_QUINN, m_creature); + break; + case 25: + if (Creature* pQuinn = GetClosestCreatureWithEntry(m_creature, NPC_QUINN, 45.0f)) + DoScriptText(SAY_QUINN_REPLY, pQuinn, m_creature); + break; + case 26: + DoScriptText(SAY_BYE, m_creature); + break; + } + } + + + + void Reset() override {} + + void Aggro(Unit* who) override + { + switch (urand(0, 2)) + { + case 0: DoScriptText(SAY_AGGRO_1, m_creature, who); break; + case 1: DoScriptText(SAY_AGGRO_2, m_creature, who); break; + case 2: DoScriptText(SAY_AGGRO_3, m_creature, who); break; + } + } +}; + +bool QuestAccept_npc_deathstalker_erland(Player* pPlayer, Creature* pCreature, const Quest* pQuest) +{ + if (pQuest->GetQuestId() == QUEST_ERLAND) + { + DoScriptText(SAY_START_1, pCreature); + + if (npc_deathstalker_erlandAI* pEscortAI = dynamic_cast(pCreature->AI())) + { + pEscortAI->Start(false, pPlayer, pQuest); + } + } + return true; +} + +CreatureAI* GetAI_npc_deathstalker_erland(Creature* pCreature) +{ + return new npc_deathstalker_erlandAI(pCreature); +} + +/*##### +## npc_deathstalker_faerleia +#####*/ + +enum +{ + QUEST_PYREWOOD_AMBUSH = 452, + + // cast it after every wave + SPELL_DRINK_POTION = 3359, + + SAY_START = -1000553, + SAY_COMPLETED = -1000554, + + // 1st wave + NPC_COUNCILMAN_SMITHERS = 2060, + // 2nd wave + NPC_COUNCILMAN_THATHER = 2061, + NPC_COUNCILMAN_HENDRICKS = 2062, + // 3rd wave + NPC_COUNCILMAN_WILHELM = 2063, + NPC_COUNCILMAN_HARTIN = 2064, + NPC_COUNCILMAN_HIGARTH = 2066, + // final wave + NPC_COUNCILMAN_COOPER = 2065, + NPC_COUNCILMAN_BRUNSWICK = 2067, + NPC_LORD_MAYOR_MORRISON = 2068 +}; + +struct SpawnPoint +{ + float fX; + float fY; + float fZ; + float fO; +}; + +SpawnPoint SpawnPoints[] = +{ + { -397.45f, 1509.56f, 18.87f, 4.73f}, + { -398.35f, 1510.75f, 18.87f, 4.76f}, + { -396.41f, 1511.06f, 18.87f, 4.74f} +}; + +static float m_afMoveCoords[] = { -410.69f, 1498.04f, 19.77f}; + + +struct npc_deathstalker_faerleiaAI : public ScriptedAI +{ + npc_deathstalker_faerleiaAI(Creature* pCreature) : ScriptedAI(pCreature) {Reset();} + + void Reset() override {} + + ObjectGuid m_playerGuid; + uint32 m_uiWaveTimer; + uint32 m_uiSummonCount; + uint8 m_uiWaveCount; + bool m_bEventStarted; + + + void StartEvent(Player* pPlayer) + { + m_creature->RemoveFlag(UNIT_NPC_FLAGS, UNIT_NPC_FLAG_QUESTGIVER); + + m_playerGuid = pPlayer->GetObjectGuid(); + m_bEventStarted = true; + m_uiWaveTimer = 10000; + m_uiSummonCount = 0; + m_uiWaveCount = 0; + } + + void FinishEvent() + { + m_playerGuid.Clear(); + m_bEventStarted = false; + m_creature->SetFlag(UNIT_NPC_FLAGS, UNIT_NPC_FLAG_QUESTGIVER); + } + + void JustDied(Unit* /*pKiller*/) override + { + if (Player* pPlayer = m_creature->GetMap()->GetPlayer(m_playerGuid)) + pPlayer->SendQuestFailed(QUEST_PYREWOOD_AMBUSH); + + FinishEvent(); + } + + void JustSummoned(Creature* pSummoned) override + { + ++m_uiSummonCount; + + // put them on correct waypoints later on + float fX, fY, fZ; + pSummoned->GetRandomPoint(m_afMoveCoords[0], m_afMoveCoords[1], m_afMoveCoords[2], 10.0f, fX, fY, fZ); + pSummoned->GetMotionMaster()->MovePoint(0, fX, fY, fZ); + } + + void SummonedCreatureJustDied(Creature* /*pKilled*/) override + { + --m_uiSummonCount; + + if (!m_uiSummonCount) + { + DoCastSpellIfCan(m_creature, SPELL_DRINK_POTION); + + // final wave + if (m_uiWaveCount == 4) + { + DoScriptText(SAY_COMPLETED, m_creature); + + if (Player* pPlayer = m_creature->GetMap()->GetPlayer(m_playerGuid)) + pPlayer->GroupEventHappens(QUEST_PYREWOOD_AMBUSH, m_creature); + + FinishEvent(); + } + + } + } + + void UpdateAI(const uint32 uiDiff) override + { + if (m_bEventStarted && !m_uiSummonCount) + { + if (m_uiWaveTimer < uiDiff) + { + switch (m_uiWaveCount) + { + case 0: + m_creature->SummonCreature(NPC_COUNCILMAN_SMITHERS, SpawnPoints[1].fX, SpawnPoints[1].fY, SpawnPoints[1].fZ, SpawnPoints[1].fO, TEMPSUMMON_CORPSE_TIMED_DESPAWN, 20000); + m_uiWaveTimer = 10000; + break; + case 1: + m_creature->SummonCreature(NPC_COUNCILMAN_THATHER, SpawnPoints[2].fX, SpawnPoints[2].fY, SpawnPoints[2].fZ, SpawnPoints[2].fO, TEMPSUMMON_CORPSE_TIMED_DESPAWN, 20000); + m_creature->SummonCreature(NPC_COUNCILMAN_HENDRICKS, SpawnPoints[1].fX, SpawnPoints[1].fY, SpawnPoints[1].fZ, SpawnPoints[1].fO, TEMPSUMMON_CORPSE_TIMED_DESPAWN, 20000); + m_uiWaveTimer = 10000; + break; + case 2: + m_creature->SummonCreature(NPC_COUNCILMAN_WILHELM, SpawnPoints[1].fX, SpawnPoints[1].fY, SpawnPoints[1].fZ, SpawnPoints[1].fO, TEMPSUMMON_CORPSE_TIMED_DESPAWN, 20000); + m_creature->SummonCreature(NPC_COUNCILMAN_HARTIN, SpawnPoints[0].fX, SpawnPoints[0].fY, SpawnPoints[0].fZ, SpawnPoints[0].fO, TEMPSUMMON_CORPSE_TIMED_DESPAWN, 20000); + m_creature->SummonCreature(NPC_COUNCILMAN_HIGARTH, SpawnPoints[2].fX, SpawnPoints[2].fY, SpawnPoints[2].fZ, SpawnPoints[2].fO, TEMPSUMMON_CORPSE_TIMED_DESPAWN, 20000); + m_uiWaveTimer = 8000; + break; + case 3: + m_creature->SummonCreature(NPC_COUNCILMAN_COOPER, SpawnPoints[1].fX, SpawnPoints[1].fY, SpawnPoints[1].fZ, SpawnPoints[1].fO, TEMPSUMMON_CORPSE_TIMED_DESPAWN, 20000); + m_creature->SummonCreature(NPC_COUNCILMAN_BRUNSWICK, SpawnPoints[2].fX, SpawnPoints[2].fY, SpawnPoints[2].fZ, SpawnPoints[2].fO, TEMPSUMMON_CORPSE_TIMED_DESPAWN, 20000); + m_creature->SummonCreature(NPC_LORD_MAYOR_MORRISON, SpawnPoints[0].fX, SpawnPoints[0].fY, SpawnPoints[0].fZ, SpawnPoints[0].fO, TEMPSUMMON_CORPSE_TIMED_DESPAWN, 20000); + break; + } + + ++m_uiWaveCount; + } + else + { + m_uiWaveTimer -= uiDiff; + } + } + + if (!m_creature->SelectHostileTarget() || !m_creature->getVictim()) + { + return; + } + + DoMeleeAttackIfReady(); + } +}; + +bool QuestAccept_npc_deathstalker_faerleia(Player* pPlayer, Creature* pCreature, const Quest* pQuest) +{ + if (pQuest->GetQuestId() == QUEST_PYREWOOD_AMBUSH) + { + DoScriptText(SAY_START, pCreature, pPlayer); + + if (npc_deathstalker_faerleiaAI* pFaerleiaAI = dynamic_cast(pCreature->AI())) + pFaerleiaAI->StartEvent(pPlayer); + } + return true; +} + +CreatureAI* GetAI_npc_deathstalker_faerleia(Creature* pCreature) +{ + return new npc_deathstalker_faerleiaAI(pCreature); +} + +void AddSC_silverpine_forest() +{ + Script* pNewScript; + + pNewScript = new Script; + pNewScript->Name = "npc_deathstalker_erland"; + pNewScript->GetAI = &GetAI_npc_deathstalker_erland; + pNewScript->pQuestAcceptNPC = &QuestAccept_npc_deathstalker_erland; + pNewScript->RegisterSelf(); + + pNewScript = new Script; + pNewScript->Name = "npc_deathstalker_faerleia"; + pNewScript->GetAI = &GetAI_npc_deathstalker_faerleia; + pNewScript->pQuestAcceptNPC = &QuestAccept_npc_deathstalker_faerleia; + pNewScript->RegisterSelf(); +} diff --git a/src/modules/SD2/scripts/eastern_kingdoms/stormwind_city.cpp b/src/modules/SD2/scripts/eastern_kingdoms/stormwind_city.cpp new file mode 100644 index 000000000..d3d9804ad --- /dev/null +++ b/src/modules/SD2/scripts/eastern_kingdoms/stormwind_city.cpp @@ -0,0 +1,1171 @@ +/** + * ScriptDev2 is an extension for mangos providing enhanced features for + * area triggers, creatures, game objects, instances, items, and spells beyond + * the default database scripting in mangos. + * + * Copyright (C) 2006-2013 ScriptDev2 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * World of Warcraft, and all World of Warcraft or Warcraft art, images, + * and lore are copyrighted by Blizzard Entertainment, Inc. + */ + +/** + * ScriptData + * SDName: Stormwind_City + * SD%Complete: 100 + * SDComment: Quest support: 1640, 1447, 4185, 6402, 6403. + * SDCategory: Stormwind City + * EndScriptData + */ + +/** + * ContentData + * npc_bartleby + * npc_dashel_stonefist + * npc_lady_katrana_prestor + * npc_squire_rowe + * npc_reginald_windsor + * EndContentData + */ + +#include "precompiled.h" +#include "../world/world_map_scripts.h" +#include "escort_ai.h" + +/*###### +## npc_bartleby +######*/ + +enum +{ + FACTION_ENEMY = 168, + QUEST_BEAT = 1640 +}; + +struct npc_bartlebyAI : public ScriptedAI +{ + npc_bartlebyAI(Creature* pCreature) : ScriptedAI(pCreature) + { + Reset(); + } + + void Reset() override {} + + void AttackedBy(Unit* pAttacker) override + { + if (m_creature->getVictim()) + { + return; + } + + if (m_creature->IsFriendlyTo(pAttacker)) + { + return; + } + + AttackStart(pAttacker); + } + + void DamageTaken(Unit* pDoneBy, uint32& uiDamage) override + { + if (uiDamage > m_creature->GetHealth() || ((m_creature->GetHealth() - uiDamage) * 100 / m_creature->GetMaxHealth() < 15)) + { + uiDamage = 0; + + if (pDoneBy->GetTypeId() == TYPEID_PLAYER) + { + ((Player*)pDoneBy)->AreaExploredOrEventHappens(QUEST_BEAT); + } + + EnterEvadeMode(); + } + } +}; + +bool QuestAccept_npc_bartleby(Player* pPlayer, Creature* pCreature, const Quest* pQuest) +{ + if (pQuest->GetQuestId() == QUEST_BEAT) + { + pCreature->SetFactionTemporary(FACTION_ENEMY, TEMPFACTION_RESTORE_RESPAWN | TEMPFACTION_RESTORE_COMBAT_STOP); + pCreature->AI()->AttackStart(pPlayer); + } + return true; +} + +CreatureAI* GetAI_npc_bartleby(Creature* pCreature) +{ + return new npc_bartlebyAI(pCreature); +} + +/*###### +## npc_dashel_stonefist +######*/ + +enum +{ + QUEST_MISSING_DIPLO_PT8 = 1447, + FACTION_HOSTILE = 168 +}; + +struct npc_dashel_stonefistAI : public ScriptedAI +{ + npc_dashel_stonefistAI(Creature* pCreature) : ScriptedAI(pCreature) + { + Reset(); + } + + void Reset() override {} + + void AttackedBy(Unit* pAttacker) override + { + if (m_creature->getVictim()) + { + return; + } + + if (m_creature->IsFriendlyTo(pAttacker)) + { + return; + } + + AttackStart(pAttacker); + } + + void DamageTaken(Unit* pDoneBy, uint32& uiDamage) override + { + if (uiDamage > m_creature->GetHealth() || ((m_creature->GetHealth() - uiDamage) * 100 / m_creature->GetMaxHealth() < 15)) + { + uiDamage = 0; + + if (pDoneBy->GetTypeId() == TYPEID_PLAYER) + { + ((Player*)pDoneBy)->AreaExploredOrEventHappens(QUEST_MISSING_DIPLO_PT8); + } + + EnterEvadeMode(); + } + } +}; + +bool QuestAccept_npc_dashel_stonefist(Player* pPlayer, Creature* pCreature, const Quest* pQuest) +{ + if (pQuest->GetQuestId() == QUEST_MISSING_DIPLO_PT8) + { + pCreature->SetFactionTemporary(FACTION_HOSTILE, TEMPFACTION_RESTORE_COMBAT_STOP | TEMPFACTION_RESTORE_RESPAWN); + pCreature->AI()->AttackStart(pPlayer); + } + 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->GetObjectGuid()); + } + + 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->GetObjectGuid()); + + return true; +} + +bool GossipSelect_npc_lady_katrana_prestor(Player* pPlayer, Creature* pCreature, uint32 /*uiSender*/, uint32 uiAction) +{ + switch (uiAction) + { + case GOSSIP_ACTION_INFO_DEF: + pPlayer->ADD_GOSSIP_ITEM(GOSSIP_ICON_CHAT, GOSSIP_ITEM_KAT_2, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 1); + pPlayer->SEND_GOSSIP_MENU(2694, pCreature->GetObjectGuid()); + break; + case GOSSIP_ACTION_INFO_DEF+1: + pPlayer->ADD_GOSSIP_ITEM(GOSSIP_ICON_CHAT, GOSSIP_ITEM_KAT_3, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 2); + pPlayer->SEND_GOSSIP_MENU(2695, pCreature->GetObjectGuid()); + break; + case GOSSIP_ACTION_INFO_DEF+2: + pPlayer->ADD_GOSSIP_ITEM(GOSSIP_ICON_CHAT, GOSSIP_ITEM_KAT_4, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 3); + pPlayer->SEND_GOSSIP_MENU(2696, pCreature->GetObjectGuid()); + break; + case GOSSIP_ACTION_INFO_DEF+3: + pPlayer->CLOSE_GOSSIP_MENU(); + pPlayer->AreaExploredOrEventHappens(4185); + break; + } + return true; +} + +/*###### +## npc_squire_rowe +######*/ + +enum +{ + SAY_SIGNAL_SENT = -1000822, + SAY_DISMOUNT = -1000823, + SAY_WELCOME = -1000824, + + GOSSIP_ITEM_WINDSOR = -3000106, + + GOSSIP_TEXT_ID_DEFAULT = 9063, + GOSSIP_TEXT_ID_PROGRESS = 9064, + GOSSIP_TEXT_ID_START = 9065, + + NPC_WINDSOR_MOUNT = 12581, + + QUEST_STORMWIND_RENDEZVOUS = 6402, + QUEST_THE_GREAT_MASQUERADE = 6403, +}; + +static const DialogueEntry aIntroDialogue[] = +{ + {NPC_WINDSOR, 0, 3000}, // wait + {NPC_WINDSOR_MOUNT, 0, 1000}, // summon horse + {SAY_DISMOUNT, NPC_WINDSOR, 2000}, + {QUEST_STORMWIND_RENDEZVOUS, 0, 2000}, // face player + {QUEST_THE_GREAT_MASQUERADE, 0, 0}, // say intro to player + {0, 0, 0}, +}; + +static const float aWindsorSpawnLoc[3] = { -9145.68f, 373.79f, 90.64f}; +static const float aWindsorMoveLoc[3] = { -9050.39f, 443.55f, 93.05f}; + +struct npc_squire_roweAI : public npc_escortAI, private DialogueHelper +{ + npc_squire_roweAI(Creature* m_creature) : npc_escortAI(m_creature), + DialogueHelper(aIntroDialogue) + { + m_bIsEventInProgress = false; + Reset(); + } + + bool m_bIsEventInProgress; + + ObjectGuid m_windsorGuid; + ObjectGuid m_horseGuid; + + void Reset() override { } + + void JustSummoned(Creature* pSummoned) override + { + if (pSummoned->GetEntry() == NPC_WINDSOR) + { + pSummoned->SetWalk(false); + pSummoned->GetMotionMaster()->MovePoint(1, aWindsorMoveLoc[0], aWindsorMoveLoc[1], aWindsorMoveLoc[2]); + + m_windsorGuid = pSummoned->GetObjectGuid(); + m_bIsEventInProgress = true; + } + else if (pSummoned->GetEntry() == NPC_WINDSOR_MOUNT) + { + m_horseGuid = pSummoned->GetObjectGuid(); + } + } + + void SummonedCreatureDespawn(Creature* pSummoned) override + { + if (pSummoned->GetEntry() == NPC_WINDSOR) + { + m_windsorGuid.Clear(); + m_bIsEventInProgress = false; + } + } + + void SummonedMovementInform(Creature* pSummoned, uint32 uiMotionType, uint32 uiPointId) override + { + if (uiMotionType != POINT_MOTION_TYPE || !uiPointId || pSummoned->GetEntry() != NPC_WINDSOR) + { + return; + } + + // Summoned npc has escort and this can trigger twice if escort state is not checked + if (uiPointId && HasEscortState(STATE_ESCORT_PAUSED)) + { + StartNextDialogueText(NPC_WINDSOR); + } + } + + void WaypointReached(uint32 uiPointId) override + { + switch (uiPointId) + { + case 2: + m_creature->SetStandState(UNIT_STAND_STATE_KNEEL); + break; + case 3: + m_creature->SetStandState(UNIT_STAND_STATE_STAND); + m_creature->SummonCreature(NPC_WINDSOR, aWindsorSpawnLoc[0], aWindsorSpawnLoc[1], aWindsorSpawnLoc[2], 0, TEMPSUMMON_CORPSE_DESPAWN, 0); + break; + case 6: + DoScriptText(SAY_SIGNAL_SENT, m_creature); + m_creature->SetFacingTo(2.15f); + SetEscortPaused(true); + break; + } + } + + void JustDidDialogueStep(int32 iEntry) override + { + switch (iEntry) + { + case NPC_WINDSOR_MOUNT: + { + if (Creature* pWindsor = m_creature->GetMap()->GetCreature(m_windsorGuid)) + { + pWindsor->Unmount(); + m_creature->SummonCreature(NPC_WINDSOR_MOUNT, pWindsor->GetPositionX() - 1.0f, pWindsor->GetPositionY() + 1.0f, pWindsor->GetPositionZ(), pWindsor->GetOrientation(), TEMPSUMMON_TIMED_DESPAWN, 30000); + } + break; + } + case SAY_DISMOUNT: + { + if (Creature* pHorse = m_creature->GetMap()->GetCreature(m_horseGuid)) + { + pHorse->SetWalk(false); + pHorse->GetMotionMaster()->MovePoint(1, aWindsorSpawnLoc[0], aWindsorSpawnLoc[1], aWindsorSpawnLoc[2]); + } + break; + } + case QUEST_STORMWIND_RENDEZVOUS: + { + Creature* pWindsor = m_creature->GetMap()->GetCreature(m_windsorGuid); + Player* pPlayer = GetPlayerForEscort(); + if (!pWindsor || !pPlayer) + { + break; + } + + pWindsor->SetFacingToObject(pPlayer); + break; + } + case QUEST_THE_GREAT_MASQUERADE: + { + Creature* pWindsor = m_creature->GetMap()->GetCreature(m_windsorGuid); + Player* pPlayer = GetPlayerForEscort(); + if (!pWindsor || !pPlayer) + { + break; + } + + DoScriptText(SAY_WELCOME, pWindsor, pPlayer); + // Allow players to finish quest and also finish the escort + pWindsor->SetFlag(UNIT_NPC_FLAGS, UNIT_NPC_FLAG_QUESTGIVER); + SetEscortPaused(false); + break; + } + } + } + + Creature* GetSpeakerByEntry(uint32 uiEntry) override + { + if (uiEntry == NPC_WINDSOR) + { + return m_creature->GetMap()->GetCreature(m_windsorGuid); + } + + return NULL; + } + + // Check if the event is already running + bool IsStormwindQuestActive() { return m_bIsEventInProgress; } + + void UpdateEscortAI(const uint32 uiDiff) { DialogueUpdate(uiDiff); } +}; + +CreatureAI* GetAI_npc_squire_rowe(Creature* pCreature) +{ + return new npc_squire_roweAI(pCreature); +} + +bool GossipHello_npc_squire_rowe(Player* pPlayer, Creature* pCreature) +{ + // Allow gossip if quest 6402 is completed but not yet rewarded or 6402 is rewarded but 6403 isn't yet completed + if ((pPlayer->GetQuestStatus(QUEST_STORMWIND_RENDEZVOUS) == QUEST_STATUS_COMPLETE && !pPlayer->GetQuestRewardStatus(QUEST_STORMWIND_RENDEZVOUS)) || + (pPlayer->GetQuestRewardStatus(QUEST_STORMWIND_RENDEZVOUS) && pPlayer->GetQuestStatus(QUEST_THE_GREAT_MASQUERADE) != QUEST_STATUS_COMPLETE)) + { + bool bIsEventInProgress = true; + + // Check if event is already in progress + if (npc_squire_roweAI* pRoweAI = dynamic_cast(pCreature->AI())) + { + bIsEventInProgress = pRoweAI->IsStormwindQuestActive(); + } + + // If event is already in progress, then inform the player to wait + if (bIsEventInProgress) + { + pPlayer->SEND_GOSSIP_MENU(GOSSIP_TEXT_ID_PROGRESS, pCreature->GetObjectGuid()); + } + else + { + pPlayer->ADD_GOSSIP_ITEM_ID(GOSSIP_ICON_CHAT, GOSSIP_ITEM_WINDSOR, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 1); + pPlayer->SEND_GOSSIP_MENU(GOSSIP_TEXT_ID_START, pCreature->GetObjectGuid()); + } + } + else + { + pPlayer->SEND_GOSSIP_MENU(GOSSIP_TEXT_ID_DEFAULT, pCreature->GetObjectGuid()); + } + + return true; +} + +bool GossipSelect_npc_squire_rowe(Player* pPlayer, Creature* pCreature, uint32 /*uiSender*/, uint32 uiAction) +{ + if (uiAction == GOSSIP_ACTION_INFO_DEF + 1) + { + if (npc_squire_roweAI* pRoweAI = dynamic_cast(pCreature->AI())) + { + pRoweAI->Start(true, pPlayer, 0, true, false); + } + + pPlayer->CLOSE_GOSSIP_MENU(); + } + + return true; +} + +/*###### +## npc_reginald_windsor +######*/ + +enum +{ + SAY_WINDSOR_QUEST_ACCEPT = -1000825, + SAY_WINDSOR_GET_READY = -1000826, + SAY_PRESTOR_SIEZE = -1000827, + + SAY_JON_DIALOGUE_1 = -1000828, + SAY_WINDSOR_DIALOGUE_2 = -1000829, + SAY_WINDSOR_DIALOGUE_3 = -1000830, + SAY_JON_DIALOGUE_4 = -1000832, + SAY_JON_DIALOGUE_5 = -1000833, + SAY_WINDSOR_DIALOGUE_6 = -1000834, + SAY_WINDSOR_DIALOGUE_7 = -1000835, + SAY_JON_DIALOGUE_8 = -1000836, + SAY_JON_DIALOGUE_9 = -1000837, + SAY_JON_DIALOGUE_10 = -1000838, + SAY_JON_DIALOGUE_11 = -1000839, + SAY_WINDSOR_DIALOGUE_12 = -1000840, + SAY_WINDSOR_DIALOGUE_13 = -1000841, + + SAY_WINDSOR_BEFORE_KEEP = -1000849, + SAY_WINDSOR_TO_KEEP = -1000850, + + SAY_WINDSOR_KEEP_1 = -1000851, + SAY_BOLVAR_KEEP_2 = -1000852, + SAY_WINDSOR_KEEP_3 = -1000853, + SAY_PRESTOR_KEEP_4 = -1000855, + SAY_PRESTOR_KEEP_5 = -1000856, + SAY_WINDSOR_KEEP_6 = -1000857, + SAY_WINDSOR_KEEP_7 = -1000859, + SAY_WINDSOR_KEEP_8 = -1000860, + SAY_PRESTOR_KEEP_9 = -1000863, + SAY_BOLVAR_KEEP_10 = -1000864, + SAY_PRESTOR_KEEP_11 = -1000865, + SAY_WINDSOR_KEEP_12 = -1000866, + SAY_PRESTOR_KEEP_13 = -1000867, + SAY_PRESTOR_KEEP_14 = -1000868, + SAY_BOLVAR_KEEP_15 = -1000869, + SAY_WINDSOR_KEEP_16 = -1000870, + + EMOTE_CONTEMPLATION = -1000831, + EMOTE_PRESTOR_LAUGH = -1000854, + EMOTE_WINDSOR_TABLETS = -1000858, + EMOTE_WINDSOR_READ = -1000861, + EMOTE_BOLVAR_GASP = -1000862, + EMOTE_WINDSOR_DIE = -1000871, + EMOTE_GUARD_TRANSFORM = -1000872, + + GOSSIP_ITEM_REGINALD = -3000107, + + GOSSIP_TEXT_ID_MASQUERADE = 5633, + + // SPELL_ONYXIA_TRANSFORM = 20409, // removed from DBC + SPELL_WINDSOR_READ = 20358, + SPELL_WINDSOR_DEATH = 20465, + SPELL_ONYXIA_DESPAWN = 20466, + + NPC_GUARD_ROYAL = 1756, + NPC_GUARD_CITY = 68, + NPC_GUARD_PATROLLER = 1976, + NPC_GUARD_ONYXIA = 12739, + NPC_LADY_ONYXIA = 12756, + + MAX_ROYAL_GUARDS = 6, + MAX_GUARD_SALUTES = 7, +}; + +static const float aGuardLocations[MAX_ROYAL_GUARDS][4] = +{ + { -8968.510f, 512.556f, 96.352f, 3.849f}, // guard right - left + { -8969.780f, 515.012f, 96.593f, 3.955f}, // guard right - middle + { -8972.410f, 518.228f, 96.594f, 4.281f}, // guard right - right + { -8965.170f, 508.565f, 96.352f, 3.825f}, // guard left - right + { -8962.960f, 506.583f, 96.593f, 3.802f}, // guard left - middle + { -8961.080f, 503.828f, 96.593f, 3.465f}, // guard left - left +}; + +static const float aMoveLocations[10][3] = +{ + { -8967.960f, 510.008f, 96.351f}, // Jonathan move + { -8959.440f, 505.424f, 96.595f}, // Guard Left - Middle kneel + { -8957.670f, 507.056f, 96.595f}, // Guard Left - Right kneel + { -8970.680f, 519.252f, 96.595f}, // Guard Right - Middle kneel + { -8969.100f, 520.395f, 96.595f}, // Guard Right - Left kneel + { -8974.590f, 516.213f, 96.590f}, // Jonathan kneel + { -8505.770f, 338.312f, 120.886f}, // Wrynn safe + { -8448.690f, 337.074f, 121.330f}, // Bolvar help + { -8448.279f, 338.398f, 121.329f} // Bolvar kneel +}; + +static const DialogueEntry aMasqueradeDialogue[] = +{ + {SAY_WINDSOR_QUEST_ACCEPT, NPC_WINDSOR, 7000}, + {SAY_WINDSOR_GET_READY, NPC_WINDSOR, 6000}, + {SAY_PRESTOR_SIEZE, NPC_PRESTOR, 0}, + + {SAY_JON_DIALOGUE_1, NPC_JONATHAN, 5000}, + {SAY_WINDSOR_DIALOGUE_2, NPC_WINDSOR, 6000}, + {SAY_WINDSOR_DIALOGUE_3, NPC_WINDSOR, 5000}, + {EMOTE_CONTEMPLATION, NPC_JONATHAN, 3000}, + {SAY_JON_DIALOGUE_4, NPC_JONATHAN, 6000}, + {SAY_JON_DIALOGUE_5, NPC_JONATHAN, 7000}, + {SAY_WINDSOR_DIALOGUE_6, NPC_WINDSOR, 8000}, + {SAY_WINDSOR_DIALOGUE_7, NPC_WINDSOR, 6000}, + {SAY_JON_DIALOGUE_8, NPC_JONATHAN, 7000}, + {SAY_JON_DIALOGUE_9, NPC_JONATHAN, 6000}, + {SAY_JON_DIALOGUE_10, NPC_JONATHAN, 5000}, + {EMOTE_ONESHOT_SALUTE, 0, 4000}, + {SAY_JON_DIALOGUE_11, NPC_JONATHAN, 3000}, + {NPC_JONATHAN, 0, 2000}, + {EMOTE_ONESHOT_KNEEL, 0, 3000}, + {SAY_WINDSOR_DIALOGUE_12, NPC_WINDSOR, 5000}, + {SAY_WINDSOR_DIALOGUE_13, NPC_WINDSOR, 3000}, + {EMOTE_ONESHOT_POINT, 0, 3000}, + {NPC_WINDSOR, 0, 0}, + + {NPC_GUARD_ROYAL, 0, 3000}, + {SAY_WINDSOR_BEFORE_KEEP, NPC_WINDSOR, 0}, + {SAY_WINDSOR_TO_KEEP, NPC_WINDSOR, 4000}, + {NPC_GUARD_CITY, 0, 0}, + + {NPC_WRYNN, 0, 3000}, + {SAY_WINDSOR_KEEP_1, NPC_WINDSOR, 3000}, + {SAY_BOLVAR_KEEP_2, NPC_BOLVAR, 2000}, + {SAY_WINDSOR_KEEP_3, NPC_WINDSOR, 4000}, + {EMOTE_PRESTOR_LAUGH, NPC_PRESTOR, 4000}, + {SAY_PRESTOR_KEEP_4, NPC_PRESTOR, 9000}, + {SAY_PRESTOR_KEEP_5, NPC_PRESTOR, 7000}, + {SAY_WINDSOR_KEEP_6, NPC_WINDSOR, 6000}, + {EMOTE_WINDSOR_TABLETS, NPC_WINDSOR, 6000}, + {SAY_WINDSOR_KEEP_7, NPC_WINDSOR, 4000}, + {SAY_WINDSOR_KEEP_8, NPC_WINDSOR, 5000}, + {EMOTE_WINDSOR_READ, NPC_WINDSOR, 3000}, + {SPELL_WINDSOR_READ, 0, 10000}, + {EMOTE_BOLVAR_GASP, NPC_BOLVAR, 3000}, + {SAY_PRESTOR_KEEP_9, NPC_PRESTOR, 4000}, + {SAY_BOLVAR_KEEP_10, NPC_BOLVAR, 3000}, + {SAY_PRESTOR_KEEP_11, NPC_PRESTOR, 2000}, + {SPELL_WINDSOR_DEATH, 0, 1500}, + {SAY_WINDSOR_KEEP_12, NPC_WINDSOR, 4000}, + {SAY_PRESTOR_KEEP_14, NPC_PRESTOR, 0}, + + {NPC_GUARD_ONYXIA, 0, 14000}, + {NPC_BOLVAR, 0, 2000}, + {SAY_BOLVAR_KEEP_15, NPC_BOLVAR, 8000}, + {NPC_GUARD_PATROLLER, 0, 0}, + {0, 0, 0}, +}; + +static const int32 aGuardSalute[MAX_GUARD_SALUTES] = { -1000842, -1000843, -1000844, -1000845, -1000846, -1000847, -1000848}; + +struct npc_reginald_windsorAI : public npc_escortAI, private DialogueHelper +{ + npc_reginald_windsorAI(Creature* m_creature) : npc_escortAI(m_creature), + DialogueHelper(aMasqueradeDialogue) + { + m_pScriptedMap = (ScriptedMap*)m_creature->GetInstanceData(); + // Npc flag is controlled by script + m_creature->RemoveFlag(UNIT_NPC_FLAGS, UNIT_NPC_FLAG_QUESTGIVER); + InitializeDialogueHelper(m_pScriptedMap); + Reset(); + } + + ScriptedMap* m_pScriptedMap; + + uint32 m_uiGuardCheckTimer; + uint8 m_uiOnyxiaGuardCount; + + bool m_bIsKeepReady; + bool m_bCanGuardSalute; + + ObjectGuid m_playerGuid; + ObjectGuid m_guardsGuid[MAX_ROYAL_GUARDS]; + + GuidList m_lRoyalGuardsGuidList; + GuidSet m_sGuardsSalutedGuidSet; + + void Reset() override + { + m_uiGuardCheckTimer = 0; + m_bIsKeepReady = false; + m_bCanGuardSalute = false; + } + + void MoveInLineOfSight(Unit* pWho) override + { + // Note: this implementation is not the best; It should be better handled by the guard script + if (m_bCanGuardSalute && (pWho->GetEntry() == NPC_GUARD_CITY || pWho->GetEntry() == NPC_GUARD_ROYAL || + pWho->GetEntry() == NPC_GUARD_PATROLLER) && pWho->IsWithinDistInMap(m_creature, 15.0f) && + m_sGuardsSalutedGuidSet.find(pWho->GetObjectGuid()) == m_sGuardsSalutedGuidSet.end() && pWho->IsWithinLOSInMap(m_creature)) + { + DoScriptText(aGuardSalute[urand(0, MAX_GUARD_SALUTES - 1)], pWho); + m_sGuardsSalutedGuidSet.insert(pWho->GetObjectGuid()); + } + } + + void WaypointReached(uint32 uiPointId) override + { + switch (uiPointId) + { + case 0: + if (!m_pScriptedMap) + { + break; + } + // Prepare Jonathan for the first event + if (Creature* pJonathan = m_pScriptedMap->GetSingleCreatureFromStorage(NPC_JONATHAN)) + { + // Summon 3 guards on each side and move Jonathan in the middle + for (uint8 i = 0; i < MAX_ROYAL_GUARDS; ++i) + { + if (Creature* pTemp = m_creature->SummonCreature(NPC_GUARD_ROYAL, aGuardLocations[i][0], aGuardLocations[i][1], aGuardLocations[i][2], aGuardLocations[i][3], TEMPSUMMON_TIMED_DESPAWN, 180000)) + { + m_guardsGuid[i] = pTemp->GetObjectGuid(); + } + } + + pJonathan->SetWalk(false); + pJonathan->Unmount(); + pJonathan->GetMotionMaster()->MovePoint(0, aMoveLocations[0][0], aMoveLocations[0][1], aMoveLocations[0][2]); + } + break; + case 1: + StartNextDialogueText(SAY_JON_DIALOGUE_1); + SetEscortPaused(true); + break; + case 3: + m_bCanGuardSalute = true; + break; + case 11: + if (!m_pScriptedMap) + { + break; + } + // We can reset Jonathan now + if (Creature* pJonathan = m_pScriptedMap->GetSingleCreatureFromStorage(NPC_JONATHAN)) + { + pJonathan->SetWalk(true); + pJonathan->SetStandState(UNIT_STAND_STATE_STAND); + pJonathan->GetMotionMaster()->MoveTargetedHome(); + } + break; + case 22: + SetEscortPaused(true); + m_creature->SetFacingTo(5.41f); + StartNextDialogueText(NPC_GUARD_ROYAL); + break; + case 24: + m_bCanGuardSalute = false; + break; + case 25: + StartNextDialogueText(NPC_WRYNN); + SetEscortPaused(true); + m_bCanGuardSalute = false; + break; + } + } + + void SummonedMovementInform(Creature* pSummoned, uint32 uiMotionType, uint32 uiPointId) override + { + if (uiMotionType != POINT_MOTION_TYPE || !uiPointId || pSummoned->GetEntry() != NPC_GUARD_ROYAL) + { + return; + } + + // Handle city gates royal guards + switch (uiPointId) + { + case 1: + case 2: + pSummoned->SetFacingTo(2.234f); + pSummoned->SetStandState(UNIT_STAND_STATE_KNEEL); + break; + case 3: + case 4: + pSummoned->SetFacingTo(5.375f); + pSummoned->SetStandState(UNIT_STAND_STATE_KNEEL); + break; + } + } + + void JustDidDialogueStep(int32 iEntry) override + { + if (!m_pScriptedMap) + { + return; + } + + switch (iEntry) + { + // Set orientation and prepare the npcs for the next event + case SAY_WINDSOR_GET_READY: + m_creature->SetFacingTo(0.6f); + break; + case SAY_PRESTOR_SIEZE: + if (Player* pPlayer = m_creature->GetMap()->GetPlayer(m_playerGuid)) + { + Start(false, pPlayer); + } + break; + case SAY_JON_DIALOGUE_8: + // Turn left and move the guards + if (Creature* pJonathan = m_pScriptedMap->GetSingleCreatureFromStorage(NPC_JONATHAN)) + { + pJonathan->SetFacingTo(5.375f); + } + if (Creature* pGuard = m_creature->GetMap()->GetCreature(m_guardsGuid[5])) + { + pGuard->SetFacingTo(2.234f); + pGuard->SetStandState(UNIT_STAND_STATE_KNEEL); + } + if (Creature* pGuard = m_creature->GetMap()->GetCreature(m_guardsGuid[4])) + { + pGuard->GetMotionMaster()->MovePoint(1, aMoveLocations[1][0], aMoveLocations[1][1], aMoveLocations[1][2]); + } + if (Creature* pGuard = m_creature->GetMap()->GetCreature(m_guardsGuid[3])) + { + pGuard->GetMotionMaster()->MovePoint(2, aMoveLocations[2][0], aMoveLocations[2][1], aMoveLocations[2][2]); + } + break; + case SAY_JON_DIALOGUE_9: + // Turn right and move the guards + if (Creature* pJonathan = m_pScriptedMap->GetSingleCreatureFromStorage(NPC_JONATHAN)) + { + pJonathan->SetFacingTo(2.234f); + } + if (Creature* pGuard = m_creature->GetMap()->GetCreature(m_guardsGuid[2])) + { + pGuard->SetFacingTo(5.375f); + pGuard->SetStandState(UNIT_STAND_STATE_KNEEL); + } + if (Creature* pGuard = m_creature->GetMap()->GetCreature(m_guardsGuid[1])) + { + pGuard->GetMotionMaster()->MovePoint(3, aMoveLocations[3][0], aMoveLocations[3][1], aMoveLocations[3][2]); + } + if (Creature* pGuard = m_creature->GetMap()->GetCreature(m_guardsGuid[0])) + { + pGuard->GetMotionMaster()->MovePoint(4, aMoveLocations[4][0], aMoveLocations[4][1], aMoveLocations[4][2]); + } + break; + case SAY_JON_DIALOGUE_10: + if (Creature* pJonathan = m_pScriptedMap->GetSingleCreatureFromStorage(NPC_JONATHAN)) + { + pJonathan->SetFacingToObject(m_creature); + } + break; + case EMOTE_ONESHOT_SALUTE: + if (Creature* pJonathan = m_pScriptedMap->GetSingleCreatureFromStorage(NPC_JONATHAN)) + { + pJonathan->HandleEmote(EMOTE_ONESHOT_SALUTE); + } + break; + case NPC_JONATHAN: + if (Creature* pJonathan = m_pScriptedMap->GetSingleCreatureFromStorage(NPC_JONATHAN)) + { + pJonathan->SetWalk(true); + pJonathan->GetMotionMaster()->MovePoint(0, aMoveLocations[5][0], aMoveLocations[5][1], aMoveLocations[5][2]); + } + break; + case EMOTE_ONESHOT_KNEEL: + if (Creature* pJonathan = m_pScriptedMap->GetSingleCreatureFromStorage(NPC_JONATHAN)) + { + pJonathan->SetFacingToObject(m_creature); + pJonathan->SetStandState(UNIT_STAND_STATE_KNEEL); + } + break; + case SAY_WINDSOR_DIALOGUE_12: + if (Creature* pJonathan = m_pScriptedMap->GetSingleCreatureFromStorage(NPC_JONATHAN)) + { + m_creature->SetFacingToObject(pJonathan); + } + break; + case SAY_WINDSOR_DIALOGUE_13: + m_creature->SetFacingTo(0.6f); + break; + case EMOTE_ONESHOT_POINT: + m_creature->HandleEmote(EMOTE_ONESHOT_POINT); + break; + case NPC_WINDSOR: + SetEscortPaused(false); + break; + case SAY_WINDSOR_BEFORE_KEEP: + m_bIsKeepReady = true; + m_creature->SetFlag(UNIT_NPC_FLAGS, UNIT_NPC_FLAG_GOSSIP); + break; + case NPC_GUARD_CITY: + SetEscortPaused(false); + break; + case NPC_WRYNN: + // Remove npc flags during the event + if (Creature* pOnyxia = m_pScriptedMap->GetSingleCreatureFromStorage(NPC_PRESTOR)) + { + pOnyxia->RemoveFlag(UNIT_NPC_FLAGS, UNIT_NPC_FLAG_GOSSIP | UNIT_NPC_FLAG_QUESTGIVER); + } + if (Creature* pWrynn = m_pScriptedMap->GetSingleCreatureFromStorage(NPC_WRYNN)) + { + pWrynn->RemoveFlag(UNIT_NPC_FLAGS, UNIT_NPC_FLAG_QUESTGIVER); + } + if (Creature* pBolvar = m_pScriptedMap->GetSingleCreatureFromStorage(NPC_BOLVAR)) + { + pBolvar->RemoveFlag(UNIT_NPC_FLAGS, UNIT_NPC_FLAG_QUESTGIVER); + } + break; + case SAY_BOLVAR_KEEP_2: + if (Creature* pWrynn = m_pScriptedMap->GetSingleCreatureFromStorage(NPC_WRYNN)) + { + pWrynn->SetWalk(false); + pWrynn->ForcedDespawn(15000); + pWrynn->GetMotionMaster()->MovePoint(0, aMoveLocations[6][0], aMoveLocations[6][1], aMoveLocations[6][2]); + + // Store all the nearby guards, in order to transform them into Onyxia guards + std::list lGuardsList; + GetCreatureListWithEntryInGrid(lGuardsList, pWrynn, NPC_GUARD_ROYAL, 25.0f); + + for (std::list::const_iterator itr = lGuardsList.begin(); itr != lGuardsList.end(); ++itr) + { + m_lRoyalGuardsGuidList.push_back((*itr)->GetObjectGuid()); + } + } + break; + case SPELL_WINDSOR_READ: + DoCastSpellIfCan(m_creature, SPELL_WINDSOR_READ); + break; + case EMOTE_BOLVAR_GASP: + if (Creature* pOnyxia = m_pScriptedMap->GetSingleCreatureFromStorage(NPC_PRESTOR)) + { + pOnyxia->UpdateEntry(NPC_LADY_ONYXIA); + + if (Creature* pBolvar = m_pScriptedMap->GetSingleCreatureFromStorage(NPC_BOLVAR)) + { + pBolvar->SetFacingToObject(pOnyxia); + } + } + break; + case SAY_PRESTOR_KEEP_9: + if (Creature* pBolvar = m_pScriptedMap->GetSingleCreatureFromStorage(NPC_BOLVAR)) + { + pBolvar->SetWalk(false); + pBolvar->GetMotionMaster()->MovePoint(0, aMoveLocations[7][0], aMoveLocations[7][1], aMoveLocations[7][2]); + } + break; + case SAY_BOLVAR_KEEP_10: + if (Creature* pBolvar = m_pScriptedMap->GetSingleCreatureFromStorage(NPC_BOLVAR)) + { + if (Creature* pOnyxia = m_pScriptedMap->GetSingleCreatureFromStorage(NPC_PRESTOR)) + { + pBolvar->SetFacingToObject(pOnyxia); + DoScriptText(EMOTE_PRESTOR_LAUGH, pOnyxia); + } + } + break; + case SAY_PRESTOR_KEEP_11: + for (GuidList::const_iterator itr = m_lRoyalGuardsGuidList.begin(); itr != m_lRoyalGuardsGuidList.end(); ++itr) + { + if (Creature* pGuard = m_creature->GetMap()->GetCreature(*itr)) + { + if (!pGuard->IsAlive()) + { + continue; + } + + pGuard->UpdateEntry(NPC_GUARD_ONYXIA); + DoScriptText(EMOTE_GUARD_TRANSFORM, pGuard); + + if (Creature* pBolvar = m_pScriptedMap->GetSingleCreatureFromStorage(NPC_BOLVAR)) + { + pGuard->AI()->AttackStart(pBolvar); + } + } + } + m_uiGuardCheckTimer = 1000; + break; + case SPELL_WINDSOR_DEATH: + if (Creature* pOnyxia = m_pScriptedMap->GetSingleCreatureFromStorage(NPC_PRESTOR)) + { + pOnyxia->CastSpell(m_creature, SPELL_WINDSOR_DEATH, false); + } + break; + case SAY_WINDSOR_KEEP_12: + if (Creature* pOnyxia = m_pScriptedMap->GetSingleCreatureFromStorage(NPC_PRESTOR)) + { + DoScriptText(SAY_PRESTOR_KEEP_13, pOnyxia); + } + + // Fake death + m_creature->InterruptNonMeleeSpells(true); + m_creature->SetHealth(0); + m_creature->StopMoving(); + m_creature->ClearComboPointHolders(); + m_creature->RemoveAllAurasOnDeath(); + m_creature->ModifyAuraState(AURA_STATE_HEALTHLESS_20_PERCENT, false); + m_creature->ModifyAuraState(AURA_STATE_HEALTHLESS_35_PERCENT, false); + m_creature->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE); + m_creature->ClearAllReactives(); + m_creature->GetMotionMaster()->Clear(); + m_creature->GetMotionMaster()->MoveIdle(); + m_creature->SetStandState(UNIT_STAND_STATE_DEAD); + break; + case SAY_PRESTOR_KEEP_14: + if (Creature* pOnyxia = m_pScriptedMap->GetSingleCreatureFromStorage(NPC_PRESTOR)) + { + pOnyxia->ForcedDespawn(1000); + pOnyxia->HandleEmote(EMOTE_ONESHOT_LIFTOFF); + pOnyxia->CastSpell(pOnyxia, SPELL_ONYXIA_DESPAWN, false); + } + break; + case NPC_GUARD_ONYXIA: + if (Creature* pBolvar = m_pScriptedMap->GetSingleCreatureFromStorage(NPC_BOLVAR)) + { + pBolvar->GetMotionMaster()->MovePoint(0, aMoveLocations[7][0], aMoveLocations[7][1], aMoveLocations[7][2]); + } + break; + case NPC_BOLVAR: + if (Creature* pBolvar = m_pScriptedMap->GetSingleCreatureFromStorage(NPC_BOLVAR)) + { + pBolvar->SetWalk(true); + pBolvar->GetMotionMaster()->MovePoint(0, aMoveLocations[8][0], aMoveLocations[8][1], aMoveLocations[8][2]); + } + break; + case SAY_BOLVAR_KEEP_15: + if (Creature* pBolvar = m_pScriptedMap->GetSingleCreatureFromStorage(NPC_BOLVAR)) + { + pBolvar->SetStandState(UNIT_STAND_STATE_KNEEL); + } + + DoScriptText(SAY_WINDSOR_KEEP_16, m_creature); + DoScriptText(EMOTE_WINDSOR_DIE, m_creature); + + if (Player* pPlayer = m_creature->GetMap()->GetPlayer(m_playerGuid)) + { + pPlayer->GroupEventHappens(QUEST_THE_GREAT_MASQUERADE, m_creature); + } + break; + case NPC_GUARD_PATROLLER: + // Reset Bolvar and Wrynn + if (Creature* pBolvar = m_pScriptedMap->GetSingleCreatureFromStorage(NPC_BOLVAR)) + { + pBolvar->SetFlag(UNIT_NPC_FLAGS, UNIT_NPC_FLAG_QUESTGIVER); + pBolvar->SetStandState(UNIT_STAND_STATE_STAND); + pBolvar->GetMotionMaster()->MoveTargetedHome(); + } + if (Creature* pWrynn = m_pScriptedMap->GetSingleCreatureFromStorage(NPC_WRYNN)) + { + pWrynn->SetFlag(UNIT_NPC_FLAGS, UNIT_NPC_FLAG_QUESTGIVER); + pWrynn->Respawn(); + pWrynn->SetWalk(true); + pWrynn->GetMotionMaster()->MoveTargetedHome(); + } + // Onyxia will respawn by herself in about 30 min, so just reset flags + if (Creature* pOnyxia = m_pScriptedMap->GetSingleCreatureFromStorage(NPC_PRESTOR)) + { + pOnyxia->SetFlag(UNIT_NPC_FLAGS, UNIT_NPC_FLAG_GOSSIP | UNIT_NPC_FLAG_QUESTGIVER); + } + // Allow creature to despawn + SetEscortPaused(false); + break; + } + } + + void DoStartKeepEvent() + { + StartNextDialogueText(SAY_WINDSOR_TO_KEEP); + m_creature->RemoveFlag(UNIT_NPC_FLAGS, UNIT_NPC_FLAG_GOSSIP); + } + + void DoStartEscort(Player* pPlayer) + { + StartNextDialogueText(SAY_WINDSOR_QUEST_ACCEPT); + m_playerGuid = pPlayer->GetObjectGuid(); + } + + bool IsKeepEventReady() { return m_bIsKeepReady; } + + void UpdateEscortAI(const uint32 uiDiff) override + { + DialogueUpdate(uiDiff); + + // Check if all Onyxia guards are dead + if (m_uiGuardCheckTimer) + { + if (m_uiGuardCheckTimer <= uiDiff) + { + uint8 uiDeadGuardsCount = 0; + for (GuidList::const_iterator itr = m_lRoyalGuardsGuidList.begin(); itr != m_lRoyalGuardsGuidList.end(); ++itr) + { + if (Creature* pGuard = m_creature->GetMap()->GetCreature(*itr)) + { + if (!pGuard->IsAlive() && pGuard->GetEntry() == NPC_GUARD_ONYXIA) + { + ++uiDeadGuardsCount; + } + } + } + if (uiDeadGuardsCount == m_lRoyalGuardsGuidList.size()) + { + StartNextDialogueText(NPC_GUARD_ONYXIA); + m_uiGuardCheckTimer = 0; + } + else + { + m_uiGuardCheckTimer = 1000; + } + } + else + { + m_uiGuardCheckTimer -= uiDiff; + } + } + } +}; + +CreatureAI* GetAI_npc_reginald_windsor(Creature* pCreature) +{ + return new npc_reginald_windsorAI(pCreature); +} + +bool QuestAccept_npc_reginald_windsor(Player* pPlayer, Creature* pCreature, const Quest* pQuest) +{ + if (pQuest->GetQuestId() == QUEST_THE_GREAT_MASQUERADE) + { + if (npc_reginald_windsorAI* pReginaldAI = dynamic_cast(pCreature->AI())) + { + pReginaldAI->DoStartEscort(pPlayer); + } + } + + return true; +} + +bool GossipHello_npc_reginald_windsor(Player* pPlayer, Creature* pCreature) +{ + bool bIsEventReady = false; + + if (npc_reginald_windsorAI* pReginaldAI = dynamic_cast(pCreature->AI())) + { + bIsEventReady = pReginaldAI->IsKeepEventReady(); + } + + // Check if event is possible and also check the status of the quests + if (bIsEventReady && pPlayer->GetQuestStatus(QUEST_THE_GREAT_MASQUERADE) != QUEST_STATUS_COMPLETE && pPlayer->GetQuestRewardStatus(QUEST_STORMWIND_RENDEZVOUS)) + { + pPlayer->ADD_GOSSIP_ITEM_ID(GOSSIP_ICON_CHAT, GOSSIP_ITEM_REGINALD, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 1); + pPlayer->SEND_GOSSIP_MENU(GOSSIP_TEXT_ID_MASQUERADE, pCreature->GetObjectGuid()); + } + else + { + if (pCreature->IsQuestGiver()) + { + pPlayer->PrepareQuestMenu(pCreature->GetObjectGuid()); + } + + pPlayer->SEND_GOSSIP_MENU(DEFAULT_GOSSIP_MESSAGE, pCreature->GetObjectGuid()); + } + + return true; +} + +bool GossipSelect_npc_reginald_windsor(Player* pPlayer, Creature* pCreature, uint32 /*uiSender*/, uint32 uiAction) +{ + if (uiAction == GOSSIP_ACTION_INFO_DEF + 1) + { + if (npc_reginald_windsorAI* pReginaldAI = dynamic_cast(pCreature->AI())) + { + pReginaldAI->DoStartKeepEvent(); + } + + pPlayer->CLOSE_GOSSIP_MENU(); + } + + return true; +} + +void AddSC_stormwind_city() +{ + Script* pNewScript; + + pNewScript = new Script; + pNewScript->Name = "npc_bartleby"; + pNewScript->GetAI = &GetAI_npc_bartleby; + pNewScript->pQuestAcceptNPC = &QuestAccept_npc_bartleby; + pNewScript->RegisterSelf(); + + pNewScript = new Script; + pNewScript->Name = "npc_dashel_stonefist"; + pNewScript->GetAI = &GetAI_npc_dashel_stonefist; + pNewScript->pQuestAcceptNPC = &QuestAccept_npc_dashel_stonefist; + pNewScript->RegisterSelf(); + + pNewScript = new Script; + pNewScript->Name = "npc_lady_katrana_prestor"; + pNewScript->pGossipHello = &GossipHello_npc_lady_katrana_prestor; + pNewScript->pGossipSelect = &GossipSelect_npc_lady_katrana_prestor; + pNewScript->RegisterSelf(); + + pNewScript = new Script; + pNewScript->Name = "npc_squire_rowe"; + pNewScript->GetAI = &GetAI_npc_squire_rowe; + pNewScript->pGossipHello = &GossipHello_npc_squire_rowe; + pNewScript->pGossipSelect = &GossipSelect_npc_squire_rowe; + pNewScript->RegisterSelf(); + + pNewScript = new Script; + pNewScript->Name = "npc_reginald_windsor"; + pNewScript->GetAI = &GetAI_npc_reginald_windsor; + pNewScript->pQuestAcceptNPC = &QuestAccept_npc_reginald_windsor; + pNewScript->pGossipHello = &GossipHello_npc_reginald_windsor; + pNewScript->pGossipSelect = &GossipSelect_npc_reginald_windsor; + pNewScript->RegisterSelf(); +} diff --git a/src/modules/SD2/scripts/eastern_kingdoms/stranglethorn_vale.cpp b/src/modules/SD2/scripts/eastern_kingdoms/stranglethorn_vale.cpp new file mode 100644 index 000000000..3d3fcb5ae --- /dev/null +++ b/src/modules/SD2/scripts/eastern_kingdoms/stranglethorn_vale.cpp @@ -0,0 +1,136 @@ +/** + * ScriptDev2 is an extension for mangos providing enhanced features for + * area triggers, creatures, game objects, instances, items, and spells beyond + * the default database scripting in mangos. + * + * Copyright (C) 2006-2013 ScriptDev2 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * World of Warcraft, and all World of Warcraft or Warcraft art, images, + * and lore are copyrighted by Blizzard Entertainment, Inc. + */ + +/** + * ScriptData + * SDName: Stranglethorn_Vale + * SD%Complete: 100 + * SDComment: Quest support: 592. + * SDCategory: Stranglethorn Vale + * EndScriptData + */ + +/** + * ContentData + * mob_yenniku + * EndContentData + */ + +#include "precompiled.h" + +/*###### +## mob_yenniku +######*/ + +enum +{ + SPELL_YENNIKUS_RELEASE = 3607, + + QUEST_ID_SAVING_YENNIKU = 592, + + FACTION_ID_HORDE_GENERIC = 83, // Note: faction may not be correct! +}; + +struct mob_yennikuAI : public ScriptedAI +{ + mob_yennikuAI(Creature* pCreature) : ScriptedAI(pCreature) { Reset(); } + + uint32 m_uiResetTimer; + + void Reset() override { m_uiResetTimer = 0; } + + void SpellHit(Unit* pCaster, const SpellEntry* pSpell) override + { + if (pSpell->Id == SPELL_YENNIKUS_RELEASE && pCaster->GetTypeId() == TYPEID_PLAYER) + { + if (!m_uiResetTimer && ((Player*)pCaster)->GetQuestStatus(QUEST_ID_SAVING_YENNIKU) == QUEST_STATUS_INCOMPLETE) + { + m_uiResetTimer = 60000; + EnterEvadeMode(); + } + } + } + + void EnterEvadeMode() override + { + if (m_uiResetTimer) + { + m_creature->RemoveAllAurasOnEvade(); + m_creature->DeleteThreatList(); + m_creature->CombatStop(true); + m_creature->LoadCreatureAddon(true); + + m_creature->SetLootRecipient(NULL); + + m_creature->HandleEmote(EMOTE_STATE_STUN); + m_creature->SetFactionTemporary(FACTION_ID_HORDE_GENERIC, TEMPFACTION_RESTORE_REACH_HOME); + } + else + { ScriptedAI::EnterEvadeMode(); } + } + + void UpdateAI(const uint32 uiDiff) override + { + if (m_uiResetTimer) + { + if (m_uiResetTimer <= uiDiff) + { + m_creature->HandleEmote(EMOTE_STATE_NONE); + m_uiResetTimer = 0; + EnterEvadeMode(); + } + else + { + m_uiResetTimer -= uiDiff; + } + } + + if (!m_creature->SelectHostileTarget() || !m_creature->getVictim()) + { + return; + } + + DoMeleeAttackIfReady(); + } +}; + +CreatureAI* GetAI_mob_yenniku(Creature* _Creature) +{ + return new mob_yennikuAI(_Creature); +} + +/*###### +## +######*/ + +void AddSC_stranglethorn_vale() +{ + Script* pNewScript; + + pNewScript = new Script; + pNewScript->Name = "mob_yenniku"; + pNewScript->GetAI = &GetAI_mob_yenniku; + pNewScript->RegisterSelf(); +} diff --git a/src/modules/SD2/scripts/eastern_kingdoms/stratholme/boss_baroness_anastari.cpp b/src/modules/SD2/scripts/eastern_kingdoms/stratholme/boss_baroness_anastari.cpp new file mode 100644 index 000000000..fb6925f44 --- /dev/null +++ b/src/modules/SD2/scripts/eastern_kingdoms/stratholme/boss_baroness_anastari.cpp @@ -0,0 +1,177 @@ +/* Copyright (C) 2006 - 2013 ScriptDev2 + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with 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: 100 +SDComment: +SDCategory: Stratholme +EndScriptData */ + +#include "precompiled.h" + +enum +{ + SPELL_BANSHEE_WAIL = 16565, + SPELL_BANSHEE_CURSE = 16867, + SPELL_SILENCE = 18327, + SPELL_POSSESS = 17244, + SPELL_POSSESSED = 17246, + SPELL_POSSESS_INV = 17250, // baroness becomes invisible while possessing a target +}; + +struct boss_baroness_anastariAI : public ScriptedAI +{ + boss_baroness_anastariAI(Creature* pCreature) : ScriptedAI(pCreature) { Reset(); } + + uint32 m_uiBansheeWailTimer; + uint32 m_uiBansheeCurseTimer; + uint32 m_uiSilenceTimer; + uint32 m_uiPossessTimer; + uint32 m_uiPossessEndTimer; + + ObjectGuid m_possessedPlayer; + + void Reset() override + { + m_uiBansheeWailTimer = 0; + m_uiBansheeCurseTimer = 10000; + m_uiSilenceTimer = 25000; + m_uiPossessTimer = 15000; + m_uiPossessEndTimer = 0; + } + + void EnterEvadeMode() override + { + // If it's invisible don't evade + if (m_uiPossessEndTimer) + return; + + ScriptedAI::EnterEvadeMode(); + } + + void UpdateAI(const uint32 uiDiff) override + { + if (m_uiPossessEndTimer) + { + // Check if the possessed player has been damaged + if (m_uiPossessEndTimer <= uiDiff) + { + // If aura has expired, return to fight + if (!m_creature->HasAura(SPELL_POSSESS_INV)) + { + m_uiPossessEndTimer = 0; + return; + } + + // Check for possessed player + Player* pPlayer = m_creature->GetMap()->GetPlayer(m_possessedPlayer); + if (!pPlayer || !pPlayer->IsAlive()) + { + m_creature->RemoveAurasDueToSpell(SPELL_POSSESS_INV); + m_uiPossessEndTimer = 0; + return; + } + + // If possessed player has less than 50% health + if (pPlayer->GetHealth() <= pPlayer->GetMaxHealth() * .5f) + { + m_creature->RemoveAurasDueToSpell(SPELL_POSSESS_INV); + pPlayer->RemoveAurasDueToSpell(SPELL_POSSESSED); + pPlayer->RemoveAurasDueToSpell(SPELL_POSSESS); + m_uiPossessEndTimer = 0; + return; + } + + m_uiPossessEndTimer = 1000; + } + else + m_uiPossessEndTimer -= uiDiff; + } + + if (!m_creature->SelectHostileTarget() || !m_creature->getVictim()) + return; + + // BansheeWail + if (m_uiBansheeWailTimer < uiDiff) + { + if (Unit* pTarget = m_creature->SelectAttackingTarget(ATTACKING_TARGET_RANDOM, 0)) + { + if (DoCastSpellIfCan(pTarget, SPELL_BANSHEE_WAIL) == CAST_OK) + m_uiBansheeWailTimer = urand(2000, 3000); + } + } + else + m_uiBansheeWailTimer -= uiDiff; + + // BansheeCurse + if (m_uiBansheeCurseTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature, SPELL_BANSHEE_CURSE) == CAST_OK) + m_uiBansheeCurseTimer = 20000; + } + else + m_uiBansheeCurseTimer -= uiDiff; + + // Silence + if (m_uiSilenceTimer < uiDiff) + { + if (Unit* pTarget = m_creature->SelectAttackingTarget(ATTACKING_TARGET_RANDOM, 0)) + { + if (DoCastSpellIfCan(pTarget, SPELL_SILENCE) == CAST_OK) + m_uiSilenceTimer = 25000; + } + } + else + m_uiSilenceTimer -= uiDiff; + + // Possess + if (m_uiPossessTimer < uiDiff) + { + if (Unit* pTarget = m_creature->SelectAttackingTarget(ATTACKING_TARGET_RANDOM, 1, SPELL_POSSESS, SELECT_FLAG_PLAYER)) + { + if (DoCastSpellIfCan(pTarget, SPELL_POSSESS) == CAST_OK) + { + DoCastSpellIfCan(pTarget, SPELL_POSSESSED, CAST_TRIGGERED); + DoCastSpellIfCan(m_creature, SPELL_POSSESS_INV, CAST_TRIGGERED); + + m_possessedPlayer = pTarget->GetObjectGuid(); + m_uiPossessEndTimer = 1000; + m_uiPossessTimer = 30000; + } + } + } + else + m_uiPossessTimer -= uiDiff; + + DoMeleeAttackIfReady(); + } +}; + +CreatureAI* GetAI_boss_baroness_anastari(Creature* pCreature) +{ + return new boss_baroness_anastariAI(pCreature); +} + +void AddSC_boss_baroness_anastari() +{ + Script* pNewScript; + + pNewScript = new Script; + pNewScript->Name = "boss_baroness_anastari"; + pNewScript->GetAI = &GetAI_boss_baroness_anastari; + pNewScript->RegisterSelf(); +} diff --git a/src/modules/SD2/scripts/eastern_kingdoms/stratholme/boss_cannon_master_willey.cpp b/src/modules/SD2/scripts/eastern_kingdoms/stratholme/boss_cannon_master_willey.cpp new file mode 100644 index 000000000..41c19449c --- /dev/null +++ b/src/modules/SD2/scripts/eastern_kingdoms/stratholme/boss_cannon_master_willey.cpp @@ -0,0 +1,119 @@ +/* Copyright (C) 2006 - 2013 ScriptDev2 + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with 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" + +enum +{ + SPELL_KNOCK_AWAY = 10101, + SPELL_PUMMEL = 15615, + SPELL_SHOOT = 16496, + SPELL_SUMMON_RIFLEMAN = 17279, // spell needs script target +}; + +struct boss_cannon_master_willeyAI : public ScriptedAI +{ + boss_cannon_master_willeyAI(Creature* pCreature) : ScriptedAI(pCreature) { Reset(); } + + uint32 m_uiKnockAwayTimer; + uint32 m_uiPummelTimer; + uint32 m_uiShootTimer; + uint32 m_uiSummonRiflemanTimer; + + void Reset() override + { + m_uiShootTimer = 1000; + m_uiPummelTimer = 7000; + m_uiKnockAwayTimer = 11000; + m_uiSummonRiflemanTimer = 15000; + } + + void JustSummoned(Creature* pSummoned) override + { + if (m_creature->getVictim()) + pSummoned->AI()->AttackStart(m_creature->getVictim()); + } + + void UpdateAI(const uint32 uiDiff) override + { + // Return since we have no target + if (!m_creature->SelectHostileTarget() || !m_creature->getVictim()) + return; + + // Pummel + if (m_uiPummelTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature->getVictim(), SPELL_PUMMEL) == CAST_OK) + m_uiPummelTimer = 12000; + } + else + m_uiPummelTimer -= uiDiff; + + // KnockAway + if (m_uiKnockAwayTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature->getVictim(), SPELL_KNOCK_AWAY) == CAST_OK) + m_uiKnockAwayTimer = 14000; + } + else + m_uiKnockAwayTimer -= uiDiff; + + // Shoot + if (m_uiShootTimer < uiDiff) + { + if (Unit* pTarget = m_creature->SelectAttackingTarget(ATTACKING_TARGET_RANDOM, 0, SPELL_SHOOT, SELECT_FLAG_NOT_IN_MELEE_RANGE)) + { + if (DoCastSpellIfCan(pTarget, SPELL_SHOOT) == CAST_OK) + m_uiShootTimer = urand(3000, 4000); + } + } + else + m_uiShootTimer -= uiDiff; + + // SummonRifleman + if (m_uiSummonRiflemanTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature, SPELL_SUMMON_RIFLEMAN) == CAST_OK) + m_uiSummonRiflemanTimer = 30000; + } + else + m_uiSummonRiflemanTimer -= uiDiff; + + DoMeleeAttackIfReady(); + } +}; + +CreatureAI* GetAI_boss_cannon_master_willey(Creature* pCreature) +{ + return new boss_cannon_master_willeyAI(pCreature); +} + +void AddSC_boss_cannon_master_willey() +{ + Script* pNewScript; + + pNewScript = new Script; + pNewScript->Name = "boss_cannon_master_willey"; + pNewScript->GetAI = &GetAI_boss_cannon_master_willey; + pNewScript->RegisterSelf(); +} diff --git a/src/modules/SD2/scripts/eastern_kingdoms/stratholme/boss_dathrohan_balnazzar.cpp b/src/modules/SD2/scripts/eastern_kingdoms/stratholme/boss_dathrohan_balnazzar.cpp new file mode 100644 index 000000000..015010710 --- /dev/null +++ b/src/modules/SD2/scripts/eastern_kingdoms/stratholme/boss_dathrohan_balnazzar.cpp @@ -0,0 +1,237 @@ +/* Copyright (C) 2006 - 2013 ScriptDev2 + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with 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 +{ + SAY_AGGRO = -1329016, + SAY_TRANSFORM = -1329017, + SAY_DEATH = -1329018, + + // 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_SKELETAL_GUARDIAN = 10390, + NPC_SKELETAL_BERSERKER = 10391 +}; + +struct SummonDef +{ + uint32 m_uiEntry; + float m_fX, m_fY, m_fZ, m_fOrient; +}; + +SummonDef m_aSummonPoint[] = +{ + {NPC_SKELETAL_BERSERKER, 3460.356f, -3070.572f, 135.086f, 0.332f}, + {NPC_SKELETAL_BERSERKER, 3465.289f, -3069.987f, 135.086f, 5.480f}, + {NPC_SKELETAL_BERSERKER, 3463.616f, -3074.912f, 135.086f, 5.009f}, + + {NPC_SKELETAL_GUARDIAN, 3460.012f, -3076.041f, 135.086f, 1.187f}, + {NPC_SKELETAL_GUARDIAN, 3467.909f, -3076.401f, 135.086f, 3.770f}, + + {NPC_SKELETAL_BERSERKER, 3509.269f, -3066.474f, 135.080f, 4.817f}, + {NPC_SKELETAL_BERSERKER, 3510.966f, -3069.011f, 135.080f, 3.491f}, + + {NPC_SKELETAL_GUARDIAN, 3516.042f, -3066.873f, 135.080f, 3.997f}, + {NPC_SKELETAL_GUARDIAN, 3513.561f, -3063.027f, 135.080f, 2.356f}, + {NPC_SKELETAL_GUARDIAN, 3518.825f, -3060.926f, 135.080f, 3.944f} +}; + +struct boss_dathrohan_balnazzarAI : public ScriptedAI +{ + boss_dathrohan_balnazzarAI(Creature* pCreature) : ScriptedAI(pCreature) {Reset();} + + uint32 m_uiCrusadersHammer_Timer; + uint32 m_uiCrusaderStrike_Timer; + uint32 m_uiMindBlast_Timer; + uint32 m_uiHolyStrike_Timer; + uint32 m_uiShadowShock_Timer; + uint32 m_uiPsychicScream_Timer; + uint32 m_uiDeepSleep_Timer; + uint32 m_uiMindControl_Timer; + bool m_bTransformed; + + void Reset() override + { + m_uiCrusadersHammer_Timer = 8000; + m_uiCrusaderStrike_Timer = 12000; + m_uiMindBlast_Timer = 6000; + m_uiHolyStrike_Timer = 18000; + m_uiShadowShock_Timer = 4000; + m_uiPsychicScream_Timer = 16000; + m_uiDeepSleep_Timer = 20000; + m_uiMindControl_Timer = 10000; + m_bTransformed = false; + + if (m_creature->GetEntry() == NPC_BALNAZZAR) + m_creature->UpdateEntry(NPC_DATHROHAN); + } + + void Aggro(Unit* /*pWho*/) override + { + DoScriptText(SAY_AGGRO, m_creature); + } + + void JustDied(Unit* /*Victim*/) override + { + DoScriptText(SAY_DEATH, m_creature); + + for (uint32 i = 0; i < countof(m_aSummonPoint); ++i) + m_creature->SummonCreature(m_aSummonPoint[i].m_uiEntry, + 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_MILLISECONDS); + } + + void UpdateAI(const uint32 uiDiff) override + { + if (!m_creature->SelectHostileTarget() || !m_creature->getVictim()) + return; + + // START NOT TRANSFORMED + if (!m_bTransformed) + { + // MindBlast + if (m_uiMindBlast_Timer < uiDiff) + { + DoCastSpellIfCan(m_creature->getVictim(), SPELL_MINDBLAST); + m_uiMindBlast_Timer = urand(15000, 20000); + } + else m_uiMindBlast_Timer -= uiDiff; + + // CrusadersHammer + if (m_uiCrusadersHammer_Timer < uiDiff) + { + DoCastSpellIfCan(m_creature->getVictim(), SPELL_CRUSADERSHAMMER); + m_uiCrusadersHammer_Timer = 12000; + } + else m_uiCrusadersHammer_Timer -= uiDiff; + + // CrusaderStrike + if (m_uiCrusaderStrike_Timer < uiDiff) + { + DoCastSpellIfCan(m_creature->getVictim(), SPELL_CRUSADERSTRIKE); + m_uiCrusaderStrike_Timer = 15000; + } + else m_uiCrusaderStrike_Timer -= uiDiff; + + // HolyStrike + if (m_uiHolyStrike_Timer < uiDiff) + { + DoCastSpellIfCan(m_creature->getVictim(), SPELL_HOLYSTRIKE); + m_uiHolyStrike_Timer = 15000; + } + else m_uiHolyStrike_Timer -= uiDiff; + + // BalnazzarTransform + if (m_creature->GetHealthPercent() < 40.0f) + { + // restore hp, mana and stun + if (DoCastSpellIfCan(m_creature, SPELL_BALNAZZARTRANSFORM) == CAST_OK) + { + m_creature->UpdateEntry(NPC_BALNAZZAR); + DoScriptText(SAY_TRANSFORM, m_creature); + m_bTransformed = true; + } + } + } + else + { + // MindBlast + if (m_uiMindBlast_Timer < uiDiff) + { + DoCastSpellIfCan(m_creature->getVictim(), SPELL_MINDBLAST); + m_uiMindBlast_Timer = urand(15000, 20000); + } + else m_uiMindBlast_Timer -= uiDiff; + + // ShadowShock + if (m_uiShadowShock_Timer < uiDiff) + { + DoCastSpellIfCan(m_creature->getVictim(), SPELL_SHADOWSHOCK); + m_uiShadowShock_Timer = 11000; + } + else m_uiShadowShock_Timer -= uiDiff; + + // PsychicScream + if (m_uiPsychicScream_Timer < uiDiff) + { + if (Unit* pTarget = m_creature->SelectAttackingTarget(ATTACKING_TARGET_RANDOM, 0)) + DoCastSpellIfCan(pTarget, SPELL_PSYCHICSCREAM); + + m_uiPsychicScream_Timer = 20000; + } + else m_uiPsychicScream_Timer -= uiDiff; + + // DeepSleep + if (m_uiDeepSleep_Timer < uiDiff) + { + if (Unit* pTarget = m_creature->SelectAttackingTarget(ATTACKING_TARGET_RANDOM, 0)) + DoCastSpellIfCan(pTarget, SPELL_SLEEP); + + m_uiDeepSleep_Timer = 15000; + } + else m_uiDeepSleep_Timer -= uiDiff; + + // MindControl + if (m_uiMindControl_Timer < uiDiff) + { + DoCastSpellIfCan(m_creature->getVictim(), SPELL_MINDCONTROL); + 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* pNewScript; + + pNewScript = new Script; + pNewScript->Name = "boss_dathrohan_balnazzar"; + pNewScript->GetAI = &GetAI_boss_dathrohan_balnazzar; + pNewScript->RegisterSelf(); +} diff --git a/src/modules/SD2/scripts/eastern_kingdoms/stratholme/boss_maleki_the_pallid.cpp b/src/modules/SD2/scripts/eastern_kingdoms/stratholme/boss_maleki_the_pallid.cpp new file mode 100644 index 000000000..6f2a3fa8d --- /dev/null +++ b/src/modules/SD2/scripts/eastern_kingdoms/stratholme/boss_maleki_the_pallid.cpp @@ -0,0 +1,121 @@ +/* Copyright (C) 2006 - 2013 ScriptDev2 + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with 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" + +enum +{ + SPELL_FROSTBOLT = 17503, + SPELL_DRAIN_LIFE = 17238, + SPELL_DRAIN_MANA = 17243, + SPELL_ICE_TOMB = 16869 +}; + +struct boss_maleki_the_pallidAI : public ScriptedAI +{ + boss_maleki_the_pallidAI(Creature* pCreature) : ScriptedAI(pCreature) { Reset(); } + + uint32 m_uiDrainManaTimer; + uint32 m_uiFrostboltTimer; + uint32 m_uiIceTombTimer; + uint32 m_uiDrainLifeTimer; + + void Reset() override + { + m_uiDrainManaTimer = 30000; + m_uiFrostboltTimer = 0; + m_uiIceTombTimer = 15000; + m_uiDrainLifeTimer = 20000; + } + + void UpdateAI(const uint32 uiDiff) override + { + if (!m_creature->SelectHostileTarget() || !m_creature->getVictim()) + return; + + // Frostbolt + if (m_uiFrostboltTimer < uiDiff) + { + if (Unit* pTarget = m_creature->SelectAttackingTarget(ATTACKING_TARGET_RANDOM, 0)) + { + if (DoCastSpellIfCan(pTarget, SPELL_FROSTBOLT) == CAST_OK) + m_uiFrostboltTimer = urand(3000, 4000); + } + } + else + m_uiFrostboltTimer -= uiDiff; + + // IceTomb + if (m_uiIceTombTimer < uiDiff) + { + if (Unit* pTarget = m_creature->SelectAttackingTarget(ATTACKING_TARGET_RANDOM, 1)) + { + if (DoCastSpellIfCan(pTarget, SPELL_ICE_TOMB) == CAST_OK) + m_uiIceTombTimer = urand(15000, 20000); + } + } + else + m_uiIceTombTimer -= uiDiff; + + // Drain Life + if (m_uiDrainLifeTimer < uiDiff) + { + if (Unit* pTarget = m_creature->SelectAttackingTarget(ATTACKING_TARGET_RANDOM, 0)) + { + if (DoCastSpellIfCan(pTarget, SPELL_DRAIN_LIFE) == CAST_OK) + m_uiDrainLifeTimer = urand(15000, 20000); + } + } + else + m_uiDrainLifeTimer -= uiDiff; + + // Drain mana + if (m_uiDrainManaTimer < uiDiff) + { + if (Unit* pTarget = m_creature->SelectAttackingTarget(ATTACKING_TARGET_RANDOM, 0, SPELL_DRAIN_MANA, SELECT_FLAG_POWER_MANA)) + { + if (DoCastSpellIfCan(pTarget, SPELL_DRAIN_MANA) == CAST_OK) + m_uiDrainManaTimer = urand(20000, 30000); + } + } + else + m_uiDrainManaTimer -= uiDiff; + + DoMeleeAttackIfReady(); + } +}; + +CreatureAI* GetAI_boss_maleki_the_pallid(Creature* pCreature) +{ + return new boss_maleki_the_pallidAI(pCreature); +} + +void AddSC_boss_maleki_the_pallid() +{ + Script* pNewScript; + + pNewScript = new Script; + pNewScript->Name = "boss_maleki_the_pallid"; + pNewScript->GetAI = &GetAI_boss_maleki_the_pallid; + pNewScript->RegisterSelf(); +} diff --git a/src/modules/SD2/scripts/eastern_kingdoms/stratholme/boss_order_of_silver_hand.cpp b/src/modules/SD2/scripts/eastern_kingdoms/stratholme/boss_order_of_silver_hand.cpp new file mode 100644 index 000000000..ff647c2bc --- /dev/null +++ b/src/modules/SD2/scripts/eastern_kingdoms/stratholme/boss_order_of_silver_hand.cpp @@ -0,0 +1,181 @@ +/* Copyright (C) 2006 - 2013 ScriptDev2 + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with 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: 80 +SDComment: Timers; Not sure if we need to respawn dead npcs on evade; May need additional adjustments / research +SDCategory: Stratholme +EndScriptData */ + +#include "precompiled.h" +#include "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. +#####*/ + +enum +{ + // Gregor + SPELL_HAMMER_JUSTICE = 13005, + SPELL_HAMMER_WRATH = 32772, + SPELL_HOLY_SHOCK = 32771, + // Cathela + SPELL_HOLY_SHIELD = 32777, + SPELL_REDOUBT = 32776, + // Aelmar + SPELL_JUDGEMENT = 32778, + // Vicar + SPELL_BLESSING = 32770, + SPELL_HOLY_LIGHT = 32769, + + TARGET_TYPE_RANDOM = 0, + TARGET_TYPE_VICTIM = 1, + TARGET_TYPE_SELF = 2, + TARGET_TYPE_FRIENDLY = 3, +}; + +struct SilverHandAbilityStruct +{ + uint32 m_uiCreatureEntry, m_uiSpellId; + uint8 m_uiTargetType; + uint32 m_uiInitialTimer, m_uiCooldown; +}; + +static SilverHandAbilityStruct m_aSilverHandAbility[8] = +{ + {NPC_GREGOR_THE_JUSTICIAR, SPELL_HAMMER_JUSTICE, TARGET_TYPE_RANDOM, 2000, 15000}, + {NPC_GREGOR_THE_JUSTICIAR, SPELL_HAMMER_WRATH, TARGET_TYPE_RANDOM, 10000, 15000}, + {NPC_GREGOR_THE_JUSTICIAR, SPELL_HOLY_SHOCK, TARGET_TYPE_RANDOM, 4000, 7000}, + {NPC_CATHELA_THE_SEEKER, SPELL_HOLY_SHIELD, TARGET_TYPE_SELF, 1000, 60000}, + {NPC_CATHELA_THE_SEEKER, SPELL_REDOUBT, TARGET_TYPE_SELF, 5000, 15000}, + {NPC_AELMAR_THE_VANQUISHER, SPELL_JUDGEMENT, TARGET_TYPE_VICTIM, 4000, 9000}, + {NPC_VICAR_HYERONIMUS, SPELL_BLESSING, TARGET_TYPE_FRIENDLY, 2000, 13000}, + {NPC_VICAR_HYERONIMUS, SPELL_HOLY_LIGHT, TARGET_TYPE_FRIENDLY, 5000, 9000}, +}; +struct boss_silver_hand_bossesAI : public ScriptedAI +{ + boss_silver_hand_bossesAI(Creature* pCreature) : ScriptedAI(pCreature) + { + m_pInstance = (instance_stratholme*)pCreature->GetInstanceData(); + for (uint8 i = 0; i < countof(m_aSilverHandAbility); ++i) + { + if (m_aSilverHandAbility[i].m_uiCreatureEntry == m_creature->GetEntry()) + m_mSpellTimers[i] = m_aSilverHandAbility[i].m_uiInitialTimer; + } + Reset(); + } + + instance_stratholme* m_pInstance; + + UNORDERED_MAP m_mSpellTimers; + + void Reset() override + { + for (UNORDERED_MAP::iterator itr = m_mSpellTimers.begin(); itr != m_mSpellTimers.end(); ++itr) + itr->second = m_aSilverHandAbility[itr->first].m_uiInitialTimer; + } + + void JustDied(Unit* pKiller) override + { + if (m_pInstance) + { + // Set data to special when each paladin dies + m_pInstance->SetData(TYPE_TRUE_MASTERS, SPECIAL); + + // For the last one which dies, give the quest credit + if (m_pInstance->GetData(TYPE_TRUE_MASTERS) == DONE) + { + if (pKiller->GetTypeId() == TYPEID_PLAYER) + { + if (Creature* pCredit = m_pInstance->GetSingleCreatureFromStorage(NPC_PALADIN_QUEST_CREDIT)) + ((Player*)pKiller)->KilledMonsterCredit(NPC_PALADIN_QUEST_CREDIT, pCredit->GetObjectGuid()); + } + } + } + } + + bool CanUseSpecialAbility(uint32 uiIndex) + { + Unit* pTarget = NULL; + + switch (m_aSilverHandAbility[uiIndex].m_uiTargetType) + { + case TARGET_TYPE_SELF: + pTarget = m_creature; + break; + case TARGET_TYPE_VICTIM: + pTarget = m_creature->getVictim(); + break; + case TARGET_TYPE_RANDOM: + pTarget = m_creature->SelectAttackingTarget(ATTACKING_TARGET_RANDOM, 0, m_aSilverHandAbility[uiIndex].m_uiSpellId, SELECT_FLAG_IN_LOS); + break; + case TARGET_TYPE_FRIENDLY: + pTarget = DoSelectLowestHpFriendly(10.0f); + break; + } + + if (pTarget) + { + if (DoCastSpellIfCan(pTarget, m_aSilverHandAbility[uiIndex].m_uiSpellId) == CAST_OK) + return true; + } + + return false; + } + + void UpdateAI(const uint32 uiDiff) override + { + // Return since we have no target + if (!m_creature->SelectHostileTarget() || !m_creature->getVictim()) + return; + + for (UNORDERED_MAP::iterator itr = m_mSpellTimers.begin(); itr != m_mSpellTimers.end(); ++itr) + { + if (itr->second < uiDiff) + { + if (CanUseSpecialAbility(itr->first)) + { + itr->second = m_aSilverHandAbility[itr->first].m_uiCooldown; + break; + } + } + else + itr->second -= uiDiff; + } + + DoMeleeAttackIfReady(); + } +}; + +CreatureAI* GetAI_boss_silver_hand_bossesAI(Creature* pCreature) +{ + return new boss_silver_hand_bossesAI(pCreature); +} + +void AddSC_boss_order_of_silver_hand() +{ + Script* pNewScript; + + pNewScript = new Script; + pNewScript->Name = "boss_silver_hand_bosses"; + pNewScript->GetAI = &GetAI_boss_silver_hand_bossesAI; + pNewScript->RegisterSelf(); +} diff --git a/src/modules/SD2/scripts/eastern_kingdoms/stratholme/instance_stratholme.cpp b/src/modules/SD2/scripts/eastern_kingdoms/stratholme/instance_stratholme.cpp new file mode 100644 index 000000000..5931b9f3e --- /dev/null +++ b/src/modules/SD2/scripts/eastern_kingdoms/stratholme/instance_stratholme.cpp @@ -0,0 +1,793 @@ +/* Copyright (C) 2006 - 2013 ScriptDev2 + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along 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: 70 +SDComment: Undead side 90% implemented, event needs better implementation, Barthildas relocation for reload case is missing, Baron Combat handling is buggy. +SDCategory: Stratholme +EndScriptData */ + +#include "precompiled.h" +#include "stratholme.h" + +instance_stratholme::instance_stratholme(Map* pMap) : ScriptedInstance(pMap), + m_uiBaronRunTimer(0), + m_uiBarthilasRunTimer(0), + m_uiMindlessSummonTimer(0), + m_uiSlaugtherSquareTimer(0), + m_uiYellCounter(0), + m_uiMindlessCount(0), + m_uiPostboxesUsed(0), + m_uiSilverHandKilled(0) +{ + Initialize(); +} + +void instance_stratholme::Initialize() +{ + memset(&m_auiEncounter, 0, sizeof(m_auiEncounter)); +} + +bool instance_stratholme::StartSlaugtherSquare() +{ + if (m_auiEncounter[TYPE_BARONESS] == SPECIAL && m_auiEncounter[TYPE_NERUB] == SPECIAL && m_auiEncounter[TYPE_PALLID] == SPECIAL) + { + DoOrSimulateScriptTextForThisInstance(SAY_ANNOUNCE_RIVENDARE, NPC_BARON); + + DoUseDoorOrButton(GO_PORT_GAUNTLET); + DoUseDoorOrButton(GO_PORT_SLAUGTHER); + + debug_log("SD2: Instance Stratholme: Open slaugther square."); + + return true; + } + + return false; +} + +void instance_stratholme::OnCreatureCreate(Creature* pCreature) +{ + switch (pCreature->GetEntry()) + { + case NPC_BARON: + case NPC_YSIDA_TRIGGER: + case NPC_BARTHILAS: + case NPC_PALADIN_QUEST_CREDIT: + m_mNpcEntryGuidStore[pCreature->GetEntry()] = pCreature->GetObjectGuid(); + break; + + case NPC_CRYSTAL: + m_luiCrystalGUIDs.push_back(pCreature->GetObjectGuid()); + break; + case NPC_ABOM_BILE: + case NPC_ABOM_VENOM: + m_sAbomnationGUID.insert(pCreature->GetObjectGuid()); + break; + case NPC_THUZADIN_ACOLYTE: + m_luiAcolyteGUIDs.push_back(pCreature->GetObjectGuid()); + break; + case NPC_CRIMSON_INITIATE: + case NPC_CRIMSON_GALLANT: + case NPC_CRIMSON_GUARDSMAN: + case NPC_CRIMSON_CONJURER: + // Only store those in the yard + if (pCreature->IsWithinDist2d(aTimmyLocation[1].m_fX, aTimmyLocation[1].m_fY, 40.0f)) + m_suiCrimsonLowGuids.insert(pCreature->GetGUIDLow()); + break; + } +} + +void instance_stratholme::OnObjectCreate(GameObject* pGo) +{ + switch (pGo->GetEntry()) + { + case GO_SERVICE_ENTRANCE: + break; + case GO_GAUNTLET_GATE1: + // TODO + // weird, but unless flag is set, client will not respond as expected. DB bug? + pGo->SetFlag(GAMEOBJECT_FLAGS, GO_FLAG_LOCKED); + break; + + case GO_ZIGGURAT_DOOR_1: + m_zigguratStorage[0].m_doorGuid = pGo->GetObjectGuid(); + if (m_auiEncounter[TYPE_BARONESS] == DONE || m_auiEncounter[TYPE_BARONESS] == SPECIAL) + pGo->SetGoState(GO_STATE_ACTIVE); + return; + case GO_ZIGGURAT_DOOR_2: + m_zigguratStorage[1].m_doorGuid = pGo->GetObjectGuid(); + if (m_auiEncounter[TYPE_NERUB] == DONE || m_auiEncounter[TYPE_NERUB] == SPECIAL) + pGo->SetGoState(GO_STATE_ACTIVE); + return; + case GO_ZIGGURAT_DOOR_3: + m_zigguratStorage[2].m_doorGuid = pGo->GetObjectGuid(); + if (m_auiEncounter[TYPE_PALLID] == DONE || m_auiEncounter[TYPE_PALLID] == SPECIAL) + pGo->SetGoState(GO_STATE_ACTIVE); + return; + + case GO_ZIGGURAT_DOOR_4: + if (m_auiEncounter[TYPE_RAMSTEIN] == DONE) + pGo->SetGoState(GO_STATE_ACTIVE); + break; + case GO_ZIGGURAT_DOOR_5: + if (m_auiEncounter[TYPE_RAMSTEIN] == DONE) + pGo->SetGoState(GO_STATE_ACTIVE); + break; + case GO_PORT_GAUNTLET: + if (m_auiEncounter[TYPE_BARONESS] == SPECIAL && m_auiEncounter[TYPE_NERUB] == SPECIAL && m_auiEncounter[TYPE_PALLID] == SPECIAL) + pGo->SetGoState(GO_STATE_ACTIVE); + break; + case GO_PORT_SLAUGTHER: + if (m_auiEncounter[TYPE_BARONESS] == SPECIAL && m_auiEncounter[TYPE_NERUB] == SPECIAL && m_auiEncounter[TYPE_PALLID] == SPECIAL) + pGo->SetGoState(GO_STATE_ACTIVE); + break; + case GO_PORT_SLAUGHTER_GATE: + if (m_auiEncounter[TYPE_RAMSTEIN] == DONE) // Might actually be uneeded + pGo->SetGoState(GO_STATE_ACTIVE); + break; + case GO_PORT_ELDERS: + case GO_YSIDA_CAGE: + break; + + default: + return; + } + m_mGoEntryGuidStore[pGo->GetEntry()] = pGo->GetObjectGuid(); +} + +void instance_stratholme::SetData(uint32 uiType, uint32 uiData) +{ + // TODO: Remove the hard-coded indexes from array accessing + switch (uiType) + { + case TYPE_BARON_RUN: + switch (uiData) + { + case IN_PROGRESS: + if (m_auiEncounter[uiType] == IN_PROGRESS || m_auiEncounter[uiType] == FAIL) + break; + + DoOrSimulateScriptTextForThisInstance(SAY_ANNOUNCE_RUN_START, NPC_BARON); + + m_uiBaronRunTimer = 45 * MINUTE * IN_MILLISECONDS; + debug_log("SD2: Instance Stratholme: Baron run in progress."); + break; + case FAIL: + // may add code to remove aura from players, but in theory the time should be up already and removed. + break; + case DONE: + m_uiBaronRunTimer = 0; + break; + } + m_auiEncounter[uiType] = uiData; + break; + case TYPE_BARONESS: + case TYPE_NERUB: + case TYPE_PALLID: + m_auiEncounter[uiType] = uiData; + if (uiData == DONE) + { + DoSortZiggurats(); + DoUseDoorOrButton(m_zigguratStorage[uiType - TYPE_BARONESS].m_doorGuid); + } + if (uiData == SPECIAL) + StartSlaugtherSquare(); + break; + case TYPE_RAMSTEIN: + if (uiData == SPECIAL) + { + if (m_auiEncounter[uiType] != SPECIAL && m_auiEncounter[uiType] != DONE) + { + m_uiSlaugtherSquareTimer = 20000; // TODO - unknown, also possible that this is not the very correct place.. + DoUseDoorOrButton(GO_PORT_GAUNTLET); + } + + uint32 uiCount = m_sAbomnationGUID.size(); + for (GuidSet::iterator itr = m_sAbomnationGUID.begin(); itr != m_sAbomnationGUID.end();) + { + if (Creature* pAbom = instance->GetCreature(*itr)) + { + ++itr; + if (!pAbom->IsAlive()) + --uiCount; + } + else + { + // Remove obsolete guid from set and decrement count + m_sAbomnationGUID.erase(itr++); + --uiCount; + } + } + + if (!uiCount) + { + // Old Comment: a bit itchy, it should close GO_ZIGGURAT_DOOR_4 door after 10 secs, but it doesn't. skipping it for now. + // However looks like that this door is no more closed + DoUseDoorOrButton(GO_ZIGGURAT_DOOR_4); + + // No more handlng of Abomnations + m_uiSlaugtherSquareTimer = 0; + + if (Creature* pBaron = GetSingleCreatureFromStorage(NPC_BARON)) + { + DoScriptText(SAY_ANNOUNCE_RAMSTEIN, pBaron); + if (Creature* pRamstein = pBaron->SummonCreature(NPC_RAMSTEIN, aStratholmeLocation[2].m_fX, aStratholmeLocation[2].m_fY, aStratholmeLocation[2].m_fZ, aStratholmeLocation[2].m_fO, TEMPSUMMON_DEAD_DESPAWN, 0)) + pRamstein->GetMotionMaster()->MovePoint(0, aStratholmeLocation[3].m_fX, aStratholmeLocation[3].m_fY, aStratholmeLocation[3].m_fZ); + + debug_log("SD2: Instance Stratholme - Slaugther event: Ramstein spawned."); + } + } + else + debug_log("SD2: Instance Stratholme - Slaugther event: %u Abomnation left to kill.", uiCount); + } + // After fail aggroing Ramstein means wipe on Ramstein, so close door again + if (uiData == IN_PROGRESS && m_auiEncounter[uiType] == FAIL) + DoUseDoorOrButton(GO_PORT_GAUNTLET); + if (uiData == DONE) + { + // Open side gate and start summoning skeletons + DoUseDoorOrButton(GO_PORT_SLAUGHTER_GATE); + // use this timer as a bool just to start summoning + m_uiMindlessSummonTimer = 500; + m_uiMindlessCount = 0; + m_luiUndeadGUIDs.clear(); + + // Summon 5 guards + if (Creature* pBaron = GetSingleCreatureFromStorage(NPC_BARON)) + { + for (uint8 i = 0; i < 5; ++i) + { + float fX, fY, fZ; + pBaron->GetRandomPoint(aStratholmeLocation[6].m_fX, aStratholmeLocation[6].m_fY, aStratholmeLocation[6].m_fZ, 5.0f, fX, fY, fZ); + if (Creature* pTemp = pBaron->SummonCreature(NPC_BLACK_GUARD, aStratholmeLocation[6].m_fX, aStratholmeLocation[6].m_fY, aStratholmeLocation[6].m_fZ, aStratholmeLocation[6].m_fO, TEMPSUMMON_DEAD_DESPAWN, 0)) + m_luiGuardGUIDs.push_back(pTemp->GetObjectGuid()); + } + + debug_log("SD2: Instance Stratholme - Slaugther event: Summoned 5 guards."); + } + } + // Open Door again and stop Abomnation + if (uiData == FAIL && m_auiEncounter[uiType] != FAIL) + { + DoUseDoorOrButton(GO_PORT_GAUNTLET); + m_uiSlaugtherSquareTimer = 0; + + // Let already moving Abomnations stop + for (GuidSet::const_iterator itr = m_sAbomnationGUID.begin(); itr != m_sAbomnationGUID.end(); ++itr) + { + Creature* pAbom = instance->GetCreature(*itr); + if (pAbom && pAbom->GetMotionMaster()->GetCurrentMovementGeneratorType() == POINT_MOTION_TYPE) + pAbom->GetMotionMaster()->MovementExpired(); + } + } + + m_auiEncounter[uiType] = uiData; + break; + case TYPE_BARON: + if (uiData == IN_PROGRESS) + { + // Reached the Baron within time-limit + if (m_auiEncounter[TYPE_BARON_RUN] == IN_PROGRESS) + SetData(TYPE_BARON_RUN, DONE); + + // Close Slaughterhouse door if needed + if (m_auiEncounter[uiType] == FAIL) + DoUseDoorOrButton(GO_PORT_GAUNTLET); + } + if (uiData == DONE) + { + if (m_auiEncounter[TYPE_BARON_RUN] == DONE) + { + Map::PlayerList const& players = instance->GetPlayers(); + + 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); + } + } + + // Open cage and finish rescue event + if (Creature* pYsidaT = GetSingleCreatureFromStorage(NPC_YSIDA_TRIGGER)) + { + if (Creature* pYsida = pYsidaT->SummonCreature(NPC_YSIDA, pYsidaT->GetPositionX(), pYsidaT->GetPositionY(), pYsidaT->GetPositionZ(), pYsidaT->GetOrientation(), TEMPSUMMON_TIMED_DESPAWN, 1800000)) + { + DoScriptText(SAY_EPILOGUE, pYsida); + pYsida->GetMotionMaster()->MovePoint(0, aStratholmeLocation[7].m_fX, aStratholmeLocation[7].m_fY, aStratholmeLocation[7].m_fZ); + } + DoUseDoorOrButton(GO_YSIDA_CAGE); + } + } + + // Open Slaughterhouse door again + DoUseDoorOrButton(GO_PORT_GAUNTLET); + } + if (uiData == FAIL) + DoUseDoorOrButton(GO_PORT_GAUNTLET); + + m_auiEncounter[uiType] = uiData; + break; + case TYPE_BARTHILAS_RUN: + if (uiData == IN_PROGRESS) + { + Creature* pBarthilas = GetSingleCreatureFromStorage(NPC_BARTHILAS); + if (pBarthilas && pBarthilas->IsAlive() && !pBarthilas->IsInCombat()) + { + DoScriptText(SAY_WARN_BARON, pBarthilas); + pBarthilas->SetWalk(false); + pBarthilas->GetMotionMaster()->MovePoint(0, aStratholmeLocation[0].m_fX, aStratholmeLocation[0].m_fY, aStratholmeLocation[0].m_fZ); + + m_uiBarthilasRunTimer = 8000; + } + } + m_auiEncounter[uiType] = uiData; + break; + case TYPE_BLACK_GUARDS: + // Prevent double action + if (m_auiEncounter[uiType] == uiData) + return; + + // Restart after failure, close Gauntlet + if (uiData == IN_PROGRESS && m_auiEncounter[uiType] == FAIL) + DoUseDoorOrButton(GO_PORT_GAUNTLET); + // Wipe case - open gauntlet + if (uiData == FAIL) + DoUseDoorOrButton(GO_PORT_GAUNTLET); + if (uiData == DONE) + { + if (Creature* pBaron = GetSingleCreatureFromStorage(NPC_BARON)) + DoScriptText(SAY_UNDEAD_DEFEAT, pBaron); + DoUseDoorOrButton(GO_ZIGGURAT_DOOR_5); + } + m_auiEncounter[uiType] = uiData; + + // No need to save anything here, so return + return; + case TYPE_POSTMASTER: + m_auiEncounter[uiType] = uiData; + if (uiData == IN_PROGRESS) + { + ++m_uiPostboxesUsed; + + // After the second post box prepare to spawn the Post Master + if (m_uiPostboxesUsed == 2) + SetData(TYPE_POSTMASTER, SPECIAL); + } + // No need to save anything here, so return + return; + case TYPE_TRUE_MASTERS: + m_auiEncounter[uiType] = uiData; + if (uiData == SPECIAL) + { + ++m_uiSilverHandKilled; + + // When the 5th paladin is killed set data to DONE in order to give the quest credit for the last paladin + if (m_uiSilverHandKilled == MAX_SILVERHAND) + SetData(TYPE_TRUE_MASTERS, DONE); + } + // No need to save anything here, so return + return; + } + + 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_strInstData = saveStream.str(); + + SaveToDB(); + OUT_SAVE_INST_DATA_COMPLETE; + } +} + +void instance_stratholme::Load(const char* chrIn) +{ + if (!chrIn) + { + 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]; + + for (uint8 i = 0; i < MAX_ENCOUNTER; ++i) + { + if (m_auiEncounter[i] == IN_PROGRESS) + m_auiEncounter[i] = NOT_STARTED; + } + + // Special Treatment for the Ziggurat-Bosses, as otherwise the event couldn't reload + if (m_auiEncounter[TYPE_BARONESS] == DONE) + m_auiEncounter[TYPE_BARONESS] = SPECIAL; + if (m_auiEncounter[TYPE_NERUB] == DONE) + m_auiEncounter[TYPE_NERUB] = SPECIAL; + if (m_auiEncounter[TYPE_PALLID] == DONE) + m_auiEncounter[TYPE_PALLID] = SPECIAL; + + OUT_LOAD_INST_DATA_COMPLETE; +} + +uint32 instance_stratholme::GetData(uint32 uiType) const +{ + switch (uiType) + { + case TYPE_BARON_RUN: + case TYPE_BARONESS: + case TYPE_NERUB: + case TYPE_PALLID: + case TYPE_RAMSTEIN: + case TYPE_BARON: + case TYPE_BARTHILAS_RUN: + case TYPE_POSTMASTER: + case TYPE_TRUE_MASTERS: + return m_auiEncounter[uiType]; + default: + return 0; + } +} + +static bool sortByHeight(Creature* pFirst, Creature* pSecond) +{ + return pFirst && pSecond && pFirst->GetPositionZ() > pSecond->GetPositionZ(); +} + +void instance_stratholme::DoSortZiggurats() +{ + if (m_luiAcolyteGUIDs.empty()) + return; + + std::list lAcolytes; // Valid pointers, only used locally + for (GuidList::const_iterator itr = m_luiAcolyteGUIDs.begin(); itr != m_luiAcolyteGUIDs.end(); ++itr) + { + if (Creature* pAcolyte = instance->GetCreature(*itr)) + lAcolytes.push_back(pAcolyte); + } + m_luiAcolyteGUIDs.clear(); + + if (lAcolytes.empty()) + return; + + if (!GetSingleCreatureFromStorage(NPC_THUZADIN_ACOLYTE, true)) + { + // Sort the acolytes by height, and the one with the biggest height is the announcer (a bit outside the map) + lAcolytes.sort(sortByHeight); + m_mNpcEntryGuidStore[NPC_THUZADIN_ACOLYTE] = (*lAcolytes.begin())->GetObjectGuid(); + lAcolytes.erase(lAcolytes.begin()); + } + + // Sort Acolytes + for (std::list::iterator itr = lAcolytes.begin(); itr != lAcolytes.end();) + { + bool bAlreadyIterated = false; + for (uint8 i = 0; i < MAX_ZIGGURATS; ++i) + { + if (GameObject* pZigguratDoor = instance->GetGameObject(m_zigguratStorage[i].m_doorGuid)) + { + if ((*itr)->IsAlive() && (*itr)->IsWithinDistInMap(pZigguratDoor, 35.0f, false)) + { + m_zigguratStorage[i].m_lZigguratAcolyteGuid.push_back((*itr)->GetObjectGuid()); + itr = lAcolytes.erase(itr); + bAlreadyIterated = true; + break; + } + } + } + + if (itr != lAcolytes.end() && !bAlreadyIterated) + ++itr; + } + + // In case some mobs have not been able to be sorted, store their GUIDs again + for (std::list::const_iterator itr = lAcolytes.begin(); itr != lAcolytes.end(); ++itr) + m_luiAcolyteGUIDs.push_back((*itr)->GetObjectGuid()); + + // Sort Crystal + for (GuidList::iterator itr = m_luiCrystalGUIDs.begin(); itr != m_luiCrystalGUIDs.end();) + { + Creature* pCrystal = instance->GetCreature(*itr); + if (!pCrystal) + { + itr = m_luiCrystalGUIDs.erase(itr); + continue; + } + + bool bAlreadyIterated = false; + for (uint8 i = 0; i < MAX_ZIGGURATS; ++i) + { + if (GameObject* pZigguratDoor = instance->GetGameObject(m_zigguratStorage[i].m_doorGuid)) + { + if (pCrystal->IsWithinDistInMap(pZigguratDoor, 50.0f, false)) + { + m_zigguratStorage[i].m_crystalGuid = pCrystal->GetObjectGuid(); + itr = m_luiCrystalGUIDs.erase(itr); + bAlreadyIterated = true; + break; + } + } + } + + if (itr != m_luiCrystalGUIDs.end() && !bAlreadyIterated) + ++itr; + } +} + +void instance_stratholme::OnCreatureEnterCombat(Creature* pCreature) +{ + switch (pCreature->GetEntry()) + { + case NPC_BARONESS_ANASTARI: SetData(TYPE_BARONESS, IN_PROGRESS); break; + case NPC_MALEKI_THE_PALLID: SetData(TYPE_PALLID, IN_PROGRESS); break; + case NPC_NERUBENKAN: SetData(TYPE_NERUB, IN_PROGRESS); break; + case NPC_RAMSTEIN: SetData(TYPE_RAMSTEIN, IN_PROGRESS); break; + // TODO - uncomment when proper working within core! case NPC_BARON: SetData(TYPE_BARON, IN_PROGRESS); break; + + case NPC_ABOM_BILE: + case NPC_ABOM_VENOM: + // Start Slaughterhouse Event + SetData(TYPE_RAMSTEIN, SPECIAL); + break; + + case NPC_MINDLESS_UNDEAD: + case NPC_BLACK_GUARD: + // Aggro in Slaughterhouse after Ramstein + SetData(TYPE_BLACK_GUARDS, IN_PROGRESS); + break; + } +} + +void instance_stratholme::OnCreatureEvade(Creature* pCreature) +{ + switch (pCreature->GetEntry()) + { + case NPC_BARONESS_ANASTARI: SetData(TYPE_BARONESS, FAIL); break; + case NPC_MALEKI_THE_PALLID: SetData(TYPE_PALLID, FAIL); break; + case NPC_NERUBENKAN: SetData(TYPE_NERUB, FAIL); break; + case NPC_RAMSTEIN: SetData(TYPE_RAMSTEIN, FAIL); break; + // TODO - uncomment when proper working within core! case NPC_BARON: SetData(TYPE_BARON, FAIL); break; + + case NPC_ABOM_BILE: + case NPC_ABOM_VENOM: + // Fail in Slaughterhouse Event before Ramstein + SetData(TYPE_RAMSTEIN, FAIL); + break; + case NPC_MINDLESS_UNDEAD: + case NPC_BLACK_GUARD: + // Fail in Slaughterhouse after Ramstein + SetData(TYPE_BLACK_GUARDS, FAIL); + break; + } +} + +void instance_stratholme::OnCreatureDeath(Creature* pCreature) +{ + switch (pCreature->GetEntry()) + { + case NPC_BARONESS_ANASTARI: SetData(TYPE_BARONESS, DONE); break; + case NPC_MALEKI_THE_PALLID: SetData(TYPE_PALLID, DONE); break; + case NPC_NERUBENKAN: SetData(TYPE_NERUB, DONE); break; + case NPC_RAMSTEIN: SetData(TYPE_RAMSTEIN, DONE); break; + case NPC_BARON: SetData(TYPE_BARON, DONE); break; + + case NPC_THUZADIN_ACOLYTE: + ThazudinAcolyteJustDied(pCreature); + break; + + case NPC_ABOM_BILE: + case NPC_ABOM_VENOM: + // Start Slaughterhouse Event + SetData(TYPE_RAMSTEIN, SPECIAL); + break; + + case NPC_MINDLESS_UNDEAD: + m_luiUndeadGUIDs.remove(pCreature->GetObjectGuid()); + if (m_luiUndeadGUIDs.empty()) + { + // Let the black Guards move out of the citadel + for (GuidList::const_iterator itr = m_luiGuardGUIDs.begin(); itr != m_luiGuardGUIDs.end(); ++itr) + { + Creature* pGuard = instance->GetCreature(*itr); + if (pGuard && pGuard->IsAlive() && !pGuard->IsInCombat()) + { + float fX, fY, fZ; + pGuard->GetRandomPoint(aStratholmeLocation[5].m_fX, aStratholmeLocation[5].m_fY, aStratholmeLocation[5].m_fZ, 10.0f, fX, fY, fZ); + pGuard->GetMotionMaster()->MovePoint(0, fX, fY, fZ); + } + } + } + break; + case NPC_BLACK_GUARD: + m_luiGuardGUIDs.remove(pCreature->GetObjectGuid()); + if (m_luiGuardGUIDs.empty()) + SetData(TYPE_BLACK_GUARDS, DONE); + + break; + + // Timmy spawn support + case NPC_CRIMSON_INITIATE: + case NPC_CRIMSON_GALLANT: + case NPC_CRIMSON_GUARDSMAN: + case NPC_CRIMSON_CONJURER: + if (m_suiCrimsonLowGuids.find(pCreature->GetGUIDLow()) != m_suiCrimsonLowGuids.end()) + { + m_suiCrimsonLowGuids.erase(pCreature->GetGUIDLow()); + + // If all courtyard mobs are dead then summon Timmy + if (m_suiCrimsonLowGuids.empty()) + pCreature->SummonCreature(NPC_TIMMY_THE_CRUEL, aTimmyLocation[0].m_fX, aTimmyLocation[0].m_fY, aTimmyLocation[0].m_fZ, aTimmyLocation[0].m_fO, TEMPSUMMON_DEAD_DESPAWN, 0); + } + break; + } +} + +void instance_stratholme::ThazudinAcolyteJustDied(Creature* pCreature) +{ + for (uint8 i = 0; i < MAX_ZIGGURATS; ++i) + { + if (m_zigguratStorage[i].m_lZigguratAcolyteGuid.empty()) + continue; // nothing to do anymore for this ziggurat + + m_zigguratStorage[i].m_lZigguratAcolyteGuid.remove(pCreature->GetObjectGuid()); + if (m_zigguratStorage[i].m_lZigguratAcolyteGuid.empty()) + { + // A random zone yell after one is cleared + int32 aAnnounceSay[MAX_ZIGGURATS] = {SAY_ANNOUNCE_ZIGGURAT_1, SAY_ANNOUNCE_ZIGGURAT_2, SAY_ANNOUNCE_ZIGGURAT_3}; + DoOrSimulateScriptTextForThisInstance(aAnnounceSay[i], NPC_THUZADIN_ACOLYTE); + + // Kill Crystal + if (Creature* pCrystal = instance->GetCreature(m_zigguratStorage[i].m_crystalGuid)) + pCrystal->DealDamage(pCrystal, pCrystal->GetHealth(), NULL, DIRECT_DAMAGE, SPELL_SCHOOL_MASK_NORMAL, NULL, false); + + switch (i) + { + case 0: SetData(TYPE_BARONESS, SPECIAL); break; + case 1: SetData(TYPE_NERUB, SPECIAL); break; + case 2: SetData(TYPE_PALLID, SPECIAL); break; + } + } + } +} + +void instance_stratholme::Update(uint32 uiDiff) +{ + if (m_uiBarthilasRunTimer) + { + if (m_uiBarthilasRunTimer <= uiDiff) + { + Creature* pBarthilas = GetSingleCreatureFromStorage(NPC_BARTHILAS); + if (pBarthilas && pBarthilas->IsAlive() && !pBarthilas->IsInCombat()) + pBarthilas->NearTeleportTo(aStratholmeLocation[1].m_fX, aStratholmeLocation[1].m_fY, aStratholmeLocation[1].m_fZ, aStratholmeLocation[1].m_fO); + + SetData(TYPE_BARTHILAS_RUN, DONE); + m_uiBarthilasRunTimer = 0; + } + else + m_uiBarthilasRunTimer -= uiDiff; + } + + if (m_uiBaronRunTimer) + { + if (m_uiYellCounter == 0 && m_uiBaronRunTimer <= 10 * MINUTE * IN_MILLISECONDS) + { + DoOrSimulateScriptTextForThisInstance(SAY_ANNOUNCE_RUN_10_MIN, NPC_BARON); + ++m_uiYellCounter; + } + else if (m_uiYellCounter == 1 && m_uiBaronRunTimer <= 5 * MINUTE * IN_MILLISECONDS) + { + DoOrSimulateScriptTextForThisInstance(SAY_ANNOUNCE_RUN_5_MIN, NPC_BARON); + ++m_uiYellCounter; + } + + if (m_uiBaronRunTimer <= uiDiff) + { + SetData(TYPE_BARON_RUN, FAIL); + + DoOrSimulateScriptTextForThisInstance(SAY_ANNOUNCE_RUN_FAIL, NPC_BARON); + + m_uiBaronRunTimer = 0; + debug_log("SD2: Instance Stratholme: Baron run event reached end. Event has state %u.", GetData(TYPE_BARON_RUN)); + } + else + m_uiBaronRunTimer -= uiDiff; + } + + if (m_uiMindlessSummonTimer) + { + if (m_uiMindlessCount < 30) + { + if (m_uiMindlessSummonTimer <= uiDiff) + { + if (Creature* pBaron = GetSingleCreatureFromStorage(NPC_BARON)) + { + // Summon mindless skeletons and move them to random point in the center of the square + if (Creature* pTemp = pBaron->SummonCreature(NPC_MINDLESS_UNDEAD, aStratholmeLocation[4].m_fX, aStratholmeLocation[4].m_fY, aStratholmeLocation[4].m_fZ, aStratholmeLocation[4].m_fO, TEMPSUMMON_DEAD_DESPAWN, 0)) + { + float fX, fY, fZ; + pBaron->GetRandomPoint(aStratholmeLocation[5].m_fX, aStratholmeLocation[5].m_fY, aStratholmeLocation[5].m_fZ, 20.0f, fX, fY, fZ); + pTemp->GetMotionMaster()->MovePoint(0, fX, fY, fZ); + m_luiUndeadGUIDs.push_back(pTemp->GetObjectGuid()); + ++m_uiMindlessCount; + } + } + m_uiMindlessSummonTimer = 400; + } + else + m_uiMindlessSummonTimer -= uiDiff; + } + else + m_uiMindlessSummonTimer = 0; + } + + if (m_uiSlaugtherSquareTimer) + { + if (m_uiSlaugtherSquareTimer <= uiDiff) + { + // Call next Abomnations + for (GuidSet::const_iterator itr = m_sAbomnationGUID.begin(); itr != m_sAbomnationGUID.end(); ++itr) + { + Creature* pAbom = instance->GetCreature(*itr); + // Skip killed and already walking Abomnations + if (!pAbom || !pAbom->IsAlive() || pAbom->GetMotionMaster()->GetCurrentMovementGeneratorType() == POINT_MOTION_TYPE) + continue; + + // Let Move to somewhere in the middle + if (!pAbom->IsInCombat()) + { + if (GameObject* pDoor = GetSingleGameObjectFromStorage(GO_PORT_SLAUGTHER)) + { + float fX, fY, fZ; + pAbom->GetRandomPoint(pDoor->GetPositionX(), pDoor->GetPositionY(), pDoor->GetPositionZ(), 10.0f, fX, fY, fZ); + pAbom->GetMotionMaster()->MovePoint(0, fX, fY, fZ); + } + } + break; + } + + // TODO - how fast are they called? + m_uiSlaugtherSquareTimer = urand(15000, 30000); + } + else + m_uiSlaugtherSquareTimer -= uiDiff; + } +} + +InstanceData* GetInstanceData_instance_stratholme(Map* pMap) +{ + return new instance_stratholme(pMap); +} + +void AddSC_instance_stratholme() +{ + Script* pNewScript; + + pNewScript = new Script; + pNewScript->Name = "instance_stratholme"; + pNewScript->GetInstanceData = &GetInstanceData_instance_stratholme; + pNewScript->RegisterSelf(); +} diff --git a/src/modules/SD2/scripts/eastern_kingdoms/stratholme/stratholme.cpp b/src/modules/SD2/scripts/eastern_kingdoms/stratholme/stratholme.cpp new file mode 100644 index 000000000..66141d0b7 --- /dev/null +++ b/src/modules/SD2/scripts/eastern_kingdoms/stratholme/stratholme.cpp @@ -0,0 +1,338 @@ +/* Copyright (C) 2006 - 2013 ScriptDev2 + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along 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. GO-script to apply aura and start event for quest 8945 +SDCategory: Stratholme +EndScriptData */ + +/* ContentData +go_service_gate +go_gauntlet_gate +go_stratholme_postbox +mob_restless_soul +mobs_spectral_ghostly_citizen +EndContentData */ + +#include "precompiled.h" +#include "stratholme.h" + +/*###### +## go_service_gate +######*/ + +bool GOUse_go_service_gate(Player* /*pPlayer*/, GameObject* pGo) +{ + ScriptedInstance* pInstance = (ScriptedInstance*)pGo->GetInstanceData(); + + if (!pInstance) + return false; + + if (pInstance->GetData(TYPE_BARTHILAS_RUN) != NOT_STARTED) + return false; + + // if the service gate is used make Barthilas flee + pInstance->SetData(TYPE_BARTHILAS_RUN, IN_PROGRESS); + return false; +} + +/*###### +## go_gauntlet_gate (this is the _first_ of the gauntlet gates, two exist) +######*/ + +bool GOUse_go_gauntlet_gate(Player* pPlayer, GameObject* pGo) +{ + ScriptedInstance* pInstance = (ScriptedInstance*)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()) + { + Player* pGroupie = itr->getSource(); + if (!pGroupie) + continue; + + if (!pGroupie->HasAura(SPELL_BARON_ULTIMATUM)) + pGroupie->CastSpell(pGroupie, SPELL_BARON_ULTIMATUM, true); + } + } + else + { + if (!pPlayer->HasAura(SPELL_BARON_ULTIMATUM)) + pPlayer->CastSpell(pPlayer, SPELL_BARON_ULTIMATUM, true); + } + + pInstance->SetData(TYPE_BARON_RUN, IN_PROGRESS); + return false; +} + +/*###### +## go_stratholme_postbox +######*/ + +bool GOUse_go_stratholme_postbox(Player* pPlayer, GameObject* pGo) +{ + ScriptedInstance* pInstance = (ScriptedInstance*)pGo->GetInstanceData(); + + if (!pInstance) + return false; + + if (pInstance->GetData(TYPE_POSTMASTER) == DONE) + return false; + + // When the data is Special, spawn the postmaster + if (pInstance->GetData(TYPE_POSTMASTER) == SPECIAL) + { + pPlayer->CastSpell(pPlayer, SPELL_SUMMON_POSTMASTER, true); + pInstance->SetData(TYPE_POSTMASTER, DONE); + } + else + pInstance->SetData(TYPE_POSTMASTER, IN_PROGRESS); + + // Summon 3 postmen for each postbox + float fX, fY, fZ; + for (uint8 i = 0; i < 3; ++i) + { + pPlayer->GetRandomPoint(pPlayer->GetPositionX(), pPlayer->GetPositionY(), pPlayer->GetPositionZ(), 3.0f, fX, fY, fZ); + pPlayer->SummonCreature(NPC_UNDEAD_POSTMAN, fX, fY, fZ, 0.0f, TEMPSUMMON_DEAD_DESPAWN, 0); + } + + return false; +} + +/*###### +## mob_restless_soul +######*/ + +enum +{ + // Possibly more of these quotes around. + SAY_ZAPPED0 = -1329000, + SAY_ZAPPED1 = -1329001, + SAY_ZAPPED2 = -1329002, + SAY_ZAPPED3 = -1329003, + + QUEST_RESTLESS_SOUL = 5282, + + SPELL_EGAN_BLASTER = 17368, + SPELL_SOUL_FREED = 17370, + + NPC_RESTLESS_SOUL = 11122, + NPC_FREED_SOUL = 11136, +}; + +// TODO - likely entirely not needed workaround +struct mob_restless_soulAI : public ScriptedAI +{ + mob_restless_soulAI(Creature* pCreature) : ScriptedAI(pCreature) { Reset(); } + + ObjectGuid m_taggerGuid; + uint32 m_uiDieTimer; + bool m_bIsTagged; + + void Reset() override + { + m_taggerGuid.Clear(); + m_uiDieTimer = 5000; + m_bIsTagged = false; + } + + void SpellHit(Unit* pCaster, const SpellEntry* pSpell) override + { + if (pCaster->GetTypeId() == TYPEID_PLAYER) + { + if (!m_bIsTagged && pSpell->Id == SPELL_EGAN_BLASTER && ((Player*)pCaster)->GetQuestStatus(QUEST_RESTLESS_SOUL) == QUEST_STATUS_INCOMPLETE) + { + m_bIsTagged = true; + m_taggerGuid = pCaster->GetObjectGuid(); + } + } + } + + void JustSummoned(Creature* pSummoned) override + { + if (pSummoned->GetEntry() == NPC_FREED_SOUL) + { + pSummoned->CastSpell(pSummoned, SPELL_SOUL_FREED, false); + + switch (urand(0, 3)) + { + case 0: DoScriptText(SAY_ZAPPED0, pSummoned); break; + case 1: DoScriptText(SAY_ZAPPED1, pSummoned); break; + case 2: DoScriptText(SAY_ZAPPED2, pSummoned); break; + case 3: DoScriptText(SAY_ZAPPED3, pSummoned); break; + } + } + } + + void JustDied(Unit* /*Killer*/) override + { + if (m_bIsTagged) + m_creature->SummonCreature(NPC_FREED_SOUL, 0.0f, 0.0f, 0.0f, 0.0f, TEMPSUMMON_TIMED_DESPAWN, 300000); + } + + void UpdateAI(const uint32 uiDiff) override + { + if (m_bIsTagged) + { + if (m_uiDieTimer < uiDiff) + { + if (Player* pPlayer = m_creature->GetMap()->GetPlayer(m_taggerGuid)) + pPlayer->DealDamage(m_creature, m_creature->GetHealth(), NULL, DIRECT_DAMAGE, SPELL_SCHOOL_MASK_NORMAL, NULL, false); + } + else + m_uiDieTimer -= uiDiff; + } + } +}; + +CreatureAI* GetAI_mob_restless_soul(Creature* pCreature) +{ + return new mob_restless_soulAI(pCreature); +} + +/*###### +## mobs_spectral_ghostly_citizen +######*/ + +enum +{ + SPELL_HAUNTING_PHANTOM = 16336, + SPELL_SLAP = 6754 +}; + +struct mobs_spectral_ghostly_citizenAI : public ScriptedAI +{ + mobs_spectral_ghostly_citizenAI(Creature* pCreature) : ScriptedAI(pCreature) {Reset();} + + uint32 m_uiDieTimer; + bool m_bIsTagged; + + void Reset() override + { + m_uiDieTimer = 5000; + m_bIsTagged = false; + } + + void SpellHit(Unit* /*pCaster*/, const SpellEntry* pSpell) override + { + if (!m_bIsTagged && pSpell->Id == SPELL_EGAN_BLASTER) + m_bIsTagged = true; + } + + void JustDied(Unit* /*Killer*/) override + { + if (m_bIsTagged) + { + for (uint32 i = 0; i < 4; ++i) + { + float x, y, z; + m_creature->GetRandomPoint(m_creature->GetPositionX(), m_creature->GetPositionY(), m_creature->GetPositionZ(), 20.0f, x, y, z); + + // 100%, 50%, 33%, 25% chance to spawn + uint32 j = urand(0, i); + if (j == 0) + m_creature->SummonCreature(NPC_RESTLESS_SOUL, x, y, z, 0, TEMPSUMMON_DEAD_DESPAWN, 0); + } + } + } + + void UpdateAI(const uint32 uiDiff) override + { + if (m_bIsTagged) + { + if (m_uiDieTimer < uiDiff) + { + m_creature->DealDamage(m_creature, m_creature->GetHealth(), NULL, DIRECT_DAMAGE, SPELL_SCHOOL_MASK_NORMAL, NULL, false); + } + else + m_uiDieTimer -= uiDiff; + } + + if (!m_creature->SelectHostileTarget() || !m_creature->getVictim()) + return; + + DoMeleeAttackIfReady(); + } + + void ReceiveEmote(Player* pPlayer, uint32 uiEmote) override + { + switch (uiEmote) + { + case TEXTEMOTE_DANCE: + EnterEvadeMode(); + break; + case TEXTEMOTE_RUDE: + if (m_creature->IsWithinDistInMap(pPlayer, INTERACTION_DISTANCE)) + m_creature->CastSpell(pPlayer, SPELL_SLAP, false); + else + m_creature->HandleEmote(EMOTE_ONESHOT_RUDE); + break; + case TEXTEMOTE_WAVE: + m_creature->HandleEmote(EMOTE_ONESHOT_WAVE); + break; + case TEXTEMOTE_BOW: + m_creature->HandleEmote(EMOTE_ONESHOT_BOW); + break; + case TEXTEMOTE_KISS: + m_creature->HandleEmote(EMOTE_ONESHOT_FLEX); + break; + } + } +}; + +CreatureAI* GetAI_mobs_spectral_ghostly_citizen(Creature* pCreature) +{ + return new mobs_spectral_ghostly_citizenAI(pCreature); +} + +void AddSC_stratholme() +{ + Script* pNewScript; + + pNewScript = new Script; + pNewScript->Name = "go_service_gate"; + pNewScript->pGOUse = &GOUse_go_service_gate; + pNewScript->RegisterSelf(); + + pNewScript = new Script; + pNewScript->Name = "go_gauntlet_gate"; + pNewScript->pGOUse = &GOUse_go_gauntlet_gate; + pNewScript->RegisterSelf(); + + pNewScript = new Script; + pNewScript->Name = "go_stratholme_postbox"; + pNewScript->pGOUse = &GOUse_go_stratholme_postbox; + pNewScript->RegisterSelf(); + + pNewScript = new Script; + pNewScript->Name = "mob_restless_soul"; + pNewScript->GetAI = &GetAI_mob_restless_soul; + pNewScript->RegisterSelf(); + + pNewScript = new Script; + pNewScript->Name = "mobs_spectral_ghostly_citizen"; + pNewScript->GetAI = &GetAI_mobs_spectral_ghostly_citizen; + pNewScript->RegisterSelf(); +} diff --git a/src/modules/SD2/scripts/eastern_kingdoms/stratholme/stratholme.h b/src/modules/SD2/scripts/eastern_kingdoms/stratholme/stratholme.h new file mode 100644 index 000000000..37eebb079 --- /dev/null +++ b/src/modules/SD2/scripts/eastern_kingdoms/stratholme/stratholme.h @@ -0,0 +1,165 @@ +/* Copyright (C) 2006 - 2013 ScriptDev2 +* This program is free software licensed under GPL version 2 +* Please see the included DOCS/LICENSE.TXT for more information */ + +#ifndef DEF_STRATHOLME_H +#define DEF_STRATHOLME_H + +enum +{ + MAX_ENCOUNTER = 10, + MAX_SILVERHAND = 5, + MAX_ZIGGURATS = 3, + + TYPE_BARON_RUN = 0, + TYPE_BARONESS = 1, + TYPE_NERUB = TYPE_BARONESS + 1, // Assert that these three TYPEs are in correct order. + TYPE_PALLID = TYPE_BARONESS + 2, + TYPE_RAMSTEIN = 4, + TYPE_BARON = 5, + TYPE_BARTHILAS_RUN = 6, + TYPE_BLACK_GUARDS = 7, + TYPE_POSTMASTER = 8, + TYPE_TRUE_MASTERS = 9, + + NPC_TIMMY_THE_CRUEL = 10808, + NPC_BARTHILAS = 10435, + NPC_BARONESS_ANASTARI = 10436, + NPC_NERUBENKAN = 10437, + NPC_MALEKI_THE_PALLID = 10438, + NPC_RAMSTEIN = 10439, + NPC_BARON = 10440, + NPC_CRYSTAL = 10415, // Three ziggurat crystals + NPC_THUZADIN_ACOLYTE = 10399, // Acolytes in ziggurats + NPC_ABOM_BILE = 10416, + NPC_ABOM_VENOM = 10417, + NPC_MINDLESS_UNDEAD = 11030, // Zombies summoned after Ramstein + NPC_BLACK_GUARD = 10394, // Zombies summoned after Ramstein + NPC_YSIDA = 16031, + NPC_YSIDA_TRIGGER = 16100, + NPC_CRIMSON_INITIATE = 10420, // A couple of them related to spawn Timmy + NPC_CRIMSON_GALLANT = 10424, + NPC_CRIMSON_GUARDSMAN = 10418, + NPC_CRIMSON_CONJURER = 10419, + NPC_UNDEAD_POSTMAN = 11142, + NPC_GREGOR_THE_JUSTICIAR = 17910, // related to quest "True Masters of the Light" + NPC_CATHELA_THE_SEEKER = 17911, + NPC_NEMAS_THE_ARBITER = 17912, + NPC_AELMAR_THE_VANQUISHER = 17913, + NPC_VICAR_HYERONIMUS = 17914, + NPC_PALADIN_QUEST_CREDIT = 17915, + + GO_SERVICE_ENTRANCE = 175368, + GO_GAUNTLET_GATE1 = 175357, + GO_PORT_SLAUGHTER_GATE = 175358, // Port used at the undeads event + GO_ZIGGURAT_DOOR_1 = 175380, // Baroness + GO_ZIGGURAT_DOOR_2 = 175379, // Nerub'enkan + GO_ZIGGURAT_DOOR_3 = 175381, // Maleki + GO_ZIGGURAT_DOOR_4 = 175405, // Ramstein + GO_ZIGGURAT_DOOR_5 = 175796, // Baron + GO_PORT_GAUNTLET = 175374, // Port from gauntlet to slaugther + GO_PORT_SLAUGTHER = 175373, // Port at slaugther + GO_PORT_ELDERS = 175377, // Port at elders square + GO_YSIDA_CAGE = 181071, // Cage to open after baron event is done + + QUEST_DEAD_MAN_PLEA = 8945, + SPELL_BARON_ULTIMATUM = 27861, + SPELL_SUMMON_POSTMASTER = 24627, + + SAY_ANNOUNCE_ZIGGURAT_1 = -1329004, + SAY_ANNOUNCE_ZIGGURAT_2 = -1329005, + SAY_ANNOUNCE_ZIGGURAT_3 = -1329006, + SAY_ANNOUNCE_RIVENDARE = -1329007, + + SAY_WARN_BARON = -1329008, + SAY_ANNOUNCE_RUN_START = -1329009, + SAY_ANNOUNCE_RUN_10_MIN = -1329010, + SAY_ANNOUNCE_RUN_5_MIN = -1329011, + SAY_ANNOUNCE_RUN_FAIL = -1329012, + SAY_ANNOUNCE_RAMSTEIN = -1329013, + SAY_UNDEAD_DEFEAT = -1329014, + SAY_EPILOGUE = -1329015, +}; + +struct EventLocation +{ + float m_fX, m_fY, m_fZ, m_fO; +}; + +static const EventLocation aStratholmeLocation[] = +{ + {3725.577f, -3599.484f, 142.367f}, // Barthilas door run + {4068.284f, -3535.678f, 122.771f, 2.50f}, // Barthilas tele + {4032.643f, -3378.546f, 119.752f, 4.74f}, // Ramstein summon loc + {4032.843f, -3390.246f, 119.732f}, // Ramstein move loc + {3969.357f, -3391.871f, 119.116f, 5.91f}, // Skeletons summon loc + {4033.044f, -3431.031f, 119.055f}, // Skeletons move loc + {4032.602f, -3378.506f, 119.752f, 4.74f}, // Guards summon loc + {4042.575f, -3337.929f, 115.059f} // Ysida move loc +}; + +static const EventLocation aTimmyLocation[] = +{ + {3696.851f, -3152.736f, 127.661f, 4.024f}, // Timmy spawn loc + {3668.603f, -3183.314f, 126.215f} // Courtyard mobs sort point +}; + +struct ZigguratStore +{ + ObjectGuid m_doorGuid; + ObjectGuid m_crystalGuid; + GuidList m_lZigguratAcolyteGuid; +}; + +class instance_stratholme : public ScriptedInstance +{ + public: + instance_stratholme(Map* pMap); + ~instance_stratholme() {} + + void Initialize() override; + + void OnCreatureCreate(Creature* pCreature) override; + void OnObjectCreate(GameObject* pGo) override; + + void SetData(uint32 uiType, uint32 uiData) override; + uint32 GetData(uint32 uiType) const override; + + const char* Save() const override { return m_strInstData.c_str(); } + void Load(const char* chrIn) override; + + void OnCreatureEnterCombat(Creature* pCreature) override; + void OnCreatureEvade(Creature* pCreature); + void OnCreatureDeath(Creature* pCreature) override; + + void Update(uint32 uiDiff) override; + + protected: + bool StartSlaugtherSquare(); + void DoSortZiggurats(); + void ThazudinAcolyteJustDied(Creature* pCreature); + + uint32 m_auiEncounter[MAX_ENCOUNTER]; + std::string m_strInstData; + + uint32 m_uiBaronRunTimer; + uint32 m_uiBarthilasRunTimer; + uint32 m_uiMindlessSummonTimer; + uint32 m_uiSlaugtherSquareTimer; + + uint32 m_uiYellCounter; + uint32 m_uiMindlessCount; + uint8 m_uiPostboxesUsed; + uint8 m_uiSilverHandKilled; + + ZigguratStore m_zigguratStorage[MAX_ZIGGURATS]; + + std::set m_suiCrimsonLowGuids; + GuidList m_luiCrystalGUIDs; + GuidSet m_sAbomnationGUID; + GuidList m_luiAcolyteGUIDs; + GuidList m_luiUndeadGUIDs; + GuidList m_luiGuardGUIDs; +}; + +#endif diff --git a/src/modules/SD2/scripts/eastern_kingdoms/sunken_temple/instance_sunken_temple.cpp b/src/modules/SD2/scripts/eastern_kingdoms/sunken_temple/instance_sunken_temple.cpp new file mode 100644 index 000000000..1a1bf60d2 --- /dev/null +++ b/src/modules/SD2/scripts/eastern_kingdoms/sunken_temple/instance_sunken_temple.cpp @@ -0,0 +1,440 @@ +/* Copyright (C) 2006 - 2013 ScriptDev2 + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along 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: 90 +SDComment: Hakkar Summon Event needs more sources to improve +SDCategory: Sunken Temple +EndScriptData */ + +#include "precompiled.h" +#include "sunken_temple.h" + +instance_sunken_temple::instance_sunken_temple(Map* pMap) : ScriptedInstance(pMap), + m_uiProtectorsRemaining(0), + m_uiStatueCounter(0), + m_uiFlameCounter(0), + m_uiAvatarSummonTimer(0), + m_uiSupressorTimer(0), + m_bIsFirstHakkarWave(false), + m_bCanSummonBloodkeeper(false) +{ + Initialize(); +} + +void instance_sunken_temple::Initialize() +{ + memset(&m_auiEncounter, 0, sizeof(m_auiEncounter)); +} + +void instance_sunken_temple::OnObjectCreate(GameObject* pGo) +{ + switch (pGo->GetEntry()) + { + case GO_JAMMALAN_BARRIER: + if (m_auiEncounter[TYPE_PROTECTORS] == DONE) + pGo->SetGoState(GO_STATE_ACTIVE); + break; + case GO_IDOL_OF_HAKKAR: + case GO_HAKKAR_DOOR_1: + case GO_HAKKAR_DOOR_2: + break; + + case GO_ATALAI_LIGHT_BIG: + m_luiBigLightGUIDs.push_back(pGo->GetObjectGuid()); + return; + case GO_EVIL_CIRCLE: + m_vuiCircleGUIDs.push_back(pGo->GetObjectGuid()); + return; + case GO_ETERNAL_FLAME_1: + case GO_ETERNAL_FLAME_2: + case GO_ETERNAL_FLAME_3: + case GO_ETERNAL_FLAME_4: + m_luiFlameGUIDs.push_back(pGo->GetObjectGuid()); + return; + + default: + return; + } + m_mGoEntryGuidStore[pGo->GetEntry()] = pGo->GetObjectGuid(); +} + +void instance_sunken_temple::OnCreatureCreate(Creature* pCreature) +{ + switch (pCreature->GetEntry()) + { + case NPC_ZOLO: + case NPC_GASHER: + case NPC_LORO: + case NPC_HUKKU: + case NPC_ZULLOR: + case NPC_MIJAN: + ++m_uiProtectorsRemaining; + break; + case NPC_JAMMALAN: + case NPC_ATALARION: + m_mNpcEntryGuidStore[pCreature->GetEntry()] = pCreature->GetObjectGuid(); + break; + } +} + +void instance_sunken_temple::OnCreatureEvade(Creature* pCreature) +{ + switch (pCreature->GetEntry()) + { + // Hakkar Event Mobs: On Wipe set as failed! + case NPC_BLOODKEEPER: + case NPC_HAKKARI_MINION: + case NPC_SUPPRESSOR: + case NPC_AVATAR_OF_HAKKAR: + SetData(TYPE_AVATAR, FAIL); + break; + } +} + +void instance_sunken_temple::OnCreatureDeath(Creature* pCreature) +{ + switch (pCreature->GetEntry()) + { + case NPC_ATALARION: SetData(TYPE_ATALARION, DONE); break; + case NPC_JAMMALAN: SetData(TYPE_JAMMALAN, DONE); break; + case NPC_AVATAR_OF_HAKKAR: SetData(TYPE_AVATAR, DONE); break; + + case NPC_SUPPRESSOR: + m_bCanSummonBloodkeeper = true; + break; + + // Jammalain mini-bosses + case NPC_ZOLO: + case NPC_GASHER: + case NPC_LORO: + case NPC_HUKKU: + case NPC_ZULLOR: + case NPC_MIJAN: + SetData(TYPE_PROTECTORS, DONE); + break; + } +} + +void instance_sunken_temple::SetData(uint32 uiType, uint32 uiData) +{ + switch (uiType) + { + case TYPE_ATALARION: + if (uiData == SPECIAL) + DoSpawnAtalarionIfCan(); + m_auiEncounter[uiType] = uiData; + break; + case TYPE_PROTECTORS: + if (uiData == DONE) + { + --m_uiProtectorsRemaining; + if (!m_uiProtectorsRemaining) + { + m_auiEncounter[uiType] = uiData; + DoUseDoorOrButton(GO_JAMMALAN_BARRIER); + // Intro yell + DoOrSimulateScriptTextForThisInstance(SAY_JAMMALAN_INTRO, NPC_JAMMALAN); + } + } + break; + case TYPE_JAMMALAN: + m_auiEncounter[uiType] = uiData; + break; + case TYPE_AVATAR: + if (uiData == SPECIAL) + { + ++m_uiFlameCounter; + + Creature* pShade = GetSingleCreatureFromStorage(NPC_SHADE_OF_HAKKAR); + if (!pShade) + return; + + switch (m_uiFlameCounter) + { + // Yells on each flame + // TODO It might be possible that these yells should be ordered randomly, however this is the seen state + case 1: DoScriptText(SAY_AVATAR_BRAZIER_1, pShade); break; + case 2: DoScriptText(SAY_AVATAR_BRAZIER_2, pShade); break; + case 3: DoScriptText(SAY_AVATAR_BRAZIER_3, pShade); break; + // Summon the avatar of all flames are used + case MAX_FLAMES: + DoScriptText(SAY_AVATAR_BRAZIER_4, pShade); + pShade->CastSpell(pShade, SPELL_SUMMON_AVATAR, true); + m_uiAvatarSummonTimer = 0; + m_uiSupressorTimer = 0; + break; + } + + // Summon the suppressors only after the flames are doused + // Summon timer is confusing random; timers were: 13, 39 and 52 secs; + if (m_uiFlameCounter != MAX_FLAMES) + m_uiSupressorTimer = urand(15000, 45000); + + return; + } + + // Prevent double processing + if (m_auiEncounter[uiType] == uiData) + return; + + if (uiData == IN_PROGRESS) + { + m_uiSupressorTimer = 0; + DoUpdateFlamesFlags(false); + + // Summon timer; use a small delay + m_uiAvatarSummonTimer = 3000; + m_bIsFirstHakkarWave = true; + + // Summon the shade + Player* pPlayer = GetPlayerInMap(); + if (!pPlayer) + return; + + if (Creature* pShade = pPlayer->SummonCreature(NPC_SHADE_OF_HAKKAR, aSunkenTempleLocation[1].m_fX, aSunkenTempleLocation[1].m_fY, aSunkenTempleLocation[1].m_fZ, aSunkenTempleLocation[1].m_fO, TEMPSUMMON_MANUAL_DESPAWN, 0)) + { + m_mNpcEntryGuidStore[NPC_SHADE_OF_HAKKAR] = pShade->GetObjectGuid(); + pShade->SetRespawnDelay(DAY); + } + + // Respawn circles + for (GuidVector::const_iterator itr = m_vuiCircleGUIDs.begin(); itr != m_vuiCircleGUIDs.end(); ++itr) + DoRespawnGameObject(*itr, 30 * MINUTE); + } + else if (uiData == FAIL) + { + // In case of wipe during the summoning ritual the shade is despawned + // The trash mobs stay in place, they are not despawned; the avatar is not sure if it's despawned or not but most likely he'll stay in place + + // Despawn the shade and the avatar if needed -- TODO, avatar really? + if (Creature* pShade = GetSingleCreatureFromStorage(NPC_SHADE_OF_HAKKAR)) + pShade->ForcedDespawn(); + + // Reset flames + DoUpdateFlamesFlags(true); + } + + // Use combat doors + DoUseDoorOrButton(GO_HAKKAR_DOOR_1); + DoUseDoorOrButton(GO_HAKKAR_DOOR_2); + + m_auiEncounter[uiType] = uiData; + + 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_strInstData = saveStream.str(); + + SaveToDB(); + OUT_SAVE_INST_DATA_COMPLETE; + } +} + +void instance_sunken_temple::DoSpawnAtalarionIfCan() +{ + // Return if already summoned + if (GetSingleCreatureFromStorage(NPC_ATALARION)) + return; + + Player* pPlayer = GetPlayerInMap(); + if (!pPlayer) + return; + + pPlayer->SummonCreature(NPC_ATALARION, aSunkenTempleLocation[0].m_fX, aSunkenTempleLocation[0].m_fY, aSunkenTempleLocation[0].m_fZ, aSunkenTempleLocation[0].m_fO, TEMPSUMMON_DEAD_DESPAWN, 0); + + // Spawn the idol of Hakkar + DoRespawnGameObject(GO_IDOL_OF_HAKKAR, 30 * MINUTE); + + // Spawn the big green lights + for (GuidList::const_iterator itr = m_luiBigLightGUIDs.begin(); itr != m_luiBigLightGUIDs.end(); ++itr) + DoRespawnGameObject(*itr, 30 * MINUTE); +} + +bool instance_sunken_temple::ProcessStatueEvent(uint32 uiEventId) +{ + bool bEventStatus = false; + + // Check if the statues are activated correctly + // Increase the counter when the correct statue is activated + for (uint8 i = 0; i < MAX_STATUES; ++i) + { + if (uiEventId == m_aAtalaiStatueEvents[i] && m_uiStatueCounter == i) + { + // Right Statue activated + ++m_uiStatueCounter; + bEventStatus = true; + break; + } + } + + if (!bEventStatus) + return false; + + // Check if all statues are active + if (m_uiStatueCounter == MAX_STATUES) + SetData(TYPE_ATALARION, SPECIAL); + + return true; +} + +void instance_sunken_temple::DoUpdateFlamesFlags(bool bRestore) +{ + for (GuidList::const_iterator itr = m_luiFlameGUIDs.begin(); itr != m_luiFlameGUIDs.end(); ++itr) + DoToggleGameObjectFlags(*itr, GO_FLAG_NO_INTERACT, bRestore); +} + +void instance_sunken_temple::Load(const char* chrIn) +{ + if (!chrIn) + { + 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]; + + for (uint8 i = 0; i < MAX_ENCOUNTER; ++i) + { + // Here a bit custom, to have proper mechanics for the statue events + if (m_auiEncounter[i] != DONE) + m_auiEncounter[i] = NOT_STARTED; + } + + OUT_LOAD_INST_DATA_COMPLETE; +} + +uint32 instance_sunken_temple::GetData(uint32 uiType) const +{ + if (uiType < MAX_ENCOUNTER) + return m_auiEncounter[uiType]; + + return 0; +} + +void instance_sunken_temple::Update(uint32 uiDiff) +{ + if (m_auiEncounter[TYPE_AVATAR] != IN_PROGRESS) + return; + + // Summon random mobs around the circles + if (m_uiAvatarSummonTimer) + { + if (m_uiAvatarSummonTimer <= uiDiff) + { + Creature* pShade = GetSingleCreatureFromStorage(NPC_SHADE_OF_HAKKAR); + if (!pShade) + return; + + // If no summon circles are spawned then return + if (m_vuiCircleGUIDs.empty()) + return; + + if (m_bIsFirstHakkarWave) // First wave summoned + { + // Summon at all circles + for (GuidVector::const_iterator itr = m_vuiCircleGUIDs.begin(); itr != m_vuiCircleGUIDs.end(); ++itr) + { + if (GameObject* pCircle = instance->GetGameObject(*itr)) + pShade->SummonCreature(NPC_HAKKARI_MINION, pCircle->GetPositionX(), pCircle->GetPositionY(), pCircle->GetPositionZ(), 0, TEMPSUMMON_DEAD_DESPAWN, 0); + } + + // Summon Bloodkeeper at random circle + if (GameObject* pCircle = instance->GetGameObject(m_vuiCircleGUIDs[urand(0, m_vuiCircleGUIDs.size() - 1)])) + pShade->SummonCreature(NPC_BLOODKEEPER, pCircle->GetPositionX(), pCircle->GetPositionY(), pCircle->GetPositionZ(), 0, TEMPSUMMON_DEAD_DESPAWN, 0); + + m_bCanSummonBloodkeeper = false; + m_bIsFirstHakkarWave = false; + m_uiAvatarSummonTimer = 50000; + } + else // Later wave + { + uint32 uiRoll = urand(0, 99); + uint8 uiMaxSummons = uiRoll < 75 ? 1 : uiRoll < 95 ? 2 : 3; + + if (m_bCanSummonBloodkeeper && roll_chance_i(30)) + { + // Summon a Bloodkeeper + if (GameObject* pCircle = instance->GetGameObject(m_vuiCircleGUIDs[urand(0, m_vuiCircleGUIDs.size() - 1)])) + pShade->SummonCreature(NPC_BLOODKEEPER, pCircle->GetPositionX(), pCircle->GetPositionY(), pCircle->GetPositionZ(), 0, TEMPSUMMON_DEAD_DESPAWN, 0); + + m_bCanSummonBloodkeeper = false; + --uiMaxSummons; + } + + for (uint8 i = 0; i < uiMaxSummons; ++i) + { + if (GameObject* pCircle = instance->GetGameObject(m_vuiCircleGUIDs[urand(0, m_vuiCircleGUIDs.size() - 1)])) + pShade->SummonCreature(NPC_HAKKARI_MINION, pCircle->GetPositionX(), pCircle->GetPositionY(), pCircle->GetPositionZ(), 0, TEMPSUMMON_DEAD_DESPAWN, 0); + } + m_uiAvatarSummonTimer = urand(3000, 15000); + } + } + else + m_uiAvatarSummonTimer -= uiDiff; + } + + // Summon nightmare suppressor after flame used + if (m_uiSupressorTimer) + { + if (m_uiSupressorTimer <= uiDiff) + { + Creature* pShade = GetSingleCreatureFromStorage(NPC_SHADE_OF_HAKKAR); + if (!pShade) + { + // Something went very wrong! + return; + } + + // Summon npc at random door; movement and script handled in DB + uint8 uiSummonLoc = urand(0, 1); + pShade->SummonCreature(NPC_SUPPRESSOR, aHakkariDoorLocations[uiSummonLoc].m_fX, aHakkariDoorLocations[uiSummonLoc].m_fY, aHakkariDoorLocations[uiSummonLoc].m_fZ, 0, TEMPSUMMON_DEAD_DESPAWN, 0); + + // This timer is finished now + m_uiSupressorTimer = 0; + } + else + m_uiSupressorTimer -= uiDiff; + } +} + +InstanceData* GetInstanceData_instance_sunken_temple(Map* pMap) +{ + return new instance_sunken_temple(pMap); +} + +void AddSC_instance_sunken_temple() +{ + Script* pNewScript; + + pNewScript = new Script; + pNewScript->Name = "instance_sunken_temple"; + pNewScript->GetInstanceData = &GetInstanceData_instance_sunken_temple; + pNewScript->RegisterSelf(); +} diff --git a/src/modules/SD2/scripts/eastern_kingdoms/sunken_temple/sunken_temple.cpp b/src/modules/SD2/scripts/eastern_kingdoms/sunken_temple/sunken_temple.cpp new file mode 100644 index 000000000..e5bd20b11 --- /dev/null +++ b/src/modules/SD2/scripts/eastern_kingdoms/sunken_temple/sunken_temple.cpp @@ -0,0 +1,278 @@ +/* Copyright (C) 2006 - 2013 ScriptDev2 + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along 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: Quest support: 8733 +SDCategory: Sunken Temple +EndScriptData */ + +/* ContentData +at_shade_of_eranikus +npc_malfurion_stormrage +event_antalarion_statue_activation +event_avatar_of_hakkar +go_eternal_flame +effectDummy_summon_hakkar +EndContentData */ + +#include "precompiled.h" +#include "sunken_temple.h" + +enum +{ + QUEST_THE_CHARGE_OF_DRAGONFLIGHTS = 8555, + QUEST_ERANIKUS_TYRANT_OF_DREAMS = 8733 +}; + +bool AreaTrigger_at_shade_of_eranikus(Player* pPlayer, AreaTriggerEntry const* /*pAt*/) +{ + if (ScriptedInstance* pInstance = (ScriptedInstance*)pPlayer->GetInstanceData()) + { + // Only do stuff, if the player has finished the PreQuest + if (pPlayer->GetQuestRewardStatus(QUEST_THE_CHARGE_OF_DRAGONFLIGHTS) && + !pPlayer->GetQuestRewardStatus(QUEST_ERANIKUS_TYRANT_OF_DREAMS) && + pPlayer->GetQuestStatus(QUEST_ERANIKUS_TYRANT_OF_DREAMS) != QUEST_STATUS_COMPLETE) + { + if (pInstance->GetData(TYPE_MALFURION) != DONE) + { + pPlayer->SummonCreature(NPC_MALFURION, aSunkenTempleLocation[2].m_fX, aSunkenTempleLocation[2].m_fY, aSunkenTempleLocation[2].m_fZ, aSunkenTempleLocation[2].m_fO, TEMPSUMMON_DEAD_DESPAWN, 0); + pInstance->SetData(TYPE_MALFURION, DONE); + } + } + } + return false; +} + +/*###### +## npc_malfurion_stormrage +######*/ + +enum +{ + EMOTE_MALFURION1 = -1109000, + SAY_MALFURION1 = -1109001, + SAY_MALFURION2 = -1109002, + SAY_MALFURION3 = -1109003, + SAY_MALFURION4 = -1109004, + + MAX_MALFURION_TEMPLE_SPEECHES = 6 +}; + +struct npc_malfurionAI : public ScriptedAI +{ + npc_malfurionAI(Creature* pCreature) : ScriptedAI(pCreature) + { + DoScriptText(EMOTE_MALFURION1, m_creature); + m_uiSpeech = 0; + m_uiSayTimer = 3000; + m_creature->RemoveFlag(UNIT_NPC_FLAGS, UNIT_NPC_FLAG_QUESTGIVER); + } + + uint32 m_uiSayTimer; + uint32 m_uiSpeech; + + void Reset() override {} + void UpdateAI(const uint32 uiDiff) override + { + // We are in Sunken Temple + if (m_creature->GetMap()->IsDungeon()) + { + if (m_uiSpeech < MAX_MALFURION_TEMPLE_SPEECHES) + { + if (m_uiSayTimer <= uiDiff) + { + switch (m_uiSpeech) + { + case 0: + m_creature->HandleEmote(EMOTE_ONESHOT_BOW); + m_uiSayTimer = 2000; + break; + case 1: + DoScriptText(SAY_MALFURION1, m_creature); + m_creature->HandleEmote(EMOTE_STATE_TALK); + m_uiSayTimer = 12000; + break; + case 2: + DoScriptText(SAY_MALFURION2, m_creature); + m_uiSayTimer = 12000; + break; + case 3: + DoScriptText(SAY_MALFURION3, m_creature); + m_uiSayTimer = 11000; + break; + case 4: + DoScriptText(SAY_MALFURION4, m_creature); + m_uiSayTimer = 4000; + break; + case 5: + m_creature->SetFlag(UNIT_NPC_FLAGS, UNIT_NPC_FLAG_QUESTGIVER); + m_creature->HandleEmote(EMOTE_STATE_NONE); + break; + } + + ++m_uiSpeech; + } + else + m_uiSayTimer -= uiDiff; + } + } + } +}; + +CreatureAI* GetAI_npc_malfurion(Creature* pCreature) +{ + return new npc_malfurionAI(pCreature); +} + +/*###### +## event_antalarion_statues +######*/ + +bool ProcessEventId_event_antalarion_statue_activation(uint32 uiEventId, Object* pSource, Object* pTarget, bool /*bIsStart*/) +{ + if (pSource->GetTypeId() == TYPEID_PLAYER && pTarget->GetTypeId() == TYPEID_GAMEOBJECT) + { + if (instance_sunken_temple* pInstance = (instance_sunken_temple*)((Player*)pSource)->GetInstanceData()) + { + // return if event completed + if (pInstance->GetData(TYPE_ATALARION) != NOT_STARTED) + return true; + + // Send the event id to process + if (pInstance->ProcessStatueEvent(uiEventId)) + { + // Activate the green light if the correct statue is activated + if (GameObject* pLight = GetClosestGameObjectWithEntry((GameObject*)pTarget, GO_ATALAI_LIGHT, INTERACTION_DISTANCE)) + pInstance->DoRespawnGameObject(pLight->GetObjectGuid(), 30 * MINUTE); + } + else + { + // If the wrong statue was activated, then trigger trap + // We don't know actually which trap goes to which statue so we need to search for each + if (GameObject* pTrap = GetClosestGameObjectWithEntry((GameObject*)pTarget, GO_ATALAI_TRAP_1, INTERACTION_DISTANCE)) + pTrap->Use((Unit*)pSource); + else if (GameObject* pTrap = GetClosestGameObjectWithEntry((GameObject*)pTarget, GO_ATALAI_TRAP_2, INTERACTION_DISTANCE)) + pTrap->Use((Unit*)pSource); + else if (GameObject* pTrap = GetClosestGameObjectWithEntry((GameObject*)pTarget, GO_ATALAI_TRAP_3, INTERACTION_DISTANCE)) + pTrap->Use((Unit*)pSource); + } + + return true; + } + } + return false; +} + +/*###### +## event_avatar_of_hakkar +######*/ +bool ProcessEventId_event_avatar_of_hakkar(uint32 /*uiEventId*/, Object* pSource, Object* /*pTarget*/, bool /*bIsStart*/) +{ + if (pSource->GetTypeId() == TYPEID_PLAYER) + { + if (instance_sunken_temple* pInstance = (instance_sunken_temple*)((Player*)pSource)->GetInstanceData()) + { + // return if not NOT_STARTED + if (pInstance->GetData(TYPE_AVATAR) != NOT_STARTED) + return true; + + pInstance->SetData(TYPE_AVATAR, IN_PROGRESS); + + return true; + } + } + return false; +} + +/*###### +## go_eternal_flame +######*/ +bool GOUse_go_eternal_flame(Player* /*pPlayer*/, GameObject* pGo) +{ + instance_sunken_temple* pInstance = (instance_sunken_temple*)pGo->GetInstanceData(); + + if (!pInstance) + return false; + + if (pInstance->GetData(TYPE_AVATAR) != IN_PROGRESS) + return false; + + // Set data to special when flame is used + pInstance->SetData(TYPE_AVATAR, SPECIAL); + pGo->SetFlag(GAMEOBJECT_FLAGS, GO_FLAG_NO_INTERACT); + + return true; +} + +/*###### +## effectDummy_summon_hakkar +######*/ +bool EffectDummyCreature_summon_hakkar(Unit* pCaster, uint32 uiSpellId, SpellEffectIndex uiEffIndex, Creature* /*pCreatureTarget*/, ObjectGuid /*originalCasterGuid*/) +{ + // Always check spellid and effectindex + if (uiSpellId == SPELL_SUMMON_AVATAR && uiEffIndex == EFFECT_INDEX_0) + { + if (!pCaster || pCaster->GetTypeId() != TYPEID_UNIT) + return true; + + // Update entry to avatar of Hakkar and cast some visuals + ((Creature*)pCaster)->UpdateEntry(NPC_AVATAR_OF_HAKKAR); + pCaster->CastSpell(pCaster, SPELL_AVATAR_SUMMONED, true); + DoScriptText(SAY_AVATAR_SPAWN, pCaster); + + // Always return true when we are handling this spell and effect + return true; + } + + return false; +} + +void AddSC_sunken_temple() +{ + Script* pNewScript; + + pNewScript = new Script; + pNewScript->Name = "at_shade_of_eranikus"; + pNewScript->pAreaTrigger = &AreaTrigger_at_shade_of_eranikus; + pNewScript->RegisterSelf(); + + pNewScript = new Script; + pNewScript->Name = "npc_malfurion_stormrage"; + pNewScript->GetAI = &GetAI_npc_malfurion; + pNewScript->RegisterSelf(); + + pNewScript = new Script; + pNewScript->Name = "event_antalarion_statue_activation"; + pNewScript->pProcessEventId = &ProcessEventId_event_antalarion_statue_activation; + pNewScript->RegisterSelf(); + + pNewScript = new Script; + pNewScript->Name = "event_avatar_of_hakkar"; + pNewScript->pProcessEventId = &ProcessEventId_event_avatar_of_hakkar; + pNewScript->RegisterSelf(); + + pNewScript = new Script; + pNewScript->Name = "go_eternal_flame"; + pNewScript->pGOUse = &GOUse_go_eternal_flame; + pNewScript->RegisterSelf(); + + pNewScript = new Script; + pNewScript->Name = "npc_shade_of_hakkar"; + pNewScript->pEffectDummyNPC = &EffectDummyCreature_summon_hakkar; + pNewScript->RegisterSelf(); +} diff --git a/src/modules/SD2/scripts/eastern_kingdoms/sunken_temple/sunken_temple.h b/src/modules/SD2/scripts/eastern_kingdoms/sunken_temple/sunken_temple.h new file mode 100644 index 000000000..85d5bb939 --- /dev/null +++ b/src/modules/SD2/scripts/eastern_kingdoms/sunken_temple/sunken_temple.h @@ -0,0 +1,149 @@ +/* Copyright (C) 2006 - 2013 ScriptDev2 + * 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 + +enum +{ + MAX_ENCOUNTER = 5, + MAX_STATUES = 6, + MAX_FLAMES = 4, + + TYPE_ATALARION = 0, + TYPE_PROTECTORS = 1, + TYPE_JAMMALAN = 2, + TYPE_MALFURION = 3, + TYPE_AVATAR = 4, + + NPC_ATALARION = 8580, + NPC_DREAMSCYTH = 5721, + NPC_WEAVER = 5720, + NPC_JAMMALAN = 5710, + NPC_AVATAR_OF_HAKKAR = 8443, + NPC_SHADE_OF_ERANIKUS = 5709, + + // Jammalain mini-bosses + NPC_ZOLO = 5712, + NPC_GASHER = 5713, + NPC_LORO = 5714, + NPC_HUKKU = 5715, + NPC_ZULLOR = 5716, + NPC_MIJAN = 5717, + + // Avatar of hakkar mobs + NPC_SHADE_OF_HAKKAR = 8440, // Shade of Hakkar appears when the event starts; will despawn when avatar of hakkar is summoned + NPC_BLOODKEEPER = 8438, // Spawned rarely and contains the hakkari blood -> used to extinguish the flames + NPC_HAKKARI_MINION = 8437, // Npc randomly spawned during the event = trash + NPC_SUPPRESSOR = 8497, // Npc summoned at one of the two doors and moves to the boss; + + NPC_MALFURION = 15362, + + GO_ALTAR_OF_HAKKAR = 148836, // Used in order to show the player the order of the statue activation + GO_IDOL_OF_HAKKAR = 148838, // Appears when atalarion is summoned; this was removed in 4.0.1 + + GO_ATALAI_LIGHT = 148883, // Green light, activates when the correct statue is chosen + GO_ATALAI_LIGHT_BIG = 148937, // Big light, used at the altar event + + GO_ATALAI_TRAP_1 = 177484, // Trapps triggered if the wrong statue is activated + GO_ATALAI_TRAP_2 = 177485, // The traps are spawned in DB randomly around the statues (we don't know exactly which statue has which trap) + GO_ATALAI_TRAP_3 = 148837, + + GO_ETERNAL_FLAME_1 = 148418, + GO_ETERNAL_FLAME_2 = 148419, + GO_ETERNAL_FLAME_3 = 148420, + GO_ETERNAL_FLAME_4 = 148421, + + GO_EVIL_CIRCLE = 148998, // Objects used at the avatar event. they are spawned when the event starts, and the mobs are summon atop of them + GO_HAKKAR_DOOR_1 = 149432, // Combat doors + GO_HAKKAR_DOOR_2 = 149433, + + GO_JAMMALAN_BARRIER = 149431, + + // Event ids related to the statue activation + EVENT_ID_STATUE_1 = 3094, + EVENT_ID_STATUE_2 = 3095, + EVENT_ID_STATUE_3 = 3097, + EVENT_ID_STATUE_4 = 3098, + EVENT_ID_STATUE_5 = 3099, + EVENT_ID_STATUE_6 = 3100, + + SPELL_SUMMON_AVATAR = 12639, // Cast by the shade of hakkar, updates entry to avatar + SPELL_AVATAR_SUMMONED = 12948, + + SAY_JAMMALAN_INTRO = -1109005, + SAY_AVATAR_BRAZIER_1 = -1109006, + SAY_AVATAR_BRAZIER_2 = -1109007, + SAY_AVATAR_BRAZIER_3 = -1109008, + SAY_AVATAR_BRAZIER_4 = -1109009, + SAY_AVATAR_SPAWN = -1109010, +}; + +// This is also the needed order for activation: S, N, SW, SE, NW, NE +static const uint32 m_aAtalaiStatueEvents[MAX_STATUES] = {EVENT_ID_STATUE_1, EVENT_ID_STATUE_2, EVENT_ID_STATUE_3, EVENT_ID_STATUE_4, EVENT_ID_STATUE_5, EVENT_ID_STATUE_6}; + +struct SummonLocations +{ + float m_fX, m_fY, m_fZ, m_fO; +}; + +static const SummonLocations aSunkenTempleLocation[] = +{ + { -466.5130f, 95.19820f, -189.646f, 0.0349f}, // Atalarion summon loc + { -466.8673f, 272.31204f, -90.7441f, 3.5255f}, // Shade of hakkar summon loc + { -660.5277f, -16.7117f, -90.8357f, 1.6055f} // Malfurion summon loc +}; + +// Summon location for the suppressors +static const SummonLocations aHakkariDoorLocations[2] = +{ + { -420.629f, 276.682f, -90.827f}, + { -512.015f, 276.134f, -90.827f} +}; + +class instance_sunken_temple : public ScriptedInstance +{ + public: + instance_sunken_temple(Map* pMap); + ~instance_sunken_temple() {} + + void Initialize() override; + + void OnObjectCreate(GameObject* pGo) override; + void OnCreatureCreate(Creature* pCreature) override; + + void OnCreatureEvade(Creature* pCreature); + void OnCreatureDeath(Creature* pCreature) override; + + void SetData(uint32 uiType, uint32 uiData) override; + uint32 GetData(uint32 uiType) const override; + + void Update(uint32 uiDiff) override; + + bool ProcessStatueEvent(uint32 uiEventId); + + const char* Save() const override { return m_strInstData.c_str(); } + void Load(const char* chrIn) override; + + protected: + void DoSpawnAtalarionIfCan(); + void DoUpdateFlamesFlags(bool bRestore); + + uint32 m_auiEncounter[MAX_ENCOUNTER]; + std::string m_strInstData; + + uint8 m_uiProtectorsRemaining; // Jammalan door handling + uint8 m_uiStatueCounter; // Atalarion Statue Event + uint8 m_uiFlameCounter; // Avatar of Hakkar Event + uint32 m_uiAvatarSummonTimer; + uint32 m_uiSupressorTimer; + bool m_bIsFirstHakkarWave; + bool m_bCanSummonBloodkeeper; + + GuidList m_luiFlameGUIDs; + GuidList m_luiBigLightGUIDs; + GuidVector m_vuiCircleGUIDs; +}; + +#endif diff --git a/src/modules/SD2/scripts/eastern_kingdoms/sunwell_plateau/boss_brutallus.cpp b/src/modules/SD2/scripts/eastern_kingdoms/sunwell_plateau/boss_brutallus.cpp new file mode 100644 index 000000000..2f2c53112 --- /dev/null +++ b/src/modules/SD2/scripts/eastern_kingdoms/sunwell_plateau/boss_brutallus.cpp @@ -0,0 +1,531 @@ +/* Copyright (C) 2006 - 2013 ScriptDev2 + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with 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: 90 +SDComment: Intro may need some adjustments +SDCategory: Sunwell Plateau +EndScriptData */ + +#include "precompiled.h" +#include "sunwell_plateau.h" + +enum +{ + YELL_INTRO = -1580017, + YELL_INTRO_BREAK_ICE = -1580018, + 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, + YELL_KILL3 = -1580025, + YELL_LOVE1 = -1580026, + YELL_LOVE2 = -1580027, + YELL_LOVE3 = -1580028, + YELL_BERSERK = -1580029, + YELL_DEATH = -1580030, + + SPELL_METEOR_SLASH = 45150, + SPELL_BURN = 45141, + SPELL_STOMP = 45185, + SPELL_BERSERK = 26662, + SPELL_SUMMON_DEATH_CLOUD = 45884, // Summoned on death + + // Epilogue spells + SPELL_BRUTALLUS_DEATH_CLOUD = 45212, + SPELL_FELBLAZE_PREVIZUAL = 44885, + SPELL_SUMMON_FELBLAZE = 45069, + + NPC_BRUTALLUS_DEATH_CLOUD = 25703, + + // spells used during the intro event + SPELL_FROST_BLAST = 45203, // Madrigosa's spells + SPELL_FREEZE = 46609, // Activates the ice barrier - script effect for 46610 + SPELL_FROSTBOLT = 44843, + SPELL_FROST_BREATH = 45065, + SPELL_ENCAPSULATE = 44883, + SPELL_FEL_FIREBALL = 44844, // Brutallus' spells + SPELL_CLEAR_DEBUFFS = 34098, + SPELL_FLAME_RING = 44874, // this spell should have a fire explosion when removed + SPELL_CHARGE = 44884, + SPELL_BREAK_ICE = 46637, // Break the ice, open the door - dummy spell for 46638 and 47030 + + POINT_MOVE_GROUND = 1, + POINT_MOVE_ICE_BLOCK = 2, +}; + +static const DialogueEntry aIntroDialogue[] = +{ + {NPC_MADRIGOSA, 0, 6000}, + {YELL_MADR_ICE_BARRIER, NPC_MADRIGOSA, 7000}, + {YELL_MADR_INTRO, NPC_MADRIGOSA, 7000}, + {YELL_INTRO, NPC_BRUTALLUS, 6000}, + {SPELL_FROST_BREATH, 0, 6000}, + {POINT_MOVE_ICE_BLOCK, 0, 5000}, + {YELL_MADR_ICE_BLOCK, NPC_MADRIGOSA, 5000}, + {SPELL_FLAME_RING, 0, 7000}, + {YELL_INTRO_BREAK_ICE, NPC_BRUTALLUS, 1000}, + {SPELL_FEL_FIREBALL, 0, 4000}, + {POINT_MOVE_GROUND, 0, 5000}, + {YELL_MADR_TRAP, NPC_MADRIGOSA, 14000}, + {YELL_INTRO_CHARGE, NPC_BRUTALLUS, 10000}, + {YELL_INTRO_KILL_MADRIGOSA, NPC_BRUTALLUS, 8000}, + {YELL_INTRO_TAUNT, NPC_BRUTALLUS, 0}, + {0, 0, 0}, +}; + +/*###### +## boss_brutallus +######*/ + +struct boss_brutallusAI : public ScriptedAI, private DialogueHelper +{ + boss_brutallusAI(Creature* pCreature) : ScriptedAI(pCreature), + DialogueHelper(aIntroDialogue) + { + m_pInstance = (ScriptedInstance*)pCreature->GetInstanceData(); + InitializeDialogueHelper(m_pInstance); + Reset(); + } + + ScriptedInstance* m_pInstance; + + uint32 m_uiSlashTimer; + uint32 m_uiBurnTimer; + uint32 m_uiStompTimer; + uint32 m_uiBerserkTimer; + uint32 m_uiLoveTimer; + + uint32 m_uiMadrigosaSpellTimer; + + bool m_bCanDoMeleeAttack; + bool m_bIsIntroInProgress; + + void Reset() override + { + m_uiSlashTimer = 11000; + m_uiStompTimer = 30000; + m_uiBurnTimer = 20000; + m_uiBerserkTimer = 6 * MINUTE * IN_MILLISECONDS; + m_uiLoveTimer = urand(10000, 17000); + + m_uiMadrigosaSpellTimer = 0; + + m_bCanDoMeleeAttack = true; + m_bIsIntroInProgress = false; + } + + void Aggro(Unit* pWho) override + { + // Don't aggro when attacking Madrigosa + if (pWho->GetEntry() == NPC_MADRIGOSA) + return; + + DoScriptText(YELL_AGGRO, m_creature); + + if (m_pInstance) + m_pInstance->SetData(TYPE_BRUTALLUS, IN_PROGRESS); + } + + void KilledUnit(Unit* pVictim) override + { + // Don't yell for Madrigosa + if (pVictim->GetEntry() == NPC_MADRIGOSA) + return; + + switch (urand(0, 2)) + { + case 0: DoScriptText(YELL_KILL1, m_creature); break; + case 1: DoScriptText(YELL_KILL2, m_creature); break; + case 2: DoScriptText(YELL_KILL3, m_creature); break; + } + } + + void JustDied(Unit* /*pKiller*/) override + { + DoScriptText(YELL_DEATH, m_creature); + DoCastSpellIfCan(m_creature, SPELL_SUMMON_DEATH_CLOUD, CAST_TRIGGERED); + + if (m_pInstance) + m_pInstance->SetData(TYPE_BRUTALLUS, DONE); + } + + void JustReachedHome() override + { + if (m_pInstance) + { + // When evade from the fight with Madrigosa skip this + if (m_pInstance->GetData(TYPE_BRUTALLUS) == SPECIAL) + return; + + m_pInstance->SetData(TYPE_BRUTALLUS, FAIL); + } + } + + void GetAIInformation(ChatHandler& reader) override + { + if (m_pInstance) + { + if (m_pInstance->GetData(TYPE_BRUTALLUS) == SPECIAL) + reader.PSendSysMessage("Brutallus intro event is currently %s", m_bIsIntroInProgress ? "in progress" : "completed"); + else + reader.PSendSysMessage("Brutallus intro event is currently %s", m_pInstance->GetData(TYPE_BRUTALLUS) == NOT_STARTED ? "not started" : "completed"); + + if (m_pInstance->GetData(TYPE_BRUTALLUS) != NOT_STARTED) + { + if (Creature* pMadrigosa = m_pInstance->GetSingleCreatureFromStorage(NPC_MADRIGOSA, true)) + reader.PSendSysMessage("Madrigosa guid is %s and has %u health.", pMadrigosa->GetObjectGuid().GetString().c_str(), pMadrigosa->GetHealth()); + } + } + } + + void SummonedCreatureJustDied(Creature* pSummoned) override + { + // Error log if Madrigosa dies + if (pSummoned->GetEntry() == NPC_MADRIGOSA) + script_error_log("Npc %u, %s, died unexpectedly. Felmyst won't be summoned anymore.", pSummoned->GetEntry(), pSummoned->GetName()); + } + + void SummonedCreatureDespawn(Creature* pSummoned) override + { + // Yell of Madrigosa on death + if (pSummoned->GetEntry() == NPC_MADRIGOSA) + pSummoned->CastSpell(pSummoned, SPELL_SUMMON_FELBLAZE, true); + } + + void JustSummoned(Creature* pSummoned) override + { + if (pSummoned->GetEntry() == NPC_MADRIGOSA) + { + pSummoned->SetWalk(false); + pSummoned->SetLevitate(true); + pSummoned->GetMotionMaster()->MovePoint(0, aMadrigosaLoc[1].m_fX, aMadrigosaLoc[1].m_fY, aMadrigosaLoc[1].m_fZ, false); + } + else if (pSummoned->GetEntry() == NPC_BRUTALLUS_DEATH_CLOUD) + pSummoned->CastSpell(pSummoned, SPELL_BRUTALLUS_DEATH_CLOUD, true); + } + + void SummonedMovementInform(Creature* pSummoned, uint32 uiType, uint32 uiPointId) override + { + if (uiType != POINT_MOTION_TYPE || pSummoned->GetEntry() != NPC_MADRIGOSA) + return; + + if (uiPointId == POINT_MOVE_GROUND) + pSummoned->SetLevitate(false); + } + + void SpellHitTarget(Unit* pTarget, const SpellEntry* pSpell) override + { + // Fake death Madrigosa when charged + if (pTarget->GetEntry() == NPC_MADRIGOSA && pSpell->Id == SPELL_CHARGE) + { + DoScriptText(YELL_MADR_DEATH, pTarget); + pTarget->InterruptNonMeleeSpells(true); + pTarget->SetHealth(0); + pTarget->StopMoving(); + pTarget->ClearComboPointHolders(); + pTarget->RemoveAllAurasOnDeath(); + pTarget->ModifyAuraState(AURA_STATE_HEALTHLESS_20_PERCENT, false); + pTarget->ModifyAuraState(AURA_STATE_HEALTHLESS_35_PERCENT, false); + pTarget->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE); + pTarget->ClearAllReactives(); + pTarget->GetMotionMaster()->Clear(); + pTarget->GetMotionMaster()->MoveIdle(); + pTarget->SetStandState(UNIT_STAND_STATE_DEAD); + + // Brutallus evades + EnterEvadeMode(); + } + } + + void JustDidDialogueStep(int32 iEntry) override + { + if (!m_pInstance) + return; + + switch (iEntry) + { + case NPC_MADRIGOSA: + if (Creature* pTrigger = m_pInstance->GetSingleCreatureFromStorage(NPC_FLIGHT_TRIGGER_LEFT)) + m_creature->SummonCreature(NPC_MADRIGOSA, pTrigger->GetPositionX(), pTrigger->GetPositionY(), pTrigger->GetPositionZ(), 0, TEMPSUMMON_DEAD_DESPAWN, 0); + m_bIsIntroInProgress = true; + break; + case YELL_MADR_ICE_BARRIER: + if (Creature* pMadrigosa = m_pInstance->GetSingleCreatureFromStorage(NPC_MADRIGOSA)) + pMadrigosa->CastSpell(pMadrigosa, SPELL_FREEZE, true); + break; + case YELL_MADR_INTRO: + if (Creature* pMadrigosa = m_pInstance->GetSingleCreatureFromStorage(NPC_MADRIGOSA)) + pMadrigosa->GetMotionMaster()->MovePoint(POINT_MOVE_GROUND, aMadrigosaLoc[0].m_fX, aMadrigosaLoc[0].m_fY, aMadrigosaLoc[0].m_fZ); + break; + case YELL_INTRO: + if (Creature* pMadrigosa = m_pInstance->GetSingleCreatureFromStorage(NPC_MADRIGOSA)) + m_creature->AI()->AttackStart(pMadrigosa); + break; + case SPELL_FROST_BREATH: + if (Creature* pMadrigosa = m_pInstance->GetSingleCreatureFromStorage(NPC_MADRIGOSA)) + { + pMadrigosa->CastSpell(m_creature, SPELL_FROST_BREATH, true); + pMadrigosa->GetMotionMaster()->MoveIdle(); + } + break; + case POINT_MOVE_ICE_BLOCK: + m_bCanDoMeleeAttack = false; + if (Creature* pMadrigosa = m_pInstance->GetSingleCreatureFromStorage(NPC_MADRIGOSA)) + { + pMadrigosa->GetMotionMaster()->MovePoint(POINT_MOVE_ICE_BLOCK, aMadrigosaLoc[1].m_fX, aMadrigosaLoc[1].m_fY, aMadrigosaLoc[1].m_fZ); + pMadrigosa->HandleEmote(EMOTE_ONESHOT_LIFTOFF); + pMadrigosa->SetLevitate(true); + } + // Temporary! This will make Brutallus not follow Madrigosa through the air until mmaps are implemented + m_creature->GetMotionMaster()->MoveIdle(); + break; + case YELL_MADR_ICE_BLOCK: + if (Creature* pMadrigosa = m_pInstance->GetSingleCreatureFromStorage(NPC_MADRIGOSA)) + pMadrigosa->CastSpell(m_creature, SPELL_FROST_BLAST, true); + m_uiMadrigosaSpellTimer = 2000; + break; + case SPELL_FLAME_RING: + DoCastSpellIfCan(m_creature, SPELL_CLEAR_DEBUFFS, CAST_TRIGGERED); + DoCastSpellIfCan(m_creature, SPELL_FLAME_RING, CAST_TRIGGERED); + break; + case YELL_INTRO_BREAK_ICE: + m_creature->RemoveAurasDueToSpell(SPELL_FLAME_RING); + break; + case SPELL_FEL_FIREBALL: + // Spell has script target Madrigosa + DoCastSpellIfCan(m_creature, SPELL_FEL_FIREBALL); + break; + case POINT_MOVE_GROUND: + if (Creature* pMadrigosa = m_pInstance->GetSingleCreatureFromStorage(NPC_MADRIGOSA)) + pMadrigosa->GetMotionMaster()->MovePoint(POINT_MOVE_GROUND, aMadrigosaLoc[0].m_fX, aMadrigosaLoc[0].m_fY, aMadrigosaLoc[0].m_fZ); + m_uiMadrigosaSpellTimer = 0; + break; + case YELL_MADR_TRAP: + if (Creature* pMadrigosa = m_pInstance->GetSingleCreatureFromStorage(NPC_MADRIGOSA)) + { + pMadrigosa->CastSpell(m_creature, SPELL_ENCAPSULATE, true); + // Need to remove the fire aura after 4 sec so Madrigosa won't die so soon + pMadrigosa->RemoveAurasDueToSpell(SPELL_FEL_FIREBALL); + } + break; + case YELL_INTRO_CHARGE: + m_bCanDoMeleeAttack = true; + if (m_creature->getVictim()) + m_creature->GetMotionMaster()->MoveChase(m_creature->getVictim()); + DoCastSpellIfCan(m_creature, SPELL_CHARGE); + break; + case YELL_INTRO_KILL_MADRIGOSA: + // Face the players + if (GameObject* pIceWall = m_pInstance->GetSingleGameObjectFromStorage(GO_ICE_BARRIER)) + m_creature->SetFacingToObject(pIceWall); + break; + case YELL_INTRO_TAUNT: + DoCastSpellIfCan(m_creature, SPELL_BREAK_ICE); + m_bIsIntroInProgress = false; + break; + } + } + + // Wrapper to start the dialogue text + void DoStartIntro() + { + StartNextDialogueText(NPC_MADRIGOSA); + } + + // Wrapper to keep all the intro event stuff together + void UpdateIntroEvent(const uint32 uiDiff) + { + // Dialogue updates outside of combat too + DialogueUpdate(uiDiff); + + if (!m_creature->SelectHostileTarget() || !m_creature->getVictim()) + return; + + if (m_uiMadrigosaSpellTimer) + { + if (m_uiMadrigosaSpellTimer <= uiDiff) + { + if (Creature* pMadrigosa = m_pInstance->GetSingleCreatureFromStorage(NPC_MADRIGOSA)) + pMadrigosa->CastSpell(m_creature, SPELL_FROSTBOLT, true); + m_uiMadrigosaSpellTimer = urand(1000, 2000); + } + else + m_uiMadrigosaSpellTimer -= uiDiff; + } + + // We need to limit the melee attacks for the intro event + if (m_bCanDoMeleeAttack) + DoMeleeAttackIfReady(); + } + + void UpdateAI(const uint32 uiDiff) override + { + // Update only the intro related stuff + if (m_pInstance && m_pInstance->GetData(TYPE_BRUTALLUS) == SPECIAL) + { + UpdateIntroEvent(uiDiff); + return; + } + + if (!m_creature->SelectHostileTarget() || !m_creature->getVictim()) + return; + + if (m_uiLoveTimer < uiDiff) + { + switch (urand(0, 2)) + { + case 0: DoScriptText(YELL_LOVE1, m_creature); break; + case 1: DoScriptText(YELL_LOVE2, m_creature); break; + case 2: DoScriptText(YELL_LOVE3, m_creature); break; + } + m_uiLoveTimer = urand(15000, 23000); + } + else + m_uiLoveTimer -= uiDiff; + + if (m_uiSlashTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature->getVictim(), SPELL_METEOR_SLASH) == CAST_OK) + m_uiSlashTimer = 11000; + } + else + m_uiSlashTimer -= uiDiff; + + if (m_uiStompTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature->getVictim(), SPELL_STOMP) == CAST_OK) + m_uiStompTimer = 30000; + } + else + m_uiStompTimer -= uiDiff; + + if (m_uiBurnTimer < uiDiff) + { + if (Unit* pTarget = m_creature->SelectAttackingTarget(ATTACKING_TARGET_RANDOM, 0, SPELL_BURN, SELECT_FLAG_PLAYER)) + { + if (DoCastSpellIfCan(pTarget, SPELL_BURN) == CAST_OK) + m_uiBurnTimer = 20000; + } + } + else + m_uiBurnTimer -= uiDiff; + + if (m_uiBerserkTimer) + { + if (m_uiBerserkTimer <= uiDiff) + { + if (DoCastSpellIfCan(m_creature, SPELL_BERSERK) == CAST_OK) + { + DoScriptText(YELL_BERSERK, m_creature); + m_uiBerserkTimer = 0; + } + } + else + m_uiBerserkTimer -= uiDiff; + } + + DoMeleeAttackIfReady(); + } +}; + +CreatureAI* GetAI_boss_brutallus(Creature* pCreature) +{ + return new boss_brutallusAI(pCreature); +} + +/*###### +## spell_aura_dummy_npc_brutallus_cloud +######*/ + +bool EffectAuraDummy_spell_aura_dummy_npc_brutallus_cloud(const Aura* pAura, bool bApply) +{ + // On Aura removal start Felmyst summon visuals + if (pAura->GetId() == SPELL_BRUTALLUS_DEATH_CLOUD && pAura->GetEffIndex() == EFFECT_INDEX_0 && !bApply) + { + if (Creature* pTarget = (Creature*)pAura->GetTarget()) + { + if (ScriptedInstance* pInstance = (ScriptedInstance*)pTarget->GetInstanceData()) + { + if (Creature* pMadrigosa = pInstance->GetSingleCreatureFromStorage(NPC_MADRIGOSA)) + { + // Set respawn pos to current pos + pMadrigosa->SetRespawnCoord(pMadrigosa->GetPositionX(), pMadrigosa->GetPositionY(), pMadrigosa->GetPositionZ(), pMadrigosa->GetOrientation()); + + pMadrigosa->CastSpell(pMadrigosa, SPELL_FELBLAZE_PREVIZUAL, true); + pMadrigosa->ForcedDespawn(10000); + } + } + } + } + return true; +} + +/*###### +## at_madrigosa +######*/ + +bool AreaTrigger_at_madrigosa(Player* pPlayer, AreaTriggerEntry const* /*pAt*/) +{ + if (ScriptedInstance* pInstance = (ScriptedInstance*)pPlayer->GetInstanceData()) + { + // this simply set encounter state, and trigger ice barrier become active + // bosses can start pre-event based on this new state + if (pInstance->GetData(TYPE_BRUTALLUS) == NOT_STARTED) + { + pInstance->SetData(TYPE_BRUTALLUS, SPECIAL); + + // Start the intro event + if (Creature* pBrutallus = pInstance->GetSingleCreatureFromStorage(NPC_BRUTALLUS)) + { + if (boss_brutallusAI* pBossAI = dynamic_cast(pBrutallus->AI())) + pBossAI->DoStartIntro(); + } + } + } + + return false; +} + +void AddSC_boss_brutallus() +{ + Script* pNewScript; + + pNewScript = new Script; + pNewScript->Name = "boss_brutallus"; + pNewScript->GetAI = &GetAI_boss_brutallus; + pNewScript->RegisterSelf(); + + pNewScript = new Script; + pNewScript->Name = "spell_dummy_npc_brutallus_cloud"; + pNewScript->pEffectAuraDummy = &EffectAuraDummy_spell_aura_dummy_npc_brutallus_cloud; + pNewScript->RegisterSelf(); + + pNewScript = new Script; + pNewScript->Name = "at_madrigosa"; + pNewScript->pAreaTrigger = &AreaTrigger_at_madrigosa; + pNewScript->RegisterSelf(); +} diff --git a/src/modules/SD2/scripts/eastern_kingdoms/sunwell_plateau/boss_eredar_twins.cpp b/src/modules/SD2/scripts/eastern_kingdoms/sunwell_plateau/boss_eredar_twins.cpp new file mode 100644 index 000000000..9876fc0c8 --- /dev/null +++ b/src/modules/SD2/scripts/eastern_kingdoms/sunwell_plateau/boss_eredar_twins.cpp @@ -0,0 +1,623 @@ +/* Copyright (C) 2006 - 2013 ScriptDev2 + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with 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: 75 +SDComment: A few spells are not working proper yet; Shadow image script needs improvement +SDCategory: Sunwell Plateau +EndScriptData */ + +#include "precompiled.h" +#include "sunwell_plateau.h" + +enum +{ + SAY_INTRO_1 = -1580044, + SAY_INTRO_2 = -1580045, + SAY_INTRO_3 = -1580046, + SAY_INTRO_4 = -1580047, + SAY_INTRO_5 = -1580048, + SAY_INTRO_6 = -1580049, + SAY_INTRO_7 = -1580050, + SAY_INTRO_8 = -1580051, + + SAY_SACROLASH_SHADOW_NOVA = -1580052, + SAY_SACROLASH_EMPOWER = -1580053, + SAY_SACROLASH_KILL_1 = -1580054, + SAY_SACROLASH_KILL_2 = -1580055, + SAY_SACROLASH_DEAD = -1580056, + SAY_SACROLASH_BERSERK = -1580057, + + SAY_ALYTHESS_CANFLAGRATION = -1580058, + SAY_ALYTHESS_EMPOWER = -1580059, + SAY_ALYTHESS_KILL_1 = -1580060, + SAY_ALYTHESS_KILL_2 = -1580061, + SAY_ALYTHESS_DEAD = -1580062, + SAY_ALYTHESS_BERSERK = -1580063, + + // Shared spells + SPELL_TWINS_ENRAGE = 46587, + SPELL_EMPOWER = 45366, // Cast on self when the twin sister dies + SPELL_DARK_FLAME = 45345, + + // Sacrolash spells + SPELL_DARK_TOUCHED = 45347, // Player debuff; removed by shadow damage + SPELL_SHADOW_BLADES = 45248, // 10 secs + SPELL_DARK_STRIKE = 45271, + SPELL_SHADOW_NOVA = 45329, // 30-35 secs + SPELL_CONFOUNDING_BLOW = 45256, // Daze; 25 secs + SPELL_SHADOW_NOVA_UNK = 45332, // Unknown + + // Shadow Image spells + NPC_SHADOW_IMAGE = 25214, + SPELL_SHADOWFURY = 45270, + SPELL_IMAGE_VISUAL = 45263, + + // Alythess spells + SPELL_PYROGENICS = 45230, // Self buff; 15secs + SPELL_FLAME_TOUCHED = 45348, // Player debuff; removed by shadow damage + SPELL_CONFLAGRATION = 45342, // 30-35 secs + SPELL_BLAZE = 45235, // On main target every 3 secs; should trigger 45236 which leaves a fire on the ground + SPELL_FLAME_SEAR = 46771, // A few random targets debuff + SPELL_CONFLAGRATION_UNK = 45333, // Unknown +}; + +static const DialogueEntry aIntroDialogue[] = +{ + {SAY_INTRO_1, NPC_SACROLASH, 1000}, + {SAY_INTRO_2, NPC_ALYTHESS, 1500}, + {SAY_INTRO_3, NPC_SACROLASH, 1500}, + {SAY_INTRO_4, NPC_ALYTHESS, 1500}, + {SAY_INTRO_5, NPC_SACROLASH, 1500}, + {SAY_INTRO_6, NPC_ALYTHESS, 1500}, + {SAY_INTRO_7, NPC_SACROLASH, 2500}, + {SAY_INTRO_8, NPC_ALYTHESS, 0}, + {0, 0, 0}, +}; + +/*###### +## boss_alythess +######*/ + +struct boss_alythessAI : public ScriptedAI +{ + boss_alythessAI(Creature* pCreature) : ScriptedAI(pCreature), + m_introDialogue(aIntroDialogue) + { + m_pInstance = ((instance_sunwell_plateau*)pCreature->GetInstanceData()); + m_introDialogue.InitializeDialogueHelper(m_pInstance); + Reset(); + } + + ScriptedInstance* m_pInstance; + DialogueHelper m_introDialogue; + + uint32 m_uiEnrageTimer; + uint32 m_uiPyrogenicsTimer; + uint32 m_uiConflagrationTimer; + uint32 m_uiBlazeTimer; + uint32 m_uiFlameSearTimer; + bool m_bDidIntro; + + void Reset() override + { + m_uiEnrageTimer = 6 * MINUTE * IN_MILLISECONDS; + m_uiPyrogenicsTimer = 20000; + m_uiConflagrationTimer = urand(25000, 30000); + m_uiBlazeTimer = 1000; + m_uiFlameSearTimer = 5000; + m_bDidIntro = false; + } + + void JustReachedHome() override + { + if (m_pInstance) + { + if (m_pInstance->GetData(TYPE_EREDAR_TWINS) != FAIL) + m_pInstance->SetData(TYPE_EREDAR_TWINS, FAIL); + + // Respawn dead sister + if (Creature* pSister = m_pInstance->GetSingleCreatureFromStorage(NPC_SACROLASH)) + { + if (!pSister->IsAlive()) + pSister->Respawn(); + } + } + } + + void Aggro(Unit* /*pWho*/) override + { + if (m_pInstance) + { + if (m_pInstance->GetData(TYPE_EREDAR_TWINS) != IN_PROGRESS) + m_pInstance->SetData(TYPE_EREDAR_TWINS, IN_PROGRESS); + } + } + + void AttackStart(Unit* pWho) override + { + if (m_creature->Attack(pWho, false)) + { + m_creature->AddThreat(pWho); + m_creature->SetInCombatWith(pWho); + pWho->SetInCombatWith(m_creature); + + // Only range attack + m_creature->GetMotionMaster()->MoveChase(pWho, 10.0f); + } + } + + void KilledUnit(Unit* /*pVictim*/) override + { + DoScriptText(urand(0, 1) ? SAY_ALYTHESS_KILL_1 : SAY_ALYTHESS_KILL_2, m_creature); + } + + void JustDied(Unit* /*pKiller*/) override + { + if (m_pInstance) + { + if (Creature* pSacrolash = m_pInstance->GetSingleCreatureFromStorage(NPC_SACROLASH)) + { + if (!pSacrolash->IsAlive()) + { + m_pInstance->SetData(TYPE_EREDAR_TWINS, DONE); + DoScriptText(SAY_ALYTHESS_DEAD, m_creature); + } + else + { + // Remove loot flag and cast empower + m_creature->RemoveFlag(UNIT_DYNAMIC_FLAGS, UNIT_DYNFLAG_LOOTABLE); + DoScriptText(SAY_SACROLASH_EMPOWER, pSacrolash); + pSacrolash->InterruptNonMeleeSpells(true); + pSacrolash->CastSpell(pSacrolash, SPELL_EMPOWER, false); + } + } + } + } + + void SpellHitTarget(Unit* pTarget, const SpellEntry* pSpell) override + { + if (pTarget->HasAura(SPELL_DARK_FLAME)) + return; + + if (pSpell->SchoolMask == SPELL_SCHOOL_MASK_FIRE) + { + if (pTarget->HasAura(SPELL_DARK_TOUCHED)) + { + pTarget->RemoveAurasDueToSpell(SPELL_DARK_TOUCHED); + pTarget->CastSpell(pTarget, SPELL_DARK_FLAME, true); + } + else + pTarget->CastSpell(pTarget, SPELL_FLAME_TOUCHED, true); + } + else if (pSpell->SchoolMask == SPELL_SCHOOL_MASK_SHADOW) + { + if (pTarget->HasAura(SPELL_FLAME_TOUCHED)) + { + pTarget->RemoveAurasDueToSpell(SPELL_FLAME_TOUCHED); + pTarget->CastSpell(pTarget, SPELL_DARK_FLAME, true); + } + else + pTarget->CastSpell(pTarget, SPELL_DARK_TOUCHED, true); + } + } + + void UpdateAI(const uint32 uiDiff) override + { + if (m_pInstance && m_pInstance->GetData(TYPE_EREDAR_TWINS) == SPECIAL) + { + if (!m_bDidIntro) + { + m_introDialogue.StartNextDialogueText(SAY_INTRO_1); + m_bDidIntro = true; + } + m_introDialogue.DialogueUpdate(uiDiff); + } + + if (!m_creature->SelectHostileTarget() || !m_creature->getVictim()) + return; + + if (m_uiEnrageTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature, SPELL_TWINS_ENRAGE) == CAST_OK) + { + DoScriptText(SAY_ALYTHESS_BERSERK, m_creature); + m_uiEnrageTimer = 6 * MINUTE * IN_MILLISECONDS; + } + } + else + m_uiEnrageTimer -= uiDiff; + + if (m_uiPyrogenicsTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature, SPELL_PYROGENICS) == CAST_OK) + m_uiPyrogenicsTimer = urand(25000, 30000); + } + else + m_uiPyrogenicsTimer -= uiDiff; + + if (m_uiConflagrationTimer < uiDiff) + { + Unit* pTarget = m_creature->SelectAttackingTarget(ATTACKING_TARGET_TOPAGGRO, 3); + if (!pTarget) + pTarget = m_creature->SelectAttackingTarget(ATTACKING_TARGET_RANDOM, 0); + + // If sister is dead cast shadownova instead of conflagration + bool bSwitchSpell = m_creature->HasAura(SPELL_EMPOWER); + if (DoCastSpellIfCan(pTarget, bSwitchSpell ? SPELL_SHADOW_NOVA : SPELL_CONFLAGRATION) == CAST_OK) + { + if (!bSwitchSpell) + DoScriptText(SAY_ALYTHESS_CANFLAGRATION, m_creature); + + m_uiConflagrationTimer = urand(20000, 25000); + } + } + else + m_uiConflagrationTimer -= uiDiff; + + if (m_uiFlameSearTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature, SPELL_FLAME_SEAR) == CAST_OK) + m_uiFlameSearTimer = 10000; + } + else + m_uiFlameSearTimer -= uiDiff; + + if (m_uiBlazeTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature->getVictim(), SPELL_BLAZE) == CAST_OK) + m_uiBlazeTimer = 3000; + } + else + m_uiBlazeTimer -= uiDiff; + } +}; + +/*###### +## boss_sacrolash +######*/ + +struct boss_sacrolashAI : public ScriptedAI +{ + boss_sacrolashAI(Creature* pCreature) : ScriptedAI(pCreature) + { + m_pInstance = ((instance_sunwell_plateau*)pCreature->GetInstanceData()); + Reset(); + } + + ScriptedInstance* m_pInstance; + + uint32 m_uiEnrageTimer; + uint32 m_uiShadowNovaTimer; + uint32 m_uiConfoundingBlowTimer; + uint32 m_uiShadowBladesTimer; + uint32 m_uiSummonShadowImage; + + void Reset() override + { + m_uiEnrageTimer = 6 * MINUTE * IN_MILLISECONDS; + m_uiShadowNovaTimer = 15000; + m_uiConfoundingBlowTimer = 30000; + m_uiShadowBladesTimer = 15000; + m_uiSummonShadowImage = 10000; + } + + void JustReachedHome() override + { + if (m_pInstance) + { + if (m_pInstance->GetData(TYPE_EREDAR_TWINS) != FAIL) + m_pInstance->SetData(TYPE_EREDAR_TWINS, FAIL); + + // Respawn dead sister + if (Creature* pSister = m_pInstance->GetSingleCreatureFromStorage(NPC_ALYTHESS)) + { + if (!pSister->IsAlive()) + pSister->Respawn(); + } + } + } + + void Aggro(Unit* /*pWho*/) override + { + if (m_pInstance) + { + if (m_pInstance->GetData(TYPE_EREDAR_TWINS) != IN_PROGRESS) + m_pInstance->SetData(TYPE_EREDAR_TWINS, IN_PROGRESS); + } + } + + void KilledUnit(Unit* /*pVictim*/) override + { + DoScriptText(urand(0, 1) ? SAY_SACROLASH_KILL_1 : SAY_SACROLASH_KILL_2, m_creature); + } + + void JustDied(Unit* /*pKiller*/) override + { + if (m_pInstance) + { + if (Creature* pAlythess = m_pInstance->GetSingleCreatureFromStorage(NPC_ALYTHESS)) + { + if (!pAlythess->IsAlive()) + { + m_pInstance->SetData(TYPE_EREDAR_TWINS, DONE); + DoScriptText(SAY_SACROLASH_DEAD, m_creature); + } + else + { + // Remove loot flag and cast empower + m_creature->RemoveFlag(UNIT_DYNAMIC_FLAGS, UNIT_DYNFLAG_LOOTABLE); + DoScriptText(SAY_ALYTHESS_EMPOWER, pAlythess); + pAlythess->InterruptNonMeleeSpells(true); + pAlythess->CastSpell(pAlythess, SPELL_EMPOWER, false); + } + } + } + } + + void SpellHitTarget(Unit* pTarget, const SpellEntry* pSpell) override + { + if (pTarget->HasAura(SPELL_DARK_FLAME)) + return; + + if (pSpell->SchoolMask == SPELL_SCHOOL_MASK_FIRE) + { + if (pTarget->HasAura(SPELL_DARK_TOUCHED)) + { + pTarget->RemoveAurasDueToSpell(SPELL_DARK_TOUCHED); + pTarget->CastSpell(pTarget, SPELL_DARK_FLAME, true); + } + else + pTarget->CastSpell(pTarget, SPELL_FLAME_TOUCHED, true); + } + else if (pSpell->SchoolMask == SPELL_SCHOOL_MASK_SHADOW) + { + if (pTarget->HasAura(SPELL_FLAME_TOUCHED)) + { + pTarget->RemoveAurasDueToSpell(SPELL_FLAME_TOUCHED); + pTarget->CastSpell(pTarget, SPELL_DARK_FLAME, true); + } + else + pTarget->CastSpell(pTarget, SPELL_DARK_TOUCHED, true); + } + } + + // Return a random target which it's not in range of 10 yards of boss + Unit* GetRandomTargetAtDist(float fDist) + { + std::vector m_vRangeTargets; + + ThreatList const& tList = m_creature->GetThreatManager().getThreatList(); + for (ThreatList::const_iterator iter = tList.begin(); iter != tList.end(); ++iter) + { + if (Unit* pTempTarget = m_creature->GetMap()->GetUnit((*iter)->getUnitGuid())) + { + if (!pTempTarget->IsWithinDistInMap(m_creature, fDist)) + m_vRangeTargets.push_back(pTempTarget); + } + } + + if (!m_vRangeTargets.empty()) + return m_vRangeTargets[urand(0, m_vRangeTargets.size() - 1)]; + else + return m_creature->SelectAttackingTarget(ATTACKING_TARGET_RANDOM, 1); + } + + void JustSummoned(Creature* pSummoned) override + { + if (pSummoned->GetEntry() == NPC_SHADOW_IMAGE) + { + pSummoned->CastSpell(pSummoned, SPELL_IMAGE_VISUAL, false); + // Attack random range target + if (Unit* pTarget = GetRandomTargetAtDist(10.0f)) + pSummoned->AI()->AttackStart(pTarget); + } + } + + void UpdateAI(const uint32 uiDiff) override + { + if (!m_creature->SelectHostileTarget() || !m_creature->getVictim()) + return; + + if (m_uiEnrageTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature, SPELL_TWINS_ENRAGE) == CAST_OK) + { + DoScriptText(SAY_SACROLASH_BERSERK, m_creature); + m_uiEnrageTimer = 6 * MINUTE * IN_MILLISECONDS; + } + } + else + m_uiEnrageTimer -= uiDiff; + + if (m_uiShadowBladesTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature, SPELL_SHADOW_BLADES) == CAST_OK) + m_uiShadowBladesTimer = urand(13000, 15000); + } + else + m_uiShadowBladesTimer -= uiDiff; + + if (m_uiShadowNovaTimer < uiDiff) + { + Unit* pTarget = m_creature->SelectAttackingTarget(ATTACKING_TARGET_TOPAGGRO, 2); + if (!pTarget) + pTarget = m_creature->SelectAttackingTarget(ATTACKING_TARGET_RANDOM, 0); + + // If sister is dead cast conflagration instead of shadownova + bool bSwitchSpell = m_creature->HasAura(SPELL_EMPOWER); + if (DoCastSpellIfCan(pTarget, bSwitchSpell ? SPELL_CONFLAGRATION : SPELL_SHADOW_NOVA) == CAST_OK) + { + if (!bSwitchSpell) + DoScriptText(SAY_SACROLASH_SHADOW_NOVA, m_creature); + + m_uiShadowNovaTimer = urand(30000, 35000); + } + } + else + m_uiShadowNovaTimer -= uiDiff; + + if (m_uiConfoundingBlowTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature->getVictim(), SPELL_CONFOUNDING_BLOW) == CAST_OK) + { + // Reset threat + if (m_creature->GetThreatManager().getThreat(m_creature->getVictim())) + m_creature->GetThreatManager().modifyThreatPercent(m_creature->getVictim(), -100); + + m_uiConfoundingBlowTimer = urand(25000, 30000); + } + } + else + m_uiConfoundingBlowTimer -= uiDiff; + + if (m_uiSummonShadowImage < uiDiff) + { + // Summon 3 shadow images at the boss position + for (uint8 i = 0; i < 3; ++i) + m_creature->SummonCreature(NPC_SHADOW_IMAGE, 0.0f, 0.0f, 0.0f, 0.0f, TEMPSUMMON_CORPSE_TIMED_DESPAWN, 3000); + + m_uiSummonShadowImage = urand(10000, 12000); + } + else + m_uiSummonShadowImage -= uiDiff; + + // Overwrite the melee attack in order to apply the dark strike + if (m_creature->CanReachWithMeleeAttack(m_creature->getVictim())) + { + // Make sure our attack is ready and we aren't currently casting + if (m_creature->isAttackReady() && !m_creature->IsNonMeleeSpellCasted(false)) + { + DoCastSpellIfCan(m_creature->getVictim(), SPELL_DARK_STRIKE); + + m_creature->AttackerStateUpdate(m_creature->getVictim()); + m_creature->resetAttackTimer(); + } + } + } +}; + +/*###### +## npc_shadow_image +######*/ + +struct npc_shadow_imageAI : public ScriptedAI +{ + npc_shadow_imageAI(Creature* pCreature) : ScriptedAI(pCreature) { Reset(); } + + uint32 m_uiChosenAbility; + uint32 m_uiSuicideTimer; + uint32 m_uiAbilityTimer; + uint8 m_uiDarkStrikes; + + void Reset() override + { + // Choose only one spell for attack + m_uiChosenAbility = urand(0, 1) ? SPELL_DARK_STRIKE : SPELL_SHADOWFURY; + m_uiAbilityTimer = 500; + m_uiDarkStrikes = 0; + m_uiSuicideTimer = 0; + } + + void UpdateAI(const uint32 uiDiff) override + { + if (!m_creature->SelectHostileTarget() || !m_creature->getVictim()) + return; + + // Suicide on timer; this is needed because of the cast time + if (m_uiSuicideTimer) + { + if (m_uiSuicideTimer <= uiDiff) + { + m_creature->DealDamage(m_creature, m_creature->GetHealth(), NULL, DIRECT_DAMAGE, SPELL_SCHOOL_MASK_NORMAL, NULL, false); + return; + } + else + m_uiSuicideTimer -= uiDiff; + } + else + { + // Do chosen ability + switch (m_uiChosenAbility) + { + case SPELL_SHADOWFURY: + if (m_uiAbilityTimer < uiDiff) + { + if (m_creature->IsWithinDistInMap(m_creature->getVictim(), INTERACTION_DISTANCE)) + { + if (DoCastSpellIfCan(m_creature, SPELL_SHADOWFURY) == CAST_OK) + m_uiSuicideTimer = 1000; + } + } + else + m_uiAbilityTimer -= uiDiff; + break; + case SPELL_DARK_STRIKE: + if (m_uiAbilityTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature->getVictim(), SPELL_DARK_STRIKE) == CAST_OK) + { + ++m_uiDarkStrikes; + // kill itself after 2 strikes + if (m_uiDarkStrikes == 2) + m_uiSuicideTimer = 1000; + else + m_uiAbilityTimer = 1000; + } + } + else + m_uiAbilityTimer -= uiDiff; + break; + } + } + } +}; + +CreatureAI* GetAI_boss_alythess(Creature* pCreature) +{ + return new boss_alythessAI(pCreature); +} + +CreatureAI* GetAI_boss_sacrolash(Creature* pCreature) +{ + return new boss_sacrolashAI(pCreature); +} + +CreatureAI* GetAI_npc_shadow_image(Creature* pCreature) +{ + return new npc_shadow_imageAI(pCreature); +} + +void AddSC_boss_eredar_twins() +{ + Script* pNewScript; + + pNewScript = new Script; + pNewScript->Name = "boss_alythess"; + pNewScript->GetAI = &GetAI_boss_alythess; + pNewScript->RegisterSelf(); + + pNewScript = new Script; + pNewScript->Name = "boss_sacrolash"; + pNewScript->GetAI = &GetAI_boss_sacrolash; + pNewScript->RegisterSelf(); + + pNewScript = new Script; + pNewScript->Name = "npc_shadow_image"; + pNewScript->GetAI = &GetAI_npc_shadow_image; + pNewScript->RegisterSelf(); +} diff --git a/src/modules/SD2/scripts/eastern_kingdoms/sunwell_plateau/boss_felmyst.cpp b/src/modules/SD2/scripts/eastern_kingdoms/sunwell_plateau/boss_felmyst.cpp new file mode 100644 index 000000000..a104b99c9 --- /dev/null +++ b/src/modules/SD2/scripts/eastern_kingdoms/sunwell_plateau/boss_felmyst.cpp @@ -0,0 +1,512 @@ +/* Copyright (C) 2006 - 2013 ScriptDev2 + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with 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: 90% +SDComment: Intro movement NYI; Event cleanup (despawn & resummon) NYI; Breath phase spells could use some improvements. +SDCategory: Sunwell Plateau +EndScriptData */ + +#include "precompiled.h" +#include "sunwell_plateau.h" +#include "TemporarySummon.h" + +enum +{ + SAY_INTRO = -1580036, + SAY_KILL_1 = -1580037, + SAY_KILL_2 = -1580038, + SAY_DEATH = -1580042, + SAY_TAKEOFF = -1580040, + SAY_BREATH = -1580039, + SAY_BERSERK = -1580041, + EMOTE_DEEP_BREATH = -1580107, + + SPELL_FELBLAZE_VISUAL = 45068, // Visual transform aura + SPELL_NOXIOUS_FUMES = 47002, + SPELL_SOUL_SEVER = 45918, // kills all charmed targets at wipe - script effect for 45917 + SPELL_BERSERK = 26662, + + // ground phase + SPELL_CLEAVE = 19983, + SPELL_CORROSION = 45866, + SPELL_GAS_NOVA = 45855, + SPELL_ENCAPSULATE = 45665, + SPELL_ENCAPSULATE_CHANNEL = 45661, + + // flight phase + SPELL_SUMMON_VAPOR = 45391, + SPELL_VAPOR_SPAWN_TRIGGER = 45388, + SPELL_SPEED_BURST = 45495, // spell needs to be confirmed + SPELL_FOG_CORRUPTION = 45582, + + // demonic vapor spells + SPELL_DEMONIC_VAPOR_PER = 45411, + SPELL_DEMONIC_VAPOR = 45399, + // SPELL_SUMMON_BLAZING_DEAD = 45400, + + // npcs + // NPC_UNYELDING_DEAD = 25268, // spawned during flight phase + NPC_DEMONIC_VAPOR = 25265, // npc which follows the player + NPC_DEMONIC_VAPOR_TRAIL = 25267, + + // phases + PHASE_GROUND = 1, + PHASE_AIR = 2, + PHASE_TRANSITION = 3, + + // subphases for air phase + SUBPHASE_VAPOR = 4, + SUBPHASE_BREATH_PREPARE = 5, + SUBPHASE_BREATH_MOVE = 6, +}; + +/*###### +## boss_felmyst +######*/ + +struct boss_felmystAI : public ScriptedAI +{ + boss_felmystAI(Creature* pCreature) : ScriptedAI(pCreature) + { + m_pInstance = (instance_sunwell_plateau*)pCreature->GetInstanceData(); + m_bHasTransformed = false; + m_uiMovementTimer = 2000; + Reset(); + } + + instance_sunwell_plateau* m_pInstance; + + bool m_bHasTransformed; + uint32 m_uiMovementTimer; + + uint8 m_uiPhase; + uint32 m_uiBerserkTimer; + + // Ground Phase timers + uint32 m_uiFlyPhaseTimer; + uint32 m_uiCorrosionTimer; + uint32 m_uiCleaveTimer; + uint32 m_uiEncapsulateTimer; + uint32 m_uiGasNovaTimer; + + // Air Phase timers + uint8 m_uiSubPhase; + bool m_bIsLeftSide; + + uint8 m_uiDemonicVaporCount; + uint8 m_uiCorruptionCount; + uint8 m_uiCorruptionIndex; + uint32 m_uiDemonicVaporTimer; + uint32 m_uiCorruptionTimer; + + void Reset() override + { + // Transform into Felmyst dragon + DoCastSpellIfCan(m_creature, SPELL_FELBLAZE_VISUAL); + + m_uiPhase = PHASE_GROUND; + m_uiBerserkTimer = 10 * MINUTE * IN_MILLISECONDS; + + // Ground Phase + m_uiCorrosionTimer = 30000; + m_uiCleaveTimer = urand(2000, 5000); + m_uiGasNovaTimer = 17000; + m_uiEncapsulateTimer = urand(30000, 40000); + m_uiFlyPhaseTimer = 60000; // flight phase after 1 min + + // Air phase + m_uiSubPhase = SUBPHASE_VAPOR; + m_uiDemonicVaporCount = 0; + m_uiDemonicVaporTimer = 1000; + m_uiCorruptionTimer = 0; + + SetCombatMovement(false); + } + + void MoveInLineOfSight(Unit* pWho) override + { + if (!m_bHasTransformed) + { + if (pWho->GetTypeId() == TYPEID_PLAYER && pWho->IsWithinLOSInMap(m_creature) && pWho->IsWithinDistInMap(m_creature, 100.0f)) + { + DoScriptText(SAY_INTRO, m_creature); + m_bHasTransformed = true; + } + } + + ScriptedAI::MoveInLineOfSight(pWho); + } + + void EnterEvadeMode() override + { + m_creature->RemoveAllAurasOnEvade(); + m_creature->DeleteThreatList(); + m_creature->CombatStop(true); + + // Add the visual aura back when evading - workaround because there is no way to remove only the negative auras + DoCastSpellIfCan(m_creature, SPELL_FELBLAZE_VISUAL, CAST_TRIGGERED); + + // Also make sure that the charmed targets are killed + DoCastSpellIfCan(m_creature, SPELL_SOUL_SEVER, CAST_TRIGGERED); + + // Fly back to the home flight location + if (m_creature->IsAlive()) + { + float fX, fY, fZ; + m_creature->SetLevitate(true); + m_creature->GetRespawnCoord(fX, fY, fZ); + m_creature->GetMotionMaster()->MovePoint(PHASE_GROUND, fX, fY, 50.083f, false); + } + + m_creature->SetLootRecipient(NULL); + + Reset(); + } + + void Aggro(Unit* pWho) override + { + DoCastSpellIfCan(m_creature, SPELL_NOXIOUS_FUMES); + + if (m_pInstance) + m_pInstance->SetData(TYPE_FELMYST, IN_PROGRESS); + + float fGroundZ = m_creature->GetMap()->GetHeight(m_creature->GetPhaseMask(), m_creature->GetPositionX(), m_creature->GetPositionY(), m_creature->GetPositionZ()); + m_creature->GetMotionMaster()->MovePoint(PHASE_TRANSITION, pWho->GetPositionX(), pWho->GetPositionY(), fGroundZ, false); + m_creature->HandleEmote(EMOTE_ONESHOT_LAND); + } + + void KilledUnit(Unit* /*pVictim*/) override + { + DoScriptText(urand(0, 1) ? SAY_KILL_1 : SAY_KILL_2, m_creature); + } + + void JustDied(Unit* /*pKiller*/) override + { + DoScriptText(SAY_DEATH, m_creature); + + if (m_pInstance) + m_pInstance->SetData(TYPE_FELMYST, DONE); + } + + void JustReachedHome() override + { + if (m_pInstance) + m_pInstance->SetData(TYPE_FELMYST, FAIL); + } + + void JustSummoned(Creature* pSummoned) override + { + if (pSummoned->GetEntry() == NPC_DEMONIC_VAPOR) + { + pSummoned->CastSpell(pSummoned, SPELL_VAPOR_SPAWN_TRIGGER, true); + pSummoned->CastSpell(pSummoned, SPELL_DEMONIC_VAPOR_PER, true); + } + } + + void MovementInform(uint32 uiMoveType, uint32 uiPointId) override + { + if (uiMoveType != POINT_MOTION_TYPE) + return; + + switch (uiPointId) + { + case PHASE_GROUND: + m_creature->SetWalk(false); + // ToDo: start WP movement here. Currently disabled because of some MMaps issues + // m_creature->GetMotionMaster()->MoveWaypoint(); + break; + case PHASE_AIR: + // switch from ground transition to flight phase + m_uiPhase = PHASE_AIR; + break; + case SUBPHASE_VAPOR: + // After the third breath land and resume phase 1 + if (m_uiCorruptionCount == 3) + { + m_uiPhase = PHASE_TRANSITION; + float fGroundZ = m_creature->GetMap()->GetHeight(m_creature->GetPhaseMask(), m_creature->GetPositionX(), m_creature->GetPositionY(), m_creature->GetPositionZ()); + m_creature->GetMotionMaster()->MovePoint(PHASE_TRANSITION, m_creature->getVictim()->GetPositionX(), m_creature->getVictim()->GetPositionY(), fGroundZ, false); + return; + } + + // prepare to move to flight trigger + ++m_uiCorruptionCount; + m_uiCorruptionTimer = 5000; + m_uiSubPhase = SUBPHASE_BREATH_PREPARE; + break; + case SUBPHASE_BREATH_PREPARE: + // move across the arena + if (!m_pInstance) + return; + + // Fly to the other side, casting the breath. Keep the same trigger index + if (Creature* pTrigger = m_creature->GetMap()->GetCreature(m_pInstance->SelectFelmystFlightTrigger(!m_bIsLeftSide, m_uiCorruptionIndex))) + { + DoScriptText(EMOTE_DEEP_BREATH, m_creature); + DoCastSpellIfCan(m_creature, SPELL_SPEED_BURST, CAST_TRIGGERED); + DoCastSpellIfCan(m_creature, SPELL_FOG_CORRUPTION, CAST_TRIGGERED); + m_creature->GetMotionMaster()->MovePoint(SUBPHASE_BREATH_MOVE, pTrigger->GetPositionX(), pTrigger->GetPositionY(), pTrigger->GetPositionZ(), false); + } + break; + case SUBPHASE_BREATH_MOVE: + if (!m_pInstance) + return; + + // remove speed aura + m_creature->RemoveAurasDueToSpell(SPELL_SPEED_BURST); + + // Get to the flight trigger on the same side of the arena + if (Creature* pTrigger = m_pInstance->GetSingleCreatureFromStorage(!m_bIsLeftSide ? NPC_FLIGHT_TRIGGER_LEFT : NPC_FLIGHT_TRIGGER_RIGHT)) + m_creature->GetMotionMaster()->MovePoint(SUBPHASE_VAPOR, pTrigger->GetPositionX(), pTrigger->GetPositionY(), pTrigger->GetPositionZ(), false); + + // switch sides + m_bIsLeftSide = !m_bIsLeftSide; + break; + case PHASE_TRANSITION: + // switch back to ground combat from flight transition + m_uiPhase = PHASE_GROUND; + SetCombatMovement(true); + m_creature->SetLevitate(false); + DoStartMovement(m_creature->getVictim()); + break; + } + } + + void SpellHitTarget(Unit* pTarget, const SpellEntry* pSpell) override + { + if (pTarget->GetTypeId() == TYPEID_PLAYER && pSpell->Id == SPELL_ENCAPSULATE_CHANNEL) + pTarget->CastSpell(pTarget, SPELL_ENCAPSULATE, true, NULL, NULL, m_creature->GetObjectGuid()); + } + + void UpdateAI(const uint32 uiDiff) override + { + if (m_uiMovementTimer) + { + if (m_uiMovementTimer <= uiDiff) + { + m_creature->SetLevitate(true); + m_creature->GetMotionMaster()->MovePoint(PHASE_GROUND, m_creature->GetPositionX(), m_creature->GetPositionY(), 50.083f, false); + m_uiMovementTimer = 0; + } + else + m_uiMovementTimer -= uiDiff; + } + + if (!m_creature->SelectHostileTarget() || !m_creature->getVictim()) + return; + + if (m_uiBerserkTimer) + { + if (m_uiBerserkTimer <= uiDiff) + { + if (DoCastSpellIfCan(m_creature, SPELL_BERSERK) == CAST_OK) + { + DoScriptText(SAY_BERSERK, m_creature); + m_uiBerserkTimer = 0; + } + } + else + m_uiBerserkTimer -= uiDiff; + } + + switch (m_uiPhase) + { + case PHASE_GROUND: + + if (m_uiCleaveTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature->getVictim(), SPELL_CLEAVE) == CAST_OK) + m_uiCleaveTimer = urand(2000, 5000); + } + else + m_uiCleaveTimer -= uiDiff; + + if (m_uiCorrosionTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature->getVictim(), SPELL_CORROSION) == CAST_OK) + { + DoScriptText(SAY_BREATH, m_creature); + m_uiCorrosionTimer = 30000; + } + } + else + m_uiCorrosionTimer -= uiDiff; + + if (m_uiGasNovaTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature, SPELL_GAS_NOVA) == CAST_OK) + m_uiGasNovaTimer = 23000; + } + else + m_uiGasNovaTimer -= uiDiff; + + if (m_uiEncapsulateTimer < uiDiff) + { + if (Unit* pTarget = m_creature->SelectAttackingTarget(ATTACKING_TARGET_RANDOM, 0)) + { + if (DoCastSpellIfCan(pTarget, SPELL_ENCAPSULATE_CHANNEL) == CAST_OK) + m_uiEncapsulateTimer = urand(30000, 40000); + } + } + else + m_uiEncapsulateTimer -= uiDiff; + + if (m_uiFlyPhaseTimer < uiDiff) + { + DoScriptText(SAY_TAKEOFF, m_creature); + + SetCombatMovement(false); + m_creature->SetLevitate(true); + m_creature->GetMotionMaster()->MoveIdle(); + m_creature->GetMotionMaster()->MovePoint(PHASE_AIR, m_creature->GetPositionX(), m_creature->GetPositionY(), 50.083f, false); + + m_uiPhase = PHASE_TRANSITION; + m_uiSubPhase = SUBPHASE_VAPOR; + m_uiDemonicVaporTimer = 1000; + m_uiDemonicVaporCount = 0; + m_uiFlyPhaseTimer = 60000; + } + else + m_uiFlyPhaseTimer -= uiDiff; + + DoMeleeAttackIfReady(); + + break; + case PHASE_AIR: + + switch (m_uiSubPhase) + { + case SUBPHASE_VAPOR: + + if (m_uiDemonicVaporTimer < uiDiff) + { + // After the second Demonic Vapor trial, start the breath phase + if (m_uiDemonicVaporCount == 2) + { + if (!m_pInstance) + return; + + // select the side on which we want to fly + m_bIsLeftSide = urand(0, 1) ? true : false; + m_uiCorruptionCount = 0; + m_uiSubPhase = SUBPHASE_BREATH_PREPARE; + if (Creature* pTrigger = m_pInstance->GetSingleCreatureFromStorage(m_bIsLeftSide ? NPC_FLIGHT_TRIGGER_LEFT : NPC_FLIGHT_TRIGGER_RIGHT)) + m_creature->GetMotionMaster()->MovePoint(SUBPHASE_VAPOR, pTrigger->GetPositionX(), pTrigger->GetPositionY(), pTrigger->GetPositionZ(), false); + } + else + { + if (DoCastSpellIfCan(m_creature, SPELL_SUMMON_VAPOR) == CAST_OK) + { + ++m_uiDemonicVaporCount; + m_uiDemonicVaporTimer = 11000; + } + } + } + else + m_uiDemonicVaporTimer -= uiDiff; + + break; + case SUBPHASE_BREATH_PREPARE: + + if (m_uiCorruptionTimer) + { + if (m_uiCorruptionTimer <= uiDiff) + { + if (!m_pInstance) + return; + + // Fly to trigger on the same side - choose a random index for the trigger + m_uiCorruptionIndex = urand(0, 2); + if (Creature* pTrigger = m_creature->GetMap()->GetCreature(m_pInstance->SelectFelmystFlightTrigger(m_bIsLeftSide, m_uiCorruptionIndex))) + m_creature->GetMotionMaster()->MovePoint(SUBPHASE_BREATH_PREPARE, pTrigger->GetPositionX(), pTrigger->GetPositionY(), pTrigger->GetPositionZ(), false); + + m_uiSubPhase = SUBPHASE_BREATH_MOVE; + m_uiCorruptionTimer = 0; + } + else + m_uiCorruptionTimer -= uiDiff; + } + + break; + case SUBPHASE_BREATH_MOVE: + // nothing here; this is handled in MovementInform + break; + } + break; + case PHASE_TRANSITION: + // nothing here; wait for transition to finish + break; + } + } +}; + +CreatureAI* GetAI_boss_felmyst(Creature* pCreature) +{ + return new boss_felmystAI(pCreature); +} + +/*###### +## npc_demonic_vapor +######*/ + +struct npc_demonic_vaporAI : public ScriptedAI +{ + npc_demonic_vaporAI(Creature* pCreature) : ScriptedAI(pCreature) { Reset(); } + + void Reset() override + { + // Start following the summoner (player) + if (m_creature->IsTemporarySummon()) + { + TemporarySummon* pTemporary = (TemporarySummon*)m_creature; + + if (Player* pSummoner = m_creature->GetMap()->GetPlayer(pTemporary->GetSummonerGuid())) + m_creature->GetMotionMaster()->MoveFollow(pSummoner, 0, 0); + } + } + + void JustSummoned(Creature* pSummoned) override + { + if (pSummoned->GetEntry() == NPC_DEMONIC_VAPOR_TRAIL) + pSummoned->CastSpell(pSummoned, SPELL_DEMONIC_VAPOR, true); + } + + void AttackStart(Unit* /*pWho*/) override { } + void MoveInLineOfSight(Unit* /*pWho*/) override { } + void UpdateAI(const uint32 /*uiDiff*/) override { } +}; + +CreatureAI* GetAI_npc_demonic_vapor(Creature* pCreature) +{ + return new npc_demonic_vaporAI(pCreature); +} + +void AddSC_boss_felmyst() +{ + Script* pNewScript; + + pNewScript = new Script; + pNewScript->Name = "boss_felmyst"; + pNewScript->GetAI = &GetAI_boss_felmyst; + pNewScript->RegisterSelf(); + + pNewScript = new Script; + pNewScript->Name = "npc_demonic_vapor"; + pNewScript->GetAI = &GetAI_npc_demonic_vapor; + pNewScript->RegisterSelf(); +} diff --git a/src/modules/SD2/scripts/eastern_kingdoms/sunwell_plateau/boss_kalecgos.cpp b/src/modules/SD2/scripts/eastern_kingdoms/sunwell_plateau/boss_kalecgos.cpp new file mode 100644 index 000000000..eebf8a1e9 --- /dev/null +++ b/src/modules/SD2/scripts/eastern_kingdoms/sunwell_plateau/boss_kalecgos.cpp @@ -0,0 +1,630 @@ +/* Copyright (C) 2006 - 2013 ScriptDev2 + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with 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: 70 +SDComment: Timers; +SDCategory: Sunwell Plateau +EndScriptData */ + +#include "precompiled.h" +#include "sunwell_plateau.h" + +enum +{ + // kalecgos dragon form + SAY_EVIL_AGGRO = -1580000, + SAY_EVIL_SPELL_1 = -1580001, + SAY_EVIL_SPELL_2 = -1580002, + SAY_EVIL_SLAY_1 = -1580003, + SAY_EVIL_SLAY_2 = -1580004, + SAY_EVIL_ENRAGE = -1580005, + + // kalecgos humanoid form + SAY_GOOD_AGGRO = -1580006, + SAY_GOOD_NEAR_DEATH_20 = -1580007, + SAY_GOOD_NEAR_DEATH_10 = -1580008, + SAY_GOOD_PLRWIN = -1580009, + + SAY_SATH_AGGRO = -1580010, + SAY_SATH_DEATH = -1580011, + SAY_SATH_SPELL_1 = -1580012, + SAY_SATH_SPELL_2 = -1580013, + SAY_SATH_SLAY_1 = -1580014, + SAY_SATH_SLAY_2 = -1580015, + SAY_SATH_ENRAGE = -1580016, + + // Kalecgos + SPELL_SPECTRAL_BLAST = 44869, + SPELL_SPECTRAL_REALM_NOTIFY = 44845, // cast by the players on teleport to notify boss + SPELL_ARCANE_BUFFET = 45018, + SPELL_FROST_BREATH = 44799, + SPELL_TAIL_LASH = 45122, + SPELL_CRAZED_RAGE = 44807, + + // Kalecgos human + SPELL_HEROIC_STRIKE = 45026, + SPELL_REVITALIZE = 45027, + + // Sathrovarr + SPELL_SPECTRAL_INVISIBILITY = 44801, + SPELL_CORRUPTING_STRIKE = 45029, + SPELL_CURSE_OF_BOUNDLESS_AGONY = 45032, + SPELL_SHADOW_BOLT_VOLLEY = 45031, + + // Misc + SPELL_BANISH = 44836 +}; + +static const uint32 aWildMagicSpells[6] = {44978, 45001, 45002, 45004, 45006, 45010}; +static const float aKalecHumanLoc[4] = {1709.094f, 927.5035f, -74.28364f, 2.932153f}; + +/*###### +## boss_kalecgos +######*/ + +struct boss_kalecgosAI : public ScriptedAI +{ + boss_kalecgosAI(Creature* pCreature) : ScriptedAI(pCreature) + { + m_pInstance = (instance_sunwell_plateau*)pCreature->GetInstanceData(); + Reset(); + } + + instance_sunwell_plateau* m_pInstance; + + uint32 m_uiArcaneBuffetTimer; + uint32 m_uiFrostBreathTimer; + uint32 m_uiWildMagicTimer; + uint32 m_uiSpectralBlastTimer; + uint32 m_uiTailLashTimer; + uint32 m_uiExitTimer; + + bool m_bIsUncorrupted; + bool m_bIsBanished; + bool m_bIsEnraged; + + void Reset() override + { + m_uiArcaneBuffetTimer = 8000; + m_uiTailLashTimer = 5000; + m_uiFrostBreathTimer = 24000; + m_uiWildMagicTimer = 18000; + m_uiSpectralBlastTimer = 30000; + m_uiExitTimer = 0; + + m_bIsUncorrupted = false; + m_bIsBanished = false; + m_bIsEnraged = false; + } + + void JustReachedHome() override + { + if (m_pInstance) + { + m_pInstance->DoEjectSpectralPlayers(); + m_pInstance->SetData(TYPE_KALECGOS, FAIL); + } + } + + void EnterEvadeMode() override + { + // Check if the boss is uncorrupted when evading + if (m_bIsUncorrupted) + { + m_creature->RemoveAllAurasOnEvade(); + m_creature->DeleteThreatList(); + m_creature->CombatStop(true); + m_creature->LoadCreatureAddon(true); + + m_creature->SetLootRecipient(NULL); + m_creature->GetMotionMaster()->MoveIdle(); + m_creature->SetStandState(UNIT_STAND_STATE_STAND); + return; + } + + ScriptedAI::EnterEvadeMode(); + } + + void Aggro(Unit* /*pWho*/) override + { + DoScriptText(SAY_EVIL_AGGRO, m_creature); + + if (m_pInstance) + m_pInstance->SetData(TYPE_KALECGOS, IN_PROGRESS); + } + + void DamageTaken(Unit* /*pDoneBy*/, uint32& uiDamage) override + { + if (uiDamage > m_creature->GetHealth()) + { + uiDamage = 0; + + // If Sathrovarr is not banished yet, then banish the boss + if (!m_bIsUncorrupted) + { + if (DoCastSpellIfCan(m_creature, SPELL_BANISH, CAST_TRIGGERED) == CAST_OK) + m_bIsBanished = true; + } + else + DoStartOutro(); + } + } + + void KilledUnit(Unit* /*pVictim*/) override + { + DoScriptText(urand(0, 1) ? SAY_EVIL_SLAY_1 : SAY_EVIL_SLAY_2, m_creature); + } + + void DoStartOutro() + { + if (!m_pInstance) + return; + + // Bring Sathrovarr in the normal realm and kill him + if (Creature* pSathrovarr = m_pInstance->GetSingleCreatureFromStorage(NPC_SATHROVARR)) + { + // The teleport spell doesn't work right for this, so we need to teleport him manually + pSathrovarr->NearTeleportTo(1704.34f, 928.17f, 53.08f, 0); + pSathrovarr->DealDamage(pSathrovarr, pSathrovarr->GetHealth(), NULL, DIRECT_DAMAGE, SPELL_SCHOOL_MASK_NORMAL, NULL, false); + } + + if (Creature* pKalec = m_pInstance->GetSingleCreatureFromStorage(NPC_KALECGOS_HUMAN)) + pKalec->ForcedDespawn(); + + EnterEvadeMode(); + m_creature->SetFactionTemporary(35, TEMPFACTION_RESTORE_RESPAWN); + m_creature->GetMotionMaster()->MoveIdle(); + DoScriptText(SAY_GOOD_PLRWIN, m_creature); + m_uiExitTimer = 10000; + } + + void MovementInform(uint32 uiMotionType, uint32 uiPointId) override + { + if (uiMotionType != POINT_MOTION_TYPE) + return; + + if (uiPointId) + { + if (m_pInstance) + m_pInstance->SetData(TYPE_KALECGOS, DONE); + + m_creature->ForcedDespawn(1000); + } + } + + void ReceiveAIEvent(AIEventType eventType, Creature* /*pSender*/, Unit* pInvoker, uint32 /*uiMiscValue*/) override + { + if (eventType == AI_EVENT_CUSTOM_A && m_pInstance) + m_pInstance->AddToSpectralRealm(pInvoker->GetObjectGuid()); + } + + void UpdateAI(const uint32 uiDiff) override + { + if (m_uiExitTimer) + { + if (m_uiExitTimer <= uiDiff) + { + float fX, fY, fZ; + m_creature->GetRandomPoint(m_creature->GetPositionX(), m_creature->GetPositionY(), m_creature->GetPositionZ(), 30.0f, fX, fY, fZ); + fZ = 70.0f; + + m_creature->SetByteValue(UNIT_FIELD_BYTES_1, 3, UNIT_BYTE1_FLAG_ALWAYS_STAND | UNIT_BYTE1_FLAG_UNK_2); + m_creature->SetLevitate(true); + m_creature->GetMotionMaster()->MovePoint(1, fX, fY, fZ); + m_uiExitTimer = 0; + } + else + m_uiExitTimer -= uiDiff; + } + + if (!m_creature->SelectHostileTarget() || !m_creature->getVictim()) + return; + + if (m_bIsBanished) + { + // When Sathrovarr is banished then start outro + if (m_bIsUncorrupted) + DoStartOutro(); + + // return when banished + return; + } + + if (!m_bIsEnraged && m_creature->GetHealthPercent() < 10.0f) + { + // If the boss already has the aura, then mark the enraged as true + if (m_creature->HasAura(SPELL_CRAZED_RAGE)) + m_bIsEnraged = true; + else + { + // Spell is targeting both bosses + if (DoCastSpellIfCan(m_creature, SPELL_CRAZED_RAGE) == CAST_OK) + m_bIsEnraged = true; + } + } + + if (m_uiArcaneBuffetTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature->getVictim(), SPELL_ARCANE_BUFFET) == CAST_OK) + { + if (!urand(0, 2)) + DoScriptText(SAY_EVIL_SPELL_1, m_creature); + + m_uiArcaneBuffetTimer = 20000; + } + } + else + m_uiArcaneBuffetTimer -= uiDiff; + + if (m_uiFrostBreathTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature->getVictim(), SPELL_FROST_BREATH) == CAST_OK) + { + if (!urand(0, 1)) + DoScriptText(SAY_EVIL_SPELL_2, m_creature); + + m_uiFrostBreathTimer = 25000; + } + } + else + m_uiFrostBreathTimer -= uiDiff; + + if (m_uiTailLashTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature, SPELL_TAIL_LASH) == CAST_OK) + m_uiTailLashTimer = urand(10000, 20000); + } + else + m_uiTailLashTimer -= uiDiff; + + if (m_uiWildMagicTimer < uiDiff) + { + if (Unit* pTarget = m_creature->SelectAttackingTarget(ATTACKING_TARGET_RANDOM, 0)) + { + if (DoCastSpellIfCan(pTarget, aWildMagicSpells[urand(0, 5)]) == CAST_OK) + m_uiWildMagicTimer = 19000; + } + } + else + m_uiWildMagicTimer -= uiDiff; + + if (m_uiSpectralBlastTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature, SPELL_SPECTRAL_BLAST) == CAST_OK) + m_uiSpectralBlastTimer = 25000; + } + else + m_uiSpectralBlastTimer -= uiDiff; + + DoMeleeAttackIfReady(); + } +}; + +/*###### +## boss_sathrovarr +######*/ + +struct boss_sathrovarrAI : public ScriptedAI +{ + boss_sathrovarrAI(Creature* pCreature) : ScriptedAI(pCreature) + { + m_pInstance = (instance_sunwell_plateau*)pCreature->GetInstanceData(); + Reset(); + } + + instance_sunwell_plateau* m_pInstance; + + uint32 m_uiCorruptingStrikeTimer; + uint32 m_uiCurseOfBoundlessAgonyTimer; + uint32 m_uiShadowBoltVolleyTimer; + bool m_bIsBanished; + bool m_bIsEnraged; + + void Reset() override + { + // FIXME: Timers + m_uiCorruptingStrikeTimer = 5000; + m_uiCurseOfBoundlessAgonyTimer = 15000; + m_uiShadowBoltVolleyTimer = 10000; + + m_bIsBanished = false; + m_bIsEnraged = false; + + DoCastSpellIfCan(m_creature, SPELL_SPECTRAL_INVISIBILITY); + } + + void Aggro(Unit* /*pWho*/) override + { + DoScriptText(SAY_SATH_AGGRO, m_creature); + + if (!m_pInstance) + return; + + // spawn human Kalec; he starts to attack + m_creature->SummonCreature(NPC_KALECGOS_HUMAN, aKalecHumanLoc[0], aKalecHumanLoc[1], aKalecHumanLoc[2], aKalecHumanLoc[3], TEMPSUMMON_DEAD_DESPAWN, 0, true); + } + + void DamageTaken(Unit* /*pDoneBy*/, uint32& uiDamage) override + { + if (uiDamage > m_creature->GetHealth()) + { + uiDamage = 0; + + if (m_bIsBanished) + return; + + // banish Sathrovarr and eject the players + if (DoCastSpellIfCan(m_creature, SPELL_BANISH, CAST_TRIGGERED) == CAST_OK) + m_bIsBanished = true; + + if (!m_pInstance) + return; + + if (Creature* pKalecgos = m_pInstance->GetSingleCreatureFromStorage(NPC_KALECGOS_DRAGON)) + { + if (boss_kalecgosAI* pKalecgosAI = dynamic_cast(pKalecgos->AI())) + pKalecgosAI->m_bIsUncorrupted = true; + } + + m_pInstance->DoEjectSpectralPlayers(); + } + } + + void KilledUnit(Unit* pVictim) override + { + DoScriptText(urand(0, 1) ? SAY_SATH_SLAY_1 : SAY_SATH_SLAY_2, m_creature); + + // !!! Workaround which ejects the players from the spectral realm on death !!! + if (pVictim->GetTypeId() == TYPEID_PLAYER) + { + pVictim->CastSpell(pVictim, SPELL_TELEPORT_NORMAL_REALM, true); + pVictim->CastSpell(pVictim, SPELL_SPECTRAL_EXHAUSTION, true); + } + } + + void MoveInLineOfSight(Unit* pWho) override + { + // !!! Workaround which ejects the players from the spectral realm !!! + if (pWho->GetTypeId() == TYPEID_PLAYER && pWho->IsWithinLOSInMap(m_creature) && pWho->IsWithinDistInMap(m_creature, 75.0f)) + { + if (!pWho->HasAura(SPELL_SPECTRAL_REALM_AURA)) + { + pWho->CastSpell(pWho, SPELL_TELEPORT_NORMAL_REALM, true); + pWho->CastSpell(pWho, SPELL_SPECTRAL_EXHAUSTION, true); + + if (m_pInstance) + m_pInstance->RemoveFromSpectralRealm(pWho->GetObjectGuid()); + } + } + + ScriptedAI::MoveInLineOfSight(pWho); + } + + void JustDied(Unit* /*pKiller*/) override + { + DoScriptText(SAY_SATH_DEATH, m_creature); + } + + void JustSummoned(Creature* pSummoned) override + { + if (pSummoned->GetEntry() == NPC_KALECGOS_HUMAN) + pSummoned->AI()->AttackStart(m_creature); + } + + void ReceiveAIEvent(AIEventType eventType, Creature* /*pSender*/, Unit* pInvoker, uint32 /*uiMiscValue*/) override + { + if (eventType == AI_EVENT_CUSTOM_A && m_pInstance) + m_pInstance->AddToSpectralRealm(pInvoker->GetObjectGuid()); + } + + void UpdateAI(const uint32 uiDiff) override + { + if (!m_creature->SelectHostileTarget() || !m_creature->getVictim()) + return; + + if (m_bIsBanished) + return; + + if (!m_bIsEnraged && m_creature->GetHealthPercent() < 10.0f) + { + // If the boss already has the aura, then mark the enraged as true + if (m_creature->HasAura(SPELL_CRAZED_RAGE)) + m_bIsEnraged = true; + else + { + // Spell is targeting both bosses + if (DoCastSpellIfCan(m_creature, SPELL_CRAZED_RAGE) == CAST_OK) + m_bIsEnraged = true; + } + } + + if (m_uiCorruptingStrikeTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature->getVictim(), SPELL_CORRUPTING_STRIKE) == CAST_OK) + { + if (!urand(0, 1)) + DoScriptText(SAY_SATH_SPELL_2, m_creature); + + m_uiCorruptingStrikeTimer = 13000; + } + } + else + m_uiCorruptingStrikeTimer -= uiDiff; + + if (m_uiCurseOfBoundlessAgonyTimer < uiDiff) + { + if (Unit* pTarget = m_creature->SelectAttackingTarget(ATTACKING_TARGET_RANDOM, 0)) + { + if (DoCastSpellIfCan(pTarget, SPELL_CURSE_OF_BOUNDLESS_AGONY) == CAST_OK) + m_uiCurseOfBoundlessAgonyTimer = 35000; + } + } + else + m_uiCurseOfBoundlessAgonyTimer -= uiDiff; + + if (m_uiShadowBoltVolleyTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature->getVictim(), SPELL_SHADOW_BOLT_VOLLEY) == CAST_OK) + { + if (!urand(0, 1)) + DoScriptText(SAY_SATH_SPELL_1, m_creature); + + m_uiShadowBoltVolleyTimer = 15000; + } + } + else + m_uiShadowBoltVolleyTimer -= uiDiff; + + DoMeleeAttackIfReady(); + } +}; + +/*###### +## boss_kalecgos_humanoid +######*/ + +struct boss_kalecgos_humanoidAI : public ScriptedAI +{ + boss_kalecgos_humanoidAI(Creature* pCreature) : ScriptedAI(pCreature) + { + m_pInstance = (instance_sunwell_plateau*)pCreature->GetInstanceData(); + Reset(); + } + + instance_sunwell_plateau* m_pInstance; + + uint32 m_uiRevitalizeTimer; + uint32 m_uiHeroicStrikeTimer; + + bool m_bHasYelled10Percent; + bool m_bHasYelled20Percent; + + void Reset() override + { + // TODO: Times! + m_uiRevitalizeTimer = 30000; + m_uiHeroicStrikeTimer = 8000; + + m_bHasYelled10Percent = false; + m_bHasYelled20Percent = false; + + DoCastSpellIfCan(m_creature, SPELL_SPECTRAL_INVISIBILITY); + } + + void Aggro(Unit* /*pWho*/) override + { + DoScriptText(SAY_GOOD_AGGRO, m_creature); + } + + void JustDied(Unit* /*pKiller*/) override + { + if (m_pInstance) + { + m_pInstance->DoEjectSpectralPlayers(); + m_pInstance->SetData(TYPE_KALECGOS, FAIL); + } + } + + void UpdateAI(const uint32 uiDiff) override + { + if (!m_creature->SelectHostileTarget() || !m_creature->getVictim()) + return; + + if (m_uiRevitalizeTimer < uiDiff) + { + // Cast on self because spell has target "all friendly units around the caster" + if (DoCastSpellIfCan(m_creature, SPELL_REVITALIZE) == CAST_OK) + m_uiRevitalizeTimer = 30000; + } + else + m_uiRevitalizeTimer -= uiDiff; + + if (m_uiHeroicStrikeTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature->getVictim(), SPELL_HEROIC_STRIKE) == CAST_OK) + m_uiHeroicStrikeTimer = 30000; + } + else + m_uiHeroicStrikeTimer -= uiDiff; + + if (m_creature->GetHealthPercent() < 20.0f && !m_bHasYelled20Percent) + { + DoScriptText(SAY_GOOD_NEAR_DEATH_20, m_creature); + m_bHasYelled20Percent = true; + } + + if (m_creature->GetHealthPercent() < 10.0f && !m_bHasYelled10Percent) + { + DoScriptText(SAY_GOOD_NEAR_DEATH_10, m_creature); + m_bHasYelled10Percent = true; + } + + DoMeleeAttackIfReady(); + } +}; + +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_kalecgos_humanoid(Creature* pCreature) +{ + return new boss_kalecgos_humanoidAI(pCreature); +} + +bool EffectDummyCreature_spell_spectral_realm_notify(Unit* pCaster, uint32 uiSpellId, SpellEffectIndex uiEffIndex, Creature* pCreatureTarget, ObjectGuid /*originalCasterGuid*/) +{ + if (uiSpellId == SPELL_SPECTRAL_REALM_NOTIFY && uiEffIndex == EFFECT_INDEX_0) + { + if (pCaster->GetTypeId() == TYPEID_PLAYER) + pCreatureTarget->AI()->SendAIEvent(AI_EVENT_CUSTOM_A, pCaster, pCreatureTarget); + + return true; + } + + return true; +} + +void AddSC_boss_kalecgos() +{ + Script* pNewScript; + + pNewScript = new Script; + pNewScript->Name = "boss_kalecgos"; + pNewScript->GetAI = &GetAI_boss_kalecgos; + pNewScript->pEffectDummyNPC = &EffectDummyCreature_spell_spectral_realm_notify; + pNewScript->RegisterSelf(); + + pNewScript = new Script; + pNewScript->Name = "boss_sathrovarr"; + pNewScript->GetAI = &GetAI_boss_sathrovarr; + pNewScript->pEffectDummyNPC = &EffectDummyCreature_spell_spectral_realm_notify; + pNewScript->RegisterSelf(); + + pNewScript = new Script; + pNewScript->Name = "boss_kalecgos_humanoid"; + pNewScript->GetAI = &GetAI_boss_kalecgos_humanoid; + pNewScript->pEffectDummyNPC = &EffectDummyCreature_spell_spectral_realm_notify; + pNewScript->RegisterSelf(); +} diff --git a/src/modules/SD2/scripts/eastern_kingdoms/sunwell_plateau/boss_kiljaeden.cpp b/src/modules/SD2/scripts/eastern_kingdoms/sunwell_plateau/boss_kiljaeden.cpp new file mode 100644 index 000000000..7c80de289 --- /dev/null +++ b/src/modules/SD2/scripts/eastern_kingdoms/sunwell_plateau/boss_kiljaeden.cpp @@ -0,0 +1,882 @@ +/* Copyright (C) 2006 - 2013 ScriptDev2 + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with 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: 90 +SDComment: Sinister Reflection needs AI support. +SDCategory: Sunwell Plateau +EndScriptData */ + +#include "precompiled.h" +#include "sunwell_plateau.h" +#include "TemporarySummon.h" + +enum +{ + SAY_EMERGE = -1580069, + SAY_SLAY_1 = -1580070, + SAY_SLAY_2 = -1580071, + SAY_REFLECTION_1 = -1580072, + SAY_REFLECTION_2 = -1580073, + SAY_DARKNESS_1 = -1580074, + SAY_DARKNESS_2 = -1580075, + SAY_DARKNESS_3 = -1580076, + SAY_PHASE_3 = -1580077, + SAY_PHASE_4 = -1580078, + SAY_PHASE_5 = -1580079, + SAY_KALECGOS_INTRO = -1580080, + SAY_KALECGOS_AWAKE_1 = -1580081, + SAY_ANVEENA_IMPRISONED = -1580082, + SAY_KALECGOS_AWAKE_2 = -1580083, + SAY_ANVEENA_LOST = -1580084, + SAY_KALECGOS_AWAKE_4 = -1580085, + SAY_ANVEENA_AWAKE = -1580086, + SAY_KALECGOS_AWAKE_5 = -1580087, + SAY_ANVEENA_SACRIFICE = -1580088, + SAY_KALECGOS_GOODBYE = -1580089, + SAY_KALECGOS_ENCOURAGE = -1580090, + SAY_KALECGOS_ORB_1 = -1580091, + SAY_KALECGOS_ORB_2 = -1580092, + SAY_KALECGOS_ORB_3 = -1580093, + SAY_KALECGOS_ORB_4 = -1580094, + + // outro + SAY_OUTRO_1 = -1580095, // Velen + SAY_OUTRO_2 = -1580096, + SAY_OUTRO_3 = -1580097, + SAY_OUTRO_4 = -1580098, + SAY_OUTRO_5 = -1580099, // Liadrin + SAY_OUTRO_6 = -1580100, // Velen + SAY_OUTRO_7 = -1580101, // Liadrin + SAY_OUTRO_8 = -1580102, // Velen + SAY_OUTRO_9 = -1580103, + SAY_OUTRO_10 = -1580104, // Liadrin + SAY_OUTRO_11 = -1580105, // Velen + SAY_OUTRO_12 = -1580106, + + // generic spells + SPELL_BIRTH = 37745, // Kiljaeden spawn animation + + // transition spells + SPELL_DESTROY_DRAKES = 46707, + SPELL_SINISTER_REFLECTION = 45892, + SPELL_SHADOW_SPIKE = 46680, + + // phase 1 + SPELL_SOUL_FLY = 45442, + SPELL_LEGION_LIGHTING = 45664, + SPELL_FIRE_BLOOM = 45641, + + // phase 2 + SPELL_FLAME_DART = 45740, + SPELL_DARKNESS_OF_SOULS = 46605, + + // phase 3 + SPELL_ARMAGEDDON = 45921, // used from 50% hp - summons 25735 on target location + + // Npc spells + SPELL_SHADOW_BOLT_AURA = 45679, // periodic aura on shield orbs + SPELL_RING_BLUE_FLAME = 45825, // cast by the orb targets when activated + SPELL_ANVEENA_PRISON = 46367, + SPELL_SACRIFICE_ANVEENA = 46474, + SPELL_ARCANE_BOLT = 45670, // used by Kalec + SPELL_SINISTER_REFL_CLASS = 45893, // increase the size of the clones + SPELL_SINISTER_REFL_CLONE = 45785, // clone the player + SPELL_VENGEANCE_BLUE_FLIGHT = 45839, // possess the dragon + SPELL_POSSESS_DRAKE_IMMUNE = 45838, // immunity while the player possesses the dragon + + // Npcs + NPC_SHIELD_ORB = 25502, + NPC_SINISTER_REFLECTION = 25708, + NPC_ARMAGEDDON = 25735, // npc handled by eventAI + NPC_BLUE_ORB_TARGET = 25640, // dummy npc near gameobjects 187869, 188114, 188115, 188116 + + // phases + PHASE_INFERNO = 1, + PHASE_DARKNESS = 2, + PHASE_ARMAGEDDON = 3, + PHASE_SACRIFICE = 4, + PHASE_TRANSITION = 5, + + // dummy members, used in the phase switch event + EVENT_SWITCH_PHASE_2 = 6, + EVENT_SWITCH_PHASE_3 = 7, + EVENT_SWITCH_PHASE_4 = 8, + EVENT_DRAGON_ORB = 9, + + // outro + SPELL_TELEPORT_VISUAL = 12980, + SPELL_KALEC_TELEPORT = 46473, // teleports and transforms Kalec in human form + SPELL_ARCANE_PORTAL = 42047, + SPELL_CALL_ENTROPIUS = 46818, + SPELL_ENTROPIUS_BODY = 46819, + SPELL_BLAZE_TO_LIGHT = 46821, + SPELL_SUNWELL_IGNITION = 46822, + + NPC_INERT_PORTAL = 26254, + NPC_CORE_ENTROPIUS = 26262, + NPC_SOLDIER = 26259, // summoned in 2 waves before Velen. Should move into 2 circle formations + NPC_RIFTWALKER = 26289, + + POINT_SUMMON_SOLDIERS = 1, + POINT_MOVE_LIADRIN = 2, + POINT_EVENT_EXIT = 3, +}; + +// Encounter phase dialogue +static const DialogueEntry aPhaseDialogue[] = +{ + {PHASE_DARKNESS, 0, 2000}, + {EVENT_SWITCH_PHASE_2, 0, 17000}, + {SAY_KALECGOS_AWAKE_1, NPC_KALECGOS, 6000}, + {SAY_ANVEENA_IMPRISONED, NPC_ANVEENA, 5000}, + {SAY_PHASE_3, NPC_KILJAEDEN, 6000}, + {SAY_KALECGOS_ORB_1, NPC_KALECGOS, 0}, // phase 2 transition end + {PHASE_ARMAGEDDON, 0, 2000}, + {EVENT_SWITCH_PHASE_3, 0, 14000}, + {SAY_KALECGOS_AWAKE_2, NPC_KALECGOS, 7000}, + {SAY_ANVEENA_LOST, NPC_ANVEENA, 7000}, + {SAY_PHASE_4, NPC_KILJAEDEN, 6000}, + {EVENT_DRAGON_ORB, 0, 0}, // phase 3 transition end + {PHASE_SACRIFICE, 0, 2000}, + {EVENT_SWITCH_PHASE_4, 0, 5000}, + {SAY_KALECGOS_AWAKE_4, NPC_KALECGOS, 10000}, + {SAY_ANVEENA_AWAKE, NPC_ANVEENA, 2000}, + {SAY_KALECGOS_AWAKE_5, NPC_KALECGOS, 6000}, + {SAY_ANVEENA_SACRIFICE, NPC_ANVEENA, 5000}, + {SAY_PHASE_5, NPC_KILJAEDEN, 13000}, + {SAY_KALECGOS_ORB_4, NPC_KALECGOS, 5000}, + {SAY_KALECGOS_ENCOURAGE, NPC_KALECGOS, 0}, // phase 4 transition end + {0, 0, 0}, +}; + +// Epilogue dialogue +static const DialogueEntry aOutroDialogue[] = +{ + {NPC_KALECGOS, 0, 15000}, + {SAY_KALECGOS_GOODBYE, NPC_KALECGOS, 40000}, + {NPC_INERT_PORTAL, 0, 10000}, + {POINT_SUMMON_SOLDIERS, 0, 18000}, + {NPC_VELEN, 0, 1000}, + {NPC_LIADRIN, 0, 4000}, + {SAY_OUTRO_1, NPC_VELEN, 25000}, + {SAY_OUTRO_2, NPC_VELEN, 15000}, + {SAY_OUTRO_3, NPC_VELEN, 13000}, + {SPELL_CALL_ENTROPIUS, 0, 10000}, + {SAY_OUTRO_4, NPC_VELEN, 20000}, + {POINT_MOVE_LIADRIN, 0, 5000}, + {SAY_OUTRO_5, NPC_LIADRIN, 10000}, + {SAY_OUTRO_6, NPC_VELEN, 15000}, + {SAY_OUTRO_7, NPC_LIADRIN, 3000}, + {SAY_OUTRO_8, NPC_VELEN, 4000}, + {SPELL_BLAZE_TO_LIGHT, 0, 13000}, + {SAY_OUTRO_9, NPC_VELEN, 14000}, + {SAY_OUTRO_10, NPC_LIADRIN, 20000}, + {SAY_OUTRO_11, NPC_VELEN, 8000}, + {SAY_OUTRO_12, NPC_VELEN, 4000}, + {POINT_EVENT_EXIT, 0, 0}, + {0, 0, 0}, +}; + +static const EventLocations aOutroLocations[] = +{ + {1725.469f, 650.939f, 30.314f, 3.78f}, // portal summon loc + {1717.776f, 645.178f, 28.223f, 3.83f}, // velen summon loc + {1720.024f, 643.233f, 28.133f, 3.76f}, // liadrin summon loc + {1712.110f, 641.044f, 27.80f}, // velen move forward + {1711.537f, 637.600f, 27.34f}, // liadrin move forward + {1698.946f, 628.206f, 83.003f, 0.76f}, // entropius core summon loc +}; + +// Note: the Z loc should be 143.69 but currently we are not using it because it's too far away +static const float aKalegSpawnLoc[4] = {1734.431f, 593.1974f, 130.6977f, 4.55f}; + +/*###### +## npc_kiljaeden_controller +######*/ + +struct npc_kiljaeden_controllerAI : public Scripted_NoMovementAI, private DialogueHelper +{ + npc_kiljaeden_controllerAI(Creature* pCreature) : Scripted_NoMovementAI(pCreature), + DialogueHelper(aOutroDialogue) + { + m_pInstance = ((instance_sunwell_plateau*)pCreature->GetInstanceData()); + InitializeDialogueHelper(m_pInstance); + Reset(); + } + + instance_sunwell_plateau* m_pInstance; + + ObjectGuid m_EntropiusGuid; + ObjectGuid m_PortalGuid; + + void Reset() override + { + // Visual spell before the encounter starts + DoCastSpellIfCan(m_creature, SPELL_ANVEENA_DRAIN); + } + + void JustDidDialogueStep(int32 iEntry) override + { + if (!m_pInstance) + return; + + switch (iEntry) + { + case NPC_KALECGOS: + if (Creature* pKalec = m_pInstance->GetSingleCreatureFromStorage(NPC_KALECGOS)) + { + pKalec->GetMotionMaster()->Clear(); + pKalec->GetMotionMaster()->MoveIdle(); + pKalec->CastSpell(pKalec, SPELL_KALEC_TELEPORT, true); + pKalec->SetLevitate(false); + } + m_creature->SummonCreature(NPC_CORE_ENTROPIUS, aOutroLocations[5].m_fX, aOutroLocations[5].m_fY, aOutroLocations[5].m_fZ, aOutroLocations[5].m_fO, TEMPSUMMON_CORPSE_DESPAWN, 0); + break; + case NPC_INERT_PORTAL: + // ToDo: summon soldiers to the right + m_creature->SummonCreature(NPC_INERT_PORTAL, aOutroLocations[0].m_fX, aOutroLocations[0].m_fY, aOutroLocations[0].m_fZ, aOutroLocations[0].m_fO, TEMPSUMMON_CORPSE_DESPAWN, 0); + break; + case POINT_SUMMON_SOLDIERS: + // ToDo: summon soldiers to the left + break; + case NPC_VELEN: + m_creature->SummonCreature(NPC_VELEN, aOutroLocations[1].m_fX, aOutroLocations[1].m_fY, aOutroLocations[1].m_fZ, aOutroLocations[1].m_fO, TEMPSUMMON_CORPSE_DESPAWN, 0); + break; + case NPC_LIADRIN: + m_creature->SummonCreature(NPC_LIADRIN, aOutroLocations[2].m_fX, aOutroLocations[2].m_fY, aOutroLocations[2].m_fZ, aOutroLocations[2].m_fO, TEMPSUMMON_TIMED_DESPAWN, 4 * MINUTE * IN_MILLISECONDS); + break; + case SPELL_CALL_ENTROPIUS: + if (Creature* pVelen = m_pInstance->GetSingleCreatureFromStorage(NPC_VELEN)) + pVelen->CastSpell(pVelen, SPELL_CALL_ENTROPIUS, false); + // Set point id = 1 for movement event + if (Creature* pEntropius = m_creature->GetMap()->GetCreature(m_EntropiusGuid)) + { + pEntropius->SetWalk(false); + pEntropius->GetMotionMaster()->MovePoint(1, m_creature->GetPositionX(), m_creature->GetPositionY(), 35.0f); + } + break; + case POINT_MOVE_LIADRIN: + if (Creature* pLiadrin = m_pInstance->GetSingleCreatureFromStorage(NPC_LIADRIN)) + pLiadrin->GetMotionMaster()->MovePoint(0, aOutroLocations[4].m_fX, aOutroLocations[4].m_fY, aOutroLocations[4].m_fZ); + break; + case SPELL_BLAZE_TO_LIGHT: + if (Creature* pEntropius = m_creature->GetMap()->GetCreature(m_EntropiusGuid)) + { + pEntropius->CastSpell(pEntropius, SPELL_BLAZE_TO_LIGHT, true); + pEntropius->RemoveAurasDueToSpell(SPELL_ENTROPIUS_BODY); + pEntropius->SetWalk(true); + pEntropius->GetMotionMaster()->MovePoint(2, m_creature->GetPositionX(), m_creature->GetPositionY(), m_creature->GetPositionZ()); + } + break; + case POINT_EVENT_EXIT: + // Set point id = 1 for the despawn event + if (Creature* pVelen = m_pInstance->GetSingleCreatureFromStorage(NPC_VELEN)) + pVelen->GetMotionMaster()->MovePoint(1, aOutroLocations[1].m_fX, aOutroLocations[1].m_fY, aOutroLocations[1].m_fZ); + break; + } + } + + void JustSummoned(Creature* pSummoned) override + { + switch (pSummoned->GetEntry()) + { + case NPC_VELEN: + pSummoned->GetMotionMaster()->MovePoint(0, aOutroLocations[3].m_fX, aOutroLocations[3].m_fY, aOutroLocations[3].m_fZ); + // no break here + case NPC_LIADRIN: + pSummoned->CastSpell(pSummoned, SPELL_TELEPORT_VISUAL, true); + break; + case NPC_CORE_ENTROPIUS: + pSummoned->CastSpell(pSummoned, SPELL_ENTROPIUS_BODY, true); + pSummoned->SetLevitate(true); + m_EntropiusGuid = pSummoned->GetObjectGuid(); + break; + case NPC_INERT_PORTAL: + m_PortalGuid = pSummoned->GetObjectGuid(); + pSummoned->CastSpell(pSummoned, SPELL_ARCANE_PORTAL, true); + break; + } + } + + void SummonedCreatureJustDied(Creature* pSummoned) override + { + // Start outro dialogue when Kil'jaeden is killed + if (pSummoned->GetEntry() == NPC_KILJAEDEN) + StartNextDialogueText(NPC_KALECGOS); + } + + void SummonedMovementInform(Creature* pSummoned, uint32 uiType, uint32 uiPointId) override + { + if (uiType != POINT_MOTION_TYPE) + return; + + if (uiPointId == 1) + { + if (pSummoned->GetEntry() == NPC_CORE_ENTROPIUS) + { + // Interrupt Velen's casting when entropius has reached the ground + if (Creature* pVelen = m_pInstance->GetSingleCreatureFromStorage(NPC_VELEN)) + pVelen->InterruptNonMeleeSpells(false); + } + else if (pSummoned->GetEntry() == NPC_VELEN) + { + // Cast teleport and despawn Velen, the portal and Kalec; Liadrin will despawn on timer + pSummoned->CastSpell(pSummoned, SPELL_TELEPORT_VISUAL, true); + pSummoned->ForcedDespawn(1000); + + // Note: portal should despawn only after all the soldiers have reached this point and "teleported" outside + if (Creature* pPortal = m_creature->GetMap()->GetCreature(m_PortalGuid)) + pPortal->ForcedDespawn(5000); + + if (Creature* pKalec = m_pInstance->GetSingleCreatureFromStorage(NPC_KALECGOS)) + pKalec->ForcedDespawn(1000); + } + } + else if (uiPointId == 2 && pSummoned->GetEntry() == NPC_CORE_ENTROPIUS) + { + // When the purified Muru reaches the ground the sunwell ignites and Muru despawns + DoCastSpellIfCan(m_creature, SPELL_SUNWELL_IGNITION); + + if (Creature* pLiadrin = m_pInstance->GetSingleCreatureFromStorage(NPC_LIADRIN)) + pLiadrin->SetStandState(UNIT_STAND_STATE_KNEEL); + + pSummoned->ForcedDespawn(); + } + } + + void UpdateAI(const uint32 uiDiff) override + { + DialogueUpdate(uiDiff); + } +}; + +/*###### +## boss_kiljaeden +######*/ + +struct boss_kiljaedenAI : public Scripted_NoMovementAI, private DialogueHelper +{ + boss_kiljaedenAI(Creature* pCreature) : Scripted_NoMovementAI(pCreature), + DialogueHelper(aPhaseDialogue) + { + m_pInstance = ((instance_sunwell_plateau*)pCreature->GetInstanceData()); + InitializeDialogueHelper(m_pInstance); + Reset(); + } + + instance_sunwell_plateau* m_pInstance; + + uint8 m_uiPhase; + uint8 m_uiMaxShieldOrbs; + uint8 m_uiShieldOrbCount; + + uint32 m_uiKalecSummonTimer; + + uint32 m_uiSoulFlyTimer; + uint32 m_uiLegionLightingTimer; + uint32 m_uiFireBloomTimer; + uint32 m_uiShieldOrbTimer; + + uint32 m_uiFlameDartTimer; + uint32 m_uiDarknessOfSoulsTimer; + + uint32 m_uiArmageddonTimer; + + void Reset() override + { + m_uiPhase = PHASE_INFERNO; + m_uiKalecSummonTimer = 35000; + m_uiMaxShieldOrbs = 1; + m_uiShieldOrbCount = 0; + + m_uiSoulFlyTimer = 10000; + m_uiLegionLightingTimer = urand(10000, 15000); + m_uiFireBloomTimer = urand(15000, 20000); + m_uiShieldOrbTimer = 30000; + + m_uiFlameDartTimer = urand(20000, 25000); + m_uiDarknessOfSoulsTimer = urand(45000, 50000); + + m_uiArmageddonTimer = 20000; + } + + void Aggro(Unit* /*pWho*/) override + { + if (m_pInstance) + m_pInstance->SetData(TYPE_KILJAEDEN, IN_PROGRESS); + + DoScriptText(SAY_EMERGE, m_creature); + DoCastSpellIfCan(m_creature, SPELL_BIRTH); + } + + void JustReachedHome() override + { + if (m_pInstance) + { + m_pInstance->SetData(TYPE_KILJAEDEN, FAIL); + + // Reset the corrupt Sunwell aura + if (Creature* pKiljaedenController = m_pInstance->GetSingleCreatureFromStorage(NPC_KILJAEDEN_CONTROLLER)) + pKiljaedenController->CastSpell(pKiljaedenController, SPELL_ANVEENA_DRAIN, true); + } + + // Despawn on wipe + m_creature->ForcedDespawn(); + } + + void KilledUnit(Unit* pVictim) override + { + if (pVictim->GetTypeId() != TYPEID_PLAYER) + return; + + DoScriptText(urand(0, 1) ? SAY_SLAY_1 : SAY_SLAY_2, m_creature); + } + + void JustDied(Unit* /*pKiller*/) override + { + if (m_pInstance) + m_pInstance->SetData(TYPE_KILJAEDEN, DONE); + } + + void JustSummoned(Creature* pSummoned) override + { + if (pSummoned->GetEntry() == NPC_KALECGOS) + { + DoScriptText(SAY_KALECGOS_INTRO, pSummoned); + pSummoned->CastSpell(pSummoned, SPELL_ARCANE_BOLT, true); + pSummoned->GetMotionMaster()->MoveRandomAroundPoint(m_creature->GetPositionX(), m_creature->GetPositionY(), pSummoned->GetPositionZ(), 30.0f); + } + else if (pSummoned->GetEntry() == NPC_SHIELD_ORB) + { + pSummoned->CastSpell(pSummoned, SPELL_SHADOW_BOLT_AURA, true); + + // Start the movement of the shadow orb - calculate new position based on the angle between the boss and orb + float fX, fY, fAng; + fAng = m_creature->GetAngle(pSummoned) + M_PI_F / 8; + // Normalize angle + if (fAng > 2 * M_PI_F) + fAng = fAng - 2 * M_PI_F; + + m_creature->GetNearPoint2D(fX, fY, 25.0f, fAng); + + // Move to new position + pSummoned->GetMotionMaster()->Clear(); + pSummoned->GetMotionMaster()->MovePoint(1, fX, fY, pSummoned->GetPositionZ()); + } + else if (pSummoned->GetEntry() == NPC_SINISTER_REFLECTION) + { + if (pSummoned->IsTemporarySummon()) + { + TemporarySummon* pTemporary = (TemporarySummon*)pSummoned; + + if (Player* pPlayer = m_creature->GetMap()->GetPlayer(pTemporary->GetSummonerGuid())) + { + pPlayer->CastSpell(pSummoned, SPELL_SINISTER_REFL_CLONE, true); + pSummoned->CastSpell(pSummoned, SPELL_SINISTER_REFL_CLASS, true); + pSummoned->AI()->AttackStart(pPlayer); + } + } + } + } + + void SummonedCreatureJustDied(Creature* pSummoned) override + { + if (pSummoned->GetEntry() == NPC_SHIELD_ORB) + --m_uiShieldOrbCount; + } + + void GetAIInformation(ChatHandler& reader) override + { + reader.PSendSysMessage("Kil'jaeden is currently in phase %u", m_uiPhase); + } + + void JustDidDialogueStep(int32 iEntry) override + { + if (!m_pInstance) + return; + + switch (iEntry) + { + case PHASE_DARKNESS: + case PHASE_ARMAGEDDON: + case PHASE_SACRIFICE: + if (DoCastSpellIfCan(m_creature, SPELL_SINISTER_REFLECTION, CAST_INTERRUPT_PREVIOUS) == CAST_OK) + { + DoScriptText(irand(0, 1) ? SAY_REFLECTION_1 : SAY_REFLECTION_2, m_creature); + + // In the 2nd and 3rd transition kill all drakes + if (iEntry == PHASE_ARMAGEDDON || iEntry == PHASE_SACRIFICE) + DoCastSpellIfCan(m_creature, SPELL_DESTROY_DRAKES, CAST_TRIGGERED); + + m_uiPhase = PHASE_TRANSITION; + // Darkness of Souls needs the timer reseted + m_uiDarknessOfSoulsTimer = iEntry == PHASE_SACRIFICE ? 30000 : 45000; + } + break; + case EVENT_SWITCH_PHASE_2: + case EVENT_SWITCH_PHASE_3: + case EVENT_SWITCH_PHASE_4: + DoCastSpellIfCan(m_creature, SPELL_SHADOW_SPIKE); + break; + case EVENT_DRAGON_ORB: + // Activate blue orbs + if (Creature* pKalec = m_pInstance->GetSingleCreatureFromStorage(NPC_KALECGOS)) + DoScriptText(irand(0, 1) ? SAY_KALECGOS_ORB_2 : SAY_KALECGOS_ORB_3, pKalec); + DoActivateDragonOrb(GO_ORB_BLUE_FLIGHT_2); + break; + case SAY_KALECGOS_ORB_1: + DoActivateDragonOrb(GO_ORB_BLUE_FLIGHT_1); + break; + case SAY_KALECGOS_ORB_4: + DoActivateDragonOrb(GO_ORB_BLUE_FLIGHT_3); + DoActivateDragonOrb(GO_ORB_BLUE_FLIGHT_4); + break; + case SAY_PHASE_3: + // Set next phase and increase the max shield orbs + m_uiPhase = PHASE_DARKNESS; + ++m_uiMaxShieldOrbs; + break; + case SAY_PHASE_4: + // Set next phase and increase the max shield orbs + m_uiPhase = PHASE_ARMAGEDDON; + ++m_uiMaxShieldOrbs; + break; + case SAY_PHASE_5: + // Set next phase and sacrifice Anveena + if (Creature* pAnveena = m_pInstance->GetSingleCreatureFromStorage(NPC_ANVEENA)) + { + pAnveena->RemoveAurasDueToSpell(SPELL_ANVEENA_PRISON); + pAnveena->CastSpell(pAnveena, SPELL_SACRIFICE_ANVEENA, true); + pAnveena->ForcedDespawn(3000); + } + m_uiPhase = PHASE_SACRIFICE; + break; + } + } + + // Wrapper to activate dragon orbs + void DoActivateDragonOrb(uint32 uiEntry) + { + if (!m_pInstance) + return; + + // Set the visual around the Orb + if (GameObject* pGo = m_pInstance->GetSingleGameObjectFromStorage(uiEntry)) + { + if (Creature* pTarget = GetClosestCreatureWithEntry(pGo, NPC_BLUE_ORB_TARGET, 5.0f)) + pTarget->CastSpell(pTarget, SPELL_RING_BLUE_FLAME, false); + } + + // Make the orb usable + m_pInstance->DoToggleGameObjectFlags(uiEntry, GO_FLAG_NO_INTERACT, false); + } + + void UpdateAI(const uint32 uiDiff) override + { + if (!m_creature->SelectHostileTarget() || !m_creature->getVictim()) + return; + + DialogueUpdate(uiDiff); + + switch (m_uiPhase) + { + case PHASE_TRANSITION: + // Transition phase is handled in the dialogue helper; however we don't want the spell timers to be decreased so we use a specific phase + break; + case PHASE_SACRIFICE: + // Final phase - use the same spells + // no break; + case PHASE_ARMAGEDDON: + + // In the last phase he uses Armageddon continuously + if (m_uiArmageddonTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature, SPELL_ARMAGEDDON) == CAST_OK) + m_uiArmageddonTimer = m_uiPhase == PHASE_SACRIFICE ? 20000 : 30000; + } + else + m_uiArmageddonTimer -= uiDiff; + + // Go to next phase and start transition dialogue + if (m_uiPhase == PHASE_ARMAGEDDON && m_creature->GetHealthPercent() < 25.0f) + StartNextDialogueText(PHASE_SACRIFICE); + + // no break - use the spells from the phases below; + case PHASE_DARKNESS: + + // In the last phase he uses this spell more often + if (m_uiDarknessOfSoulsTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature, SPELL_DARKNESS_OF_SOULS) == CAST_OK) + m_uiDarknessOfSoulsTimer = m_uiPhase == PHASE_SACRIFICE ? 30000 : 45000; + } + else + m_uiDarknessOfSoulsTimer -= uiDiff; + + if (m_uiFlameDartTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature, SPELL_FLAME_DART) == CAST_OK) + m_uiFlameDartTimer = urand(25000, 30000); + } + else + m_uiFlameDartTimer -= uiDiff; + + // Go to next phase and start transition dialogue + if (m_uiPhase == PHASE_DARKNESS && m_creature->GetHealthPercent() < 55.0f) + StartNextDialogueText(PHASE_ARMAGEDDON); + + // no break - use the spells from the phase below; + case PHASE_INFERNO: + + if (m_uiKalecSummonTimer) + { + if (m_uiKalecSummonTimer <= uiDiff) + { + m_creature->SummonCreature(NPC_KALECGOS, aKalegSpawnLoc[0], aKalegSpawnLoc[1], aKalegSpawnLoc[2], aKalegSpawnLoc[3], TEMPSUMMON_CORPSE_DESPAWN, 0); + m_uiKalecSummonTimer = 0; + } + else + m_uiKalecSummonTimer -= uiDiff; + } + + if (m_uiLegionLightingTimer < uiDiff) + { + if (Unit* pTarget = m_creature->SelectAttackingTarget(ATTACKING_TARGET_RANDOM, 0)) + { + if (DoCastSpellIfCan(pTarget, SPELL_LEGION_LIGHTING) == CAST_OK) + m_uiLegionLightingTimer = urand(10000, 15000); + } + } + else + m_uiLegionLightingTimer -= uiDiff; + + if (m_uiFireBloomTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature, SPELL_FIRE_BLOOM) == CAST_OK) + m_uiFireBloomTimer = 20000; + } + else + m_uiFireBloomTimer -= uiDiff; + + if (m_uiSoulFlyTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature->getVictim(), SPELL_SOUL_FLY) == CAST_OK) + m_uiSoulFlyTimer = urand(3000, 10000); + } + else + m_uiSoulFlyTimer -= uiDiff; + + // Only spawn a Shadow orb when necessary + if (m_uiShieldOrbCount < m_uiMaxShieldOrbs) + { + if (m_uiShieldOrbTimer < uiDiff) + { + // Get some random coords for the Orb + float fX, fY, fZ; + m_creature->GetNearPoint2D(fX, fY, 25.0f, frand(0, 2 * M_PI_F)); + fZ = frand(35.0f, 45.0f); + + m_creature->SummonCreature(NPC_SHIELD_ORB, fX, fY, fZ, 0, TEMPSUMMON_CORPSE_DESPAWN, 0); + ++m_uiShieldOrbCount; + m_uiShieldOrbTimer = 30000; + } + else + m_uiShieldOrbTimer -= uiDiff; + } + + // Go to next phase and start transition dialogue + if (m_uiPhase == PHASE_INFERNO && m_creature->GetHealthPercent() < 85.0f) + StartNextDialogueText(PHASE_DARKNESS); + + DoMeleeAttackIfReady(); + + break; + } + } +}; + +bool EffectAuraDummy_spell_aura_dummy_darkness_of_souls(const Aura* pAura, bool bApply) +{ + // On Aura removal cast the explosion and yell + // This is a special case when the dummy effect should be triggered at the end of the channeling + if (pAura->GetId() == SPELL_DARKNESS_OF_SOULS && pAura->GetEffIndex() == EFFECT_INDEX_0 && !bApply) + { + if (Creature* pTarget = (Creature*)pAura->GetTarget()) + { + pTarget->CastSpell(pTarget, pAura->GetSpellProto()->CalculateSimpleValue(EFFECT_INDEX_2), true); + + switch (urand(0, 2)) + { + case 0: DoScriptText(SAY_DARKNESS_1, pTarget); break; + case 1: DoScriptText(SAY_DARKNESS_2, pTarget); break; + case 2: DoScriptText(SAY_DARKNESS_3, pTarget); break; + } + } + } + return true; +} + +/*###### +## npc_shield_orb +######*/ + +struct npc_shield_orbAI : public ScriptedAI +{ + npc_shield_orbAI(Creature* pCreature) : ScriptedAI(pCreature) + { + m_pInstance = ((instance_sunwell_plateau*)pCreature->GetInstanceData()); + Reset(); + } + + instance_sunwell_plateau* m_pInstance; + + void Reset() override { } + + // Handle circel movement around the boss + void MovementInform(uint32 uiMoveType, uint32 uiPointId) override + { + if (uiMoveType != POINT_MOTION_TYPE || !uiPointId || !m_pInstance) + return; + + if (Creature* pSummoner = m_pInstance->GetSingleCreatureFromStorage(NPC_KILJAEDEN)) + { + // Calculate new position based on the angle between the boss and self + float fX, fY, fAng; + fAng = pSummoner->GetAngle(m_creature) + M_PI_F / 8; + // Normalize angle + if (fAng > 2 * M_PI_F) + fAng = fAng - 2 * M_PI_F; + + pSummoner->GetNearPoint2D(fX, fY, 25.0f, fAng); + + // Move to new position + m_creature->GetMotionMaster()->Clear(); + m_creature->GetMotionMaster()->MovePoint(1, fX, fY, m_creature->GetPositionZ()); + } + } + + void AttackStart(Unit* /*pWho*/) override {} + void MoveInLineOfSight(Unit* /*pWho*/) override {} + void UpdateAI(const uint32 /*uiDiff*/) override { } +}; + +/*###### +## npc_power_blue_flight +######*/ + +struct npc_power_blue_flightAI : public ScriptedAI +{ + npc_power_blue_flightAI(Creature* pCreature) : ScriptedAI(pCreature) + { + SetCombatMovement(false); + m_bHasPossessed = false; + Reset(); + } + + bool m_bHasPossessed; + + void Reset() override { } + + void JustDied(Unit* /*pKiller*/) override + { + if (m_creature->IsTemporarySummon()) + { + TemporarySummon* pTemporary = (TemporarySummon*)m_creature; + + if (Player* pPlayer = m_creature->GetMap()->GetPlayer(pTemporary->GetSummonerGuid())) + pPlayer->RemoveAurasDueToSpell(SPELL_POSSESS_DRAKE_IMMUNE); + } + } + + void UpdateAI(const uint32 /*uiDiff*/) override + { + if (!m_bHasPossessed) + { + if (m_creature->IsTemporarySummon()) + { + TemporarySummon* pTemporary = (TemporarySummon*)m_creature; + + if (Player* pPlayer = m_creature->GetMap()->GetPlayer(pTemporary->GetSummonerGuid())) + { + pPlayer->CastSpell(m_creature, SPELL_VENGEANCE_BLUE_FLIGHT, true); + pPlayer->CastSpell(pPlayer, SPELL_POSSESS_DRAKE_IMMUNE, true); + } + } + + // Reset the No Interact flag of the closest orb + GameObject* pOrb = GetClosestGameObjectWithEntry(m_creature, GO_ORB_BLUE_FLIGHT_1, 10.0f); + if (!pOrb) + pOrb = GetClosestGameObjectWithEntry(m_creature, GO_ORB_BLUE_FLIGHT_2, 10.0f); + if (!pOrb) + pOrb = GetClosestGameObjectWithEntry(m_creature, GO_ORB_BLUE_FLIGHT_3, 10.0f); + if (!pOrb) + pOrb = GetClosestGameObjectWithEntry(m_creature, GO_ORB_BLUE_FLIGHT_4, 10.0f); + + if (pOrb) + pOrb->SetFlag(GAMEOBJECT_FLAGS, GO_FLAG_NO_INTERACT); + + m_bHasPossessed = true; + } + } +}; + +CreatureAI* GetAI_boss_kiljaeden(Creature* pCreature) +{ + return new boss_kiljaedenAI(pCreature); +} + +CreatureAI* GetAI_npc_kiljaeden_controller(Creature* pCreature) +{ + return new npc_kiljaeden_controllerAI(pCreature); +} + +CreatureAI* GetAI_npc_shield_orb(Creature* pCreature) +{ + return new npc_shield_orbAI(pCreature); +} + +CreatureAI* GetAI_npc_power_blue_flight(Creature* pCreature) +{ + return new npc_power_blue_flightAI(pCreature); +} + +void AddSC_boss_kiljaeden() +{ + Script* pNewScript; + + pNewScript = new Script; + pNewScript->Name = "boss_kiljaeden"; + pNewScript->GetAI = &GetAI_boss_kiljaeden; + pNewScript->pEffectAuraDummy = &EffectAuraDummy_spell_aura_dummy_darkness_of_souls; + pNewScript->RegisterSelf(); + + pNewScript = new Script; + pNewScript->Name = "npc_kiljaeden_controller"; + pNewScript->GetAI = &GetAI_npc_kiljaeden_controller; + pNewScript->RegisterSelf(); + + pNewScript = new Script; + pNewScript->Name = "npc_shield_orb"; + pNewScript->GetAI = &GetAI_npc_shield_orb; + pNewScript->RegisterSelf(); + + pNewScript = new Script; + pNewScript->Name = "npc_power_blue_flight"; + pNewScript->GetAI = &GetAI_npc_power_blue_flight; + pNewScript->RegisterSelf(); +} diff --git a/src/modules/SD2/scripts/eastern_kingdoms/sunwell_plateau/boss_muru.cpp b/src/modules/SD2/scripts/eastern_kingdoms/sunwell_plateau/boss_muru.cpp new file mode 100644 index 000000000..4978cfa39 --- /dev/null +++ b/src/modules/SD2/scripts/eastern_kingdoms/sunwell_plateau/boss_muru.cpp @@ -0,0 +1,485 @@ +/* Copyright (C) 2006 - 2013 ScriptDev2 + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with 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: Spell Negative Energy for Entropius needs core support; Summon humanoids spells have some core issues; Dark Fiend, Singularity and Darkness NPCs need eventAI support +SDCategory: Sunwell Plateau +EndScriptData */ + +#include "precompiled.h" +#include "sunwell_plateau.h" + +enum +{ + // muru spells + SPELL_NEGATIVE_ENERGY = 46009, + SPELL_DARKNESS = 45996, // big void zone; at 45 sec + SPELL_OPEN_PORTAL_PERIODIC = 45994, // periodic spell which opens a portal at 30 secs; triggers 45976 + SPELL_OPEN_PORTAL = 45976, // has muru portal as target + SPELL_SUMMON_BERSERKER_1 = 46037, // humanoids summoned at 15 secs (3 on each side) then after 60 secs + SPELL_SUMMON_BERSERKER_2 = 46040, // there are two spells. one for each side + SPELL_SUMMON_FURY_MAGE_1 = 46038, + SPELL_SUMMON_FURY_MAGE_2 = 46039, + + SPELL_SUMMON_DARK_FIEND_1 = 46000, // summons 8 dark fiends (25744); ToDo: script npc in eventAI + SPELL_SUMMON_DARK_FIEND_2 = 46001, + SPELL_SUMMON_DARK_FIEND_3 = 46002, + SPELL_SUMMON_DARK_FIEND_4 = 46003, + SPELL_SUMMON_DARK_FIEND_5 = 46004, + SPELL_SUMMON_DARK_FIEND_6 = 46005, + SPELL_SUMMON_DARK_FIEND_7 = 46006, + SPELL_SUMMON_DARK_FIEND_8 = 46007, + + // transition + SPELL_OPEN_ALL_PORTALS = 46177, // dummy spell which opens all the portals to begin the transition phase - has muru portal as target + SPELL_SUMMON_ENTROPIUS = 46217, + SPELL_ENTROPIUS_SPAWN = 46223, // visual effect after spawn + + // entropius spells + SPELL_NEGATIVE_ENERGY_ENT = 46284, // periodic aura spell; triggers 46289 which has script effect. Damage spell is 46285 but it needs core support + SPELL_SUMMON_BLACK_HOLE = 46282, // 15 sec cooldown; summons 25855 + SPELL_SUMMON_DARKNESS = 46269, // summons 25879 by missile + + // portal spells + SPELL_SENTINEL_SUMMONER_VISUAL = 45989, // hits the summoner, so it will summon the sentinel + SPELL_SUMMON_SENTINEL_SUMMONER = 45978, + SPELL_TRANSFORM_VISUAL_1 = 46178, // Visual - has Muru as script target + SPELL_TRANSFORM_VISUAL_2 = 46208, // Visual - has Muru as script target + + // Muru npcs + NPC_VOID_SENTINEL_SUMMONER = 25782, + NPC_VOID_SENTINEL = 25772, // scripted in Acid + + MAX_TRANSFORM_CASTS = 10 +}; + +/*###### +## boss_muru +######*/ + +struct boss_muruAI : public Scripted_NoMovementAI +{ + boss_muruAI(Creature* pCreature) : Scripted_NoMovementAI(pCreature) + { + m_pInstance = ((instance_sunwell_plateau*)pCreature->GetInstanceData()); + Reset(); + } + + instance_sunwell_plateau* m_pInstance; + + uint32 m_uiDarknessTimer; + uint32 m_uiSummonHumanoidsTimer; + uint32 m_uiDarkFiendsTimer; + bool m_bIsTransition; + + void Reset() override + { + m_uiDarknessTimer = 45000; + m_uiSummonHumanoidsTimer = 15000; + m_uiDarkFiendsTimer = 0; + m_bIsTransition = false; + } + + void Aggro(Unit* /*pWho*/) override + { + if (m_pInstance) + m_pInstance->SetData(TYPE_MURU, IN_PROGRESS); + + DoCastSpellIfCan(m_creature, SPELL_NEGATIVE_ENERGY, CAST_TRIGGERED); + DoCastSpellIfCan(m_creature, SPELL_OPEN_PORTAL_PERIODIC, CAST_TRIGGERED); + } + + void JustReachedHome() override + { + if (m_pInstance) + m_pInstance->SetData(TYPE_MURU, FAIL); + } + + void DamageTaken(Unit* /*pDoneBy*/, uint32& uiDamage) override + { + if (uiDamage > m_creature->GetHealth()) + { + uiDamage = 0; + + if (!m_bIsTransition) + { + // Start transition + if (DoCastSpellIfCan(m_creature, SPELL_OPEN_ALL_PORTALS) == CAST_OK) + { + // remove the auras + m_creature->RemoveAurasDueToSpell(SPELL_NEGATIVE_ENERGY); + m_creature->RemoveAurasDueToSpell(SPELL_OPEN_PORTAL_PERIODIC); + m_bIsTransition = true; + } + } + } + } + + void JustSummoned(Creature* pSummoned) override + { + switch (pSummoned->GetEntry()) + { + case NPC_ENTROPIUS: + // Cast the Entropius spawn effect and force despawn + pSummoned->CastSpell(pSummoned, SPELL_ENTROPIUS_SPAWN, true); + m_creature->ForcedDespawn(1000); + // no break here; All other summons should behave the same way + default: + pSummoned->AI()->AttackStart(m_creature->getVictim()); + break; + } + } + + // Wrapper for summoning the humanoids + void DoSummonHumanoids() + { + // summon 2 berserkers and 1 fury mage on each side + for (uint8 i = 0; i < 2; i++) + { + DoCastSpellIfCan(m_creature, SPELL_SUMMON_BERSERKER_1, CAST_TRIGGERED); + DoCastSpellIfCan(m_creature, SPELL_SUMMON_BERSERKER_2, CAST_TRIGGERED); + } + + DoCastSpellIfCan(m_creature, SPELL_SUMMON_FURY_MAGE_1, CAST_TRIGGERED); + DoCastSpellIfCan(m_creature, SPELL_SUMMON_FURY_MAGE_2, CAST_TRIGGERED); + } + + // Wrapper for summoning the dark fiends + void DoSummonDarkFiends() + { + DoCastSpellIfCan(m_creature, SPELL_SUMMON_DARK_FIEND_1, CAST_TRIGGERED); + DoCastSpellIfCan(m_creature, SPELL_SUMMON_DARK_FIEND_2, CAST_TRIGGERED); + DoCastSpellIfCan(m_creature, SPELL_SUMMON_DARK_FIEND_3, CAST_TRIGGERED); + DoCastSpellIfCan(m_creature, SPELL_SUMMON_DARK_FIEND_4, CAST_TRIGGERED); + DoCastSpellIfCan(m_creature, SPELL_SUMMON_DARK_FIEND_5, CAST_TRIGGERED); + DoCastSpellIfCan(m_creature, SPELL_SUMMON_DARK_FIEND_6, CAST_TRIGGERED); + DoCastSpellIfCan(m_creature, SPELL_SUMMON_DARK_FIEND_7, CAST_TRIGGERED); + DoCastSpellIfCan(m_creature, SPELL_SUMMON_DARK_FIEND_8, CAST_TRIGGERED); + } + + void UpdateAI(const uint32 uiDiff) override + { + if (!m_creature->SelectHostileTarget() || !m_creature->getVictim()) + return; + + // Return if already in transition + if (m_bIsTransition) + return; + + if (m_uiDarknessTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature, SPELL_DARKNESS) == CAST_OK) + { + m_uiDarknessTimer = 45000; + m_uiDarkFiendsTimer = 4000; // in about 4 secs after darkness + } + } + else + m_uiDarknessTimer -= uiDiff; + + if (m_uiDarkFiendsTimer) + { + if (m_uiDarkFiendsTimer <= uiDiff) + { + DoSummonDarkFiends(); + m_uiDarkFiendsTimer = 0; + } + else + m_uiDarkFiendsTimer -= uiDiff; + } + + if (m_uiSummonHumanoidsTimer < uiDiff) + { + DoSummonHumanoids(); + m_uiSummonHumanoidsTimer = 1 * MINUTE * IN_MILLISECONDS; + } + else + m_uiSummonHumanoidsTimer -= uiDiff; + } +}; + +/*###### +## boss_entropius +######*/ + +struct boss_entropiusAI : public ScriptedAI +{ + boss_entropiusAI(Creature* pCreature) : ScriptedAI(pCreature) + { + m_pInstance = ((instance_sunwell_plateau*)pCreature->GetInstanceData()); + Reset(); + } + + instance_sunwell_plateau* m_pInstance; + + uint32 m_uiBlackHoleTimer; + uint32 m_uiDarknessTimer; + + GuidList m_lSummonedCreaturesList; + + void Reset() override + { + m_uiBlackHoleTimer = 15000; + m_uiDarknessTimer = 20000; + } + + void Aggro(Unit* /*pWho*/) override + { + DoCastSpellIfCan(m_creature, SPELL_NEGATIVE_ENERGY_ENT); + } + + void JustDied(Unit* /*pKiller*/) override + { + if (m_pInstance) + m_pInstance->SetData(TYPE_MURU, DONE); + + // Despawn summoned creatures + DespawnSummonedCreatures(); + } + + void JustSummoned(Creature* pSummoned) override + { + // Add the Darkness and Singularity into the list + m_lSummonedCreaturesList.push_back(pSummoned->GetObjectGuid()); + } + + // Wrapper to despawn the Singularities and Darkness on death or on evade + void DespawnSummonedCreatures() + { + for (GuidList::const_iterator itr = m_lSummonedCreaturesList.begin(); itr != m_lSummonedCreaturesList.end(); ++itr) + { + if (Creature* pTemp = m_creature->GetMap()->GetCreature(*itr)) + pTemp->ForcedDespawn(); + } + } + + void JustReachedHome() override + { + if (m_pInstance) + { + m_pInstance->SetData(TYPE_MURU, FAIL); + + // respawn muru + if (Creature* pMuru = m_pInstance->GetSingleCreatureFromStorage(NPC_MURU)) + pMuru->Respawn(); + } + + // despawn boss and summons for reset + DespawnSummonedCreatures(); + m_creature->ForcedDespawn(); + } + + void UpdateAI(const uint32 uiDiff) override + { + if (!m_creature->SelectHostileTarget() || !m_creature->getVictim()) + return; + + if (m_uiBlackHoleTimer < uiDiff) + { + if (Unit* pTarget = m_creature->SelectAttackingTarget(ATTACKING_TARGET_RANDOM, 0)) + { + if (DoCastSpellIfCan(pTarget, SPELL_SUMMON_BLACK_HOLE) == CAST_OK) + m_uiBlackHoleTimer = 15000; + } + } + else + m_uiBlackHoleTimer -= uiDiff; + + if (m_uiDarknessTimer < uiDiff) + { + if (Unit* pTarget = m_creature->SelectAttackingTarget(ATTACKING_TARGET_RANDOM, 0)) + { + if (DoCastSpellIfCan(pTarget, SPELL_SUMMON_DARKNESS) == CAST_OK) + m_uiDarknessTimer = urand(15000, 20000); + } + } + else + m_uiDarknessTimer -= uiDiff; + + DoMeleeAttackIfReady(); + } +}; + +/*###### +## npc_portal_target +######*/ + +struct npc_portal_targetAI : public Scripted_NoMovementAI +{ + npc_portal_targetAI(Creature* pCreature) : Scripted_NoMovementAI(pCreature) + { + m_pInstance = ((instance_sunwell_plateau*)pCreature->GetInstanceData()); + Reset(); + } + + instance_sunwell_plateau* m_pInstance; + + uint8 m_uiTransformCount; + uint32 m_uiTransformTimer; + uint32 m_uiSentinelTimer; + + void Reset() override + { + m_uiTransformCount = 0; + m_uiTransformTimer = 0; + m_uiSentinelTimer = 0; + } + + void JustSummoned(Creature* pSummoned) override + { + // Cast a visual ball on the summoner + if (pSummoned->GetEntry() == NPC_VOID_SENTINEL_SUMMONER) + DoCastSpellIfCan(pSummoned, SPELL_SENTINEL_SUMMONER_VISUAL, CAST_TRIGGERED); + } + + void SpellHit(Unit* /*pCaster*/, const SpellEntry* pSpell) override + { + // These spells are dummies, but are used only to init the timers + // They could use the EffectDummyCreature to handle this, but this makes code easier + switch (pSpell->Id) + { + // Init sentinel summon timer + case SPELL_OPEN_PORTAL: + m_uiSentinelTimer = 5000; + break; + // Start transition effect + case SPELL_OPEN_ALL_PORTALS: + m_uiTransformTimer = 2000; + break; + } + } + + void UpdateAI(const uint32 uiDiff) override + { + if (m_uiSentinelTimer) + { + // Summon the sentinel on a short timer after the portal opens + if (m_uiSentinelTimer <= uiDiff) + { + if (DoCastSpellIfCan(m_creature, SPELL_SUMMON_SENTINEL_SUMMONER) == CAST_OK) + m_uiSentinelTimer = 0; + } + else + m_uiSentinelTimer -= uiDiff; + } + + if (m_uiTransformTimer) + { + if (m_uiTransformTimer <= uiDiff) + { + // Alternate the visuals + ++m_uiTransformCount; + DoCastSpellIfCan(m_creature, (m_uiTransformCount % 2) ? SPELL_TRANSFORM_VISUAL_1 : SPELL_TRANSFORM_VISUAL_2, CAST_TRIGGERED); + + if (m_uiTransformCount < MAX_TRANSFORM_CASTS) + m_uiTransformTimer = 1000; + else + { + m_uiTransformTimer = 0; + m_uiTransformCount = 0; + } + + // Summon Entropius when reached half of the transition + if (m_uiTransformCount == MAX_TRANSFORM_CASTS / 2) + { + if (Creature* pMuru = m_pInstance->GetSingleCreatureFromStorage(NPC_MURU)) + pMuru->CastSpell(pMuru, SPELL_SUMMON_ENTROPIUS, false); + } + } + else + m_uiTransformTimer -= uiDiff; + } + } +}; + +/*###### +## npc_void_sentinel_summoner +######*/ + +struct npc_void_sentinel_summonerAI : public Scripted_NoMovementAI +{ + npc_void_sentinel_summonerAI(Creature* pCreature) : Scripted_NoMovementAI(pCreature) + { + m_pInstance = ((instance_sunwell_plateau*)pCreature->GetInstanceData()); + Reset(); + } + + instance_sunwell_plateau* m_pInstance; + + void Reset() override { } + + void JustSummoned(Creature* pSummoned) override + { + if (pSummoned->GetEntry() == NPC_VOID_SENTINEL) + { + // Attack Muru's target + if (Creature* pMuru = m_pInstance->GetSingleCreatureFromStorage(NPC_MURU)) + { + if (Unit* pTarget = pMuru->SelectAttackingTarget(ATTACKING_TARGET_RANDOM, 0)) + pSummoned->AI()->AttackStart(pTarget); + } + } + } + + void UpdateAI(const uint32 /*uiDiff*/) override { } +}; + +CreatureAI* GetAI_boss_muru(Creature* pCreature) +{ + return new boss_muruAI(pCreature); +} + +CreatureAI* GetAI_boss_entropius(Creature* pCreature) +{ + return new boss_entropiusAI(pCreature); +} + +CreatureAI* GetAI_npc_portal_target(Creature* pCreature) +{ + return new npc_portal_targetAI(pCreature); +} + +CreatureAI* GetAI_npc_void_sentinel_summoner(Creature* pCreature) +{ + return new npc_void_sentinel_summonerAI(pCreature); +} + +void AddSC_boss_muru() +{ + Script* pNewScript; + + pNewScript = new Script; + pNewScript->Name = "boss_muru"; + pNewScript->GetAI = &GetAI_boss_muru; + pNewScript->RegisterSelf(); + + pNewScript = new Script; + pNewScript->Name = "boss_entropius"; + pNewScript->GetAI = &GetAI_boss_entropius; + pNewScript->RegisterSelf(); + + pNewScript = new Script; + pNewScript->Name = "npc_portal_target"; + pNewScript->GetAI = &GetAI_npc_portal_target; + pNewScript->RegisterSelf(); + + pNewScript = new Script; + pNewScript->Name = "npc_void_sentinel_summoner"; + pNewScript->GetAI = &GetAI_npc_void_sentinel_summoner; + pNewScript->RegisterSelf(); +} diff --git a/src/modules/SD2/scripts/eastern_kingdoms/sunwell_plateau/instance_sunwell_plateau.cpp b/src/modules/SD2/scripts/eastern_kingdoms/sunwell_plateau/instance_sunwell_plateau.cpp new file mode 100644 index 000000000..33f2ba38f --- /dev/null +++ b/src/modules/SD2/scripts/eastern_kingdoms/sunwell_plateau/instance_sunwell_plateau.cpp @@ -0,0 +1,504 @@ +/* Copyright (C) 2006 - 2013 ScriptDev2 + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along 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_Sunwell_Plateau +SD%Complete: 70% +SDComment: +SDCategory: Sunwell_Plateau +EndScriptData */ + +#include "precompiled.h" +#include "sunwell_plateau.h" + +/* Sunwell Plateau: +0 - Kalecgos and Sathrovarr +1 - Brutallus +2 - Felmyst +3 - Eredar Twins (Alythess and Sacrolash) +4 - M'uru +5 - Kil'Jaeden +*/ + +static const DialogueEntry aFelmystOutroDialogue[] = +{ + {NPC_KALECGOS_MADRIGOSA, 0, 10000}, + {SAY_KALECGOS_OUTRO, NPC_KALECGOS_MADRIGOSA, 5000}, + {NPC_FELMYST, 0, 5000}, + {SPELL_OPEN_BACK_DOOR, 0, 9000}, + {NPC_BRUTALLUS, 0, 0}, + {0, 0, 0}, +}; + +instance_sunwell_plateau::instance_sunwell_plateau(Map* pMap) : ScriptedInstance(pMap), DialogueHelper(aFelmystOutroDialogue), + m_uiDeceiversKilled(0), + m_uiSpectralRealmTimer(5000), + m_uiKalecRespawnTimer(0), + m_uiMuruBerserkTimer(0), + m_uiKiljaedenYellTimer(90000) +{ + Initialize(); +} + +void instance_sunwell_plateau::Initialize() +{ + memset(&m_auiEncounter, 0, sizeof(m_auiEncounter)); + InitializeDialogueHelper(this); +} + +bool instance_sunwell_plateau::IsEncounterInProgress() const +{ + for (uint8 i = 0; i < MAX_ENCOUNTER; ++i) + { + if (m_auiEncounter[i] == IN_PROGRESS) + return true; + } + + return false; +} + +void instance_sunwell_plateau::OnPlayerEnter(Player* pPlayer) +{ + // Return if Felmyst already dead, or Brutallus alive + if (m_auiEncounter[TYPE_BRUTALLUS] != DONE || m_auiEncounter[TYPE_FELMYST] == DONE) + return; + + // Return if already summoned + if (GetSingleCreatureFromStorage(NPC_FELMYST, true)) + return; + + // Summon Felmyst in reload case + pPlayer->SummonCreature(NPC_FELMYST, aMadrigosaLoc[0].m_fX, aMadrigosaLoc[0].m_fY, aMadrigosaLoc[0].m_fZ, aMadrigosaLoc[0].m_fO, TEMPSUMMON_DEAD_DESPAWN, 0); +} + +void instance_sunwell_plateau::OnCreatureCreate(Creature* pCreature) +{ + switch (pCreature->GetEntry()) + { + case NPC_KALECGOS_DRAGON: + case NPC_KALECGOS_HUMAN: + case NPC_SATHROVARR: + case NPC_FLIGHT_TRIGGER_LEFT: + case NPC_FLIGHT_TRIGGER_RIGHT: + case NPC_MADRIGOSA: + case NPC_BRUTALLUS: + case NPC_FELMYST: + case NPC_KALECGOS_MADRIGOSA: + case NPC_ALYTHESS: + case NPC_SACROLASH: + case NPC_MURU: + case NPC_ENTROPIUS: + case NPC_KILJAEDEN_CONTROLLER: + case NPC_KILJAEDEN: + case NPC_KALECGOS: + case NPC_ANVEENA: + case NPC_VELEN: + case NPC_LIADRIN: + m_mNpcEntryGuidStore[pCreature->GetEntry()] = pCreature->GetObjectGuid(); + break; + case NPC_DECEIVER: + m_lDeceiversGuidList.push_back(pCreature->GetObjectGuid()); + break; + case NPC_WORLD_TRIGGER: + // sort triggers for flightpath + if (pCreature->GetPositionZ() < 51.0f) + m_lAllFlightTriggersList.push_back(pCreature->GetObjectGuid()); + break; + case NPC_WORLD_TRIGGER_LARGE: + if (pCreature->GetPositionY() < 523.0f) + m_lBackdoorTriggersList.push_back(pCreature->GetObjectGuid()); + break; + } +} + +void instance_sunwell_plateau::OnCreatureDeath(Creature* pCreature) +{ + if (pCreature->GetEntry() == NPC_DECEIVER) + { + ++m_uiDeceiversKilled; + // Spawn Kiljaeden when all deceivers are killed + if (m_uiDeceiversKilled == MAX_DECEIVERS) + { + if (Creature* pController = GetSingleCreatureFromStorage(NPC_KILJAEDEN_CONTROLLER)) + { + if (Creature* pKiljaeden = pController->SummonCreature(NPC_KILJAEDEN, pController->GetPositionX(), pController->GetPositionY(), pController->GetPositionZ(), pController->GetOrientation(), TEMPSUMMON_DEAD_DESPAWN, 0)) + pKiljaeden->SetInCombatWithZone(); + + pController->RemoveAurasDueToSpell(SPELL_ANVEENA_DRAIN); + } + } + } +} + +void instance_sunwell_plateau::OnCreatureEvade(Creature* pCreature) +{ + // Reset encounter if raid wipes at deceivers + if (pCreature->GetEntry() == NPC_DECEIVER) + SetData(TYPE_KILJAEDEN, FAIL); +} + +void instance_sunwell_plateau::OnObjectCreate(GameObject* pGo) +{ + switch (pGo->GetEntry()) + { + case GO_FORCEFIELD: + case GO_BOSS_COLLISION_1: + case GO_BOSS_COLLISION_2: + case GO_ICE_BARRIER: + break; + case GO_FIRE_BARRIER: + if (m_auiEncounter[TYPE_KALECGOS] == DONE && m_auiEncounter[TYPE_BRUTALLUS] == DONE && m_auiEncounter[TYPE_FELMYST] == DONE) + pGo->SetGoState(GO_STATE_ACTIVE); + break; + case GO_FIRST_GATE: + break; + case GO_SECOND_GATE: + if (m_auiEncounter[TYPE_EREDAR_TWINS] == DONE) + pGo->SetGoState(GO_STATE_ACTIVE); + break; + case GO_MURU_ENTER_GATE: + if (m_auiEncounter[TYPE_EREDAR_TWINS] == DONE) + pGo->SetGoState(GO_STATE_ACTIVE); + break; + case GO_MURU_EXIT_GATE: + if (m_auiEncounter[TYPE_MURU] == DONE) + pGo->SetGoState(GO_STATE_ACTIVE); + break; + case GO_THIRD_GATE: + if (m_auiEncounter[TYPE_MURU] == DONE) + pGo->SetGoState(GO_STATE_ACTIVE); + break; + case GO_ORB_BLUE_FLIGHT_1: + case GO_ORB_BLUE_FLIGHT_2: + case GO_ORB_BLUE_FLIGHT_3: + case GO_ORB_BLUE_FLIGHT_4: + break; + + default: + return; + } + m_mGoEntryGuidStore[pGo->GetEntry()] = pGo->GetObjectGuid(); +} + +void instance_sunwell_plateau::SetData(uint32 uiType, uint32 uiData) +{ + switch (uiType) + { + case TYPE_KALECGOS: + m_auiEncounter[uiType] = uiData; + // combat doors + DoUseDoorOrButton(GO_FORCEFIELD); + DoUseDoorOrButton(GO_BOSS_COLLISION_1); + DoUseDoorOrButton(GO_BOSS_COLLISION_2); + if (uiData == FAIL) + { + m_uiKalecRespawnTimer = 20000; + + if (Creature* pKalecDragon = GetSingleCreatureFromStorage(NPC_KALECGOS_DRAGON)) + pKalecDragon->ForcedDespawn(); + if (Creature* pKalecHuman = GetSingleCreatureFromStorage(NPC_KALECGOS_HUMAN)) + pKalecHuman->ForcedDespawn(); + if (Creature* pSathrovarr = GetSingleCreatureFromStorage(NPC_SATHROVARR)) + pSathrovarr->AI()->EnterEvadeMode(); + } + break; + case TYPE_BRUTALLUS: + m_auiEncounter[uiType] = uiData; + break; + case TYPE_FELMYST: + m_auiEncounter[uiType] = uiData; + if (uiData == DONE) + StartNextDialogueText(NPC_KALECGOS_MADRIGOSA); + else if (uiData == IN_PROGRESS) + DoSortFlightTriggers(); + break; + case TYPE_EREDAR_TWINS: + m_auiEncounter[uiType] = uiData; + if (uiData == DONE) + { + DoUseDoorOrButton(GO_SECOND_GATE); + DoUseDoorOrButton(GO_MURU_ENTER_GATE); + } + break; + case TYPE_MURU: + m_auiEncounter[uiType] = uiData; + // combat door + DoUseDoorOrButton(GO_MURU_ENTER_GATE); + if (uiData == DONE) + { + DoUseDoorOrButton(GO_MURU_EXIT_GATE); + DoUseDoorOrButton(GO_THIRD_GATE); + } + else if (uiData == IN_PROGRESS) + m_uiMuruBerserkTimer = 10 * MINUTE * IN_MILLISECONDS; + break; + case TYPE_KILJAEDEN: + m_auiEncounter[uiType] = uiData; + if (uiData == FAIL) + { + m_uiDeceiversKilled = 0; + + // Reset Orbs + DoToggleGameObjectFlags(GO_ORB_BLUE_FLIGHT_1, GO_FLAG_NO_INTERACT, true); + DoToggleGameObjectFlags(GO_ORB_BLUE_FLIGHT_2, GO_FLAG_NO_INTERACT, true); + DoToggleGameObjectFlags(GO_ORB_BLUE_FLIGHT_3, GO_FLAG_NO_INTERACT, true); + DoToggleGameObjectFlags(GO_ORB_BLUE_FLIGHT_4, GO_FLAG_NO_INTERACT, true); + + // Respawn deceivers + for (GuidList::const_iterator itr = m_lDeceiversGuidList.begin(); itr != m_lDeceiversGuidList.end(); ++itr) + { + if (Creature* pDeceiver = instance->GetCreature(*itr)) + { + if (!pDeceiver->IsAlive()) + pDeceiver->Respawn(); + } + } + } + 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_strInstData = saveStream.str(); + + SaveToDB(); + OUT_SAVE_INST_DATA_COMPLETE; + } +} + +uint32 instance_sunwell_plateau::GetData(uint32 uiType) const +{ + if (uiType < MAX_ENCOUNTER) + return m_auiEncounter[uiType]; + + return 0; +} + +void instance_sunwell_plateau::Update(uint32 uiDiff) +{ + DialogueUpdate(uiDiff); + + if (m_uiKalecRespawnTimer) + { + if (m_uiKalecRespawnTimer <= uiDiff) + { + if (Creature* pKalecDragon = GetSingleCreatureFromStorage(NPC_KALECGOS_DRAGON)) + pKalecDragon->Respawn(); + if (Creature* pKalecHuman = GetSingleCreatureFromStorage(NPC_KALECGOS_HUMAN)) + pKalecHuman->Respawn(); + m_uiKalecRespawnTimer = 0; + } + else + m_uiKalecRespawnTimer -= uiDiff; + } + + // Muru berserk timer; needs to be done here because it involves two distinct creatures + if (m_auiEncounter[TYPE_MURU] == IN_PROGRESS) + { + if (m_uiMuruBerserkTimer < uiDiff) + { + if (Creature* pEntrpius = GetSingleCreatureFromStorage(NPC_ENTROPIUS, true)) + pEntrpius->CastSpell(pEntrpius, SPELL_MURU_BERSERK, true); + else if (Creature* pMuru = GetSingleCreatureFromStorage(NPC_MURU)) + pMuru->CastSpell(pMuru, SPELL_MURU_BERSERK, true); + + m_uiMuruBerserkTimer = 10 * MINUTE * IN_MILLISECONDS; + } + else + m_uiMuruBerserkTimer -= uiDiff; + } + + if (m_auiEncounter[TYPE_KILJAEDEN] == NOT_STARTED || m_auiEncounter[TYPE_KILJAEDEN] == FAIL) + { + if (m_uiKiljaedenYellTimer < uiDiff) + { + switch (urand(0, 4)) + { + case 0: DoOrSimulateScriptTextForThisInstance(SAY_ORDER_1, NPC_KILJAEDEN_CONTROLLER); break; + case 1: DoOrSimulateScriptTextForThisInstance(SAY_ORDER_2, NPC_KILJAEDEN_CONTROLLER); break; + case 2: DoOrSimulateScriptTextForThisInstance(SAY_ORDER_3, NPC_KILJAEDEN_CONTROLLER); break; + case 3: DoOrSimulateScriptTextForThisInstance(SAY_ORDER_4, NPC_KILJAEDEN_CONTROLLER); break; + case 4: DoOrSimulateScriptTextForThisInstance(SAY_ORDER_5, NPC_KILJAEDEN_CONTROLLER); break; + } + m_uiKiljaedenYellTimer = 90000; + } + else + m_uiKiljaedenYellTimer -= uiDiff; + } +} + +void instance_sunwell_plateau::Load(const char* in) +{ + if (!in) + { + 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]; + + for (uint8 i = 0; i < MAX_ENCOUNTER; ++i) + { + if (m_auiEncounter[i] == IN_PROGRESS) + m_auiEncounter[i] = NOT_STARTED; + } + + OUT_LOAD_INST_DATA_COMPLETE; +} + +static bool sortByPositionX(Creature* pFirst, Creature* pSecond) +{ + return pFirst && pSecond && pFirst->GetPositionX() > pSecond->GetPositionX(); +} + +void instance_sunwell_plateau::DoSortFlightTriggers() +{ + if (m_lAllFlightTriggersList.empty()) + { + script_error_log("Instance Sunwell Plateau: ERROR Failed to load flight triggers for creature id %u.", NPC_FELMYST); + return; + } + + std::list lTriggers; // Valid pointers, only used locally + for (GuidList::const_iterator itr = m_lAllFlightTriggersList.begin(); itr != m_lAllFlightTriggersList.end(); ++itr) + { + if (Creature* pTrigger = instance->GetCreature(*itr)) + lTriggers.push_back(pTrigger); + } + + if (lTriggers.empty()) + return; + + // sort the flight triggers; first by position X, then group them by Y (left and right) + lTriggers.sort(sortByPositionX); + for (std::list::iterator itr = lTriggers.begin(); itr != lTriggers.end(); ++itr) + { + if ((*itr)->GetPositionY() < 600.0f) + m_vRightFlightTriggersVect.push_back((*itr)->GetObjectGuid()); + else + m_vLeftFlightTriggersVect.push_back((*itr)->GetObjectGuid()); + } +} + +ObjectGuid instance_sunwell_plateau::SelectFelmystFlightTrigger(bool bLeftSide, uint8 uiIndex) +{ + // Return the flight trigger from the selected index + GuidVector& vTemp = bLeftSide ? m_vLeftFlightTriggersVect : m_vRightFlightTriggersVect; + + if (uiIndex >= vTemp.size()) + return ObjectGuid(); + + return vTemp[uiIndex]; +} + +void instance_sunwell_plateau::DoEjectSpectralPlayers() +{ + for (GuidSet::const_iterator itr = m_spectralRealmPlayers.begin(); itr != m_spectralRealmPlayers.end(); ++itr) + { + if (Player* pPlayer = instance->GetPlayer(*itr)) + { + if (!pPlayer->HasAura(SPELL_SPECTRAL_REALM_AURA)) + continue; + + pPlayer->CastSpell(pPlayer, SPELL_TELEPORT_NORMAL_REALM, true); + pPlayer->CastSpell(pPlayer, SPELL_SPECTRAL_EXHAUSTION, true); + pPlayer->RemoveAurasDueToSpell(SPELL_SPECTRAL_REALM_AURA); + } + } +} + +void instance_sunwell_plateau::JustDidDialogueStep(int32 iEntry) +{ + switch (iEntry) + { + case NPC_KALECGOS_MADRIGOSA: + if (Creature* pTrigger = GetSingleCreatureFromStorage(NPC_FLIGHT_TRIGGER_LEFT)) + { + if (Creature* pKalec = pTrigger->SummonCreature(NPC_KALECGOS_MADRIGOSA, aKalecLoc[0].m_fX, aKalecLoc[0].m_fY, aKalecLoc[0].m_fZ, aKalecLoc[0].m_fO, TEMPSUMMON_CORPSE_DESPAWN, 0)) + { + pKalec->SetWalk(false); + pKalec->SetLevitate(true); + pKalec->GetMotionMaster()->MovePoint(0, aKalecLoc[1].m_fX, aKalecLoc[1].m_fY, aKalecLoc[1].m_fZ, false); + } + } + break; + case NPC_FELMYST: + if (Creature* pKalec = GetSingleCreatureFromStorage(NPC_KALECGOS_MADRIGOSA)) + pKalec->GetMotionMaster()->MovePoint(0, aKalecLoc[2].m_fX, aKalecLoc[2].m_fY, aKalecLoc[2].m_fZ, false); + break; + case SPELL_OPEN_BACK_DOOR: + if (Creature* pKalec = GetSingleCreatureFromStorage(NPC_KALECGOS_MADRIGOSA)) + { + // ToDo: update this when the AoE spell targeting will support many explicit target. Kalec should target all creatures from the list + if (Creature* pTrigger = instance->GetCreature(m_lBackdoorTriggersList.front())) + pKalec->CastSpell(pTrigger, SPELL_OPEN_BACK_DOOR, true); + } + break; + case NPC_BRUTALLUS: + if (Creature* pKalec = GetSingleCreatureFromStorage(NPC_KALECGOS_MADRIGOSA)) + { + pKalec->ForcedDespawn(10000); + pKalec->GetMotionMaster()->MovePoint(0, aKalecLoc[3].m_fX, aKalecLoc[3].m_fY, aKalecLoc[3].m_fZ, false); + } + break; + } +} + +InstanceData* GetInstanceData_instance_sunwell_plateau(Map* pMap) +{ + return new instance_sunwell_plateau(pMap); +} + +bool AreaTrigger_at_sunwell_plateau(Player* pPlayer, AreaTriggerEntry const* pAt) +{ + if (pAt->id == AREATRIGGER_TWINS) + { + if (pPlayer->isGameMaster() || pPlayer->IsDead()) + return false; + + instance_sunwell_plateau* pInstance = (instance_sunwell_plateau*)pPlayer->GetInstanceData(); + + if (pInstance && pInstance->GetData(TYPE_EREDAR_TWINS) == NOT_STARTED) + pInstance->SetData(TYPE_EREDAR_TWINS, SPECIAL); + } + + return false; +} + +void AddSC_instance_sunwell_plateau() +{ + Script* pNewScript; + + pNewScript = new Script; + pNewScript->Name = "instance_sunwell_plateau"; + pNewScript->GetInstanceData = &GetInstanceData_instance_sunwell_plateau; + pNewScript->RegisterSelf(); + + pNewScript = new Script; + pNewScript->Name = "at_sunwell_plateau"; + pNewScript->pAreaTrigger = &AreaTrigger_at_sunwell_plateau; + pNewScript->RegisterSelf(); +} diff --git a/src/modules/SD2/scripts/eastern_kingdoms/sunwell_plateau/sunwell_plateau.h b/src/modules/SD2/scripts/eastern_kingdoms/sunwell_plateau/sunwell_plateau.h new file mode 100644 index 000000000..c5d1f96a9 --- /dev/null +++ b/src/modules/SD2/scripts/eastern_kingdoms/sunwell_plateau/sunwell_plateau.h @@ -0,0 +1,153 @@ +/* Copyright (C) 2006 - 2013 ScriptDev2 + * This program is free software licensed under GPL version 2 + * Please see the included DOCS/LICENSE.TXT for more information */ + +#ifndef DEF_SUNWELLPLATEAU_H +#define DEF_SUNWELLPLATEAU_H + +enum +{ + MAX_ENCOUNTER = 6, + + TYPE_KALECGOS = 0, + TYPE_BRUTALLUS = 1, + TYPE_FELMYST = 2, + TYPE_EREDAR_TWINS = 3, + TYPE_MURU = 4, + TYPE_KILJAEDEN = 5, + + NPC_KALECGOS_DRAGON = 24850, // kalecgos blue dragon hostile + NPC_KALECGOS_HUMAN = 24891, // kalecgos human form in spectral realm + NPC_SATHROVARR = 24892, + NPC_MADRIGOSA = 24895, + NPC_FLIGHT_TRIGGER_LEFT = 25357, // Related to Felmyst flight path. Also the anchor to summon Madrigosa + NPC_FLIGHT_TRIGGER_RIGHT = 25358, // related to Felmyst flight path + NPC_WORLD_TRIGGER = 22515, + NPC_WORLD_TRIGGER_LARGE = 23472, // ground triggers spawned in Brutallus / Felmyst arena + NPC_BRUTALLUS = 24882, + NPC_FELMYST = 25038, + NPC_KALECGOS_MADRIGOSA = 24844, // kalecgos blue dragon; spawns after Felmyst + NPC_ALYTHESS = 25166, + NPC_SACROLASH = 25165, + NPC_MURU = 25741, + NPC_ENTROPIUS = 25840, + NPC_DECEIVER = 25588, + NPC_KILJAEDEN = 25315, + NPC_KILJAEDEN_CONTROLLER = 25608, // kiljaeden event controller + NPC_ANVEENA = 26046, // related to kiljaeden event + NPC_KALECGOS = 25319, // related to kiljaeden event + NPC_VELEN = 26246, + NPC_LIADRIN = 26247, + + GO_FORCEFIELD = 188421, // kalecgos door + collisions + GO_BOSS_COLLISION_1 = 188523, + GO_BOSS_COLLISION_2 = 188524, + GO_ICE_BARRIER = 188119, // used to block the players path during the Brutallus intro event + GO_FIRE_BARRIER = 188075, // door after felmyst + GO_FIRST_GATE = 187766, // door between felmyst and eredar twins + GO_SECOND_GATE = 187764, // door after eredar twins + GO_MURU_ENTER_GATE = 187990, // muru gates + GO_MURU_EXIT_GATE = 188118, + GO_THIRD_GATE = 187765, // door after muru; why another? + + GO_ORB_BLUE_FLIGHT_1 = 188115, // orbs used in the Kil'jaeden fight + GO_ORB_BLUE_FLIGHT_2 = 188116, + GO_ORB_BLUE_FLIGHT_3 = 187869, + GO_ORB_BLUE_FLIGHT_4 = 188114, + + SAY_KALECGOS_OUTRO = -1580043, + SAY_TWINS_INTRO = -1580044, + + // Kil'jaeden yells + SAY_ORDER_1 = -1580064, + SAY_ORDER_2 = -1580065, + SAY_ORDER_3 = -1580066, + SAY_ORDER_4 = -1580067, + SAY_ORDER_5 = -1580068, + + AREATRIGGER_TWINS = 4937, + + // Kalec spectral realm spells + SPELL_TELEPORT_NORMAL_REALM = 46020, + SPELL_SPECTRAL_REALM_AURA = 46021, + SPELL_SPECTRAL_EXHAUSTION = 44867, + // Felmyst ouro spell + SPELL_OPEN_BACK_DOOR = 46650, // Opens the fire barrier - script effect for 46652 + // used by both muru and entropius + SPELL_MURU_BERSERK = 26662, + // visuals for Kiljaeden encounter + SPELL_ANVEENA_DRAIN = 46410, + + MAX_DECEIVERS = 3 +}; + +struct EventLocations +{ + float m_fX, m_fY, m_fZ, m_fO; +}; + +static const EventLocations aMadrigosaLoc[] = +{ + {1463.82f, 661.212f, 19.79f, 4.88f}, // reload spawn loc - the place where to spawn Felmyst + {1463.82f, 661.212f, 39.234f}, // fly loc during the cinematig +}; + +static const EventLocations aKalecLoc[] = +{ + {1573.146f, 755.2025f, 99.524f, 3.59f}, // spawn loc + {1474.235f, 624.0703f, 29.325f}, // first move + {1511.655f, 550.7028f, 25.510f}, // open door + {1648.255f, 519.377f, 165.848f}, // fly away +}; + +class instance_sunwell_plateau : public ScriptedInstance, private DialogueHelper +{ + public: + instance_sunwell_plateau(Map* pMap); + ~instance_sunwell_plateau() {} + + void Initialize() override; + bool IsEncounterInProgress() const override; + + void OnPlayerEnter(Player* pPlayer) override; + void OnObjectCreate(GameObject* pGo) override; + void OnCreatureCreate(Creature* pCreature) override; + void OnCreatureDeath(Creature* pCreature) override; + void OnCreatureEvade(Creature* pCreature); + + void SetData(uint32 uiType, uint32 uiData) override; + uint32 GetData(uint32 uiType) const override; + + void Update(uint32 uiDiff) override; + + ObjectGuid SelectFelmystFlightTrigger(bool bLeftSide, uint8 uiIndex); + + void AddToSpectralRealm(ObjectGuid playerGuid) { m_spectralRealmPlayers.insert(playerGuid); } + void RemoveFromSpectralRealm(ObjectGuid playerGuid) { m_spectralRealmPlayers.erase(playerGuid); } + void DoEjectSpectralPlayers(); + + const char* Save() const override { return m_strInstData.c_str(); } + void Load(const char* chrIn) override; + + protected: + void JustDidDialogueStep(int32 iEntry) override; + void DoSortFlightTriggers(); + + uint32 m_auiEncounter[MAX_ENCOUNTER]; + std::string m_strInstData; + + // Misc + uint8 m_uiDeceiversKilled; + uint32 m_uiSpectralRealmTimer; + uint32 m_uiKalecRespawnTimer; + uint32 m_uiMuruBerserkTimer; + uint32 m_uiKiljaedenYellTimer; + + GuidSet m_spectralRealmPlayers; + GuidVector m_vRightFlightTriggersVect; + GuidVector m_vLeftFlightTriggersVect; + GuidList m_lAllFlightTriggersList; + GuidList m_lBackdoorTriggersList; + GuidList m_lDeceiversGuidList; +}; +#endif diff --git a/src/modules/SD2/scripts/eastern_kingdoms/swamp_of_sorrows.cpp b/src/modules/SD2/scripts/eastern_kingdoms/swamp_of_sorrows.cpp new file mode 100644 index 000000000..77acc559c --- /dev/null +++ b/src/modules/SD2/scripts/eastern_kingdoms/swamp_of_sorrows.cpp @@ -0,0 +1,187 @@ +/** + * ScriptDev2 is an extension for mangos providing enhanced features for + * area triggers, creatures, game objects, instances, items, and spells beyond + * the default database scripting in mangos. + * + * Copyright (C) 2006-2013 ScriptDev2 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * World of Warcraft, and all World of Warcraft or Warcraft art, images, + * and lore are copyrighted by Blizzard Entertainment, Inc. + */ + +/** + * ScriptData + * SDName: Swamp_of_Sorrows + * SD%Complete: 100 + * SDComment: Quest support: 1393. + * SDCategory: Swap of Sorrows + * EndScriptData + */ + +/** + * ContentData + * npc_galen_goodward + * EndContentData + */ + +#include "precompiled.h" +#include "escort_ai.h" + +/*###### +## npc_galen_goodward +######*/ + +enum Galen +{ + QUEST_GALENS_ESCAPE = 1393, + + GO_GALENS_CAGE = 37118, + + SAY_PERIODIC = -1000582, + SAY_QUEST_ACCEPTED = -1000583, + SAY_ATTACKED_1 = -1000584, + SAY_ATTACKED_2 = -1000585, + SAY_QUEST_COMPLETE = -1000586, + EMOTE_WHISPER = -1000587, + EMOTE_DISAPPEAR = -1000588 +}; + +struct npc_galen_goodwardAI : public npc_escortAI +{ + npc_galen_goodwardAI(Creature* pCreature) : npc_escortAI(pCreature) { Reset(); } + + ObjectGuid m_galensCageGuid; + uint32 m_uiPeriodicSay; + + void Reset() override + { + m_uiPeriodicSay = 6000; + } + + void Aggro(Unit* pWho) override + { + if (HasEscortState(STATE_ESCORT_ESCORTING)) + { + DoScriptText(urand(0, 1) ? SAY_ATTACKED_1 : SAY_ATTACKED_2, m_creature, pWho); + } + } + + void WaypointStart(uint32 uiPointId) override + { + switch (uiPointId) + { + case 0: + { + GameObject* pCage = NULL; + if (m_galensCageGuid) + { + pCage = m_creature->GetMap()->GetGameObject(m_galensCageGuid); + } + else + { + pCage = GetClosestGameObjectWithEntry(m_creature, GO_GALENS_CAGE, INTERACTION_DISTANCE); + } + + if (pCage) + { + pCage->UseDoorOrButton(); + m_galensCageGuid = pCage->GetObjectGuid(); + } + break; + } + case 21: + DoScriptText(EMOTE_DISAPPEAR, m_creature); + break; + } + } + + void WaypointReached(uint32 uiPointId) override + { + switch (uiPointId) + { + case 0: + if (GameObject* pCage = m_creature->GetMap()->GetGameObject(m_galensCageGuid)) + { + pCage->ResetDoorOrButton(); + } + break; + case 20: + if (Player* pPlayer = GetPlayerForEscort()) + { + m_creature->SetFacingToObject(pPlayer); + DoScriptText(SAY_QUEST_COMPLETE, m_creature, pPlayer); + DoScriptText(EMOTE_WHISPER, m_creature, pPlayer); + pPlayer->GroupEventHappens(QUEST_GALENS_ESCAPE, m_creature); + } + SetRun(true); + break; + } + } + + void UpdateEscortAI(const uint32 uiDiff) override + { + + if (m_uiPeriodicSay < uiDiff) + { + if (HasEscortState(STATE_ESCORT_NONE)) + { + DoScriptText(SAY_PERIODIC, m_creature); + } + m_uiPeriodicSay = 6000; + } + else + { m_uiPeriodicSay -= uiDiff; } + + if (!m_creature->SelectHostileTarget() || !m_creature->getVictim()) + { + return; + } + + DoMeleeAttackIfReady(); + } +}; + +bool QuestAccept_npc_galen_goodward(Player* pPlayer, Creature* pCreature, const Quest* pQuest) +{ + if (pQuest->GetQuestId() == QUEST_GALENS_ESCAPE) + { + + if (npc_galen_goodwardAI* pEscortAI = dynamic_cast(pCreature->AI())) + { + pEscortAI->Start(false, pPlayer, pQuest); + pCreature->SetFactionTemporary(FACTION_ESCORT_N_NEUTRAL_ACTIVE, TEMPFACTION_RESTORE_RESPAWN); + DoScriptText(SAY_QUEST_ACCEPTED, pCreature); + } + } + return true; +} + +CreatureAI* GetAI_npc_galen_goodward(Creature* pCreature) +{ + return new npc_galen_goodwardAI(pCreature); +} + +void AddSC_swamp_of_sorrows() +{ + Script* pNewScript; + + pNewScript = new Script; + pNewScript->Name = "npc_galen_goodward"; + pNewScript->GetAI = &GetAI_npc_galen_goodward; + pNewScript->pQuestAcceptNPC = &QuestAccept_npc_galen_goodward; + pNewScript->RegisterSelf(); +} diff --git a/src/modules/SD2/scripts/eastern_kingdoms/throne_of_the_tides/boss_commander_ulthok.cpp b/src/modules/SD2/scripts/eastern_kingdoms/throne_of_the_tides/boss_commander_ulthok.cpp new file mode 100644 index 000000000..c22b9c6c1 --- /dev/null +++ b/src/modules/SD2/scripts/eastern_kingdoms/throne_of_the_tides/boss_commander_ulthok.cpp @@ -0,0 +1,28 @@ +/* Copyright (C) 2006 - 2013 ScriptDev2 + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +/* ScriptData +SDName: boss_commander_ulthok +SD%Complete: 0 +SDComment: Placeholder +SDCategory: Throne of the Tides +EndScriptData */ + +#include "precompiled.h" + +void AddSC_boss_commander_ulthok() +{ +} diff --git a/src/modules/SD2/scripts/eastern_kingdoms/throne_of_the_tides/boss_erunak_and_ghursha.cpp b/src/modules/SD2/scripts/eastern_kingdoms/throne_of_the_tides/boss_erunak_and_ghursha.cpp new file mode 100644 index 000000000..a7aee76eb --- /dev/null +++ b/src/modules/SD2/scripts/eastern_kingdoms/throne_of_the_tides/boss_erunak_and_ghursha.cpp @@ -0,0 +1,28 @@ +/* Copyright (C) 2006 - 2013 ScriptDev2 + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +/* ScriptData +SDName: boss_erunak_and_ghursha +SD%Complete: 0 +SDComment: Placeholder +SDCategory: Throne of the Tides +EndScriptData */ + +#include "precompiled.h" + +void AddSC_boss_erunak_and_ghursha() +{ +} diff --git a/src/modules/SD2/scripts/eastern_kingdoms/throne_of_the_tides/boss_lady_nazjar.cpp b/src/modules/SD2/scripts/eastern_kingdoms/throne_of_the_tides/boss_lady_nazjar.cpp new file mode 100644 index 000000000..0f0bee376 --- /dev/null +++ b/src/modules/SD2/scripts/eastern_kingdoms/throne_of_the_tides/boss_lady_nazjar.cpp @@ -0,0 +1,28 @@ +/* Copyright (C) 2006 - 2013 ScriptDev2 + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +/* ScriptData +SDName: boss_lady_nazjar +SD%Complete: 0 +SDComment: Placeholder +SDCategory: Throne of the Tides +EndScriptData */ + +#include "precompiled.h" + +void AddSC_boss_lady_nazjar() +{ +} diff --git a/src/modules/SD2/scripts/eastern_kingdoms/throne_of_the_tides/boss_ozumat.cpp b/src/modules/SD2/scripts/eastern_kingdoms/throne_of_the_tides/boss_ozumat.cpp new file mode 100644 index 000000000..7a91756f1 --- /dev/null +++ b/src/modules/SD2/scripts/eastern_kingdoms/throne_of_the_tides/boss_ozumat.cpp @@ -0,0 +1,28 @@ +/* Copyright (C) 2006 - 2013 ScriptDev2 + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +/* ScriptData +SDName: boss_ozumat +SD%Complete: 0 +SDComment: Placeholder +SDCategory: Throne of the Tides +EndScriptData */ + +#include "precompiled.h" + +void AddSC_boss_ozumat() +{ +} diff --git a/src/modules/SD2/scripts/eastern_kingdoms/throne_of_the_tides/instance_throne_of_the_tides.cpp b/src/modules/SD2/scripts/eastern_kingdoms/throne_of_the_tides/instance_throne_of_the_tides.cpp new file mode 100644 index 000000000..6742081f2 --- /dev/null +++ b/src/modules/SD2/scripts/eastern_kingdoms/throne_of_the_tides/instance_throne_of_the_tides.cpp @@ -0,0 +1,28 @@ +/* Copyright (C) 2006 - 2013 ScriptDev2 + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along 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_throne_of_the_tides +SD%Complete: 0 +SDComment: Placeholder +SDCategory: Throne of the Tides +EndScriptData */ + +#include "precompiled.h" + +void AddSC_instance_throne_of_the_tides() +{ +} diff --git a/src/modules/SD2/scripts/eastern_kingdoms/throne_of_the_tides/throne_of_the_tides.h b/src/modules/SD2/scripts/eastern_kingdoms/throne_of_the_tides/throne_of_the_tides.h new file mode 100644 index 000000000..6b5f9ef47 --- /dev/null +++ b/src/modules/SD2/scripts/eastern_kingdoms/throne_of_the_tides/throne_of_the_tides.h @@ -0,0 +1,3 @@ +/* Copyright (C) 2006 - 2013 ScriptDev2 + * This program is free software licensed under GPL version 2 + * Please see the included DOCS/LICENSE.TXT for more information */ diff --git a/src/modules/SD2/scripts/eastern_kingdoms/tirisfal_glades.cpp b/src/modules/SD2/scripts/eastern_kingdoms/tirisfal_glades.cpp new file mode 100644 index 000000000..1b926406a --- /dev/null +++ b/src/modules/SD2/scripts/eastern_kingdoms/tirisfal_glades.cpp @@ -0,0 +1,229 @@ +/** + * ScriptDev2 is an extension for mangos providing enhanced features for + * area triggers, creatures, game objects, instances, items, and spells beyond + * the default database scripting in mangos. + * + * Copyright (C) 2006-2013 ScriptDev2 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * World of Warcraft, and all World of Warcraft or Warcraft art, images, + * and lore are copyrighted by Blizzard Entertainment, Inc. + */ + +/** + * ScriptData + * SDName: Tirisfal_Glades + * SD%Complete: 100 + * SDComment: Quest support: 590, 1819. + * SDCategory: Tirisfal Glades + * EndScriptData + */ + +/** + * ContentData + * go_mausoleum_door + * go_mausoleum_trigger + * npc_calvin_montague + * EndContentData + */ + +#include "precompiled.h" + +/*###### +## go_mausoleum_door +## go_mausoleum_trigger +######*/ + +enum +{ + QUEST_ULAG = 1819, + NPC_ULAG = 6390, + GO_TRIGGER = 104593, + GO_DOOR = 176594 +}; + +bool GOUse_go_mausoleum_door(Player* pPlayer, GameObject* /*pGo*/) +{ + if (pPlayer->GetQuestStatus(QUEST_ULAG) != QUEST_STATUS_INCOMPLETE) + { + return false; + } + + if (GameObject* pTrigger = GetClosestGameObjectWithEntry(pPlayer, GO_TRIGGER, 30.0f)) + { + pTrigger->SetGoState(GO_STATE_READY); + pPlayer->SummonCreature(NPC_ULAG, 2390.26f, 336.47f, 40.01f, 2.26f, TEMPSUMMON_TIMED_OOC_OR_DEAD_DESPAWN, 300000); + return false; + } + + return false; +} + +bool GOUse_go_mausoleum_trigger(Player* pPlayer, GameObject* pGo) +{ + if (pPlayer->GetQuestStatus(QUEST_ULAG) != QUEST_STATUS_INCOMPLETE) + { + return false; + } + + if (GameObject* pDoor = GetClosestGameObjectWithEntry(pPlayer, GO_DOOR, 30.0f)) + { + pGo->SetGoState(GO_STATE_ACTIVE); + pDoor->RemoveFlag(GAMEOBJECT_FLAGS, GO_FLAG_INTERACT_COND); + return true; + } + + return false; +} + +/*###### +## npc_calvin_montague +######*/ + +enum +{ + SAY_COMPLETE = -1000356, + SPELL_DRINK = 2639, // possibly not correct spell (but iconId is correct) + QUEST_590 = 590, + FACTION_HOSTILE = 168 +}; + +struct npc_calvin_montagueAI : public ScriptedAI +{ + npc_calvin_montagueAI(Creature* pCreature) : ScriptedAI(pCreature) + { + Reset(); + } + + uint32 m_uiPhase; + uint32 m_uiPhaseTimer; + ObjectGuid m_playerGuid; + + void Reset() override + { + m_uiPhase = 0; + m_uiPhaseTimer = 5000; + m_playerGuid.Clear(); + } + + void AttackedBy(Unit* pAttacker) override + { + if (m_creature->getVictim() || m_creature->IsFriendlyTo(pAttacker)) + { + return; + } + + AttackStart(pAttacker); + } + + void DamageTaken(Unit* pDoneBy, uint32& uiDamage) override + { + if (uiDamage > m_creature->GetHealth() || ((m_creature->GetHealth() - uiDamage) * 100 / m_creature->GetMaxHealth() < 15)) + { + uiDamage = 0; + + m_creature->CombatStop(true); + + m_uiPhase = 1; + + if (pDoneBy->GetTypeId() == TYPEID_PLAYER) + { + m_playerGuid = pDoneBy->GetObjectGuid(); + } + } + } + + void UpdateAI(const uint32 uiDiff) override + { + if (m_uiPhase) + { + if (m_uiPhaseTimer < uiDiff) + { + m_uiPhaseTimer = 7500; + } + else + { + m_uiPhaseTimer -= uiDiff; + return; + } + + switch (m_uiPhase) + { + case 1: + DoScriptText(SAY_COMPLETE, m_creature); + ++m_uiPhase; + break; + case 2: + if (Player* pPlayer = m_creature->GetMap()->GetPlayer(m_playerGuid)) + { + pPlayer->AreaExploredOrEventHappens(QUEST_590); + } + + m_creature->CastSpell(m_creature, SPELL_DRINK, true); + ++m_uiPhase; + break; + case 3: + EnterEvadeMode(); + break; + } + + return; + } + + if (!m_creature->SelectHostileTarget() || !m_creature->getVictim()) + { + return; + } + + DoMeleeAttackIfReady(); + } +}; + +CreatureAI* GetAI_npc_calvin_montague(Creature* pCreature) +{ + return new npc_calvin_montagueAI(pCreature); +} + +bool QuestAccept_npc_calvin_montague(Player* pPlayer, Creature* pCreature, const Quest* pQuest) +{ + if (pQuest->GetQuestId() == QUEST_590) + { + pCreature->SetFactionTemporary(FACTION_HOSTILE, TEMPFACTION_RESTORE_COMBAT_STOP | TEMPFACTION_RESTORE_RESPAWN); + pCreature->AI()->AttackStart(pPlayer); + } + return true; +} + +void AddSC_tirisfal_glades() +{ + Script* pNewScript; + + pNewScript = new Script; + pNewScript->Name = "go_mausoleum_door"; + pNewScript->pGOUse = &GOUse_go_mausoleum_door; + pNewScript->RegisterSelf(); + + pNewScript = new Script; + pNewScript->Name = "go_mausoleum_trigger"; + pNewScript->pGOUse = &GOUse_go_mausoleum_trigger; + pNewScript->RegisterSelf(); + + pNewScript = new Script; + pNewScript->Name = "npc_calvin_montague"; + pNewScript->GetAI = &GetAI_npc_calvin_montague; + pNewScript->pQuestAcceptNPC = &QuestAccept_npc_calvin_montague; + pNewScript->RegisterSelf(); +} diff --git a/src/modules/SD2/scripts/eastern_kingdoms/twilight_highlands.cpp b/src/modules/SD2/scripts/eastern_kingdoms/twilight_highlands.cpp new file mode 100644 index 000000000..30dd96bb2 --- /dev/null +++ b/src/modules/SD2/scripts/eastern_kingdoms/twilight_highlands.cpp @@ -0,0 +1,35 @@ +/* Copyright (C) 2006 - 2013 ScriptDev2 + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +/* ScriptData +SDName: Twilight Highlands +SD%Complete: 0 +SDComment: Placeholder +SDCategory: Twilight Highlands +EndScriptData */ + +/* ContentData +EndContentData */ + +#include "precompiled.h" + +/*###### +# +######*/ + +void AddSC_twilight_highlands() +{ +} diff --git a/src/modules/SD2/scripts/eastern_kingdoms/uldaman/boss_archaedas.cpp b/src/modules/SD2/scripts/eastern_kingdoms/uldaman/boss_archaedas.cpp new file mode 100644 index 000000000..cd5d18bd7 --- /dev/null +++ b/src/modules/SD2/scripts/eastern_kingdoms/uldaman/boss_archaedas.cpp @@ -0,0 +1,248 @@ +/* Copyright (C) 2006 - 2013 ScriptDev2 + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with 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 +SDComment: +SDCategory: Uldaman +EndScriptData */ + +#include "precompiled.h" +#include "uldaman.h" + +enum +{ + SPELL_ARCHAEDAS_AWAKEN_VISUAL = 10347, + SPELL_GROUND_TREMOR = 6524, + + SPELL_AWAKEN_EARTHEN_GUARDIAN = 10252, // awaken all 7076 npcs + SPELL_AWAKEN_VAULT_WARDER = 10258, // awaken 2 npcs 10120 + SPELL_AWAKEN_EARTHEN_DWARF = 10259, // awaken random npc 7309 or 7077 + + SAY_AGGRO = -1070001, + SAY_AWAKE_GUARDIANS = -1070002, + SAY_AWAKE_WARDERS = -1070003, + SAY_UNIT_SLAIN = -1070004, + EMOTE_BREAKS_FREE = -1070005, +}; + +struct boss_archaedasAI : public ScriptedAI +{ + boss_archaedasAI(Creature* pCreature) : ScriptedAI(pCreature) + { + m_pInstance = (instance_uldaman*)pCreature->GetInstanceData(); + Reset(); + } + + instance_uldaman* m_pInstance; + + uint32 m_uiAwakeningTimer; + uint32 m_uiAwakeDwarfTimer; + uint32 m_uiTremorTimer; + uint8 m_uiSubevent; + bool m_bDwarvesAwaken; + + uint8 m_uiHpPhaseCheck; + + void Reset() override + { + m_uiAwakeningTimer = 1000; + m_uiSubevent = 0; + m_uiAwakeDwarfTimer = 10000; + m_uiTremorTimer = urand(7000, 14000); + m_bDwarvesAwaken = false; + m_uiHpPhaseCheck = 1; + + DoCastSpellIfCan(m_creature, SPELL_FREEZE_ANIM); + m_creature->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE); + } + + void Aggro(Unit* /*pWho*/) override + { + if (m_pInstance) + m_pInstance->SetData(TYPE_ARCHAEDAS, IN_PROGRESS); + } + + void KilledUnit(Unit* /*pVictim*/) override + { + DoScriptText(SAY_UNIT_SLAIN, m_creature); + } + + void JustDied(Unit* /*pKiller*/) override + { + // open door to vault (handled by instance script) + if (m_pInstance) + m_pInstance->SetData(TYPE_ARCHAEDAS, DONE); + } + + void JustReachedHome() override + { + if (m_pInstance) + m_pInstance->SetData(TYPE_ARCHAEDAS, FAIL); + } + + void UpdateAI(const uint32 uiDiff) override + { + // so many things are based in this script on instance data + // so if we don't have access to it better do nothing + if (!m_pInstance) + return; + + // OOC Intro part triggered by Altar activation + if (m_pInstance->GetData(TYPE_ARCHAEDAS) == SPECIAL) + { + if (m_uiAwakeningTimer <= uiDiff) + { + switch (m_uiSubevent) + { + case 0: + DoCastSpellIfCan(m_creature, SPELL_ARCHAEDAS_AWAKEN_VISUAL); + m_uiAwakeningTimer = 2000; + break; + case 1: + DoScriptText(EMOTE_BREAKS_FREE, m_creature); + m_uiAwakeningTimer = 3000; + break; + case 2: + DoScriptText(SAY_AGGRO, m_creature); + m_creature->RemoveAurasDueToSpell(SPELL_FREEZE_ANIM); + m_creature->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE); + + // Attack player + if (Player* pPlayer = m_creature->GetMap()->GetPlayer(m_pInstance->GetGuid(DATA_EVENT_STARTER))) + AttackStart(pPlayer); + else + EnterEvadeMode(); + break; + default: + break; + } + + ++m_uiSubevent; + } + else + m_uiAwakeningTimer -= uiDiff; + } + + if (!m_creature->SelectHostileTarget() || !m_creature->getVictim()) + return; + + // Phase switch + if (m_creature->GetHealthPercent() < 100.0f - 33.4f * (float)m_uiHpPhaseCheck) + { + if (DoCastSpellIfCan(m_creature, m_uiHpPhaseCheck == 1 ? SPELL_AWAKEN_EARTHEN_GUARDIAN : SPELL_AWAKEN_VAULT_WARDER) == CAST_OK) + { + DoScriptText(m_uiHpPhaseCheck == 1 ? SAY_AWAKE_GUARDIANS : SAY_AWAKE_WARDERS, m_creature); + ++m_uiHpPhaseCheck; + } + } + + // Awake random Dwarf + if (!m_bDwarvesAwaken && m_creature->GetHealthPercent() >= 33.0f) + { + if (m_uiAwakeDwarfTimer < uiDiff) + { + if (Creature* pEarthen = m_pInstance->GetClosestDwarfNotInCombat(m_creature)) + { + if (DoCastSpellIfCan(pEarthen, SPELL_AWAKEN_EARTHEN_DWARF) == CAST_OK) + m_uiAwakeDwarfTimer = urand(9000, 12000); + } + else + m_bDwarvesAwaken = true; + } + else + m_uiAwakeDwarfTimer -= uiDiff; + } + + if (m_uiTremorTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature, SPELL_GROUND_TREMOR) == CAST_OK) + m_uiTremorTimer = urand(8000, 17000); + } + else + m_uiTremorTimer -= uiDiff; + + DoMeleeAttackIfReady(); + } +}; + +CreatureAI* GetAI_boss_archaedas(Creature* pCreature) +{ + return new boss_archaedasAI(pCreature); +} + +bool EffectDummyCreature_npc_vault_warder(Unit* /*pCaster*/, uint32 uiSpellId, SpellEffectIndex uiEffIndex, Creature* pCreatureTarget, ObjectGuid /*originalCasterGuid*/) +{ + // always check spellid and effectindex + if (uiSpellId == SPELL_AWAKEN_VAULT_WARDER && uiEffIndex == EFFECT_INDEX_0) + { + if (pCreatureTarget->GetEntry() == NPC_VAULT_WARDER) + { + pCreatureTarget->RemoveAurasDueToSpell(SPELL_STONED); + + ScriptedInstance* pInstance = (ScriptedInstance*)pCreatureTarget->GetInstanceData(); + if (!pInstance) + return true; + + if (Creature* pArchaedas = pInstance->GetSingleCreatureFromStorage(NPC_ARCHAEDAS)) + pCreatureTarget->AI()->AttackStart(pArchaedas->getVictim()); + + return true; + } + } + + return false; +} + +bool EffectAuraDummy_spell_aura_dummy_awaken_dwarf(const Aura* pAura, bool bApply) +{ + if (bApply) + return true; + + if ((pAura->GetId() == SPELL_AWAKEN_EARTHEN_DWARF || pAura->GetId() == SPELL_AWAKEN_EARTHEN_GUARDIAN) && pAura->GetEffIndex() == EFFECT_INDEX_0) + { + if (Creature* pTarget = (Creature*)pAura->GetTarget()) + { + pTarget->RemoveAurasDueToSpell(SPELL_STONED); + + ScriptedInstance* pInstance = (ScriptedInstance*)pTarget->GetInstanceData(); + if (!pInstance) + return true; + + if (Creature* pArchaedas = pInstance->GetSingleCreatureFromStorage(NPC_ARCHAEDAS)) + pTarget->AI()->AttackStart(pArchaedas->getVictim()); + } + } + + return true; +} + +void AddSC_boss_archaedas() +{ + Script* pNewScript; + + pNewScript = new Script; + pNewScript->Name = "boss_archaedas"; + pNewScript->GetAI = &GetAI_boss_archaedas; + pNewScript->RegisterSelf(); + + pNewScript = new Script; + pNewScript->Name = "mob_archaeras_add"; + pNewScript->pEffectDummyNPC = &EffectDummyCreature_npc_vault_warder; + pNewScript->pEffectAuraDummy = &EffectAuraDummy_spell_aura_dummy_awaken_dwarf; + pNewScript->RegisterSelf(); +} diff --git a/src/modules/SD2/scripts/eastern_kingdoms/uldaman/instance_uldaman.cpp b/src/modules/SD2/scripts/eastern_kingdoms/uldaman/instance_uldaman.cpp new file mode 100644 index 000000000..b6ebefd4c --- /dev/null +++ b/src/modules/SD2/scripts/eastern_kingdoms/uldaman/instance_uldaman.cpp @@ -0,0 +1,330 @@ +/* Copyright (C) 2006 - 2013 ScriptDev2 + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along 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_Uldaman +SD%Complete: 60 +SDComment: +SDCategory: Uldaman +EndScriptData +*/ + +#include "precompiled.h" +#include "uldaman.h" + +instance_uldaman::instance_uldaman(Map* pMap) : ScriptedInstance(pMap), + m_uiKeeperCooldown(0), + m_uiStoneKeepersFallen(0) +{ + Initialize(); +} + +void instance_uldaman::Initialize() +{ + memset(&m_auiEncounter, 0, sizeof(m_auiEncounter)); +} + +void instance_uldaman::OnObjectCreate(GameObject* pGo) +{ + switch (pGo->GetEntry()) + { + case GO_TEMPLE_DOOR_UPPER: + case GO_TEMPLE_DOOR_LOWER: + if (GetData(TYPE_ALTAR_EVENT) == DONE) + pGo->SetGoState(GO_STATE_ACTIVE); + break; + case GO_ANCIENT_VAULT: + if (GetData(TYPE_ARCHAEDAS) == DONE) + pGo->SetGoState(GO_STATE_ACTIVE); + break; + case GO_ANCIENT_TREASURE: + break; + default: + return; + } + m_mGoEntryGuidStore[pGo->GetEntry()] = pGo->GetObjectGuid(); +} + +void instance_uldaman::OnCreatureCreate(Creature* pCreature) +{ + switch (pCreature->GetEntry()) + { + case NPC_HALLSHAPER: + case NPC_CUSTODIAN: + m_lWardens.push_back(pCreature->GetObjectGuid()); + break; + case NPC_STONE_KEEPER: + m_lKeepers.push_back(pCreature->GetObjectGuid()); + break; + case NPC_ARCHAEDAS: + m_mNpcEntryGuidStore[NPC_ARCHAEDAS] = pCreature->GetObjectGuid(); + break; + default: + break; + } +} + +void instance_uldaman::SetData(uint32 uiType, uint32 uiData) +{ + switch (uiType) + { + case TYPE_ALTAR_EVENT: + if (uiData == DONE) + { + DoUseDoorOrButton(GO_TEMPLE_DOOR_UPPER); + DoUseDoorOrButton(GO_TEMPLE_DOOR_LOWER); + } + else if (uiData == IN_PROGRESS) + { + // Also do a reset before starting the event - this will respawn dead Keepers + DoResetKeeperEvent(); + m_uiKeeperCooldown = 5000; + } + else if (uiData == NOT_STARTED) + { + DoResetKeeperEvent(); + m_uiStoneKeepersFallen = 0; + } + m_auiEncounter[0] = uiData; + break; + + case TYPE_ARCHAEDAS: + if (uiData == DONE) + { + DoUseDoorOrButton(GO_ANCIENT_VAULT); + DoRespawnGameObject(GO_ANCIENT_TREASURE, HOUR); + } + m_auiEncounter[1] = uiData; + break; + } + + if (uiData == DONE) + { + OUT_SAVE_INST_DATA; + + std::ostringstream saveStream; + + saveStream << m_auiEncounter[0] << " " << m_auiEncounter[1]; + + m_strInstData = saveStream.str(); + SaveToDB(); + OUT_SAVE_INST_DATA_COMPLETE; + } +} + +void instance_uldaman::Load(const char* chrIn) +{ + if (!chrIn) + { + OUT_LOAD_INST_DATA_FAIL; + return; + } + + OUT_LOAD_INST_DATA(chrIn); + + std::istringstream loadStream(chrIn); + loadStream >> m_auiEncounter[0] >> m_auiEncounter[1]; + + for (uint8 i = 0; i < MAX_ENCOUNTER; ++i) + { + if (m_auiEncounter[i] == IN_PROGRESS) + m_auiEncounter[i] = NOT_STARTED; + } + + OUT_LOAD_INST_DATA_COMPLETE; +} + +void instance_uldaman::SetData64(uint32 uiData, uint64 uiGuid) +{ + switch (uiData) + { + // ToDo: check if this one is used in ACID. Otherwise it can be dropped + case DATA_EVENT_STARTER: + m_playerGuid = ObjectGuid(uiGuid); + break; + } +} + +uint32 instance_uldaman::GetData(uint32 uiType) const +{ + switch (uiType) + { + case TYPE_ALTAR_EVENT: + return m_auiEncounter[0]; + case TYPE_ARCHAEDAS: + return m_auiEncounter[1]; + } + return 0; +} + +uint64 instance_uldaman::GetData64(uint32 uiData) const +{ + switch (uiData) + { + case DATA_EVENT_STARTER: + return m_playerGuid.GetRawValue(); + } + return 0; +} + +void instance_uldaman::StartEvent(uint32 uiEventId, Player* pPlayer) +{ + m_playerGuid = pPlayer->GetObjectGuid(); + + if (uiEventId == EVENT_ID_ALTAR_KEEPER) + { + if (GetData(TYPE_ALTAR_EVENT) == NOT_STARTED) + SetData(TYPE_ALTAR_EVENT, IN_PROGRESS); + } + else if (uiEventId == EVENT_ID_ALTAR_ARCHAEDAS) + { + if (GetData(TYPE_ARCHAEDAS) == NOT_STARTED || GetData(TYPE_ARCHAEDAS) == FAIL) + SetData(TYPE_ARCHAEDAS, SPECIAL); + } +} + +void instance_uldaman::DoResetKeeperEvent() +{ + if (m_lKeepers.empty()) + { + script_error_log("Instance Uldaman: ERROR creature %u couldn't be found or something really bad happened.", NPC_STONE_KEEPER); + return; + } + + // Force reset all keepers to the original state + for (GuidList::const_iterator itr = m_lKeepers.begin(); itr != m_lKeepers.end(); ++itr) + { + if (Creature* pKeeper = instance->GetCreature(*itr)) + { + if (!pKeeper->IsAlive()) + pKeeper->Respawn(); + } + } +} + +Creature* instance_uldaman::GetClosestDwarfNotInCombat(Creature* pSearcher) +{ + std::list lTemp; + + for (GuidList::const_iterator itr = m_lWardens.begin(); itr != m_lWardens.end(); ++itr) + { + Creature* pTemp = instance->GetCreature(*itr); + + if (pTemp && pTemp->IsAlive() && !pTemp->getVictim()) + lTemp.push_back(pTemp); + } + + if (lTemp.empty()) + return NULL; + + lTemp.sort(ObjectDistanceOrder(pSearcher)); + return lTemp.front(); +} + +void instance_uldaman::OnCreatureEvade(Creature* pCreature) +{ + // Reset Altar event + if (pCreature->GetEntry() == NPC_STONE_KEEPER) + SetData(TYPE_ALTAR_EVENT, NOT_STARTED); +} + +void instance_uldaman::OnCreatureDeath(Creature* pCreature) +{ + if (pCreature->GetEntry() == NPC_STONE_KEEPER) + { + ++m_uiStoneKeepersFallen; + + if (m_lKeepers.size() == m_uiStoneKeepersFallen) + SetData(TYPE_ALTAR_EVENT, DONE); + else + m_uiKeeperCooldown = 5000; + } +} + +void instance_uldaman::Update(uint32 uiDiff) +{ + if (GetData(TYPE_ALTAR_EVENT) != IN_PROGRESS) + return; + + if (!m_uiKeeperCooldown) + return; + + if (m_uiKeeperCooldown <= uiDiff) + { + for (GuidList::const_iterator itr = m_lKeepers.begin(); itr != m_lKeepers.end(); ++itr) + { + // Get Keeper which is alive and out of combat + Creature* pKeeper = instance->GetCreature(*itr); + if (!pKeeper || !pKeeper->IsAlive() || pKeeper->getVictim()) + continue; + + // Get starter player for attack + Player* pPlayer = pKeeper->GetMap()->GetPlayer(m_playerGuid); + if (!pPlayer || !pPlayer->IsAlive()) + { + // If he's not available, then get a random player, within a reasonamble distance in map + pPlayer = GetPlayerInMap(true, false); + if (!pPlayer || !pPlayer->IsWithinDistInMap(pKeeper, 50.0f)) + { + SetData(TYPE_ALTAR_EVENT, NOT_STARTED); + return; + } + } + + // Attack the player + pKeeper->RemoveAurasDueToSpell(SPELL_STONED); + pKeeper->AI()->AttackStart(pPlayer); + break; + } + + m_uiKeeperCooldown = 0; + } + else + m_uiKeeperCooldown -= uiDiff; +} + +InstanceData* GetInstanceData_instance_uldaman(Map* pMap) +{ + return new instance_uldaman(pMap); +} + +bool ProcessEventId_event_spell_altar_boss_aggro(uint32 uiEventId, Object* pSource, Object* /*pTarget*/, bool bIsStart) +{ + if (bIsStart && pSource->GetTypeId() == TYPEID_PLAYER) + { + if (instance_uldaman* pInstance = (instance_uldaman*)((Player*)pSource)->GetInstanceData()) + { + pInstance->StartEvent(uiEventId, (Player*)pSource); + return true; + } + } + return false; +} + +void AddSC_instance_uldaman() +{ + Script* pNewScript; + + pNewScript = new Script; + pNewScript->Name = "instance_uldaman"; + pNewScript->GetInstanceData = &GetInstanceData_instance_uldaman; + pNewScript->RegisterSelf(); + + pNewScript = new Script; + pNewScript->Name = "event_spell_altar_boss_aggro"; + pNewScript->pProcessEventId = &ProcessEventId_event_spell_altar_boss_aggro; + pNewScript->RegisterSelf(); +} diff --git a/src/modules/SD2/scripts/eastern_kingdoms/uldaman/uldaman.cpp b/src/modules/SD2/scripts/eastern_kingdoms/uldaman/uldaman.cpp new file mode 100644 index 000000000..4d2371b02 --- /dev/null +++ b/src/modules/SD2/scripts/eastern_kingdoms/uldaman/uldaman.cpp @@ -0,0 +1,29 @@ +/* Copyright (C) 2006 - 2013 ScriptDev2 + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along 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: 0 +SDComment: Placeholder +SDCategory: Uldaman +EndScriptData */ + +#include "precompiled.h" +#include "uldaman.h" + +void AddSC_uldaman() +{ +} diff --git a/src/modules/SD2/scripts/eastern_kingdoms/uldaman/uldaman.h b/src/modules/SD2/scripts/eastern_kingdoms/uldaman/uldaman.h new file mode 100644 index 000000000..5553ed921 --- /dev/null +++ b/src/modules/SD2/scripts/eastern_kingdoms/uldaman/uldaman.h @@ -0,0 +1,78 @@ +/* Copyright (C) 2006 - 2013 ScriptDev2 + * This program is free software licensed under GPL version 2 + * Please see the included DOCS/LICENSE.TXT for more information */ + +#ifndef DEF_ULDAMAN_H +#define DEF_ULDAMAN_H + +enum +{ + MAX_ENCOUNTER = 2, + + TYPE_ALTAR_EVENT = 1, + TYPE_ARCHAEDAS = 2, + DATA_EVENT_STARTER = 3, + + GO_TEMPLE_DOOR_UPPER = 124367, + GO_TEMPLE_DOOR_LOWER = 141869, + GO_ANCIENT_VAULT = 124369, + GO_ANCIENT_TREASURE = 141979, + + NPC_ARCHAEDAS = 2748, + NPC_CUSTODIAN = 7309, + NPC_HALLSHAPER = 7077, + NPC_GUARDIAN = 7076, + NPC_VAULT_WARDER = 10120, + NPC_STONE_KEEPER = 4857, + + SPELL_STONED = 10255, + SPELL_FREEZE_ANIM = 16245, + + EVENT_ID_ALTAR_KEEPER = 2228, // spell 11568 + EVENT_ID_ALTAR_ARCHAEDAS = 2268 // spell 10340 +}; + +class instance_uldaman : public ScriptedInstance +{ + public: + instance_uldaman(Map* pMap); + ~instance_uldaman() {} + + void Initialize() override; + + void OnObjectCreate(GameObject* pGo) override; + void OnCreatureCreate(Creature* pCreature) override; + + void OnCreatureDeath(Creature* pCreature) override; + void OnCreatureEvade(Creature* pCreature) override; + + void Update(uint32 uiDiff) override; + + void SetData(uint32 uiType, uint32 uiData) override; + void SetData64(uint32 uiData, uint64 uiGuid) override; + uint32 GetData(uint32 uiType) const override; + uint64 GetData64(uint32 uiData) const override; + + void StartEvent(uint32 uiEventId, Player* pPlayer); + + Creature* GetClosestDwarfNotInCombat(Creature* pSearcher); + + const char* Save() const override { return m_strInstData.c_str(); } + void Load(const char* chrIn) override; + + protected: + void DoResetKeeperEvent(); + + uint32 m_auiEncounter[MAX_ENCOUNTER]; + std::string m_strInstData; + + ObjectGuid m_playerGuid; + + uint32 m_uiKeeperCooldown; + uint32 m_uiStoneKeepersFallen; + + GuidList m_lWardens; + GuidList m_lKeepers; +}; + +#endif diff --git a/src/modules/SD2/scripts/eastern_kingdoms/undercity.cpp b/src/modules/SD2/scripts/eastern_kingdoms/undercity.cpp new file mode 100644 index 000000000..9c62b1aa3 --- /dev/null +++ b/src/modules/SD2/scripts/eastern_kingdoms/undercity.cpp @@ -0,0 +1,171 @@ +/** + * ScriptDev2 is an extension for mangos providing enhanced features for + * area triggers, creatures, game objects, instances, items, and spells beyond + * the default database scripting in mangos. + * + * Copyright (C) 2006-2013 ScriptDev2 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * World of Warcraft, and all World of Warcraft or Warcraft art, images, + * and lore are copyrighted by Blizzard Entertainment, Inc. + */ + +/* ScriptData +SDName: Undercity +SD%Complete: 95 +SDComment: Quest support: 9180(post-event). +SDCategory: Undercity +EndScriptData */ + +/* ContentData +npc_lady_sylvanas_windrunner +EndContentData */ + +#include "precompiled.h" + +/*###### +## npc_lady_sylvanas_windrunner +######*/ + +enum +{ + EMOTE_LAMENT_START = -1000193, + SAY_LAMENT_END = -1000196, + EMOTE_LAMENT_END = -1000197, + + SPELL_HIGHBORNE_AURA = 37090, + SPELL_SYLVANAS_CAST = 36568, + SPELL_RIBBON_OF_SOULS = 37099, + + NPC_HIGHBORNE_LAMENTER = 21628, + NPC_HIGHBORNE_BUNNY = 21641, + + QUEST_ID_JOURNEY_UNDERCITY = 9180, + + MAX_LAMENTERS = 4, +}; + +static const float aHighborneLoc[MAX_LAMENTERS][4] = +{ + {1285.41f, 312.47f, -61.0f, 0.51f}, + {1286.96f, 310.40f, -61.0f, 1.00f}, + {1289.66f, 309.66f, -61.0f, 1.52f}, + {1292.51f, 310.50f, -61.0f, 1.99f}, +}; + +struct npc_lady_sylvanas_windrunnerAI : public ScriptedAI +{ + npc_lady_sylvanas_windrunnerAI(Creature* pCreature) : ScriptedAI(pCreature) { Reset(); } + + uint32 m_uiLamentEventTimer; + uint32 m_uiSummonTimer; + + void Reset() override + { + m_uiLamentEventTimer = 0; + m_uiSummonTimer = 0; + } + + void JustSummoned(Creature* pSummoned) override + { + if (pSummoned->GetEntry() == NPC_HIGHBORNE_BUNNY) + { pSummoned->CastSpell(pSummoned, SPELL_RIBBON_OF_SOULS, false); } + else if (pSummoned->GetEntry() == NPC_HIGHBORNE_LAMENTER) + { + pSummoned->CastSpell(pSummoned, SPELL_HIGHBORNE_AURA, false); + + pSummoned->SetLevitate(true); + pSummoned->GetMotionMaster()->MovePoint(0, pSummoned->GetPositionX(), pSummoned->GetPositionY(), pSummoned->GetPositionZ() + 5.0f); + } + } + + void DoStartLamentEvent() + { + DoScriptText(EMOTE_LAMENT_START, m_creature); + DoCastSpellIfCan(m_creature, SPELL_SYLVANAS_CAST); + m_uiSummonTimer = 13000; + } + + void UpdateAI(const uint32 uiDiff) override + { + if (m_uiLamentEventTimer) + { + if (m_uiLamentEventTimer <= uiDiff) + { + float fX, fY, fZ; + m_creature->GetRandomPoint(m_creature->GetPositionX(), m_creature->GetPositionY(), m_creature->GetPositionZ(), 20.0f, fX, fY, fZ); + m_creature->SummonCreature(NPC_HIGHBORNE_BUNNY, fX, fY, fZ + 15.0f, 0, TEMPSUMMON_TIMED_DESPAWN, 3000); + + m_uiLamentEventTimer = 2000; + + if (!m_creature->HasAura(SPELL_SYLVANAS_CAST)) + { + DoScriptText(SAY_LAMENT_END, m_creature); + DoScriptText(EMOTE_LAMENT_END, m_creature); + m_uiLamentEventTimer = 0; + } + } + else + { m_uiLamentEventTimer -= uiDiff; } + } + + if (m_uiSummonTimer) + { + if (m_uiSummonTimer <= uiDiff) + { + for (uint8 i = 0; i < MAX_LAMENTERS; ++i) + { m_creature->SummonCreature(NPC_HIGHBORNE_LAMENTER, aHighborneLoc[i][0], aHighborneLoc[i][1], aHighborneLoc[i][2], aHighborneLoc[i][3], TEMPSUMMON_TIMED_DESPAWN, 160000); } + + m_uiLamentEventTimer = 2000; + m_uiSummonTimer = 0; + } + else + { m_uiSummonTimer -= uiDiff; } + } + + if (!m_creature->SelectHostileTarget() || !m_creature->getVictim()) + { return; } + + DoMeleeAttackIfReady(); + } +}; + +CreatureAI* GetAI_npc_lady_sylvanas_windrunner(Creature* pCreature) +{ + return new npc_lady_sylvanas_windrunnerAI(pCreature); +} + +bool QuestRewarded_npc_lady_sylvanas_windrunner(Player* /*pPlayer*/, Creature* pCreature, Quest const* pQuest) +{ + if (pQuest->GetQuestId() == QUEST_ID_JOURNEY_UNDERCITY) + { + if (npc_lady_sylvanas_windrunnerAI* pSylvanAI = dynamic_cast(pCreature->AI())) + { pSylvanAI->DoStartLamentEvent(); } + } + + return true; +} + +void AddSC_undercity() +{ + Script* pNewScript; + + pNewScript = new Script; + pNewScript->Name = "npc_lady_sylvanas_windrunner"; + pNewScript->GetAI = &GetAI_npc_lady_sylvanas_windrunner; + pNewScript->pQuestRewardedNPC = &QuestRewarded_npc_lady_sylvanas_windrunner; + pNewScript->RegisterSelf(); +} diff --git a/src/modules/SD2/scripts/eastern_kingdoms/vashjir.cpp b/src/modules/SD2/scripts/eastern_kingdoms/vashjir.cpp new file mode 100644 index 000000000..c24f0224b --- /dev/null +++ b/src/modules/SD2/scripts/eastern_kingdoms/vashjir.cpp @@ -0,0 +1,35 @@ +/* Copyright (C) 2006 - 2013 ScriptDev2 + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +/* ScriptData +SDName: Vashjir +SD%Complete: 0 +SDComment: Placeholder +SDCategory: Vashjir +EndScriptData */ + +/* ContentData +EndContentData */ + +#include "precompiled.h" + +/*###### +# +######*/ + +void AddSC_vashjir() +{ +} diff --git a/src/modules/SD2/scripts/eastern_kingdoms/western_plaguelands.cpp b/src/modules/SD2/scripts/eastern_kingdoms/western_plaguelands.cpp new file mode 100644 index 000000000..2142e4432 --- /dev/null +++ b/src/modules/SD2/scripts/eastern_kingdoms/western_plaguelands.cpp @@ -0,0 +1,253 @@ +/** + * ScriptDev2 is an extension for mangos providing enhanced features for + * area triggers, creatures, game objects, instances, items, and spells beyond + * the default database scripting in mangos. + * + * Copyright (C) 2006-2013 ScriptDev2 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * World of Warcraft, and all World of Warcraft or Warcraft art, images, + * and lore are copyrighted by Blizzard Entertainment, Inc. + */ + +/* ScriptData +SDName: Western_Plaguelands +SD%Complete: 90 +SDComment: Quest support: 5216, 5219, 5222, 5225, 5229, 5231, 5233, 5235, 9446. +SDCategory: Western Plaguelands +EndScriptData */ + +/* ContentData +npc_the_scourge_cauldron +npc_anchorite_truuen +EndContentData */ + +#include "precompiled.h" +#include "escort_ai.h" + +/*###### +## npc_the_scourge_cauldron +######*/ + +struct npc_the_scourge_cauldronAI : public ScriptedAI +{ + npc_the_scourge_cauldronAI(Creature* pCreature) : ScriptedAI(pCreature) {Reset();} + + void Reset() override {} + + void DoDie() + { + // summoner dies here + m_creature->DealDamage(m_creature, m_creature->GetHealth(), NULL, DIRECT_DAMAGE, SPELL_SCHOOL_MASK_NORMAL, NULL, false); + // override any database `spawntimesecs` to prevent duplicated summons + uint32 rTime = m_creature->GetRespawnDelay(); + if (rTime < 600) + { + m_creature->SetRespawnDelay(600); + } + } + + void MoveInLineOfSight(Unit* who) override + { + if (!who || who->GetTypeId() != TYPEID_PLAYER) + { + return; + } + + if (who->GetTypeId() == TYPEID_PLAYER) + { + switch (m_creature->GetAreaId()) + { + case 199: // felstone + if (((Player*)who)->GetQuestStatus(5216) == QUEST_STATUS_INCOMPLETE || + ((Player*)who)->GetQuestStatus(5229) == QUEST_STATUS_INCOMPLETE) + { + m_creature->SummonCreature(11075, 0.0f, 0.0f, 0.0f, 0.0f, TEMPSUMMON_TIMED_OOC_OR_DEAD_DESPAWN, 600000); + DoDie(); + } + break; + case 200: // dalson + if (((Player*)who)->GetQuestStatus(5219) == QUEST_STATUS_INCOMPLETE || + ((Player*)who)->GetQuestStatus(5231) == QUEST_STATUS_INCOMPLETE) + { + m_creature->SummonCreature(11077, 0.0f, 0.0f, 0.0f, 0.0f, TEMPSUMMON_TIMED_OOC_OR_DEAD_DESPAWN, 600000); + DoDie(); + } + break; + case 201: // gahrron + if (((Player*)who)->GetQuestStatus(5225) == QUEST_STATUS_INCOMPLETE || + ((Player*)who)->GetQuestStatus(5235) == QUEST_STATUS_INCOMPLETE) + { + m_creature->SummonCreature(11078, 0.0f, 0.0f, 0.0f, 0.0f, TEMPSUMMON_TIMED_OOC_OR_DEAD_DESPAWN, 600000); + DoDie(); + } + break; + case 202: // writhing + if (((Player*)who)->GetQuestStatus(5222) == QUEST_STATUS_INCOMPLETE || + ((Player*)who)->GetQuestStatus(5233) == QUEST_STATUS_INCOMPLETE) + { + m_creature->SummonCreature(11076, 0.0f, 0.0f, 0.0f, 0.0f, TEMPSUMMON_TIMED_OOC_OR_DEAD_DESPAWN, 600000); + DoDie(); + } + break; + } + } + } +}; + +CreatureAI* GetAI_npc_the_scourge_cauldron(Creature* pCreature) +{ + return new npc_the_scourge_cauldronAI(pCreature); +} + +/*###### +## npc_anchorite_truuen +######*/ + +enum +{ + SAY_BEGIN = -1000910, + SAY_FIRST_STOP = -1000911, + SAY_CONTINUE = -1000912, + SAY_FIRST_ATTACK = -1000913, + SAY_PURITY = -1000914, + SAY_SECOND_ATTACK = -1000915, + SAY_CLEANSE = -1000916, + SAY_WELCOME = -1000917, + SAY_EPILOGUE_1 = -1000918, + SAY_EPILOGUE_2 = -1000919, + + NPC_PRIEST_THELDANIS = 1854, + NPC_HUNGERING_WRAITH = 1802, + NPC_HAUNDING_VISION = 4472, + NPC_BLIGHTED_ZOMBIE = 4475, + NPC_GHOST_OF_UTHER = 17233, + + QUEST_ID_TOMB_LIGHTBRINGER = 9446, +}; + +struct npc_anchorite_truuenAI: public npc_escortAI +{ + npc_anchorite_truuenAI(Creature* pCreature): npc_escortAI(pCreature) { Reset(); } + + ObjectGuid m_utherGhostGuid; + + void Reset() override { } + + void ReceiveAIEvent(AIEventType eventType, Creature* /*pSender*/, Unit* pInvoker, uint32 uiMiscValue) override + { + if (eventType == AI_EVENT_START_ESCORT && pInvoker->GetTypeId() == TYPEID_PLAYER) + { + DoScriptText(SAY_BEGIN, m_creature); + Start(false, (Player*)pInvoker, GetQuestTemplateStore(uiMiscValue)); + } + } + + void WaypointReached(uint32 uiPointId) override + { + switch (uiPointId) + { + case 4: + DoScriptText(SAY_FIRST_STOP, m_creature); + break; + case 5: + DoScriptText(SAY_CONTINUE, m_creature); + break; + case 10: + DoScriptText(SAY_FIRST_ATTACK, m_creature); + // spawn first attacker wave + m_creature->SummonCreature(NPC_HAUNDING_VISION, 1045.26f, -1576.50f, 62.42f, 2.82f, TEMPSUMMON_TIMED_OOC_OR_DEAD_DESPAWN, 30000); + m_creature->SummonCreature(NPC_HUNGERING_WRAITH, 1021.74f, -1547.49f, 63.44f, 5.24f, TEMPSUMMON_TIMED_OOC_OR_DEAD_DESPAWN, 30000); + break; + case 11: + DoScriptText(SAY_PURITY, m_creature); + break; + case 21: + DoScriptText(SAY_SECOND_ATTACK, m_creature); + // spawn second attacker wave + m_creature->SummonCreature(NPC_BLIGHTED_ZOMBIE, 1123.08f, -1738.70f, 61.65f, 3.63f, TEMPSUMMON_TIMED_OOC_OR_DEAD_DESPAWN, 30000); + m_creature->SummonCreature(NPC_BLIGHTED_ZOMBIE, 1117.07f, -1763.47f, 62.72f, 1.83f, TEMPSUMMON_TIMED_OOC_OR_DEAD_DESPAWN, 30000); + m_creature->SummonCreature(NPC_BLIGHTED_ZOMBIE, 1096.79f, -1719.14f, 62.69f, 4.88f, TEMPSUMMON_TIMED_OOC_OR_DEAD_DESPAWN, 30000); + m_creature->SummonCreature(NPC_BLIGHTED_ZOMBIE, 1068.92f, -1739.68f, 62.23f, 6.21f, TEMPSUMMON_TIMED_OOC_OR_DEAD_DESPAWN, 30000); + break; + case 22: + DoScriptText(SAY_CLEANSE, m_creature); + break; + case 35: + if (Creature* pPriest = GetClosestCreatureWithEntry(m_creature, NPC_PRIEST_THELDANIS, 60.0f)) + DoScriptText(SAY_WELCOME, pPriest); + break; + case 38: + m_creature->SetStandState(UNIT_STAND_STATE_KNEEL); + m_creature->SummonCreature(NPC_GHOST_OF_UTHER, 972.96f, -1824.82f, 82.54f, 0.27f, TEMPSUMMON_TIMED_DESPAWN, 45000); + // complete the quest - the event continues with the dialogue + if (Player* pPlayer = GetPlayerForEscort()) + pPlayer->GroupEventHappens(QUEST_ID_TOMB_LIGHTBRINGER, m_creature); + break; + case 39: + if (Creature* pUther = m_creature->GetMap()->GetCreature(m_utherGhostGuid)) + { + pUther->SetFacingToObject(m_creature); + DoScriptText(SAY_EPILOGUE_1, pUther); + } + break; + case 40: + if (Creature* pUther = m_creature->GetMap()->GetCreature(m_utherGhostGuid)) + DoScriptText(SAY_EPILOGUE_2, pUther); + break; + case 41: + m_creature->SetStandState(UNIT_STAND_STATE_STAND); + break; + } + } + + void JustSummoned(Creature* pSummoned) override + { + if (pSummoned->GetEntry() != NPC_GHOST_OF_UTHER) + pSummoned->AI()->AttackStart(m_creature); + else + m_utherGhostGuid = pSummoned->GetObjectGuid(); + } +}; + +CreatureAI* GetAI_npc_anchorite_truuen(Creature* pCreature) +{ + return new npc_anchorite_truuenAI(pCreature); +} + +bool QuestAccept_npc_anchorite_truuen(Player* pPlayer, Creature* pCreature, const Quest* pQuest) +{ + if (pQuest->GetQuestId() == QUEST_ID_TOMB_LIGHTBRINGER) + pCreature->AI()->SendAIEvent(AI_EVENT_START_ESCORT, pPlayer, pCreature, pQuest->GetQuestId()); + + return true; +} + +void AddSC_western_plaguelands() +{ + Script* pNewScript; + + pNewScript = new Script; + pNewScript->Name = "npc_the_scourge_cauldron"; + pNewScript->GetAI = &GetAI_npc_the_scourge_cauldron; + pNewScript->RegisterSelf(); + + pNewScript = new Script; + pNewScript->Name = "npc_anchorite_truuen"; + pNewScript->GetAI = &GetAI_npc_anchorite_truuen; + pNewScript->pQuestAcceptNPC = &QuestAccept_npc_anchorite_truuen; + pNewScript->RegisterSelf(); +} diff --git a/src/modules/SD2/scripts/eastern_kingdoms/westfall.cpp b/src/modules/SD2/scripts/eastern_kingdoms/westfall.cpp new file mode 100644 index 000000000..541657dd6 --- /dev/null +++ b/src/modules/SD2/scripts/eastern_kingdoms/westfall.cpp @@ -0,0 +1,298 @@ +/** + * ScriptDev2 is an extension for mangos providing enhanced features for + * area triggers, creatures, game objects, instances, items, and spells beyond + * the default database scripting in mangos. + * + * Copyright (C) 2006-2013 ScriptDev2 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * World of Warcraft, and all World of Warcraft or Warcraft art, images, + * and lore are copyrighted by Blizzard Entertainment, Inc. + */ + +/** + * 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 +{ + SAY_DS_START = -1000293, + SAY_DS_DOWN_1 = -1000294, + SAY_DS_DOWN_2 = -1000295, + SAY_DS_DOWN_3 = -1000296, + SAY_DS_PROLOGUE = -1000297, + + SPELL_SHOOT = 6660, + QUEST_TOME_VALOR = 1651, + NPC_DEFIAS_RAIDER = 6180, + EQUIP_ID_RIFLE = 2511 +}; + +struct npc_daphne_stilwellAI : public npc_escortAI +{ + npc_daphne_stilwellAI(Creature* pCreature) : npc_escortAI(pCreature) + { + m_uiWPHolder = 0; + Reset(); + } + + uint32 m_uiWPHolder; + uint32 m_uiShootTimer; + + void Reset() override + { + if (HasEscortState(STATE_ESCORT_ESCORTING)) + { + switch (m_uiWPHolder) + { + case 7: + DoScriptText(SAY_DS_DOWN_1, m_creature); + break; + case 8: + DoScriptText(SAY_DS_DOWN_2, m_creature); + break; + case 9: + DoScriptText(SAY_DS_DOWN_3, m_creature); + break; + } + } + else + { m_uiWPHolder = 0; } + + m_uiShootTimer = 0; + } + + void WaypointReached(uint32 uiPointId) override + { + m_uiWPHolder = uiPointId; + + switch (uiPointId) + { + case 4: + SetEquipmentSlots(false, EQUIP_NO_CHANGE, EQUIP_NO_CHANGE, EQUIP_ID_RIFLE); + m_creature->SetSheath(SHEATH_STATE_RANGED); + m_creature->HandleEmote(EMOTE_STATE_USESTANDING_NOSHEATHE); + break; + case 7: + m_creature->SummonCreature(NPC_DEFIAS_RAIDER, -11450.836f, 1569.755f, 54.267f, 4.230f, TEMPSUMMON_TIMED_OOC_DESPAWN, 30000); + m_creature->SummonCreature(NPC_DEFIAS_RAIDER, -11449.697f, 1569.124f, 54.421f, 4.206f, TEMPSUMMON_TIMED_OOC_DESPAWN, 30000); + m_creature->SummonCreature(NPC_DEFIAS_RAIDER, -11448.237f, 1568.307f, 54.620f, 4.206f, TEMPSUMMON_TIMED_OOC_DESPAWN, 30000); + break; + case 8: + m_creature->SetSheath(SHEATH_STATE_RANGED); + m_creature->SummonCreature(NPC_DEFIAS_RAIDER, -11450.836f, 1569.755f, 54.267f, 4.230f, TEMPSUMMON_TIMED_OOC_DESPAWN, 30000); + m_creature->SummonCreature(NPC_DEFIAS_RAIDER, -11449.697f, 1569.124f, 54.421f, 4.206f, TEMPSUMMON_TIMED_OOC_DESPAWN, 30000); + m_creature->SummonCreature(NPC_DEFIAS_RAIDER, -11448.237f, 1568.307f, 54.620f, 4.206f, TEMPSUMMON_TIMED_OOC_DESPAWN, 30000); + m_creature->SummonCreature(NPC_DEFIAS_RAIDER, -11448.037f, 1570.213f, 54.961f, 4.283f, TEMPSUMMON_TIMED_OOC_DESPAWN, 30000); + break; + case 9: + m_creature->SetSheath(SHEATH_STATE_RANGED); + m_creature->SummonCreature(NPC_DEFIAS_RAIDER, -11450.836f, 1569.755f, 54.267f, 4.230f, TEMPSUMMON_TIMED_OOC_DESPAWN, 30000); + m_creature->SummonCreature(NPC_DEFIAS_RAIDER, -11449.697f, 1569.124f, 54.421f, 4.206f, TEMPSUMMON_TIMED_OOC_DESPAWN, 30000); + m_creature->SummonCreature(NPC_DEFIAS_RAIDER, -11448.237f, 1568.307f, 54.620f, 4.206f, TEMPSUMMON_TIMED_OOC_DESPAWN, 30000); + m_creature->SummonCreature(NPC_DEFIAS_RAIDER, -11448.037f, 1570.213f, 54.961f, 4.283f, TEMPSUMMON_TIMED_OOC_DESPAWN, 30000); + m_creature->SummonCreature(NPC_DEFIAS_RAIDER, -11449.018f, 1570.738f, 54.828f, 4.220f, TEMPSUMMON_TIMED_OOC_DESPAWN, 30000); + break; + case 10: + SetRun(false); + break; + case 11: + DoScriptText(SAY_DS_PROLOGUE, m_creature); + break; + case 13: + SetEquipmentSlots(true); + m_creature->SetSheath(SHEATH_STATE_UNARMED); + m_creature->HandleEmote(EMOTE_STATE_USESTANDING_NOSHEATHE); + break; + case 17: + if (Player* pPlayer = GetPlayerForEscort()) + { + pPlayer->GroupEventHappens(QUEST_TOME_VALOR, m_creature); + } + break; + } + } + + void AttackStart(Unit* pWho) override + { + if (!pWho) + { + return; + } + + if (m_creature->Attack(pWho, false)) + { + m_creature->AddThreat(pWho); + m_creature->SetInCombatWith(pWho); + pWho->SetInCombatWith(m_creature); + + m_creature->GetMotionMaster()->MoveChase(pWho, 30.0f); + } + } + + void JustSummoned(Creature* pSummoned) override + { + pSummoned->AI()->AttackStart(m_creature); + } + + void UpdateEscortAI(const uint32 uiDiff) override + { + if (!m_creature->SelectHostileTarget() || !m_creature->getVictim()) + { + return; + } + + if (m_uiShootTimer < uiDiff) + { + m_uiShootTimer = 1000; + + if (!m_creature->CanReachWithMeleeAttack(m_creature->getVictim())) + { + DoCastSpellIfCan(m_creature->getVictim(), SPELL_SHOOT); + } + } + else + { m_uiShootTimer -= uiDiff; } + + DoMeleeAttackIfReady(); + } +}; + +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_daphne_stilwellAI* pEscortAI = dynamic_cast(pCreature->AI())) + { + pEscortAI->Start(true, pPlayer, pQuest); + } + } + + return true; +} + +CreatureAI* GetAI_npc_daphne_stilwell(Creature* pCreature) +{ + return new npc_daphne_stilwellAI(pCreature); +} + +/*###### +## npc_defias_traitor +######*/ + +enum +{ + SAY_START = -1000101, + SAY_PROGRESS = -1000102, + SAY_END = -1000103, + SAY_AGGRO_1 = -1000104, + SAY_AGGRO_2 = -1000105, + + QUEST_DEFIAS_BROTHERHOOD = 155 +}; + +struct npc_defias_traitorAI : public npc_escortAI +{ + npc_defias_traitorAI(Creature* pCreature) : npc_escortAI(pCreature) { Reset(); } + + void WaypointReached(uint32 uiPointId) override + { + switch (uiPointId) + { + case 35: + SetRun(false); + break; + case 36: + if (Player* pPlayer = GetPlayerForEscort()) + { + DoScriptText(SAY_PROGRESS, m_creature, pPlayer); + } + break; + case 44: + if (Player* pPlayer = GetPlayerForEscort()) + { + DoScriptText(SAY_END, m_creature, pPlayer); + pPlayer->GroupEventHappens(QUEST_DEFIAS_BROTHERHOOD, m_creature); + } + break; + } + } + + void Aggro(Unit* pWho) override + { + DoScriptText(urand(0, 1) ? SAY_AGGRO_1 : SAY_AGGRO_2, m_creature, pWho); + } + + void Reset() override { } +}; + +bool QuestAccept_npc_defias_traitor(Player* pPlayer, Creature* pCreature, const Quest* pQuest) +{ + if (pQuest->GetQuestId() == QUEST_DEFIAS_BROTHERHOOD) + { + DoScriptText(SAY_START, pCreature, pPlayer); + + if (npc_defias_traitorAI* pEscortAI = dynamic_cast(pCreature->AI())) + { + pEscortAI->Start(true, pPlayer, pQuest); + } + } + + return true; +} + +CreatureAI* GetAI_npc_defias_traitor(Creature* pCreature) +{ + return new npc_defias_traitorAI(pCreature); +} + +void AddSC_westfall() +{ + Script* pNewScript; + + pNewScript = new Script; + pNewScript->Name = "npc_daphne_stilwell"; + pNewScript->GetAI = &GetAI_npc_daphne_stilwell; + pNewScript->pQuestAcceptNPC = &QuestAccept_npc_daphne_stilwell; + pNewScript->RegisterSelf(); + + pNewScript = new Script; + pNewScript->Name = "npc_defias_traitor"; + pNewScript->GetAI = &GetAI_npc_defias_traitor; + pNewScript->pQuestAcceptNPC = &QuestAccept_npc_defias_traitor; + pNewScript->RegisterSelf(); +} diff --git a/src/modules/SD2/scripts/eastern_kingdoms/wetlands.cpp b/src/modules/SD2/scripts/eastern_kingdoms/wetlands.cpp new file mode 100644 index 000000000..e534051d5 --- /dev/null +++ b/src/modules/SD2/scripts/eastern_kingdoms/wetlands.cpp @@ -0,0 +1,179 @@ +/** + * ScriptDev2 is an extension for mangos providing enhanced features for + * area triggers, creatures, game objects, instances, items, and spells beyond + * the default database scripting in mangos. + * + * Copyright (C) 2006-2013 ScriptDev2 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * World of Warcraft, and all World of Warcraft or Warcraft art, images, + * and lore are copyrighted by Blizzard Entertainment, Inc. + */ + +/* 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 +{ + QUEST_MISSING_DIPLO_PT11 = 1249, + FACTION_ENEMY = 168, // ToDo: faction needs to be confirmed! + + SPELL_STEALTH = 1785, + SPELL_CALL_FRIENDS = 16457, // summons 1x friend + NPC_SLIMS_FRIEND = 4971, + NPC_TAPOKE_SLIM_JAHN = 4962 +}; + +struct npc_tapoke_slim_jahnAI : public npc_escortAI +{ + npc_tapoke_slim_jahnAI(Creature* pCreature) : npc_escortAI(pCreature) { Reset(); } + + bool m_bFriendSummoned; + + void Reset() override + { + if (!HasEscortState(STATE_ESCORT_ESCORTING)) + m_bFriendSummoned = false; + } + + void WaypointReached(uint32 uiPointId) override + { + switch (uiPointId) + { + case 2: + if (m_creature->HasStealthAura()) + m_creature->RemoveSpellsCausingAura(SPELL_AURA_MOD_STEALTH); + + SetRun(); + m_creature->SetFactionTemporary(FACTION_ENEMY, TEMPFACTION_RESTORE_RESPAWN | TEMPFACTION_RESTORE_COMBAT_STOP); + break; + } + } + + void Aggro(Unit* /*pWho*/) override + { + Player* pPlayer = GetPlayerForEscort(); + + if (HasEscortState(STATE_ESCORT_ESCORTING) && !m_bFriendSummoned && pPlayer) + { + DoCastSpellIfCan(m_creature, SPELL_CALL_FRIENDS); + m_bFriendSummoned = true; + } + } + + void JustSummoned(Creature* pSummoned) override + { + if (Player* pPlayer = GetPlayerForEscort()) + pSummoned->AI()->AttackStart(pPlayer); + } + + void AttackedBy(Unit* pAttacker) override + { + if (m_creature->getVictim()) + return; + + if (m_creature->IsFriendlyTo(pAttacker)) + return; + + AttackStart(pAttacker); + } + + void DamageTaken(Unit* /*pDoneBy*/, uint32& uiDamage) override + { + if (m_creature->GetHealthPercent() < 20.0f) + { + if (Player* pPlayer = GetPlayerForEscort()) + { + pPlayer->GroupEventHappens(QUEST_MISSING_DIPLO_PT11, m_creature); + + uiDamage = 0; + + 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 = GetClosestCreatureWithEntry(pCreature, 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 = dynamic_cast(pSlim->AI())) + pEscortAI->Start(false, pPlayer, pQuest); + } + + return false; +} + +/*###### +## AddSC +######*/ + +void AddSC_wetlands() +{ + Script* pNewScript; + + pNewScript = new Script; + pNewScript->Name = "npc_tapoke_slim_jahn"; + pNewScript->GetAI = &GetAI_npc_tapoke_slim_jahn; + pNewScript->RegisterSelf(); + + pNewScript = new Script; + pNewScript->Name = "npc_mikhail"; + pNewScript->pQuestAcceptNPC = &QuestAccept_npc_mikhail; + pNewScript->RegisterSelf(); +} diff --git a/src/modules/SD2/scripts/eastern_kingdoms/zulaman/boss_akilzon.cpp b/src/modules/SD2/scripts/eastern_kingdoms/zulaman/boss_akilzon.cpp new file mode 100644 index 000000000..f62d1fc5a --- /dev/null +++ b/src/modules/SD2/scripts/eastern_kingdoms/zulaman/boss_akilzon.cpp @@ -0,0 +1,320 @@ +/* Copyright (C) 2006 - 2013 ScriptDev2 + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with 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: 80 +SDComment: Timers; Some details may need adjustments. +SDCategory: Zul'Aman +EndScriptData */ + +#include "precompiled.h" +#include "zulaman.h" + +enum +{ + SAY_EVENT1 = -1568024, + SAY_EVENT2 = -1568025, + SAY_AGGRO = -1568026, + SAY_SUMMON = -1568027, + SAY_SUMMON_ALT = -1568028, + SAY_ENRAGE = -1568029, + SAY_SLAY1 = -1568030, + SAY_SLAY2 = -1568031, + SAY_DEATH = -1568032, + EMOTE_STORM = -1568033, + + SPELL_STATIC_DISRUPTION = 43622, + SPELL_CALL_LIGHTNING = 43661, + SPELL_GUST_OF_WIND = 43621, + SPELL_ELECTRICAL_STORM = 43648, + SPELL_STORMCLOUD_VISUAL = 45213, + SPELL_BERSERK = 45078, + + // spell used by eagles + SPELL_EAGLE_SWOOP = 44732, + + NPC_SOARING_EAGLE = 24858, + MAX_EAGLE_COUNT = 6, +}; + +struct boss_akilzonAI : public ScriptedAI +{ + boss_akilzonAI(Creature* pCreature) : ScriptedAI(pCreature) + { + m_pInstance = (ScriptedInstance*)pCreature->GetInstanceData(); + Reset(); + } + + ScriptedInstance* m_pInstance; + + uint32 m_uiStaticDisruptTimer; + uint32 m_uiCallLightTimer; + uint32 m_uiGustOfWindTimer; + uint32 m_uiStormTimer; + uint32 m_uiSummonEagleTimer; + uint32 m_uiBerserkTimer; + + void Reset() override + { + m_uiStaticDisruptTimer = urand(7000, 14000); + m_uiCallLightTimer = urand(15000, 25000); + m_uiGustOfWindTimer = urand(20000, 30000); + m_uiStormTimer = 50000; + m_uiSummonEagleTimer = 65000; + m_uiBerserkTimer = 10 * MINUTE * IN_MILLISECONDS; + } + + void Aggro(Unit* /*pWho*/) override + { + DoScriptText(SAY_AGGRO, m_creature); + + if (m_pInstance) + m_pInstance->SetData(TYPE_AKILZON, IN_PROGRESS); + } + + void KilledUnit(Unit* /*pVictim*/) override + { + DoScriptText(urand(0, 1) ? SAY_SLAY1 : SAY_SLAY2, m_creature); + } + + void JustDied(Unit* /*pKiller*/) override + { + DoScriptText(SAY_DEATH, m_creature); + + if (!m_pInstance) + return; + + m_pInstance->SetData(TYPE_AKILZON, DONE); + } + + void JustReachedHome() override + { + if (m_pInstance) + m_pInstance->SetData(TYPE_AKILZON, FAIL); + } + + void JustSummoned(Creature* pSummoned) override + { + if (pSummoned->GetEntry() == NPC_SOARING_EAGLE) + { + pSummoned->SetLevitate(true); + pSummoned->SetInCombatWithZone(); + } + } + + void DoSummonEagles() + { + for (uint32 i = 0; i < MAX_EAGLE_COUNT; ++i) + { + float fX, fY, fZ; + m_creature->GetRandomPoint(m_creature->GetPositionX(), m_creature->GetPositionY(), m_creature->GetPositionZ() + 15.0f, 30.0f, fX, fY, fZ); + m_creature->SummonCreature(NPC_SOARING_EAGLE, fX, fY, fZ, m_creature->GetOrientation(), TEMPSUMMON_DEAD_DESPAWN, 0); + } + } + + void UpdateAI(const uint32 uiDiff) override + { + if (!m_creature->SelectHostileTarget() || !m_creature->getVictim()) + return; + + if (m_uiCallLightTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature->getVictim(), SPELL_CALL_LIGHTNING) == CAST_OK) + m_uiCallLightTimer = urand(15000, 25000); + } + else + m_uiCallLightTimer -= uiDiff; + + if (m_uiStaticDisruptTimer < uiDiff) + { + if (Unit* pTarget = m_creature->SelectAttackingTarget(ATTACKING_TARGET_RANDOM, 1)) + { + if (DoCastSpellIfCan(pTarget, SPELL_STATIC_DISRUPTION) == CAST_OK) + m_uiStaticDisruptTimer = urand(7000, 14000); + } + } + else + m_uiStaticDisruptTimer -= uiDiff; + + if (m_uiStormTimer < uiDiff) + { + if (Unit* pTarget = m_creature->SelectAttackingTarget(ATTACKING_TARGET_RANDOM, 0)) + { + if (DoCastSpellIfCan(pTarget, SPELL_ELECTRICAL_STORM) == CAST_OK) + { + DoScriptText(EMOTE_STORM, m_creature); + m_uiStormTimer = 55000; + } + } + } + else + m_uiStormTimer -= uiDiff; + + if (m_uiGustOfWindTimer < uiDiff) + { + if (Unit* pTarget = m_creature->SelectAttackingTarget(ATTACKING_TARGET_RANDOM, 1)) + { + if (DoCastSpellIfCan(pTarget, SPELL_GUST_OF_WIND) == CAST_OK) + m_uiGustOfWindTimer = urand(20000, 30000); + } + } + else + m_uiGustOfWindTimer -= uiDiff; + + if (m_uiSummonEagleTimer < uiDiff) + { + DoScriptText(urand(0, 1) ? SAY_SUMMON : SAY_SUMMON_ALT, m_creature); + DoSummonEagles(); + m_uiSummonEagleTimer = 60000; + } + else + m_uiSummonEagleTimer -= uiDiff; + + if (m_uiBerserkTimer) + { + if (m_uiBerserkTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature, SPELL_BERSERK) == CAST_OK) + { + DoScriptText(SAY_ENRAGE, m_creature); + m_uiBerserkTimer = 0; + } + } + else + m_uiBerserkTimer -= uiDiff; + } + + DoMeleeAttackIfReady(); + } +}; + +CreatureAI* GetAI_boss_akilzon(Creature* pCreature) +{ + return new boss_akilzonAI(pCreature); +} + +struct mob_soaring_eagleAI : public ScriptedAI +{ + mob_soaring_eagleAI(Creature* pCreature) : ScriptedAI(pCreature) + { + m_pInstance = (ScriptedInstance*)pCreature->GetInstanceData(); + Reset(); + } + + ScriptedInstance* m_pInstance; + + uint32 m_uiEagleSwoopTimer; + uint32 m_uiReturnTimer; + bool m_bCanMoveToRandom; + + void Reset() override + { + m_uiEagleSwoopTimer = 0; + m_uiReturnTimer = 800; + m_bCanMoveToRandom = false; + } + + void AttackStart(Unit* pWho) override + { + if (!pWho) + return; + + if (m_creature->Attack(pWho, false)) + { + m_creature->AddThreat(pWho); + m_creature->SetInCombatWith(pWho); + pWho->SetInCombatWith(m_creature); + } + } + + void MovementInform(uint32 uiType, uint32 uiPointId) override + { + if (uiType != POINT_MOTION_TYPE || !uiPointId) + return; + + m_uiEagleSwoopTimer = urand(2000, 6000); + } + + void DoMoveToRandom() + { + if (!m_pInstance) + return; + + if (Creature* pAzkil = m_pInstance->GetSingleCreatureFromStorage(NPC_AKILZON)) + { + float fX, fY, fZ; + pAzkil->GetRandomPoint(pAzkil->GetPositionX(), pAzkil->GetPositionY(), pAzkil->GetPositionZ() + 15.0f, 30.0f, fX, fY, fZ); + + m_creature->SetWalk(false); + m_creature->GetMotionMaster()->MovePoint(1, fX, fY, fZ); + } + } + + void UpdateAI(const uint32 uiDiff) override + { + if (!m_creature->SelectHostileTarget() || !m_creature->getVictim()) + return; + + if (m_uiReturnTimer) + { + if (m_uiReturnTimer <= uiDiff) + { + DoMoveToRandom(); + m_uiReturnTimer = 0; + } + else + m_uiReturnTimer -= uiDiff; + } + + if (m_uiEagleSwoopTimer) + { + if (m_uiEagleSwoopTimer <= uiDiff) + { + if (Unit* pTarget = m_creature->SelectAttackingTarget(ATTACKING_TARGET_RANDOM, 0)) + { + if (DoCastSpellIfCan(pTarget, SPELL_EAGLE_SWOOP) == CAST_OK) + { + m_uiEagleSwoopTimer = 0; + m_uiReturnTimer = 1000; + } + } + } + else + m_uiEagleSwoopTimer -= uiDiff; + } + } +}; + +CreatureAI* GetAI_mob_soaring_eagle(Creature* pCreature) +{ + return new mob_soaring_eagleAI(pCreature); +} + +void AddSC_boss_akilzon() +{ + Script* pNewScript; + + pNewScript = new Script; + pNewScript->Name = "boss_akilzon"; + pNewScript->GetAI = &GetAI_boss_akilzon; + pNewScript->RegisterSelf(); + + pNewScript = new Script; + pNewScript->Name = "mob_soaring_eagle"; + pNewScript->GetAI = &GetAI_mob_soaring_eagle; + pNewScript->RegisterSelf(); +} diff --git a/src/modules/SD2/scripts/eastern_kingdoms/zulaman/boss_halazzi.cpp b/src/modules/SD2/scripts/eastern_kingdoms/zulaman/boss_halazzi.cpp new file mode 100644 index 000000000..2103dcdc8 --- /dev/null +++ b/src/modules/SD2/scripts/eastern_kingdoms/zulaman/boss_halazzi.cpp @@ -0,0 +1,390 @@ +/* Copyright (C) 2006 - 2013 ScriptDev2 + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with 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: 90 +SDComment: A few details and timers need check. +SDCategory: Zul'Aman +EndScriptData */ + +#include "precompiled.h" +#include "zulaman.h" + +enum +{ + SAY_AGGRO = -1568034, + SAY_SPLIT = -1568035, + SAY_MERGE = -1568036, + SAY_SABERLASH1 = -1568037, + SAY_SABERLASH2 = -1568038, + SAY_BERSERK = -1568039, + SAY_KILL1 = -1568040, + SAY_KILL2 = -1568041, + SAY_DEATH = -1568042, + SAY_EVENT1 = -1568043, + SAY_EVENT2 = -1568044, + + // generic spells + SPELL_BERSERK = 45078, + SPELL_TRANSFORM_TO_ORIGINAL = 43311, + // SPELL_DUAL_WIELD = 42459, // spell not confirmed + // SPELL_TRANSFIGURE = 44054, // purpose unk + + // Phase single spells + SPELL_SABER_LASH = 43267, + SPELL_FRENZY = 43139, + + // Phase switch spells + SPELL_HALAZZI_TRANSFORM_SUMMON = 43143, // summons 24143 + SPELL_TRANSFIGURE_TO_TROLL = 43142, // triggers 43573 + SPELL_TRANSFIGURE_TRANSFORM = 43573, + + SPELL_TRANSFORM_TO_LYNX_75 = 43145, + SPELL_TRANSFORM_TO_LYNX_50 = 43271, + SPELL_TRANSFORM_TO_LYNX_25 = 43272, + SPELL_HALAZZI_TRANSFORM_DUMMY = 43615, + // SPELL_HALAZZI_TRANSFORM_VISUAL= 43293, + + // Phase spirits spells + SPELL_FLAMESHOCK = 43303, + SPELL_EARTHSHOCK = 43305, + SPELL_LIGHTNING_TOTEM = 43302, // summons 24224 + + NPC_HALAZZI_TROLL = 24144, // dummy creature - used to update stats + NPC_SPIRIT_LYNX = 24143, +}; + +enum HalazziPhase +{ + PHASE_SINGLE = 0, + PHASE_TOTEM = 1, + PHASE_FINAL = 2 +}; + +struct boss_halazziAI : public ScriptedAI +{ + boss_halazziAI(Creature* pCreature) : ScriptedAI(pCreature) + { + m_pInstance = (ScriptedInstance*)pCreature->GetInstanceData(); + Reset(); + } + + ScriptedInstance* m_pInstance; + + HalazziPhase m_uiPhase; + + uint32 m_uiPhaseCounter; + uint32 m_uiFrenzyTimer; + uint32 m_uiSaberLashTimer; + uint32 m_uiShockTimer; + uint32 m_uiTotemTimer; + uint32 m_uiBerserkTimer; + + bool m_bHasTransformed; + + ObjectGuid m_spiritLynxGuid; + + void Reset() override + { + m_uiPhase = PHASE_SINGLE; + m_uiPhaseCounter = 3; + + m_uiFrenzyTimer = 16000; + m_uiSaberLashTimer = 20000; + m_uiShockTimer = 10000; + m_uiTotemTimer = 12000; + m_uiBerserkTimer = 10 * MINUTE * IN_MILLISECONDS; + + m_bHasTransformed = false; + } + + void EnterEvadeMode() override + { + // Transform back on evade + if (DoCastSpellIfCan(m_creature, SPELL_TRANSFORM_TO_ORIGINAL) == CAST_OK) + m_creature->UpdateEntry(NPC_HALAZZI); + + ScriptedAI::EnterEvadeMode(); + } + + void JustReachedHome() override + { + if (m_pInstance) + m_pInstance->SetData(TYPE_HALAZZI, FAIL); + } + + void Aggro(Unit* /*pWho*/) override + { + DoScriptText(SAY_AGGRO, m_creature); + + if (m_pInstance) + m_pInstance->SetData(TYPE_HALAZZI, IN_PROGRESS); + } + + void KilledUnit(Unit* pVictim) override + { + if (pVictim->GetTypeId() != TYPEID_PLAYER) + return; + + DoScriptText(urand(0, 1) ? SAY_KILL1 : SAY_KILL2, m_creature); + } + + void JustDied(Unit* /*pKiller*/) override + { + DoScriptText(SAY_DEATH, m_creature); + + if (m_pInstance) + m_pInstance->SetData(TYPE_HALAZZI, DONE); + } + + void JustSummoned(Creature* pSummoned) override + { + if (pSummoned->GetEntry() == NPC_SPIRIT_LYNX) + { + m_spiritLynxGuid = pSummoned->GetObjectGuid(); + pSummoned->SetInCombatWithZone(); + pSummoned->CastSpell(m_creature, SPELL_HALAZZI_TRANSFORM_DUMMY, true); + } + } + + void SpellHit(Unit* /*pCaster*/, const SpellEntry* pSpell) override + { + if (pSpell->Id == SPELL_TRANSFIGURE_TRANSFORM) + { + DoCastSpellIfCan(m_creature, SPELL_HALAZZI_TRANSFORM_SUMMON, CAST_TRIGGERED); + m_creature->UpdateEntry(NPC_HALAZZI_TROLL); + + m_uiPhase = PHASE_TOTEM; + m_uiShockTimer = 10000; + m_uiTotemTimer = 12000; + } + } + + // Wrapper to handle the phase transform + void DoReuniteSpirits() + { + uint32 uiSpellId = 0; + + // Each health level has it's own spell - but they all do the same thing + switch (m_uiPhaseCounter) + { + case 3: uiSpellId = SPELL_TRANSFORM_TO_LYNX_75; break; + case 2: uiSpellId = SPELL_TRANSFORM_TO_LYNX_50; break; + case 1: uiSpellId = SPELL_TRANSFORM_TO_LYNX_25; break; + } + + if (DoCastSpellIfCan(m_creature, uiSpellId) == CAST_OK) + { + DoScriptText(SAY_MERGE, m_creature); + // Update stats back to the original Halazzi + m_creature->UpdateEntry(NPC_HALAZZI); + + // Despawn the Lynx + if (Creature* pLynx = m_creature->GetMap()->GetCreature(m_spiritLynxGuid)) + pLynx->ForcedDespawn(); + + // Set the proper health level - workaround for missing server side spell 43538 + m_creature->SetHealth(m_creature->GetMaxHealth() / 4 * m_uiPhaseCounter); + --m_uiPhaseCounter; + + m_uiPhase = m_uiPhaseCounter > 0 ? PHASE_SINGLE : PHASE_FINAL; + m_uiFrenzyTimer = 16000; + m_uiSaberLashTimer = 20000; + m_bHasTransformed = false; + } + } + + void UpdateAI(const uint32 uiDiff) override + { + if (!m_creature->SelectHostileTarget() || !m_creature->getVictim()) + return; + + if (m_uiBerserkTimer) + { + if (m_uiBerserkTimer <= uiDiff) + { + if (DoCastSpellIfCan(m_creature, SPELL_BERSERK) == CAST_OK) + { + DoScriptText(SAY_BERSERK, m_creature); + m_uiBerserkTimer = 0; + } + } + else + m_uiBerserkTimer -= uiDiff; + } + + // Abilities used only in the single or final phase + if (m_uiPhase == PHASE_SINGLE || m_uiPhase == PHASE_FINAL) + { + // Split boss at 75%, 50% and 25% + if (!m_bHasTransformed && m_creature->GetHealthPercent() <= float(25 * m_uiPhaseCounter)) + { + if (DoCastSpellIfCan(m_creature, SPELL_TRANSFIGURE_TO_TROLL) == CAST_OK) + { + DoScriptText(SAY_SPLIT, m_creature); + m_bHasTransformed = true; + } + } + + if (m_uiFrenzyTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature, SPELL_FRENZY) == CAST_OK) + m_uiFrenzyTimer = 16000; + } + else + m_uiFrenzyTimer -= uiDiff; + + if (m_uiSaberLashTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature->getVictim(), SPELL_SABER_LASH) == CAST_OK) + { + DoScriptText(urand(0, 1) ? SAY_SABERLASH1 : SAY_SABERLASH2, m_creature); + m_uiSaberLashTimer = 20000; + } + } + else + m_uiSaberLashTimer -= uiDiff; + } + + // Abilities used during the split phase or when the boss is below 25% health + if (m_uiPhase == PHASE_TOTEM || m_uiPhase == PHASE_FINAL) + { + if (m_uiTotemTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature, SPELL_LIGHTNING_TOTEM) == CAST_OK) + m_uiTotemTimer = 20000; + } + else + m_uiTotemTimer -= uiDiff; + + if (m_uiShockTimer < uiDiff) + { + if (Unit* pTarget = m_creature->SelectAttackingTarget(ATTACKING_TARGET_RANDOM, 0)) + { + if (DoCastSpellIfCan(pTarget, urand(0, 1) ? SPELL_EARTHSHOCK : SPELL_FLAMESHOCK) == CAST_OK) + m_uiShockTimer = urand(10000, 14000); + } + } + else + m_uiShockTimer -= uiDiff; + } + + // Transform back from Totem phase + if (m_uiPhase == PHASE_TOTEM && m_creature->GetHealthPercent() < 20.0f) + DoReuniteSpirits(); + + DoMeleeAttackIfReady(); + } +}; + +CreatureAI* GetAI_boss_halazzi(Creature* pCreature) +{ + return new boss_halazziAI(pCreature); +} + +enum +{ + SPELL_LYNX_FRENZY = 43290, + SPELL_SHRED_ARMOR = 43243 +}; + +struct boss_spirit_lynxAI : public ScriptedAI +{ + boss_spirit_lynxAI(Creature* pCreature) : ScriptedAI(pCreature) + { + m_pInstance = (ScriptedInstance*)pCreature->GetInstanceData(); + Reset(); + } + + ScriptedInstance* m_pInstance; + + uint32 m_uiFrenzyTimer; + uint32 m_uiShredArmorTimer; + bool m_bHasUnited; + + void Reset() override + { + m_uiFrenzyTimer = urand(10000, 20000); // first frenzy after 10-20 seconds + m_uiShredArmorTimer = 4000; + m_bHasUnited = false; + } + + void KilledUnit(Unit* pVictim) override + { + if (!m_pInstance) + return; + + if (Creature* pHalazzi = m_pInstance->GetSingleCreatureFromStorage(NPC_HALAZZI)) + pHalazzi->AI()->KilledUnit(pVictim); + } + + void UpdateAI(const uint32 uiDiff) override + { + if (!m_creature->SelectHostileTarget() || !m_creature->getVictim()) + return; + + if (m_uiFrenzyTimer < uiDiff) + { + DoCastSpellIfCan(m_creature, SPELL_LYNX_FRENZY); + m_uiFrenzyTimer = urand(20000, 30000); // subsequent frenzys casted every 20-30 seconds + } + else + m_uiFrenzyTimer -= uiDiff; + + if (m_uiShredArmorTimer < uiDiff) + { + DoCastSpellIfCan(m_creature->getVictim(), SPELL_SHRED_ARMOR); + m_uiShredArmorTimer = 4000; + } + else + m_uiShredArmorTimer -= uiDiff; + + // Unite spirits at 10% health + // Note: maybe there is some spell related to this - needs research + if (!m_bHasUnited && m_creature->GetHealthPercent() < 10.0f && m_pInstance) + { + if (Creature* pHalazzi = m_pInstance->GetSingleCreatureFromStorage(NPC_HALAZZI)) + { + if (boss_halazziAI* pBossAI = dynamic_cast(pHalazzi->AI())) + pBossAI->DoReuniteSpirits(); + } + m_bHasUnited = true; + } + + DoMeleeAttackIfReady(); + } +}; + +CreatureAI* GetAI_boss_spirit_lynx(Creature* pCreature) +{ + return new boss_spirit_lynxAI(pCreature); +} + +void AddSC_boss_halazzi() +{ + Script* pNewScript; + + pNewScript = new Script; + pNewScript->Name = "boss_halazzi"; + pNewScript->GetAI = &GetAI_boss_halazzi; + pNewScript->RegisterSelf(); + + pNewScript = new Script; + pNewScript->Name = "boss_spirit_lynx"; + pNewScript->GetAI = &GetAI_boss_spirit_lynx; + pNewScript->RegisterSelf(); +} diff --git a/src/modules/SD2/scripts/eastern_kingdoms/zulaman/boss_janalai.cpp b/src/modules/SD2/scripts/eastern_kingdoms/zulaman/boss_janalai.cpp new file mode 100644 index 000000000..1073f335a --- /dev/null +++ b/src/modules/SD2/scripts/eastern_kingdoms/zulaman/boss_janalai.cpp @@ -0,0 +1,543 @@ +/* Copyright (C) 2006 - 2013 ScriptDev2 + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with 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: 90 +SDComment: The hatchers may need some additional behavior adjustments. +SDCategory: Zul'Aman +EndScriptData */ + +#include "precompiled.h" +#include "zulaman.h" + +enum +{ + SAY_AGGRO = -1568000, + SAY_FIRE_BOMBS = -1568001, + SAY_SUMMON_HATCHER = -1568002, + SAY_ALL_EGGS = -1568003, + SAY_BERSERK = -1568004, + SAY_SLAY_1 = -1568005, + SAY_SLAY_2 = -1568006, + SAY_DEATH = -1568007, + SAY_EVENT_STRANGERS = -1568008, + SAY_EVENT_FRIENDS = -1568009, + + // Jan'alai + SPELL_FLAME_BREATH = 43140, + SPELL_HATCH_ALL_EGGS = 43144, // triggers 42493 + SPELL_TELEPORT_TO_CENTER = 43098, + SPELL_SUMMON_ALL_PLAYERS = 43096, // triggers 43097 + SPELL_ENRAGE = 44779, + SPELL_BERSERK = 47008, + SPELL_SUMMON_HATCHER_1 = 43962, + SPELL_SUMMON_HATCHER_2 = 45340, + + // Fire Bob Spells + SPELL_FIRE_BOMB_CHANNEL = 42621, + SPELL_FIRE_BOMB_THROW = 42628, // triggers 42629 + SPELL_FIRE_BOMB_EXPLODE = 42631, // triggers 42630 + + // NPCs + NPC_FIRE_BOMB = 23920, + NPC_AMANI_HATCHER_1 = 23818, + NPC_AMANI_HATCHER_2 = 24504, + NPC_HATCHLING = 23598, + NPC_DRAGONHAWK_EGG = 23817, + + // Hatcher Spells + SPELL_HATCH_EGG_1 = 43734, + SPELL_HATCH_EGG_2 = 42471, + + // Fire Wall + SPELL_FIRE_WALL = 43113, + + // Eggs spells + SPELL_SUMMON_DRAGONHAWK = 42493, + + MAX_EGGS_ON_SIDE = 20, // there are 20 eggs spawned on each side +}; + +static const float afFireWallCoords[4][4] = +{ + { -10.13f, 1149.27f, 19.0f, M_PI_F}, + { -33.93f, 1123.90f, 19.0f, 0.5f * M_PI_F}, + { -54.80f, 1150.08f, 19.0f, 0.0f}, + { -33.93f, 1175.68f, 19.0f, 1.5f * M_PI_F} +}; + +struct WaypointDef +{ + float m_fX, m_fY, m_fZ; +}; + +static const WaypointDef m_aHatcherRight[] = +{ + { -74.783f, 1145.827f, 5.420f}, + { -54.476f, 1146.934f, 18.705f}, + { -56.957f, 1146.713f, 18.725f}, + { -45.428f, 1141.697f, 18.709f}, + { -34.002f, 1124.427f, 18.711f}, + { -34.085f, 1106.158f, 18.711f} +}; + +static const WaypointDef m_aHatcherLeft[] = +{ + { -73.569f, 1154.960f, 5.510f}, + { -54.264f, 1153.968f, 18.705f}, + { -56.985f, 1153.373f, 18.608f}, + { -45.515f, 1158.356f, 18.709f}, + { -33.314f, 1174.816f, 18.709f}, + { -33.097f, 1195.359f, 18.709f} +}; + +struct boss_janalaiAI : public ScriptedAI +{ + boss_janalaiAI(Creature* pCreature) : ScriptedAI(pCreature) + { + m_pInstance = (ScriptedInstance*)pCreature->GetInstanceData(); + Reset(); + } + + ScriptedInstance* m_pInstance; + + uint32 m_uiFireBreathTimer; + uint32 m_uiEnrageTimer; + uint32 m_uiHatcherTimer; + uint32 m_uiBerserkTimer; + uint32 m_uiBombTimer; + uint32 m_uiBombAuraTimer; + uint32 m_uiExplodeTimer; + + uint8 m_uiEggsHatchedLeft; + uint8 m_uiEggsHatchedRight; + + bool m_bIsFlameWall; + bool m_bHasHatchedEggs; + bool m_bIsEnraged; + + ObjectGuid m_hatcherOneGuid; + ObjectGuid m_hatcherTwoGuid; + + void Reset() override + { + m_uiFireBreathTimer = 8000; + m_uiEnrageTimer = 5 * MINUTE * IN_MILLISECONDS; + m_uiHatcherTimer = 10000; + m_uiBerserkTimer = 10 * MINUTE * IN_MILLISECONDS; + m_uiBombTimer = 30000; + m_uiBombAuraTimer = 0; + m_uiExplodeTimer = 0; + + m_uiEggsHatchedLeft = 0; + m_uiEggsHatchedRight = 0; + + m_bHasHatchedEggs = false; + m_bIsEnraged = false; + m_bIsFlameWall = false; + } + + void JustReachedHome() override + { + if (m_pInstance) + m_pInstance->SetData(TYPE_JANALAI, FAIL); + } + + void JustDied(Unit* /*pKiller*/) override + { + DoScriptText(SAY_DEATH, m_creature); + + if (m_pInstance) + m_pInstance->SetData(TYPE_JANALAI, DONE); + } + + void KilledUnit(Unit* /*pVictim*/) override + { + DoScriptText(urand(0, 1) ? SAY_SLAY_1 : SAY_SLAY_2, m_creature); + } + + void Aggro(Unit* /*pWho*/) override + { + DoScriptText(SAY_AGGRO, m_creature); + + if (m_pInstance) + m_pInstance->SetData(TYPE_JANALAI, IN_PROGRESS); + } + + void JustSummoned(Creature* pSummoned) override + { + switch (pSummoned->GetEntry()) + { + case NPC_AMANI_HATCHER_1: + m_hatcherOneGuid = pSummoned->GetObjectGuid(); + // If all the eggs from one side are hatched, move to the other side + if (m_uiEggsHatchedRight == MAX_EGGS_ON_SIDE) + pSummoned->GetMotionMaster()->MovePoint(1, m_aHatcherLeft[0].m_fX, m_aHatcherLeft[0].m_fY, m_aHatcherLeft[0].m_fZ); + else + pSummoned->GetMotionMaster()->MovePoint(1, m_aHatcherRight[0].m_fX, m_aHatcherRight[0].m_fY, m_aHatcherRight[0].m_fZ); + break; + case NPC_AMANI_HATCHER_2: + m_hatcherTwoGuid = pSummoned->GetObjectGuid(); + // If all the eggs from one side are hatched, move to the other side + if (m_uiEggsHatchedLeft == MAX_EGGS_ON_SIDE) + pSummoned->GetMotionMaster()->MovePoint(1, m_aHatcherRight[0].m_fX, m_aHatcherRight[0].m_fY, m_aHatcherRight[0].m_fZ); + else + pSummoned->GetMotionMaster()->MovePoint(1, m_aHatcherLeft[0].m_fX, m_aHatcherLeft[0].m_fY, m_aHatcherLeft[0].m_fZ); + break; + case NPC_FIRE_BOMB: + if (!m_bIsFlameWall) + DoCastSpellIfCan(pSummoned, SPELL_FIRE_BOMB_THROW, CAST_TRIGGERED); + else + pSummoned->CastSpell(pSummoned, SPELL_FIRE_WALL, true); + break; + case NPC_HATCHLING: + pSummoned->SetInCombatWithZone(); + // Count the Hatched eggs + pSummoned->GetPositionY() > 1100.0f ? ++m_uiEggsHatchedLeft : ++m_uiEggsHatchedRight; + // Notify the script when all the eggs were hatched + if (m_uiEggsHatchedRight == MAX_EGGS_ON_SIDE && m_uiEggsHatchedLeft == MAX_EGGS_ON_SIDE) + m_bHasHatchedEggs = true; + // Change the side of the hatcher if necessary + if (m_uiEggsHatchedRight == MAX_EGGS_ON_SIDE && m_uiEggsHatchedLeft < MAX_EGGS_ON_SIDE) + { + if (Creature* pHatcer = m_creature->GetMap()->GetCreature(m_hatcherOneGuid)) + pHatcer->GetMotionMaster()->MovePoint(1, m_aHatcherLeft[5].m_fX, m_aHatcherLeft[5].m_fY, m_aHatcherLeft[5].m_fZ); + } + if (m_uiEggsHatchedLeft == MAX_EGGS_ON_SIDE && m_uiEggsHatchedRight < MAX_EGGS_ON_SIDE) + { + if (Creature* pHatcer = m_creature->GetMap()->GetCreature(m_hatcherTwoGuid)) + pHatcer->GetMotionMaster()->MovePoint(1, m_aHatcherRight[5].m_fX, m_aHatcherRight[5].m_fY, m_aHatcherRight[5].m_fZ); + } + break; + } + } + + // Wrapper to create the firewalls during Bomb phase + void DoCreateFireWall() + { + // This function involves a lot of guesswork!!! + // The npc entry isn't sure and the locations are guessed + m_bIsFlameWall = true; + m_creature->SummonCreature(NPC_FIRE_BOMB, afFireWallCoords[0][0], afFireWallCoords[0][1], afFireWallCoords[0][2], afFireWallCoords[0][3], TEMPSUMMON_TIMED_DESPAWN, 12000); + m_creature->SummonCreature(NPC_FIRE_BOMB, afFireWallCoords[1][0], afFireWallCoords[1][1], afFireWallCoords[1][2], afFireWallCoords[1][3], TEMPSUMMON_TIMED_DESPAWN, 12000); + m_creature->SummonCreature(NPC_FIRE_BOMB, afFireWallCoords[2][0], afFireWallCoords[2][1], afFireWallCoords[2][2], afFireWallCoords[2][3], TEMPSUMMON_TIMED_DESPAWN, 12000); + m_creature->SummonCreature(NPC_FIRE_BOMB, afFireWallCoords[3][0], afFireWallCoords[3][1], afFireWallCoords[3][2], afFireWallCoords[3][3], TEMPSUMMON_TIMED_DESPAWN, 12000); + m_bIsFlameWall = false; + } + + void UpdateAI(const uint32 uiDiff) override + { + if (!m_creature->SelectHostileTarget() || !m_creature->getVictim()) + return; + + // Start bombing + if (m_uiBombTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature, SPELL_FIRE_BOMB_CHANNEL) == CAST_OK) + { + DoCastSpellIfCan(m_creature, SPELL_TELEPORT_TO_CENTER, CAST_TRIGGERED); + DoCastSpellIfCan(m_creature, SPELL_SUMMON_ALL_PLAYERS, CAST_TRIGGERED); + DoScriptText(SAY_FIRE_BOMBS, m_creature); + DoCreateFireWall(); + + m_uiBombAuraTimer = 5000; + m_uiBombTimer = urand(20000, 40000); + } + } + else + m_uiBombTimer -= uiDiff; + + if (m_uiFireBreathTimer < uiDiff) + { + if (Unit* pTarget = m_creature->SelectAttackingTarget(ATTACKING_TARGET_RANDOM, 0)) + { + if (DoCastSpellIfCan(pTarget, SPELL_FLAME_BREATH) == CAST_OK) + m_uiFireBreathTimer = 8000; + } + } + else + m_uiFireBreathTimer -= uiDiff; + + // Remove bomb aura after five seconds + if (m_uiBombAuraTimer) + { + if (m_uiBombAuraTimer <= uiDiff) + { + m_creature->RemoveAurasDueToSpell(SPELL_FIRE_BOMB_CHANNEL); + m_uiBombAuraTimer = 0; + m_uiExplodeTimer = 5000; + } + else + m_uiBombAuraTimer -= uiDiff; + } + + // Explode the summoned bombs on timer + if (m_uiExplodeTimer) + { + if (m_uiExplodeTimer <= uiDiff) + { + if (DoCastSpellIfCan(m_creature, SPELL_FIRE_BOMB_EXPLODE) == CAST_OK) + m_uiExplodeTimer = 0; + } + else + m_uiExplodeTimer -= uiDiff; + } + + // Hatch all eggs at 35% health + if (!m_bHasHatchedEggs && m_creature->GetHealthPercent() < 35.0f) + { + if (DoCastSpellIfCan(m_creature, SPELL_HATCH_ALL_EGGS) == CAST_OK) + { + DoScriptText(SAY_ALL_EGGS, m_creature); + m_bHasHatchedEggs = true; + } + } + + // Soft Enrage - after 5 min, or at 20% health + if (!m_bIsEnraged) + { + if (m_uiEnrageTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature, SPELL_ENRAGE) == CAST_OK) + m_bIsEnraged = true; + } + else + m_uiEnrageTimer -= uiDiff; + + if (m_creature->GetHealthPercent() < 20.0f) + { + if (DoCastSpellIfCan(m_creature, SPELL_ENRAGE) == CAST_OK) + m_bIsEnraged = true; + } + } + + // Spawn Hatchers - if necessary + if (!m_bHasHatchedEggs) + { + if (m_uiHatcherTimer < uiDiff) + { + DoScriptText(SAY_SUMMON_HATCHER, m_creature); + + Creature* pHatcer1 = m_creature->GetMap()->GetCreature(m_hatcherOneGuid); + Creature* pHatcer2 = m_creature->GetMap()->GetCreature(m_hatcherTwoGuid); + + if (!pHatcer1 || !pHatcer1->IsAlive()) + DoCastSpellIfCan(m_creature, SPELL_SUMMON_HATCHER_1, CAST_TRIGGERED); + + if (!pHatcer2 || !pHatcer2->IsAlive()) + DoCastSpellIfCan(m_creature, SPELL_SUMMON_HATCHER_2, CAST_TRIGGERED); + + m_uiHatcherTimer = 90000; + } + else + m_uiHatcherTimer -= uiDiff; + } + + // Hard enrage + if (m_uiBerserkTimer) + { + if (m_uiBerserkTimer <= uiDiff) + { + if (DoCastSpellIfCan(m_creature, SPELL_BERSERK) == CAST_OK) + { + DoScriptText(SAY_BERSERK, m_creature); + m_uiBerserkTimer = 0; + } + } + else + m_uiBerserkTimer -= uiDiff; + } + + DoMeleeAttackIfReady(); + + // check for reset ... exploit preventing ... pulled from his podest + EnterEvadeIfOutOfCombatArea(uiDiff); + } +}; + +CreatureAI* GetAI_boss_janalaiAI(Creature* pCreature) +{ + return new boss_janalaiAI(pCreature); +} + +struct npc_amanishi_hatcherAI : public ScriptedAI +{ + npc_amanishi_hatcherAI(Creature* pCreature) : ScriptedAI(pCreature) + { + m_pInstance = (ScriptedInstance*)pCreature->GetInstanceData(); + Reset(); + } + + ScriptedInstance* m_pInstance; + + uint32 m_uiWaypoint; + uint32 m_uiHatchlingTimer; + uint8 m_uiHatchlingCount; + uint8 m_uiEggsHatched; + bool m_bWaypointEnd; + + void Reset() override + { + m_uiWaypoint = 0; + m_uiHatchlingTimer = 0; + m_uiHatchlingCount = 0; + m_uiEggsHatched = 0; + m_bWaypointEnd = false; + + m_creature->SetWalk(false); + } + + void MoveInLineOfSight(Unit* /*pWho*/) override { } + void AttackStart(Unit* /*pWho*/) override { } + + void MovementInform(uint32 uiType, uint32 uiPointId) override + { + if (uiType != POINT_MOTION_TYPE) + return; + + // Used when a hatcher is forced to switch sides + if (m_bWaypointEnd && uiPointId) + { + m_creature->GetMotionMaster()->Clear(); + m_uiHatchlingTimer = 1000; + return; + } + + uint32 uiCount = m_creature->GetEntry() == NPC_AMANI_HATCHER_1 ? countof(m_aHatcherRight) : countof(m_aHatcherLeft); + + m_uiWaypoint = uiPointId + 1; + + if (uiCount == m_uiWaypoint) + { + m_creature->GetMotionMaster()->Clear(); + m_uiHatchlingTimer = 1000; + m_bWaypointEnd = true; + } + else + { + if (m_creature->GetEntry() == NPC_AMANI_HATCHER_1) + m_creature->GetMotionMaster()->MovePoint(m_uiWaypoint, m_aHatcherRight[m_uiWaypoint].m_fX, m_aHatcherRight[m_uiWaypoint].m_fY, m_aHatcherRight[m_uiWaypoint].m_fZ); + else + m_creature->GetMotionMaster()->MovePoint(m_uiWaypoint, m_aHatcherLeft[m_uiWaypoint].m_fX, m_aHatcherLeft[m_uiWaypoint].m_fY, m_aHatcherLeft[m_uiWaypoint].m_fZ); + } + } + + void SpellHitTarget(Unit* pTarget, SpellEntry const* pSpell) override + { + if ((pSpell->Id != SPELL_HATCH_EGG_1 && pSpell->Id != SPELL_HATCH_EGG_2) || pTarget->GetEntry() != NPC_DRAGONHAWK_EGG) + return; + + // If we already hatched the number of eggs allowed per hatch phase, stop the hatching + if (m_uiEggsHatched >= m_uiHatchlingCount) + return; + + if (!m_pInstance) + return; + + if (Creature* pJanalai = m_pInstance->GetSingleCreatureFromStorage(NPC_JANALAI)) + { + pTarget->CastSpell(pTarget, SPELL_SUMMON_DRAGONHAWK, true, NULL, NULL, pJanalai->GetObjectGuid()); + ++m_uiEggsHatched; + } + } + + void UpdateAI(const uint32 uiDiff) override + { + if (!m_bWaypointEnd) + return; + + if (m_uiHatchlingTimer) + { + if (m_uiHatchlingTimer <= uiDiff) + { + // Note: there are 2 Hatch Eggs spells. Not sure which one to use + if (DoCastSpellIfCan(m_creature, SPELL_HATCH_EGG_2) == CAST_OK) + { + m_uiHatchlingTimer = m_uiHatchlingCount < 5 ? 10000 : 0; + m_uiEggsHatched = 0; + ++m_uiHatchlingCount; + } + } + else + m_uiHatchlingTimer -= uiDiff; + } + } +}; + +CreatureAI* GetAI_npc_amanishi_hatcherAI(Creature* pCreature) +{ + return new npc_amanishi_hatcherAI(pCreature); +} + +// TODO Remove this 'script' when combat can be proper prevented from core-side +struct npc_dragonhawk_eggAI : public Scripted_NoMovementAI +{ + npc_dragonhawk_eggAI(Creature* pCreature) : Scripted_NoMovementAI(pCreature) {Reset();} + + void Reset() override {} + + void AttackStart(Unit* /*pWho*/) override {} + void MoveInLineOfSight(Unit* /*pWho*/) override {} + void UpdateAI(const uint32 /*uiDiff*/) override {} +}; + +CreatureAI* GetAI_npc_dragonhawk_eggAI(Creature* pCreature) +{ + return new npc_dragonhawk_eggAI(pCreature); +} + +// TODO Remove this 'script' when combat can be proper prevented from core-side +struct npc_janalai_firebombAI : public Scripted_NoMovementAI +{ + npc_janalai_firebombAI(Creature* pCreature) : Scripted_NoMovementAI(pCreature) {Reset();} + + void Reset() override {} + + void AttackStart(Unit* /*pWho*/) override {} + void MoveInLineOfSight(Unit* /*pWho*/) override {} + void UpdateAI(const uint32 /*uiDiff*/) override {} +}; + +CreatureAI* GetAI_npc_janalai_firebombAI(Creature* pCreature) +{ + return new npc_janalai_firebombAI(pCreature); +} + +void AddSC_boss_janalai() +{ + Script* pNewScript; + + pNewScript = new Script; + pNewScript->Name = "boss_janalai"; + pNewScript->GetAI = &GetAI_boss_janalaiAI; + pNewScript->RegisterSelf(); + + pNewScript = new Script; + pNewScript->Name = "npc_dragonhawk_egg"; + pNewScript->GetAI = &GetAI_npc_dragonhawk_eggAI; + pNewScript->RegisterSelf(); + + pNewScript = new Script; + pNewScript->Name = "npc_janalai_firebomb"; + pNewScript->GetAI = &GetAI_npc_janalai_firebombAI; + pNewScript->RegisterSelf(); + + pNewScript = new Script; + pNewScript->Name = "npc_amanishi_hatcher"; + pNewScript->GetAI = &GetAI_npc_amanishi_hatcherAI; + pNewScript->RegisterSelf(); +} diff --git a/src/modules/SD2/scripts/eastern_kingdoms/zulaman/boss_malacrass.cpp b/src/modules/SD2/scripts/eastern_kingdoms/zulaman/boss_malacrass.cpp new file mode 100644 index 000000000..3f9aca89d --- /dev/null +++ b/src/modules/SD2/scripts/eastern_kingdoms/zulaman/boss_malacrass.cpp @@ -0,0 +1,452 @@ +/* Copyright (C) 2006 - 2013 ScriptDev2 + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +/* ScriptData +SDName: Boss_Malacrass +SD%Complete: 80 +SDComment: Contain adds and adds selection; Stolen abilities timers need improvement +SDCategory: Zul'Aman +EndScriptData */ + +#include "precompiled.h" +#include "zulaman.h" + +enum +{ + SAY_AGGRO = -1568045, + SAY_ENRAGE = -1568046, + SAY_KILL1 = -1568047, + SAY_KILL2 = -1568048, + SAY_SOUL_SIPHON = -1568049, + SAY_DRAIN_POWER = -1568050, + SAY_SPIRIT_BOLTS = -1568051, + SAY_ADD_DIED1 = -1568052, + SAY_ADD_DIED2 = -1568053, + SAY_ADD_DIED3 = -1568054, + SAY_DEATH = -1568055, + + /* Notes about the event: + * The boss casts siphon soul right after he finishes the spirit bolts channel, which takes 10 sec + * The siphon soul is a channeled spell for 30 sec during which the boss uses some class abilities of the target + * Basically the boss casts a dummy spell which chooses a random target on which it casts the actuall channel spell + * The drain power spell acts as a enrage timer. It's cast each 30 seconds after the boss' health is below 80% + */ + SPELL_SPIRIT_BOLTS = 43383, + SPELL_SIPHON_SOUL_DUMMY = 43498, + SPELL_SIPHON_SOUL = 43501, + SPELL_DRAIN_POWER = 44131, + + // for various powers he uses after using soul drain + // Death Knight + SPELL_DK_DEATH_AND_DECAY = 61603, + SPELL_DK_PLAGUE_STRIKE = 61600, + SPELL_DK_MARK_OF_BLOOD = 61606, + + // Druid + SPELL_DR_THORNS = 43420, + SPELL_DR_LIFEBLOOM = 43421, + SPELL_DR_MOONFIRE = 43545, + + // Hunter + SPELL_HU_EXPLOSIVE_TRAP = 43444, + SPELL_HU_FREEZING_TRAP = 43447, + SPELL_HU_SNAKE_TRAP = 43449, + + // Mage + SPELL_MG_FIREBALL = 41383, + SPELL_MG_FROST_NOVA = 43426, + SPELL_MG_ICE_LANCE = 43427, + SPELL_MG_FROSTBOLT = 43428, + + // Paladin + SPELL_PA_CONSECRATION = 43429, + SPELL_PA_AVENGING_WRATH = 43430, + SPELL_PA_HOLY_LIGHT = 43451, + + // Priest + SPELL_PR_HEAL = 41372, + SPELL_PR_MIND_BLAST = 41374, + SPELL_PR_SW_DEATH = 41375, + SPELL_PR_PSYCHIC_SCREAM = 43432, + SPELL_PR_MIND_CONTROL = 43550, + SPELL_PR_PAIN_SUPP = 44416, + + // Rogue + SPELL_RO_WOUND_POISON = 39665, + SPELL_RO_BLIND = 43433, + SPELL_RO_SLICE_DICE = 43547, + + // Shaman + SPELL_SH_CHAIN_LIGHT = 43435, + SPELL_SH_FIRE_NOVA = 43436, + SPELL_SH_HEALING_WAVE = 43548, + + // Warlock + SPELL_WL_CURSE_OF_DOOM = 43439, + SPELL_WL_RAIN_OF_FIRE = 43440, + SPELL_WL_UNSTABLE_AFFL = 35183, + + // Warrior + SPELL_WR_MORTAL_STRIKE = 43441, + SPELL_WR_WHIRLWIND = 43442, + SPELL_WR_SPELL_REFLECT = 43443, + + // misc + TARGET_TYPE_RANDOM = 0, + TARGET_TYPE_VICTIM = 1, + TARGET_TYPE_SELF = 2, + TARGET_TYPE_FRIENDLY = 3, + + MAX_ACTIVE_ADDS = 4 +}; + +// Adds positions +static const float m_aAddPositions[MAX_ACTIVE_ADDS][4] = +{ + {128.279f, 921.279f, 33.889f, 1.527f}, + {123.261f, 921.279f, 33.889f, 1.527f}, + {112.084f, 921.279f, 33.889f, 1.527f}, + {106.473f, 921.279f, 33.889f, 1.527f}, +}; + +// Each position is a random of two spawns +static const uint32 aSpawnEntries[MAX_ACTIVE_ADDS][2] = +{ + {NPC_ALYSON, NPC_THURG}, + {NPC_SLITHER, NPC_RADAAN}, + {NPC_GAZAKROTH, NPC_FENSTALKER}, + {NPC_DARKHEART, NPC_KORAGG}, +}; + +struct PlayerAbilityStruct +{ + uint32 m_uiSpellId; + uint8 m_uiTargetType; + uint32 m_uiInitialTimer, m_uiCooldown; +}; + +// Classes are in the same order as they are in DBC +static PlayerAbilityStruct m_aMalacrassStolenAbility[][4] = +{ + { + // 0* shadow priest - exception: it seems that the priest has two specs. We use this slot for the shadow priest + {SPELL_PR_MIND_CONTROL, TARGET_TYPE_RANDOM, 15000, 30000}, + {SPELL_PR_MIND_BLAST, TARGET_TYPE_RANDOM, 23000, 30000}, + {SPELL_PR_SW_DEATH, TARGET_TYPE_RANDOM, 5000, 16000} + }, + { + // 1 warrior + {SPELL_WR_SPELL_REFLECT, TARGET_TYPE_SELF, 2000, 30000}, + {SPELL_WR_WHIRLWIND, TARGET_TYPE_SELF, 10000, 30000}, + {SPELL_WR_MORTAL_STRIKE, TARGET_TYPE_VICTIM, 6000, 15000} + }, + { + // 2 paladin + {SPELL_PA_CONSECRATION, TARGET_TYPE_SELF, 10000, 30000}, + {SPELL_PA_HOLY_LIGHT, TARGET_TYPE_FRIENDLY, 17000, 30000}, + {SPELL_PA_AVENGING_WRATH, TARGET_TYPE_SELF, 0, 30000} + }, + { + // 3 hunter + {SPELL_HU_EXPLOSIVE_TRAP, TARGET_TYPE_SELF, 12000, 30000}, + {SPELL_HU_FREEZING_TRAP, TARGET_TYPE_SELF, 3000, 30000}, + {SPELL_HU_SNAKE_TRAP, TARGET_TYPE_SELF, 21000, 30000} + }, + { + // 4 rogue + {SPELL_RO_WOUND_POISON, TARGET_TYPE_VICTIM, 3000, 17000}, + {SPELL_RO_SLICE_DICE, TARGET_TYPE_SELF, 17000, 30000}, + {SPELL_RO_BLIND, TARGET_TYPE_RANDOM, 12000, 30000} + }, + { + // 5 priest + {SPELL_PR_PAIN_SUPP, TARGET_TYPE_FRIENDLY, 24000, 30000}, + {SPELL_PR_HEAL, TARGET_TYPE_FRIENDLY, 16000, 30000}, + {SPELL_PR_PSYCHIC_SCREAM, TARGET_TYPE_RANDOM, 8000, 30000} + }, + { + // 6 death knight + {SPELL_DK_DEATH_AND_DECAY, TARGET_TYPE_RANDOM, 25000, 30000}, + {SPELL_DK_PLAGUE_STRIKE, TARGET_TYPE_VICTIM, 5000, 17000}, + {SPELL_DK_MARK_OF_BLOOD, TARGET_TYPE_RANDOM, 14000, 30000} + }, + { + // 7 shaman + {SPELL_SH_FIRE_NOVA, TARGET_TYPE_SELF, 25000, 30000}, + {SPELL_SH_HEALING_WAVE, TARGET_TYPE_FRIENDLY, 15000, 30000}, + {SPELL_SH_CHAIN_LIGHT, TARGET_TYPE_RANDOM, 4000, 16000} + }, + { + // 8 mage + {SPELL_MG_FIREBALL, TARGET_TYPE_RANDOM, 8000, 30000}, + {SPELL_MG_FROSTBOLT, TARGET_TYPE_RANDOM, 25000, 30000}, + {SPELL_MG_ICE_LANCE, TARGET_TYPE_RANDOM, 2000, 18000}, + {SPELL_MG_FROST_NOVA, TARGET_TYPE_SELF, 17000, 30000} + }, + { + // 9 warlock + {SPELL_WL_CURSE_OF_DOOM, TARGET_TYPE_RANDOM, 0, 30000}, + {SPELL_WL_RAIN_OF_FIRE, TARGET_TYPE_RANDOM, 16000, 30000}, + {SPELL_WL_UNSTABLE_AFFL, TARGET_TYPE_RANDOM, 8000, 13000} + }, + { + // 10 unused - no class in DBC here + }, + { + // 11 druid + {SPELL_DR_LIFEBLOOM, TARGET_TYPE_FRIENDLY, 15000, 30000}, + {SPELL_DR_THORNS, TARGET_TYPE_SELF, 0, 30000}, + {SPELL_DR_MOONFIRE, TARGET_TYPE_RANDOM, 8000, 13000} + } +}; + +struct boss_malacrassAI : public ScriptedAI +{ + boss_malacrassAI(Creature* pCreature) : ScriptedAI(pCreature) + { + m_pInstance = (ScriptedInstance*)pCreature->GetInstanceData(); + Reset(); + } + + ScriptedInstance* m_pInstance; + + uint32 m_uiSpiritBoltsTimer; + uint32 m_uiDrainPowerTimer; + uint32 m_uiSiphonSoulTimer; + uint32 m_uiPlayerAbilityTimer; + uint8 m_uiPlayerClass; + + bool m_bCanUsePlayerSpell; + + std::vector m_vAddsEntryList; + std::vector m_vPlayerSpellTimer; + + void Reset() override + { + m_uiSpiritBoltsTimer = 30000; + m_uiDrainPowerTimer = 0; + m_uiSiphonSoulTimer = 40000; + m_uiPlayerAbilityTimer = 10000; + m_uiPlayerClass = 0; + + m_bCanUsePlayerSpell = false; + + DoInitializeAdds(); + } + + void JustReachedHome() override + { + if (m_pInstance) + m_pInstance->SetData(TYPE_MALACRASS, FAIL); + } + + void DoInitializeAdds() + { + // not if m_creature are dead, so avoid + if (!m_creature->IsAlive()) + return; + + // it's empty, so first time + if (m_vAddsEntryList.empty()) + { + m_vAddsEntryList.resize(MAX_ACTIVE_ADDS); + + for (uint8 i = 0; i < MAX_ACTIVE_ADDS; ++i) + { + uint8 uiAddVersion = urand(0, 1); + m_vAddsEntryList[i] = aSpawnEntries[i][uiAddVersion]; + m_creature->SummonCreature(aSpawnEntries[i][uiAddVersion], m_aAddPositions[i][0], m_aAddPositions[i][1], m_aAddPositions[i][2], m_aAddPositions[i][3], TEMPSUMMON_CORPSE_DESPAWN, 0); + } + } + // Resummon the killed adds + else + { + if (!m_pInstance) + return; + + for (uint8 i = 0; i < MAX_ACTIVE_ADDS; ++i) + { + // If we already have the creature on the map, then don't summon it + if (m_pInstance->GetSingleCreatureFromStorage(m_vAddsEntryList[i], true)) + continue; + + m_creature->SummonCreature(m_vAddsEntryList[i], m_aAddPositions[i][0], m_aAddPositions[i][1], m_aAddPositions[i][2], m_aAddPositions[i][3], TEMPSUMMON_CORPSE_DESPAWN, 0); + } + } + } + + void Aggro(Unit* /*pWho*/) override + { + DoScriptText(SAY_AGGRO, m_creature); + + if (m_pInstance) + m_pInstance->SetData(TYPE_MALACRASS, IN_PROGRESS); + } + + void KilledUnit(Unit* pVictim) override + { + if (pVictim->GetTypeId() != TYPEID_PLAYER) + return; + + DoScriptText(urand(0, 1) ? SAY_KILL1 : SAY_KILL2, m_creature); + } + + void JustDied(Unit* /*pKiller*/) override + { + DoScriptText(SAY_DEATH, m_creature); + + if (m_pInstance) + m_pInstance->SetData(TYPE_MALACRASS, DONE); + } + + void SummonedCreatureJustDied(Creature* /*pSummoned*/) override + { + switch (urand(0, 2)) + { + case 0: DoScriptText(SAY_ADD_DIED1, m_creature); break; + case 1: DoScriptText(SAY_ADD_DIED2, m_creature); break; + case 2: DoScriptText(SAY_ADD_DIED3, m_creature); break; + } + } + + void SpellHitTarget(Unit* pTarget, const SpellEntry* pSpell) override + { + // Set the player's class when hit with soul siphon + if (pTarget->GetTypeId() == TYPEID_PLAYER && pSpell->Id == SPELL_SIPHON_SOUL) + { + m_uiPlayerClass = ((Player*)pTarget)->getClass(); + m_bCanUsePlayerSpell = true; + + // In case the player it's priest we can choose either a holy priest or a shadow priest + if (m_uiPlayerClass == CLASS_PRIEST) + m_uiPlayerClass = urand(0, 1) ? CLASS_PRIEST : 0; + + // Init the spell timers + uint8 m_uiMaxSpells = m_uiPlayerClass == CLASS_MAGE ? 4 : 3; + + m_vPlayerSpellTimer.clear(); + m_vPlayerSpellTimer.reserve(m_uiMaxSpells); + for (uint8 i = 0; i < m_uiMaxSpells; ++i) + m_vPlayerSpellTimer.push_back(m_aMalacrassStolenAbility[m_uiPlayerClass][i].m_uiInitialTimer); + } + } + + bool CanUseSpecialAbility(uint32 uiSpellIndex) + { + Unit* pTarget = NULL; + + switch (m_aMalacrassStolenAbility[m_uiPlayerClass][uiSpellIndex].m_uiTargetType) + { + case TARGET_TYPE_SELF: + pTarget = m_creature; + break; + case TARGET_TYPE_VICTIM: + pTarget = m_creature->getVictim(); + break; + case TARGET_TYPE_RANDOM: + pTarget = m_creature->SelectAttackingTarget(ATTACKING_TARGET_RANDOM, 0); + break; + case TARGET_TYPE_FRIENDLY: + pTarget = DoSelectLowestHpFriendly(50.0f); + break; + } + + if (pTarget) + { + if (DoCastSpellIfCan(pTarget, m_aMalacrassStolenAbility[m_uiPlayerClass][uiSpellIndex].m_uiSpellId, CAST_TRIGGERED) == CAST_OK) + return true; + } + + return false; + } + + void UpdateAI(const uint32 uiDiff) override + { + if (!m_creature->SelectHostileTarget() || !m_creature->getVictim()) + return; + + // Acts as an enrage timer + if (m_creature->GetHealthPercent() < 80.0f) + { + if (m_uiDrainPowerTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature, SPELL_DRAIN_POWER) == CAST_OK) + { + DoScriptText(SAY_DRAIN_POWER, m_creature); + m_uiDrainPowerTimer = 30000; + } + } + else + m_uiDrainPowerTimer -= uiDiff; + } + + if (m_uiSpiritBoltsTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature, SPELL_SPIRIT_BOLTS) == CAST_OK) + { + DoScriptText(SAY_SPIRIT_BOLTS, m_creature); + m_bCanUsePlayerSpell = false; + m_uiSpiritBoltsTimer = 40000; + } + } + else + m_uiSpiritBoltsTimer -= uiDiff; + + if (m_uiSiphonSoulTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature, SPELL_SIPHON_SOUL_DUMMY) == CAST_OK) + { + DoScriptText(SAY_SOUL_SIPHON, m_creature); + m_uiSiphonSoulTimer = 40000; + } + } + else + m_uiSiphonSoulTimer -= uiDiff; + + // Use abilities only during the siphon soul phases + if (m_bCanUsePlayerSpell) + { + // Loop through all abilities + for (uint8 i = 0; i < m_vPlayerSpellTimer.size(); ++i) + { + if (m_vPlayerSpellTimer[i] < uiDiff) + { + if (CanUseSpecialAbility(i)) + m_vPlayerSpellTimer[i] = m_aMalacrassStolenAbility[m_uiPlayerClass][i].m_uiCooldown; + } + else + m_vPlayerSpellTimer[i] -= uiDiff; + } + } + + DoMeleeAttackIfReady(); + } +}; + +CreatureAI* GetAI_boss_malacrass(Creature* pCreature) +{ + return new boss_malacrassAI(pCreature); +} + +void AddSC_boss_malacrass() +{ + Script* pNewScript; + + pNewScript = new Script; + pNewScript->Name = "boss_malacrass"; + pNewScript->GetAI = &GetAI_boss_malacrass; + pNewScript->RegisterSelf(); +} diff --git a/src/modules/SD2/scripts/eastern_kingdoms/zulaman/boss_nalorakk.cpp b/src/modules/SD2/scripts/eastern_kingdoms/zulaman/boss_nalorakk.cpp new file mode 100644 index 000000000..365b4db0e --- /dev/null +++ b/src/modules/SD2/scripts/eastern_kingdoms/zulaman/boss_nalorakk.cpp @@ -0,0 +1,309 @@ +/* Copyright (C) 2006 - 2013 ScriptDev2 + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with 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: 95 +SDComment: Small adjustments may be required +SDCategory: Zul'Aman +EndScriptData */ + +#include "precompiled.h" +#include "zulaman.h" + +enum +{ + SAY_EVENT1_SACRIFICE = -1568014, + SAY_EVENT2_SACRIFICE = -1568015, + + SAY_AGGRO = -1568016, + SAY_SURGE = -1568017, + SAY_TOBEAR = -1568018, + SAY_TOTROLL = -1568019, + SAY_BERSERK = -1568020, + SAY_SLAY1 = -1568021, + SAY_SLAY2 = -1568022, + SAY_DEATH = -1568023, + + SPELL_BERSERK = 45078, // unsure, this increases damage, size and speed + + // Defines for Troll form + SPELL_BRUTAL_SWIPE = 42384, + SPELL_MANGLE = 42389, + SPELL_SURGE = 42402, + SPELL_BEAR_SHAPE = 42377, + + // Defines for Bear form + SPELL_LACERATING_SLASH = 42395, + SPELL_REND_FLESH = 42397, + SPELL_DEAFENING_ROAR = 42398 +}; + +struct boss_nalorakkAI : public ScriptedAI +{ + boss_nalorakkAI(Creature* pCreature) : ScriptedAI(pCreature) + { + m_pInstance = (instance_zulaman*)pCreature->GetInstanceData(); + m_uiCurrentWave = 0; + Reset(); + } + + instance_zulaman* m_pInstance; + + uint32 m_uiChangeFormTimer; + uint32 m_uiBrutalSwipeTimer; + uint32 m_uiMangleTimer; + uint32 m_uiSurgeTimer; + uint32 m_uiLaceratingSlashTimer; + uint32 m_uiRendFleshTimer; + uint32 m_uiDeafeningRoarTimer; + uint32 m_uiBerserkTimer; + uint8 m_uiCurrentWave; + bool m_bIsInBearForm; + + void Reset() override + { + m_uiChangeFormTimer = 45000; + m_uiBrutalSwipeTimer = 12000; + m_uiMangleTimer = 15000; + m_uiSurgeTimer = 20000; + m_uiLaceratingSlashTimer = 6000; + m_uiRendFleshTimer = 6000; + m_uiDeafeningRoarTimer = 20000; + m_uiBerserkTimer = 10 * MINUTE * IN_MILLISECONDS; + m_bIsInBearForm = false; + } + + void MoveInLineOfSight(Unit* pWho) override + { + ScriptedAI::MoveInLineOfSight(pWho); + + if (m_pInstance && m_pInstance->IsBearPhaseInProgress()) + return; + + if (pWho->GetTypeId() == TYPEID_PLAYER && !((Player*)pWho)->isGameMaster() && m_creature->IsWithinDistInMap(pWho, aBearEventInfo[m_uiCurrentWave].fAggroDist)) + { + DoScriptText(aBearEventInfo[m_uiCurrentWave].iYellId, m_creature); + if (m_pInstance) + m_pInstance->SendNextBearWave(pWho); + } + } + + void MovementInform(uint32 uiMotionType, uint32 uiPointId) override + { + if (uiMotionType != POINT_MOTION_TYPE) + return; + + if (uiPointId) + { + m_creature->SetFacingTo(aBearEventInfo[m_uiCurrentWave].fO); + + if (m_uiCurrentWave < MAX_BEAR_WAVES - 1) + { + if (m_pInstance) + m_pInstance->SetBearEventProgress(false); + ++m_uiCurrentWave; + } + else + { + // Set the instance data to fail on movement inform because we are not moving the boss to home position + if (m_pInstance) + m_pInstance->SetData(TYPE_NALORAKK, FAIL); + } + } + } + + // Nalorakk evades only after the trash waves are finished + void EnterEvadeMode() override + { + m_creature->RemoveAllAurasOnEvade(); + m_creature->DeleteThreatList(); + m_creature->CombatStop(true); + m_creature->LoadCreatureAddon(true); + + // Boss should evade on the top of the platform + if (m_creature->IsAlive()) + m_creature->GetMotionMaster()->MovePoint(1, aBearEventInfo[m_uiCurrentWave].fX, aBearEventInfo[m_uiCurrentWave].fY, aBearEventInfo[m_uiCurrentWave].fZ); + + m_creature->SetLootRecipient(NULL); + + Reset(); + } + + void Aggro(Unit* /*pWho*/) override + { + DoScriptText(SAY_AGGRO, m_creature); + + if (m_pInstance) + m_pInstance->SetData(TYPE_NALORAKK, IN_PROGRESS); + } + + void KilledUnit(Unit* /*pVictim*/) override + { + DoScriptText(urand(0, 1) ? SAY_SLAY1 : SAY_SLAY2, m_creature); + } + + void JustDied(Unit* /*pKiller*/) override + { + DoScriptText(SAY_DEATH, m_creature); + + if (!m_pInstance) + return; + + m_pInstance->SetData(TYPE_NALORAKK, DONE); + } + + void UpdateAI(const uint32 uiDiff) override + { + // Return since we have no target + if (!m_creature->SelectHostileTarget() || !m_creature->getVictim()) + return; + + // Berserking + if (m_uiBerserkTimer) + { + if (m_uiBerserkTimer <= uiDiff) + { + if (DoCastSpellIfCan(m_creature, SPELL_BERSERK) == CAST_OK) + { + DoScriptText(SAY_BERSERK, m_creature); + m_uiBerserkTimer = 0; + } + } + else + m_uiBerserkTimer -= uiDiff; + } + + // Spells for Troll Form (only to be casted if we NOT have bear phase aura) + if (!m_bIsInBearForm) + { + // Brutal Swipe (some sources may say otherwise, but I've never seen this in Bear form) + if (m_uiBrutalSwipeTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature->getVictim(), SPELL_BRUTAL_SWIPE) == CAST_OK) + m_uiBrutalSwipeTimer = urand(7000, 15000); + } + else + m_uiBrutalSwipeTimer -= uiDiff; + + // Mangle + if (m_uiMangleTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature->getVictim(), SPELL_MANGLE) == CAST_OK) + m_uiMangleTimer = urand(3000, 15000); + } + else + m_uiMangleTimer -= uiDiff; + + // Surge + if (m_uiSurgeTimer < uiDiff) + { + // select a random unit other than the main tank + Unit* pTtarget = m_creature->SelectAttackingTarget(ATTACKING_TARGET_RANDOM, 1); + + // if there aren't other units, cast on the tank + if (!pTtarget) + pTtarget = m_creature->getVictim(); + + if (DoCastSpellIfCan(pTtarget, SPELL_SURGE) == CAST_OK) + { + DoScriptText(SAY_SURGE, m_creature); + m_uiSurgeTimer = urand(15000, 32500); + } + } + else + m_uiSurgeTimer -= uiDiff; + + // Change to Bear Form if we're in Troll Form for 45sec + if (m_uiChangeFormTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature, SPELL_BEAR_SHAPE, CAST_INTERRUPT_PREVIOUS) == CAST_OK) + { + DoScriptText(SAY_TOBEAR, m_creature); + m_uiChangeFormTimer = 30000; + m_bIsInBearForm = true; + // Reset bear form timers + m_uiLaceratingSlashTimer = urand(6000, 25000); + m_uiRendFleshTimer = urand(6000, 25000); + m_uiDeafeningRoarTimer = urand(15000, 25000); + } + } + else + m_uiChangeFormTimer -= uiDiff; + } + // Spells for Bear Form (only to be casted if we have bear phase aura) + else + { + // Timer to yell and reset spell timers when bear aura expires + if (m_uiChangeFormTimer < uiDiff) + { + DoScriptText(SAY_TOTROLL, m_creature); + m_uiChangeFormTimer = 45000; + m_bIsInBearForm = false; + // Reset troll form timers + m_uiSurgeTimer = urand(15000, 32000); + m_uiBrutalSwipeTimer = urand(7000, 20000); + m_uiMangleTimer = urand(3000, 20000); + } + else + m_uiChangeFormTimer -= uiDiff; + + // Lacerating Slash + if (m_uiLaceratingSlashTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature->getVictim(), SPELL_LACERATING_SLASH) == CAST_OK) + m_uiLaceratingSlashTimer = urand(6000, 20000); + } + else + m_uiLaceratingSlashTimer -= uiDiff; + + // Rend Flesh + if (m_uiRendFleshTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature->getVictim(), SPELL_REND_FLESH) == CAST_OK) + m_uiRendFleshTimer = urand(6000, 20000); + } + else + m_uiRendFleshTimer -= uiDiff; + + // Deafening Roar + if (m_uiDeafeningRoarTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature, SPELL_DEAFENING_ROAR) == CAST_OK) + m_uiDeafeningRoarTimer = urand(15000, 25000); + } + else + m_uiDeafeningRoarTimer -= uiDiff; + } + + DoMeleeAttackIfReady(); + } +}; + +CreatureAI* GetAI_boss_nalorakk(Creature* pCreature) +{ + return new boss_nalorakkAI(pCreature); +} + +void AddSC_boss_nalorakk() +{ + Script* pNewScript; + + pNewScript = new Script; + pNewScript->Name = "boss_nalorakk"; + pNewScript->GetAI = &GetAI_boss_nalorakk; + pNewScript->RegisterSelf(); +} diff --git a/src/modules/SD2/scripts/eastern_kingdoms/zulaman/boss_zuljin.cpp b/src/modules/SD2/scripts/eastern_kingdoms/zulaman/boss_zuljin.cpp new file mode 100644 index 000000000..896651ccb --- /dev/null +++ b/src/modules/SD2/scripts/eastern_kingdoms/zulaman/boss_zuljin.cpp @@ -0,0 +1,533 @@ +/* Copyright (C) 2006 - 2013 ScriptDev2 + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with 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: 90 +SDComment: Timers should be improved. +SDCategory: Zul'Aman +EndScriptData */ + +#include "precompiled.h" +#include "zulaman.h" + +enum +{ + SAY_INTRO = -1568056, + SAY_AGGRO = -1568057, + SAY_BEAR_TRANSFORM = -1568058, + SAY_EAGLE_TRANSFORM = -1568059, + SAY_LYNX_TRANSFORM = -1568060, + SAY_DRAGONHAWK_TRANSFORM = -1568061, + SAY_FIRE_BREATH = -1568062, + SAY_BERSERK = -1568053, + SAY_KILL1 = -1568064, + SAY_KILL2 = -1568065, + SAY_DEATH = -1568066, + + EMOTE_BEAR_SPIRIT = -1568082, + EMOTE_EAGLE_SPIRIT = -1568083, + EMOTE_LYNX_SPIRIT = -1568084, + EMOTE_DRAGONHAWK_SPIRIT = -1568085, + + // Troll Form + SPELL_WHIRLWIND = 17207, + SPELL_GRIEVOUS_THROW = 43093, // removes debuff after full healed + + // Bear Form + SPELL_CREEPING_PARALYSIS = 43095, // should cast on the whole raid + SPELL_OVERPOWER = 43456, // use after melee attack dodged + + // Eagle Form + SPELL_ENERGY_STORM = 43983, // enemy area aura, trigger 42577 on vortexes which cast 43137 on targets + SPELL_SUMMON_CYCLONE = 43112, // summon four feather vortex + NPC_FEATHER_VORTEX = 24136, // ToDo: script via ACID + SPELL_CYCLONE_VISUAL = 43119, // trigger 43147 visual + SPELL_CYCLONE_PASSIVE = 43120, // trigger 43121 (4y aoe) every second + SPELL_CYCLONE = 43121, + + // Lynx Form + SPELL_CLAW_RAGE = 42583, // Charges a random target and applies dummy effect 43149 on it + SPELL_CLAW_RAGE_TRIGGER = 43149, + SPELL_LYNX_RUSH = 43152, // Charges 9 targets in a row - Dummy effect should apply 43153 + SPELL_LYNX_RUSH_CHARGE = 43153, + + // Dragonhawk Form + SPELL_FLAME_WHIRL = 43213, // trigger two spells + SPELL_FLAME_BREATH = 43215, + SPELL_SUMMON_PILLAR = 43216, // summon 24187 + NPC_COLUMN_OF_FIRE = 24187, + SPELL_PILLAR_TRIGGER = 43218, // trigger 43217 + + // Cosmetic + SPELL_SPIRIT_DRAINED = 42520, + SPELL_SPIRIT_DRAIN = 42542, + + // Transforms + SPELL_SHAPE_OF_THE_BEAR = 42594, + SPELL_SHAPE_OF_THE_EAGLE = 42606, + SPELL_SHAPE_OF_THE_LYNX = 42607, + SPELL_SHAPE_OF_THE_DRAGONHAWK = 42608, + + SPELL_BERSERK = 45078, // Berserk timer or existance is unk + + MAX_VORTEXES = 4, + MAX_LYNX_RUSH = 10, + POINT_ID_CENTER = 0, + + PHASE_BEAR = 0, + PHASE_EAGLE = 1, + PHASE_LYNX = 2, + PHASE_DRAGONHAWK = 3, + PHASE_TROLL = 4, +}; + +struct BossPhase +{ + uint32 uiSpiritSpellId; + int32 iYellId, iEmoteId; + uint32 uiSpiritId; + uint8 uiPhase; +}; + +static const BossPhase aZuljinPhases[] = +{ + {SPELL_SHAPE_OF_THE_BEAR, SAY_BEAR_TRANSFORM, EMOTE_BEAR_SPIRIT, NPC_BEAR_SPIRIT, PHASE_BEAR}, + {SPELL_SHAPE_OF_THE_EAGLE, SAY_EAGLE_TRANSFORM, EMOTE_EAGLE_SPIRIT, NPC_EAGLE_SPIRIT, PHASE_EAGLE}, + {SPELL_SHAPE_OF_THE_LYNX, SAY_LYNX_TRANSFORM, EMOTE_LYNX_SPIRIT, NPC_LYNX_SPIRIT, PHASE_LYNX}, + {SPELL_SHAPE_OF_THE_DRAGONHAWK, SAY_DRAGONHAWK_TRANSFORM, EMOTE_DRAGONHAWK_SPIRIT, NPC_DRAGONHAWK_SPIRIT, PHASE_DRAGONHAWK} +}; + +// coords for going for changing form +static const float fZuljinMoveLoc[3] = {120.148811f, 703.713684f, 45.111477f}; + +/*###### +## boss_zuljin +######*/ + +struct boss_zuljinAI : public ScriptedAI +{ + boss_zuljinAI(Creature* pCreature) : ScriptedAI(pCreature) + { + m_pInstance = (ScriptedInstance*)pCreature->GetInstanceData(); + m_bHasTaunted = false; + Reset(); + } + + ScriptedInstance* m_pInstance; + + uint8 m_uiPhase; + uint8 m_uiHealthCheck; + + uint32 m_uiWhirlwindTimer; + uint32 m_uiGrievousThrowTimer; + + uint32 m_uiParalysisTimer; + uint32 m_uiOverpowerTimer; + + uint32 m_uiClawRageTimer; + uint32 m_uiLynxRushTimer; + uint8 m_uiLynxRushCount; + + uint32 m_uiFlameWhirlTimer; + uint32 m_uiFlameBreathTimer; + uint32 m_uiPillarOfFireTimer; + + bool m_bHasTaunted; + bool m_bIsInTransition; + uint32 m_uiTransformTimer; + + GuidList m_lSummonsList; + + void Reset() override + { + m_uiHealthCheck = 80; + m_uiPhase = PHASE_TROLL; + + m_uiWhirlwindTimer = 7000; + m_uiGrievousThrowTimer = 8000; + + m_uiParalysisTimer = 7000; + m_uiOverpowerTimer = 5000; + + m_uiClawRageTimer = 5000; + m_uiLynxRushTimer = 15000; + m_uiLynxRushCount = 0; + + m_uiFlameWhirlTimer = 7000; + m_uiFlameBreathTimer = 15000; + m_uiPillarOfFireTimer = 7000; + + m_bIsInTransition = false; + + SetCombatMovement(true); + } + + void Aggro(Unit* /*pWho*/) override + { + DoScriptText(SAY_AGGRO, m_creature); + + if (m_pInstance) + m_pInstance->SetData(TYPE_ZULJIN, IN_PROGRESS); + } + + void JustReachedHome() override + { + if (m_pInstance) + m_pInstance->SetData(TYPE_ZULJIN, FAIL); + + // Despawn all feather vortexes + DoDespawnVortexes(); + + // Reset all spirits + for (uint8 i = 0; i < MAX_VORTEXES; ++i) + { + if (Creature* pSpirit = m_pInstance->GetSingleCreatureFromStorage(aZuljinPhases[i].uiSpiritId)) + { + pSpirit->SetStandState(UNIT_STAND_STATE_STAND); + pSpirit->AI()->EnterEvadeMode(); + } + } + } + + void KilledUnit(Unit* /*pVictim*/) override + { + DoScriptText(urand(0, 1) ? SAY_KILL1 : SAY_KILL2, m_creature); + } + + void JustDied(Unit* /*pKiller*/) override + { + DoScriptText(SAY_DEATH, m_creature); + + if (!m_pInstance) + return; + + m_pInstance->SetData(TYPE_ZULJIN, DONE); + } + + void MoveInLineOfSight(Unit* pWho) override + { + if (!m_bHasTaunted && pWho->GetTypeId() == TYPEID_PLAYER && m_creature->IsWithinDistInMap(pWho, 60.0f)) + { + DoScriptText(SAY_INTRO, m_creature); + m_bHasTaunted = true; + } + + ScriptedAI::MoveInLineOfSight(pWho); + } + + // Function to handle the Feather Vortexes despawn on phase change + void DoDespawnVortexes() + { + for (GuidList::const_iterator itr = m_lSummonsList.begin(); itr != m_lSummonsList.end(); ++itr) + { + if (Creature* pVortex = m_creature->GetMap()->GetCreature(*itr)) + pVortex->ForcedDespawn(); + } + } + + void JustSummoned(Creature* pSummoned) override + { + switch (pSummoned->GetEntry()) + { + case NPC_FEATHER_VORTEX: + pSummoned->CastSpell(pSummoned, SPELL_CYCLONE_VISUAL, true); + pSummoned->CastSpell(pSummoned, SPELL_CYCLONE_PASSIVE, true); + m_lSummonsList.push_back(pSummoned->GetObjectGuid()); + + // Attack random target + if (Unit* pTarget = m_creature->SelectAttackingTarget(ATTACKING_TARGET_RANDOM, 0)) + pSummoned->GetMotionMaster()->MoveFollow(pTarget, 0, 0); + break; + case NPC_COLUMN_OF_FIRE: + pSummoned->CastSpell(pSummoned, SPELL_PILLAR_TRIGGER, true); + break; + } + } + + void MovementInform(uint32 uiMotionType, uint32 uiPointId) override + { + if (uiMotionType != POINT_MOTION_TYPE || uiPointId != POINT_ID_CENTER) + return; + + // increment phase + if (m_uiPhase == PHASE_TROLL) + m_uiPhase = PHASE_BEAR; + else + ++m_uiPhase; + + // drain the spirit + if (Creature* pSpirit = m_pInstance->GetSingleCreatureFromStorage(aZuljinPhases[m_uiPhase].uiSpiritId)) + pSpirit->CastSpell(m_creature, SPELL_SPIRIT_DRAIN, false); + } + + void SpellHit(Unit* /*pCaster*/, const SpellEntry* pSpell) override + { + if (pSpell->Id == SPELL_SPIRIT_DRAIN) + { + DoCastSpellIfCan(m_creature, aZuljinPhases[m_uiPhase].uiSpiritSpellId, CAST_INTERRUPT_PREVIOUS); + DoScriptText(aZuljinPhases[m_uiPhase].iYellId, m_creature); + DoScriptText(aZuljinPhases[m_uiPhase].iEmoteId, m_creature); + + // in eagle phase we don't move + if (m_uiPhase != PHASE_EAGLE) + { + SetCombatMovement(true); + if (m_creature->getVictim()) + { + m_creature->GetMotionMaster()->Clear(); + m_creature->GetMotionMaster()->MoveChase(m_creature->getVictim()); + } + } + // In Eagle phase we just cast Energy storm and summon 4 Feather cyclones; Boss doesn't move in this phase + else + { + DoCastSpellIfCan(m_creature, SPELL_ENERGY_STORM, CAST_TRIGGERED); + + // summon 4 vortexes + DoCastSpellIfCan(m_creature, SPELL_SUMMON_CYCLONE, CAST_TRIGGERED); + } + + m_bIsInTransition = false; + } + } + + void SpellHitTarget(Unit* pTarget, SpellEntry const* pSpellEntry) override + { + if (pSpellEntry->Id == SPELL_CLAW_RAGE && pTarget->GetTypeId() == TYPEID_PLAYER) + { + DoCastSpellIfCan(m_creature, SPELL_CLAW_RAGE_TRIGGER, CAST_TRIGGERED); + m_uiLynxRushTimer += 8000; + } + } + + void UpdateAI(const uint32 uiDiff) override + { + if (!m_creature->SelectHostileTarget() || !m_creature->getVictim() || m_bIsInTransition) + return; + + if (m_creature->GetHealthPercent() < m_uiHealthCheck) + { + m_uiHealthCheck -= 20; + m_bIsInTransition = true; + + SetCombatMovement(false); + m_creature->GetMotionMaster()->MovePoint(POINT_ID_CENTER, fZuljinMoveLoc[0], fZuljinMoveLoc[1], fZuljinMoveLoc[2]); + + // Despawn vortexes and remvoe the energy storm after eagle phase is complete + if (m_uiPhase == PHASE_EAGLE) + { + m_creature->RemoveAurasDueToSpell(SPELL_ENERGY_STORM); + DoDespawnVortexes(); + } + + // Reset threat + DoResetThreat(); + + // don't do this after troll phase + if (m_uiPhase != PHASE_TROLL) + { + if (m_creature->HasAura(aZuljinPhases[m_uiPhase].uiSpiritSpellId)) + m_creature->RemoveAurasDueToSpell(aZuljinPhases[m_uiPhase].uiSpiritSpellId); + + // drain spirit + if (Creature* pSpirit = m_pInstance->GetSingleCreatureFromStorage(aZuljinPhases[m_uiPhase].uiSpiritId)) + { + pSpirit->InterruptNonMeleeSpells(false); + pSpirit->CastSpell(m_creature, SPELL_SPIRIT_DRAINED, false); + pSpirit->SetStandState(UNIT_STAND_STATE_DEAD); + } + } + } + + switch (m_uiPhase) + { + case PHASE_TROLL: + + if (m_uiWhirlwindTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature, SPELL_WHIRLWIND) == CAST_OK) + m_uiWhirlwindTimer = urand(15000, 20000); + } + else + m_uiWhirlwindTimer -= uiDiff; + + if (m_uiGrievousThrowTimer < uiDiff) + { + if (Unit* pTarget = m_creature->SelectAttackingTarget(ATTACKING_TARGET_RANDOM, 0)) + { + if (DoCastSpellIfCan(pTarget, SPELL_GRIEVOUS_THROW) == CAST_OK) + m_uiGrievousThrowTimer = 10000; + } + } + else + m_uiGrievousThrowTimer -= uiDiff; + + break; + case PHASE_BEAR: + + if (m_uiParalysisTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature, SPELL_CREEPING_PARALYSIS) == CAST_OK) + m_uiParalysisTimer = 27000; + } + else + m_uiParalysisTimer -= uiDiff; + + if (m_uiOverpowerTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature->getVictim(), SPELL_OVERPOWER) == CAST_OK) + m_uiOverpowerTimer = urand(12000, 16000); + } + else + m_uiOverpowerTimer -= uiDiff; + + break; + case PHASE_EAGLE: + // Nothing here; Spells casted just once at the beginning of the phase; + break; + case PHASE_LYNX: + + // Don't apply Claw Rage during Lynx Rush + if (!m_uiLynxRushCount) + { + if (m_uiClawRageTimer < uiDiff) + { + if (Unit* pTarget = m_creature->SelectAttackingTarget(ATTACKING_TARGET_RANDOM, 1, SPELL_CLAW_RAGE, SELECT_FLAG_PLAYER)) + { + if (DoCastSpellIfCan(pTarget, SPELL_CLAW_RAGE) == CAST_OK) + m_uiClawRageTimer = urand(15000, 20000); + } + } + else + m_uiClawRageTimer -= uiDiff; + } + + if (m_uiLynxRushTimer < uiDiff) + { + if (!m_uiLynxRushCount) + DoCastSpellIfCan(m_creature, SPELL_LYNX_RUSH); + else + { + if (Unit* pTarget = m_creature->SelectAttackingTarget(ATTACKING_TARGET_RANDOM, 0)) + DoCastSpellIfCan(pTarget, SPELL_LYNX_RUSH_CHARGE); + } + + ++m_uiLynxRushCount; + + if (m_uiLynxRushCount == MAX_LYNX_RUSH) + { + m_uiLynxRushTimer = urand(20000, 25000); + m_uiLynxRushCount = 0; + } + else + m_uiLynxRushTimer = 400; + } + else + m_uiLynxRushTimer -= uiDiff; + + break; + case PHASE_DRAGONHAWK: + + if (m_uiFlameWhirlTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature, SPELL_FLAME_WHIRL) == CAST_OK) + m_uiFlameWhirlTimer = 15000; + } + else + m_uiFlameWhirlTimer -= uiDiff; + + if (m_uiPillarOfFireTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature, SPELL_SUMMON_PILLAR) == CAST_OK) + m_uiPillarOfFireTimer = urand(17000, 22000); + } + else + m_uiPillarOfFireTimer -= uiDiff; + + if (m_uiFlameBreathTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature, SPELL_FLAME_BREATH) == CAST_OK) + m_uiFlameBreathTimer = 15000; + } + else + m_uiFlameBreathTimer -= uiDiff; + + break; + } + + DoMeleeAttackIfReady(); + } +}; + +CreatureAI* GetAI_boss_zuljin(Creature* pCreature) +{ + return new boss_zuljinAI(pCreature); +} + +/*###### +## npc_feather_vortex +######*/ + +struct npc_feather_vortexAI : public ScriptedAI +{ + npc_feather_vortexAI(Creature* pCreature) : ScriptedAI(pCreature) + { + m_pInstance = (ScriptedInstance*)pCreature->GetInstanceData(); + Reset(); + } + + ScriptedInstance* m_pInstance; + + void Reset() override { } + + void SpellHitTarget(Unit* pTarget, SpellEntry const* pSpellEntry) override + { + if (pSpellEntry->Id == SPELL_CYCLONE && pTarget->GetTypeId() == TYPEID_PLAYER && m_pInstance) + { + if (Creature* pZuljin = m_pInstance->GetSingleCreatureFromStorage(NPC_ZULJIN)) + { + // Change target on player hit + if (Unit* pTarget = pZuljin->SelectAttackingTarget(ATTACKING_TARGET_RANDOM, 0)) + m_creature->GetMotionMaster()->MoveFollow(pTarget, 0, 0); + } + } + } + + void AttackStart(Unit* /*pWho*/) override { } + void MoveInLineOfSight(Unit* /*pWho*/) override { } + void UpdateAI(const uint32 /*uiDiff*/) override { } +}; + +CreatureAI* GetAI_npc_feather_vortex(Creature* pCreature) +{ + return new npc_feather_vortexAI(pCreature); +} + +void AddSC_boss_zuljin() +{ + Script* pNewScript; + + pNewScript = new Script; + pNewScript->Name = "boss_zuljin"; + pNewScript->GetAI = &GetAI_boss_zuljin; + pNewScript->RegisterSelf(); + + pNewScript = new Script; + pNewScript->Name = "npc_feather_vortex"; + pNewScript->GetAI = &GetAI_npc_feather_vortex; + pNewScript->RegisterSelf(); +} diff --git a/src/modules/SD2/scripts/eastern_kingdoms/zulaman/instance_zulaman.cpp b/src/modules/SD2/scripts/eastern_kingdoms/zulaman/instance_zulaman.cpp new file mode 100644 index 000000000..3a631df19 --- /dev/null +++ b/src/modules/SD2/scripts/eastern_kingdoms/zulaman/instance_zulaman.cpp @@ -0,0 +1,537 @@ +/* Copyright (C) 2006 - 2013 ScriptDev2 + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along 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: 50 +SDComment: Support for Quests and Mini-Events still TODO +SDCategory: Zul'Aman +EndScriptData */ + +#include "precompiled.h" +#include "zulaman.h" + +instance_zulaman::instance_zulaman(Map* pMap) : ScriptedInstance(pMap), + m_uiEventTimer(MINUTE* IN_MILLISECONDS), + m_uiGongCount(0), + m_uiBearEventPhase(0), + m_bIsBearPhaseInProgress(false) +{ + Initialize(); +} + +void instance_zulaman::Initialize() +{ + memset(&m_auiEncounter, 0, sizeof(m_auiEncounter)); + memset(&m_auiRandVendor, 0, sizeof(m_auiRandVendor)); +} + +bool instance_zulaman::IsEncounterInProgress() const +{ + // Skip Time-Event and Time-Event timer + for (uint8 i = 1; i < MAX_ENCOUNTER - 1; ++i) + { + if (m_auiEncounter[i] == IN_PROGRESS) + return true; + } + + return false; +} + +void instance_zulaman::OnPlayerEnter(Player* /*pPlayer*/) +{ + if (GetData(TYPE_EVENT_RUN) == IN_PROGRESS) + { + DoUpdateWorldState(WORLD_STATE_ID, 1); + DoUpdateWorldState(WORLD_STATE_COUNTER, GetData(TYPE_RUN_EVENT_TIME)); + } +} + +void instance_zulaman::OnCreatureCreate(Creature* pCreature) +{ + switch (pCreature->GetEntry()) + { + case NPC_AKILZON: + case NPC_HALAZZI: + case NPC_NALORAKK: + case NPC_JANALAI: + case NPC_MALACRASS: + case NPC_ZULJIN: + case NPC_HARRISON: + case NPC_BEAR_SPIRIT: + case NPC_EAGLE_SPIRIT: + case NPC_LYNX_SPIRIT: + case NPC_DRAGONHAWK_SPIRIT: + // Insert Malacrass companions here for better handling + case NPC_ALYSON: + case NPC_THURG: + case NPC_SLITHER: + case NPC_RADAAN: + case NPC_GAZAKROTH: + case NPC_FENSTALKER: + case NPC_DARKHEART: + case NPC_KORAGG: + m_mNpcEntryGuidStore[pCreature->GetEntry()] = pCreature->GetObjectGuid(); + break; + + case NPC_TANZAR: m_aEventNpcInfo[INDEX_NALORAKK].npGuid = pCreature->GetObjectGuid(); break; + case NPC_KRAZ: m_aEventNpcInfo[INDEX_JANALAI].npGuid = pCreature->GetObjectGuid(); break; + case NPC_ASHLI: m_aEventNpcInfo[INDEX_HALAZZI].npGuid = pCreature->GetObjectGuid(); break; + case NPC_HARKOR: m_aEventNpcInfo[INDEX_AKILZON].npGuid = pCreature->GetObjectGuid(); break; + + case NPC_MEDICINE_MAN: + case NPC_TRIBES_MAN: + case NPC_WARBRINGER: + case NPC_AXETHROWER: + if (pCreature->GetPositionZ() > 10.0f && pCreature->GetPositionZ() < 15.0f) + m_aNalorakkEvent[0].sBearTrashGuidSet.insert(pCreature->GetObjectGuid()); + else if (pCreature->GetPositionZ() > 25.0f && pCreature->GetPositionZ() < 30.0f) + m_aNalorakkEvent[1].sBearTrashGuidSet.insert(pCreature->GetObjectGuid()); + else if (pCreature->GetPositionZ() > 40.0f && pCreature->GetPositionZ() < 41.0f) + m_aNalorakkEvent[2].sBearTrashGuidSet.insert(pCreature->GetObjectGuid()); + else if (pCreature->GetPositionZ() > 41.0f) + m_aNalorakkEvent[3].sBearTrashGuidSet.insert(pCreature->GetObjectGuid()); + break; + } +} + +void instance_zulaman::OnCreatureDeath(Creature* pCreature) +{ + switch (pCreature->GetEntry()) + { + case NPC_MEDICINE_MAN: + case NPC_TRIBES_MAN: + case NPC_WARBRINGER: + case NPC_AXETHROWER: + if (m_aNalorakkEvent[m_uiBearEventPhase].sBearTrashGuidSet.find(pCreature->GetObjectGuid()) != m_aNalorakkEvent[m_uiBearEventPhase].sBearTrashGuidSet.end()) + { + ++m_aNalorakkEvent[m_uiBearEventPhase].uiTrashKilled; + if (m_aNalorakkEvent[m_uiBearEventPhase].uiTrashKilled == m_aNalorakkEvent[m_uiBearEventPhase].sBearTrashGuidSet.size()) + { + if (Creature* pNalorakk = GetSingleCreatureFromStorage(NPC_NALORAKK)) + { + ++m_uiBearEventPhase; + if (m_uiBearEventPhase == MAX_BEAR_WAVES) + pNalorakk->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_PASSIVE); + else + { + pNalorakk->SetWalk(false); + pNalorakk->GetMotionMaster()->MovePoint(1, aBearEventInfo[m_uiBearEventPhase].fX, aBearEventInfo[m_uiBearEventPhase].fY, aBearEventInfo[m_uiBearEventPhase].fZ); + } + } + } + } + break; + } +} + +void instance_zulaman::OnCreatureEvade(Creature* pCreature) +{ + switch (pCreature->GetEntry()) + { + case NPC_MEDICINE_MAN: + case NPC_TRIBES_MAN: + case NPC_WARBRINGER: + case NPC_AXETHROWER: + for (GuidSet::const_iterator itr = m_aNalorakkEvent[m_uiBearEventPhase].sBearTrashGuidSet.begin(); itr != m_aNalorakkEvent[m_uiBearEventPhase].sBearTrashGuidSet.end(); ++itr) + { + Creature* pTemp = instance->GetCreature(*itr); + if (pTemp && !pTemp->IsAlive()) + pTemp->Respawn(); + } + m_aNalorakkEvent[m_uiBearEventPhase].uiTrashKilled = 0; + m_bIsBearPhaseInProgress = false; + break; + } +} + +void instance_zulaman::OnObjectCreate(GameObject* pGo) +{ + switch (pGo->GetEntry()) + { + case GO_STRANGE_GONG: + break; + case GO_MASSIVE_GATE: + // The gate needs to be opened even if the event is still in progress + if (m_auiEncounter[TYPE_EVENT_RUN] == DONE || m_auiEncounter[TYPE_EVENT_RUN] == FAIL || m_auiEncounter[TYPE_EVENT_RUN] == IN_PROGRESS) + pGo->SetGoState(GO_STATE_ACTIVE); + break; + case GO_WIND_DOOR: + break; + case GO_LYNX_TEMPLE_ENTRANCE: + break; + case GO_LYNX_TEMPLE_EXIT: + if (m_auiEncounter[TYPE_HALAZZI] == DONE) + pGo->SetGoState(GO_STATE_ACTIVE); + break; + case GO_HEXLORD_ENTRANCE: + if (GetKilledPreBosses() == 4) + pGo->SetGoState(GO_STATE_ACTIVE); + break; + case GO_WOODEN_DOOR: + if (m_auiEncounter[TYPE_MALACRASS] == DONE) + pGo->SetGoState(GO_STATE_ACTIVE); + break; + case GO_FIRE_DOOR: + break; + + default: + return; + } + m_mGoEntryGuidStore[pGo->GetEntry()] = pGo->GetObjectGuid(); +} + +void instance_zulaman::SetData(uint32 uiType, uint32 uiData) +{ + debug_log("SD2: Instance Zulaman: SetData received for type %u with data %u", uiType, uiData); + + switch (uiType) + { + case TYPE_EVENT_RUN: + if (uiData == SPECIAL) + { + ++m_uiGongCount; + if (m_uiGongCount == 5) + m_auiEncounter[TYPE_EVENT_RUN] = uiData; + return; + } + if (uiData == IN_PROGRESS) + { + DoTimeRunSay(RUN_START); + DoUseDoorOrButton(GO_MASSIVE_GATE); + if (m_auiEncounter[TYPE_RUN_EVENT_TIME]) + SetData(TYPE_RUN_EVENT_TIME, m_auiEncounter[TYPE_RUN_EVENT_TIME]); + else + SetData(TYPE_RUN_EVENT_TIME, 20); // 20 Minutes as default time + DoUpdateWorldState(WORLD_STATE_ID, 1); + } + if (uiData == FAIL) + { + DoTimeRunSay(RUN_FAIL); + DoUpdateWorldState(WORLD_STATE_ID, 0); + // Kill remaining Event NPCs + for (uint8 i = 0; i < MAX_CHESTS; ++i) + { + // Not yet rescued, so too late + if (!m_aEventNpcInfo[i].uiSavePosition) + { + if (Creature* pCreature = instance->GetCreature(m_aEventNpcInfo[i].npGuid)) + pCreature->ForcedDespawn(); + } + } + } + if (uiData == DONE) + { + DoTimeRunSay(RUN_DONE); + DoUpdateWorldState(WORLD_STATE_ID, 0); + } + m_auiEncounter[uiType] = uiData; + break; + case TYPE_AKILZON: + DoUseDoorOrButton(GO_WIND_DOOR); + if (uiData == DONE) + { + if (m_auiEncounter[TYPE_EVENT_RUN] == IN_PROGRESS) + { + m_auiEncounter[TYPE_RUN_EVENT_TIME] += 10; // Add 10 minutes + SetData(TYPE_RUN_EVENT_TIME, m_auiEncounter[TYPE_RUN_EVENT_TIME]); + DoChestEvent(INDEX_AKILZON); + } + } + m_auiEncounter[uiType] = uiData; + break; + case TYPE_NALORAKK: + if (uiData == DONE) + { + if (m_auiEncounter[TYPE_EVENT_RUN] == IN_PROGRESS) + { + m_auiEncounter[TYPE_RUN_EVENT_TIME] += 15; // Add 15 minutes + SetData(TYPE_RUN_EVENT_TIME, m_auiEncounter[TYPE_RUN_EVENT_TIME]); + DoChestEvent(INDEX_NALORAKK); + } + } + m_auiEncounter[uiType] = uiData; + break; + case TYPE_JANALAI: + if (uiData == DONE) + { + if (m_auiEncounter[TYPE_EVENT_RUN] == IN_PROGRESS) + DoChestEvent(INDEX_JANALAI); + } + m_auiEncounter[uiType] = uiData; + break; + case TYPE_HALAZZI: + DoUseDoorOrButton(GO_LYNX_TEMPLE_ENTRANCE); + if (uiData == DONE) + { + DoUseDoorOrButton(GO_LYNX_TEMPLE_EXIT); + if (m_auiEncounter[TYPE_EVENT_RUN] == IN_PROGRESS) + DoChestEvent(INDEX_HALAZZI); + } + m_auiEncounter[uiType] = uiData; + break; + case TYPE_MALACRASS: + DoUseDoorOrButton(GO_HEXLORD_ENTRANCE); + if (uiData == DONE) + DoUseDoorOrButton(GO_WOODEN_DOOR); + m_auiEncounter[uiType] = uiData; + break; + case TYPE_ZULJIN: + DoUseDoorOrButton(GO_FIRE_DOOR); + m_auiEncounter[uiType] = uiData; + break; + case TYPE_RUN_EVENT_TIME: + m_auiEncounter[uiType] = uiData; + DoUpdateWorldState(WORLD_STATE_COUNTER, m_auiEncounter[uiType]); + break; + + case TYPE_RAND_VENDOR_1: + m_auiRandVendor[0] = uiData; + break; + case TYPE_RAND_VENDOR_2: + m_auiRandVendor[1] = uiData; + break; + + default: + script_error_log("Instance Zulaman: ERROR SetData = %u for type %u does not exist/not implemented.", uiType, uiData); + return; + } + + if (uiData == DONE && GetKilledPreBosses() == 4 && (uiType == TYPE_AKILZON || uiType == TYPE_NALORAKK || uiType == TYPE_JANALAI || uiType == TYPE_HALAZZI)) + { + DoUseDoorOrButton(GO_HEXLORD_ENTRANCE); + if (m_auiEncounter[TYPE_EVENT_RUN] == IN_PROGRESS) + SetData(TYPE_EVENT_RUN, DONE); + } + + if (uiData == DONE || uiType == TYPE_RUN_EVENT_TIME || uiType == TYPE_EVENT_RUN) + { + 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_strInstData = saveStream.str(); + + SaveToDB(); + OUT_SAVE_INST_DATA_COMPLETE; + } +} + +void instance_zulaman::Load(const char* chrIn) +{ + if (!chrIn) + { + 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]; + + // Skip m_auiEncounter[7], to start the time event properly if needed + for (uint8 i = 0; i < MAX_ENCOUNTER - 1; ++i) + { + if (m_auiEncounter[i] == IN_PROGRESS) + m_auiEncounter[i] = NOT_STARTED; + } + + // Restart TYPE_EVENT_RUN if was already started + if (m_auiEncounter[TYPE_RUN_EVENT_TIME] != 0 && m_auiEncounter[TYPE_EVENT_RUN] != DONE && m_auiEncounter[TYPE_EVENT_RUN] != FAIL) + SetData(TYPE_EVENT_RUN, IN_PROGRESS); + + OUT_LOAD_INST_DATA_COMPLETE; +} + +uint32 instance_zulaman::GetData(uint32 uiType) const +{ + switch (uiType) + { + case TYPE_EVENT_RUN: + case TYPE_AKILZON: + case TYPE_NALORAKK: + case TYPE_JANALAI: + case TYPE_HALAZZI: + case TYPE_ZULJIN: + case TYPE_MALACRASS: + case TYPE_RUN_EVENT_TIME: + return m_auiEncounter[uiType]; + case TYPE_RAND_VENDOR_1: return m_auiRandVendor[0]; + case TYPE_RAND_VENDOR_2: return m_auiRandVendor[1]; + default: + return 0; + } +} + +void instance_zulaman::SendNextBearWave(Unit* pTarget) +{ + for (GuidSet::const_iterator itr = m_aNalorakkEvent[m_uiBearEventPhase].sBearTrashGuidSet.begin(); itr != m_aNalorakkEvent[m_uiBearEventPhase].sBearTrashGuidSet.end(); ++itr) + { + Creature* pTemp = instance->GetCreature(*itr); + if (pTemp && pTemp->IsAlive()) + { + pTemp->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_PASSIVE); + pTemp->AI()->AttackStart(pTarget); + + // For the first wave we need to make them jump to the ground before attacking + if (!m_uiBearEventPhase) + { + float fX, fY, fZ; + pTemp->GetRandomPoint(35.31f, 1412.24f, 2.04f, 3.0f, fX, fY, fZ); + pTemp->GetMotionMaster()->MoveJump(fX, fY, fZ, pTemp->GetSpeed(MOVE_RUN) * 2, 5.0f); + } + } + } + + m_bIsBearPhaseInProgress = true; +} + +bool instance_zulaman::CheckConditionCriteriaMeet(Player const* pPlayer, uint32 uiInstanceConditionId, WorldObject const* pConditionSource, uint32 conditionSourceType) const +{ + switch (uiInstanceConditionId) + { + case INSTANCE_CONDITION_ID_NORMAL_MODE: // Not rescued + case INSTANCE_CONDITION_ID_HARD_MODE: // Rescued as first + case INSTANCE_CONDITION_ID_HARD_MODE_2: // Rescued as first + case INSTANCE_CONDITION_ID_HARD_MODE_3: // Rescued as second + case INSTANCE_CONDITION_ID_HARD_MODE_4: // Rescued as third + { + if (!pConditionSource) + break; + + int32 index = -1; + switch (pConditionSource->GetEntry()) + { + case NPC_TANZAR: + case GO_TANZARS_TRUNK: + index = INDEX_NALORAKK; + break; + case NPC_KRAZ: + case GO_KRAZS_PACKAGE: + index = INDEX_JANALAI; + break; + case NPC_ASHLI: + case GO_ASHLIS_BAG: + index = INDEX_HALAZZI; + break; + case NPC_HARKOR: + case GO_HARKORS_SATCHEL: + index = INDEX_AKILZON; + break; + } + if (index < 0) + break; + + return m_aEventNpcInfo[index].uiSavePosition == uiInstanceConditionId; + } + } + + script_error_log("instance_zulaman::CheckConditionCriteriaMeet called with unsupported Id %u. Called with param plr %s, src %s, condition source type %u", + uiInstanceConditionId, pPlayer ? pPlayer->GetGuidStr().c_str() : "NULL", pConditionSource ? pConditionSource->GetGuidStr().c_str() : "NULL", conditionSourceType); + return false; +} + +uint8 instance_zulaman::GetKilledPreBosses() +{ + return (m_auiEncounter[TYPE_AKILZON] == DONE ? 1 : 0) + (m_auiEncounter[TYPE_NALORAKK] == DONE ? 1 : 0) + (m_auiEncounter[TYPE_JANALAI] == DONE ? 1 : 0) + (m_auiEncounter[TYPE_HALAZZI] == DONE ? 1 : 0); +} + +void instance_zulaman::DoTimeRunSay(RunEventSteps uiData) +{ + switch (uiData) + { + case RUN_START: DoOrSimulateScriptTextForThisInstance(SAY_INST_BEGIN, NPC_MALACRASS); break; + case RUN_FAIL: DoOrSimulateScriptTextForThisInstance(urand(0, 1) ? SAY_INST_SACRIF1 : SAY_INST_SACRIF2, NPC_MALACRASS); break; + case RUN_DONE: DoOrSimulateScriptTextForThisInstance(SAY_INST_COMPLETE, NPC_MALACRASS); break; + case RUN_PROGRESS: + // This function is on progress called before the data is set to the array + switch (GetKilledPreBosses() + 1) + { + case 1: DoOrSimulateScriptTextForThisInstance(SAY_INST_PROGRESS_1, NPC_MALACRASS); break; + case 2: DoOrSimulateScriptTextForThisInstance(SAY_INST_PROGRESS_2, NPC_MALACRASS); break; + case 3: DoOrSimulateScriptTextForThisInstance(SAY_INST_PROGRESS_3, NPC_MALACRASS); break; + } + break; + case RUN_FAIL_SOON: + switch (GetKilledPreBosses()) + { + case 0: DoOrSimulateScriptTextForThisInstance(SAY_INST_WARN_1, NPC_MALACRASS); break; + case 1: DoOrSimulateScriptTextForThisInstance(SAY_INST_WARN_2, NPC_MALACRASS); break; + case 2: DoOrSimulateScriptTextForThisInstance(SAY_INST_WARN_3, NPC_MALACRASS); break; + case 3: DoOrSimulateScriptTextForThisInstance(SAY_INST_WARN_4, NPC_MALACRASS); break; + } + break; + } +} + +void instance_zulaman::DoChestEvent(BossToChestIndex uiIndex) +{ + // Store Order of this kill + m_aEventNpcInfo[uiIndex].uiSavePosition = GetKilledPreBosses() + 1; + + // Do Yell + DoTimeRunSay(RUN_PROGRESS); + + // related NPC: m_aEventNpcInfo[uiIndex].npGuid + // related Chest: m_aEventNpcInfo[uiIndex] // Not yet stored, because likely unneeded +} + +void instance_zulaman::Update(uint32 uiDiff) +{ + if (m_auiEncounter[TYPE_EVENT_RUN] == IN_PROGRESS) + { + if (m_uiEventTimer <= uiDiff) + { + if (m_auiEncounter[TYPE_RUN_EVENT_TIME] == 5) // TODO, verify 5min for warning texts + DoTimeRunSay(RUN_FAIL_SOON); + + if (m_auiEncounter[TYPE_RUN_EVENT_TIME] == 0) + { + debug_log("SD2: Instance Zulaman: event time reach end, event failed."); + SetData(TYPE_EVENT_RUN, FAIL); + return; + } + + --m_auiEncounter[TYPE_RUN_EVENT_TIME]; + SetData(TYPE_RUN_EVENT_TIME, m_auiEncounter[TYPE_RUN_EVENT_TIME]); + debug_log("SD2: Instance Zulaman: minute decrease to %u.", m_auiEncounter[TYPE_RUN_EVENT_TIME]); + + m_uiEventTimer = MINUTE * IN_MILLISECONDS; + } + else + m_uiEventTimer -= uiDiff; + } +} + +InstanceData* GetInstanceData_instance_zulaman(Map* pMap) +{ + return new instance_zulaman(pMap); +} + +void AddSC_instance_zulaman() +{ + Script* pNewScript; + + pNewScript = new Script; + pNewScript->Name = "instance_zulaman"; + pNewScript->GetInstanceData = &GetInstanceData_instance_zulaman; + pNewScript->RegisterSelf(); +} diff --git a/src/modules/SD2/scripts/eastern_kingdoms/zulaman/zulaman.cpp b/src/modules/SD2/scripts/eastern_kingdoms/zulaman/zulaman.cpp new file mode 100644 index 000000000..89409028c --- /dev/null +++ b/src/modules/SD2/scripts/eastern_kingdoms/zulaman/zulaman.cpp @@ -0,0 +1,261 @@ +/* Copyright (C) 2006 - 2013 ScriptDev2 + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along 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 "zulaman.h" +#include "escort_ai.h" + +/*###### +## npc_forest_frog +######*/ + +enum +{ + SPELL_REMOVE_AMANI_CURSE = 43732, + SPELL_PUSH_MOJO = 43923, + NPC_FOREST_FROG = 24396 +}; + +struct npc_forest_frogAI : public ScriptedAI +{ + npc_forest_frogAI(Creature* pCreature) : ScriptedAI(pCreature) + { + m_pInstance = (ScriptedInstance*)pCreature->GetInstanceData(); + Reset(); + } + + ScriptedInstance* m_pInstance; + + void Reset() override { } + + void DoSpawnRandom() + { + if (m_pInstance) + { + uint32 cEntry = 0; + switch (urand(0, 10)) + { + case 0: cEntry = 24024; break; // Kraz // wrong here? + case 1: cEntry = 24397; break; // Mannuth + case 2: cEntry = 24403; break; // Deez + case 3: cEntry = 24404; break; // Galathryn + case 4: cEntry = 24405; break; // Adarrah + case 5: cEntry = 24406; break; // Fudgerick + case 6: cEntry = 24407; break; // Darwen + case 7: cEntry = 24445; break; // Mitzi + case 8: cEntry = 24448; break; // Christian + case 9: cEntry = 24453; break; // Brennan + case 10: cEntry = 24455; break; // Hollee + } + + if (!m_pInstance->GetData(TYPE_RAND_VENDOR_1)) + if (!urand(0, 9)) + cEntry = 24408; // Gunter + + if (!m_pInstance->GetData(TYPE_RAND_VENDOR_2)) + if (!urand(0, 9)) + cEntry = 24409; // Kyren + + if (cEntry) + m_creature->UpdateEntry(cEntry); + + if (cEntry == 24408) + m_pInstance->SetData(TYPE_RAND_VENDOR_1, DONE); + + if (cEntry == 24409) + m_pInstance->SetData(TYPE_RAND_VENDOR_2, DONE); + } + } + + void SpellHit(Unit* caster, const SpellEntry* spell) override + { + if (spell->Id == SPELL_REMOVE_AMANI_CURSE && caster->GetTypeId() == TYPEID_PLAYER && m_creature->GetEntry() == NPC_FOREST_FROG) + { + // increase or decrease chance of mojo? + if (!urand(0, 49)) + DoCastSpellIfCan(caster, SPELL_PUSH_MOJO, CAST_TRIGGERED); + else + DoSpawnRandom(); + } + } +}; +CreatureAI* GetAI_npc_forest_frog(Creature* pCreature) +{ + return new npc_forest_frogAI(pCreature); +} + +/*###### +## npc_harrison_jones_za +######*/ + +enum +{ + SAY_START = -1568079, + SAY_AT_GONG = -1568080, + SAY_OPEN_ENTRANCE = -1568081, + + GOSSIP_ITEM_ID_BEGIN = -3568000, + + SPELL_BANGING_THE_GONG = 45225 +}; + +struct npc_harrison_jones_zaAI : public npc_escortAI +{ + npc_harrison_jones_zaAI(Creature* pCreature) : npc_escortAI(pCreature) + { + m_pInstance = (ScriptedInstance*)pCreature->GetInstanceData(); + Reset(); + } + + ScriptedInstance* m_pInstance; + + void WaypointReached(uint32 uiPointId) override + { + if (!m_pInstance) + return; + + switch (uiPointId) + { + case 1: + DoScriptText(SAY_AT_GONG, m_creature); + + m_pInstance->DoToggleGameObjectFlags(GO_STRANGE_GONG, GO_FLAG_NO_INTERACT, false); + + // Start bang gong for 2min + DoCastSpellIfCan(m_creature, SPELL_BANGING_THE_GONG); + SetEscortPaused(true); + break; + case 3: + DoScriptText(SAY_OPEN_ENTRANCE, m_creature); + break; + case 4: + m_pInstance->SetData(TYPE_EVENT_RUN, IN_PROGRESS); + // TODO: Spawn group of Amani'shi Savage and make them run to entrance + break; + } + } + + void Reset() override { } + + void StartEvent() + { + DoScriptText(SAY_START, m_creature); + Start(); + } + + void SetHoldState(bool bOnHold) + { + SetEscortPaused(bOnHold); + + // Stop banging gong if still + if (m_pInstance && m_pInstance->GetData(TYPE_EVENT_RUN) == SPECIAL && m_creature->HasAura(SPELL_BANGING_THE_GONG)) + m_creature->RemoveAurasDueToSpell(SPELL_BANGING_THE_GONG); + } +}; + +bool GossipHello_npc_harrison_jones_za(Player* pPlayer, Creature* pCreature) +{ + ScriptedInstance* pInstance = (ScriptedInstance*)pCreature->GetInstanceData(); + + if (pCreature->IsQuestGiver()) + pPlayer->PrepareQuestMenu(pCreature->GetObjectGuid()); + + if (pInstance && pInstance->GetData(TYPE_EVENT_RUN) == NOT_STARTED) + pPlayer->ADD_GOSSIP_ITEM_ID(GOSSIP_ICON_CHAT, GOSSIP_ITEM_ID_BEGIN, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 1); + + pPlayer->SEND_GOSSIP_MENU(pPlayer->GetGossipTextId(pCreature), pCreature->GetObjectGuid()); + return true; +} + +bool GossipSelect_npc_harrison_jones_za(Player* pPlayer, Creature* pCreature, uint32 /*uiSender*/, uint32 uiAction) +{ + if (uiAction == GOSSIP_ACTION_INFO_DEF + 1) + { + if (npc_harrison_jones_zaAI* pHarrisonAI = dynamic_cast(pCreature->AI())) + pHarrisonAI->StartEvent(); + + pPlayer->CLOSE_GOSSIP_MENU(); + } + return true; +} + +CreatureAI* GetAI_npc_harrison_jones_za(Creature* pCreature) +{ + return new npc_harrison_jones_zaAI(pCreature); +} + +/*###### +## go_strange_gong +######*/ + +// Unsure how this Gong must work. Here we always return false to allow Mangos always process further. +bool GOUse_go_strange_gong(Player* /*pPlayer*/, GameObject* pGo) +{ + ScriptedInstance* pInstance = (ScriptedInstance*)pGo->GetInstanceData(); + + if (!pInstance) + return false; + + if (pInstance->GetData(TYPE_EVENT_RUN) == SPECIAL) + { + if (Creature* pCreature = pInstance->GetSingleCreatureFromStorage(NPC_HARRISON)) + { + if (npc_harrison_jones_zaAI* pHarrisonAI = dynamic_cast(pCreature->AI())) + pHarrisonAI->SetHoldState(false); + } + else + script_error_log("Instance Zulaman: go_strange_gong failed"); + + pGo->SetFlag(GAMEOBJECT_FLAGS, GO_FLAG_NO_INTERACT); + return false; + } + + pInstance->SetData(TYPE_EVENT_RUN, SPECIAL); + return false; +} + +void AddSC_zulaman() +{ + Script* pNewScript; + + pNewScript = new Script; + pNewScript->Name = "npc_forest_frog"; + pNewScript->GetAI = &GetAI_npc_forest_frog; + pNewScript->RegisterSelf(); + + pNewScript = new Script; + pNewScript->Name = "npc_harrison_jones_za"; + pNewScript->GetAI = &GetAI_npc_harrison_jones_za; + pNewScript->pGossipHello = &GossipHello_npc_harrison_jones_za; + pNewScript->pGossipSelect = &GossipSelect_npc_harrison_jones_za; + pNewScript->RegisterSelf(); + + pNewScript = new Script; + pNewScript->Name = "go_strange_gong"; + pNewScript->pGOUse = &GOUse_go_strange_gong; + pNewScript->RegisterSelf(); +} diff --git a/src/modules/SD2/scripts/eastern_kingdoms/zulaman/zulaman.h b/src/modules/SD2/scripts/eastern_kingdoms/zulaman/zulaman.h new file mode 100644 index 000000000..7cd7f62d6 --- /dev/null +++ b/src/modules/SD2/scripts/eastern_kingdoms/zulaman/zulaman.h @@ -0,0 +1,196 @@ +/* Copyright (C) 2006 - 2013 ScriptDev2 + * This program is free software licensed under GPL version 2 + * Please see the included DOCS/LICENSE.TXT for more information */ + +#ifndef DEF_ZULAMAN_H +#define DEF_ZULAMAN_H + +enum InstanceZA +{ + MAX_ENCOUNTER = 8, + MAX_VENDOR = 2, + MAX_CHESTS = 4, + MAX_BEAR_WAVES = 4, + + SAY_INST_RELEASE = -1568067, // TODO Event NYI + SAY_INST_BEGIN = -1568068, + SAY_INST_PROGRESS_1 = -1568069, + SAY_INST_PROGRESS_2 = -1568070, + SAY_INST_PROGRESS_3 = -1568071, + SAY_INST_WARN_1 = -1568072, + SAY_INST_WARN_2 = -1568073, + SAY_INST_WARN_3 = -1568074, + SAY_INST_WARN_4 = -1568075, + SAY_INST_SACRIF1 = -1568076, + SAY_INST_SACRIF2 = -1568077, + SAY_INST_COMPLETE = -1568078, + + // Bear event yells + SAY_WAVE1_AGGRO = -1568010, + SAY_WAVE2_STAIR1 = -1568011, + SAY_WAVE3_STAIR2 = -1568012, + SAY_WAVE4_PLATFORM = -1568013, + + WORLD_STATE_ID = 3104, + WORLD_STATE_COUNTER = 3106, + + TYPE_EVENT_RUN = 0, + TYPE_AKILZON = 1, + TYPE_NALORAKK = 2, + TYPE_JANALAI = 3, + TYPE_HALAZZI = 4, + TYPE_MALACRASS = 5, + TYPE_ZULJIN = 6, + TYPE_RUN_EVENT_TIME = 7, // Must be MAX_ENCOUNTER -1 + + TYPE_RAND_VENDOR_1 = 8, + TYPE_RAND_VENDOR_2 = 9, + + NPC_AKILZON = 23574, + NPC_NALORAKK = 23576, + NPC_JANALAI = 23578, + NPC_HALAZZI = 23577, + NPC_MALACRASS = 24239, + NPC_ZULJIN = 23863, + + // Narolakk event npcs + NPC_MEDICINE_MAN = 23581, + NPC_TRIBES_MAN = 23582, + NPC_AXETHROWER = 23542, + NPC_WARBRINGER = 23580, + + // Malacrass companions + NPC_ALYSON = 24240, + NPC_THURG = 24241, + NPC_SLITHER = 24242, + NPC_RADAAN = 24243, + NPC_GAZAKROTH = 24244, + NPC_FENSTALKER = 24245, + NPC_DARKHEART = 24246, + NPC_KORAGG = 24247, + + NPC_HARRISON = 24358, + // Time Run Event NPCs + NPC_TANZAR = 23790, // at bear + NPC_KRAZ = 24024, // at phoenix + NPC_ASHLI = 24001, // at lynx + NPC_HARKOR = 23999, // at eagle + // unused (TODO or TODO with DB-tools) + NPC_TANZAR_CORPSE = 24442, + NPC_KRAZ_CORPSE = 24444, + NPC_ASHIL_CORPSE = 24441, + NPC_HARKOR_CORPSE = 24443, + + // Zul'jin event spirits + NPC_BEAR_SPIRIT = 23878, // They should all have aura 42466 + NPC_EAGLE_SPIRIT = 23880, + NPC_LYNX_SPIRIT = 23877, + NPC_DRAGONHAWK_SPIRIT = 23879, + + GO_STRANGE_GONG = 187359, + GO_MASSIVE_GATE = 186728, + GO_WIND_DOOR = 186858, + GO_LYNX_TEMPLE_ENTRANCE = 186304, + GO_LYNX_TEMPLE_EXIT = 186303, + GO_HEXLORD_ENTRANCE = 186305, + GO_WOODEN_DOOR = 186306, + GO_FIRE_DOOR = 186859, + + GO_TANZARS_TRUNK = 186648, + GO_KRAZS_PACKAGE = 186667, + GO_ASHLIS_BAG = 186672, + GO_HARKORS_SATCHEL = 187021, +}; + +enum BossToChestIndex +{ + INDEX_NALORAKK = 0, + INDEX_JANALAI = 1, + INDEX_HALAZZI = 2, + INDEX_AKILZON = 3 +}; + +enum RunEventSteps +{ + RUN_START = 1, + RUN_FAIL = 2, + RUN_DONE = 3, + RUN_PROGRESS = 4, + RUN_FAIL_SOON = 5 +}; + +struct TimeEventNpcInfo +{ + TimeEventNpcInfo() : uiSavePosition(0) {} + + uint8 uiSavePosition; // stores in what order this npc was saved (0 means unsaved) + ObjectGuid npGuid; +}; + +struct NalorakkBearEventInfo +{ + int iYellId; + float fX, fY, fZ, fO, fAggroDist; +}; + +static const NalorakkBearEventInfo aBearEventInfo[MAX_BEAR_WAVES] = +{ + {SAY_WAVE1_AGGRO, 0, 0, 0, 0, 45.0f}, + {SAY_WAVE2_STAIR1, -54.948f, 1419.772f, 27.303f, 0.03f, 37.0f}, + {SAY_WAVE3_STAIR2, -80.303f, 1372.622f, 40.764f, 1.67f, 35.0f}, + {SAY_WAVE4_PLATFORM, -77.495f, 1294.760f, 48.487f, 1.66f, 60.0f} +}; + +struct NalorakkTrashInfo +{ + GuidSet sBearTrashGuidSet; + uint8 uiTrashKilled; +}; + +class instance_zulaman : public ScriptedInstance +{ + public: + instance_zulaman(Map* pMap); + + void Initialize() override; + bool IsEncounterInProgress() const override; + + void OnPlayerEnter(Player* pPlayer) override; + void OnCreatureCreate(Creature* pCreature) override; + void OnObjectCreate(GameObject* pGo) override; + void OnCreatureDeath(Creature* pCreature) override; + void OnCreatureEvade(Creature* pCreature); + + void SetData(uint32 uiType, uint32 uiData) override; + uint32 GetData(uint32 uiType) const override; + + const char* Save() const override { return m_strInstData.c_str(); } + void Load(const char* chrIn) override; + + bool IsBearPhaseInProgress() { return m_bIsBearPhaseInProgress; } + void SetBearEventProgress(bool bIsInProgress) { m_bIsBearPhaseInProgress = bIsInProgress; } + void SendNextBearWave(Unit* pTarget); + + bool CheckConditionCriteriaMeet(Player const* pPlayer, uint32 uiInstanceConditionId, WorldObject const* pConditionSource, uint32 conditionSourceType) const override; + + void Update(uint32 uiDiff) override; + + private: + uint8 GetKilledPreBosses(); + void DoTimeRunSay(RunEventSteps uiData); + void DoChestEvent(BossToChestIndex uiIndex); + + std::string m_strInstData; + uint32 m_auiEncounter[MAX_ENCOUNTER]; + uint32 m_auiRandVendor[MAX_VENDOR]; + TimeEventNpcInfo m_aEventNpcInfo[MAX_CHESTS]; + + uint32 m_uiEventTimer; + uint32 m_uiGongCount; + + NalorakkTrashInfo m_aNalorakkEvent[MAX_BEAR_WAVES]; + uint8 m_uiBearEventPhase; + bool m_bIsBearPhaseInProgress; +}; + +#endif diff --git a/src/modules/SD2/scripts/eastern_kingdoms/zulgurub/boss_arlokk.cpp b/src/modules/SD2/scripts/eastern_kingdoms/zulgurub/boss_arlokk.cpp new file mode 100644 index 000000000..9d4edf74d --- /dev/null +++ b/src/modules/SD2/scripts/eastern_kingdoms/zulgurub/boss_arlokk.cpp @@ -0,0 +1,297 @@ +/* Copyright (C) 2006 - 2013 ScriptDev2 + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with 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: 80 +SDComment: Vanish spell is replaced by workaround; Timers +SDCategory: Zul'Gurub +EndScriptData */ + +#include "precompiled.h" +#include "zulgurub.h" + +enum +{ + SAY_AGGRO = -1309011, + SAY_FEAST_PANTHER = -1309012, + SAY_DEATH = -1309013, + + SPELL_SHADOW_WORD_PAIN = 23952, + SPELL_GOUGE = 24698, + SPELL_MARK_ARLOKK = 24210, + SPELL_RAVAGE = 24213, + SPELL_TRASH = 3391, + SPELL_WHIRLWIND = 24236, + SPELL_PANTHER_TRANSFORM = 24190, + + NPC_ZULIAN_PROWLER = 15101 +}; + +struct boss_arlokkAI : public ScriptedAI +{ + boss_arlokkAI(Creature* pCreature) : ScriptedAI(pCreature) + { + m_pInstance = (instance_zulgurub*)pCreature->GetInstanceData(); + Reset(); + } + + instance_zulgurub* m_pInstance; + + uint32 m_uiShadowWordPainTimer; + uint32 m_uiGougeTimer; + uint32 m_uiMarkTimer; + uint32 m_uiRavageTimer; + uint32 m_uiTrashTimer; + uint32 m_uiWhirlwindTimer; + uint32 m_uiVanishTimer; + uint32 m_uiVisibleTimer; + uint32 m_uiTransformTimer; + uint32 m_uiSummonTimer; + + bool m_bIsPhaseTwo; + + void Reset() override + { + m_uiShadowWordPainTimer = 8000; + m_uiGougeTimer = 14000; + m_uiMarkTimer = 5000; + m_uiRavageTimer = 12000; + m_uiTrashTimer = 20000; + m_uiWhirlwindTimer = 15000; + m_uiTransformTimer = 30000; + m_uiVanishTimer = 5000; + m_uiVisibleTimer = 0; + m_uiSummonTimer = 5000; + + m_bIsPhaseTwo = false; + + // Restore visibility + if (m_creature->GetVisibility() != VISIBILITY_ON) + m_creature->SetVisibility(VISIBILITY_ON); + } + + void Aggro(Unit* /*pWho*/) override + { + DoScriptText(SAY_AGGRO, m_creature); + } + + void JustReachedHome() override + { + if (m_pInstance) + m_pInstance->SetData(TYPE_ARLOKK, FAIL); + + // we should be summoned, so despawn + m_creature->ForcedDespawn(); + } + + void JustDied(Unit* /*pKiller*/) override + { + DoScriptText(SAY_DEATH, m_creature); + // Restore visibility in case of killed by dots + if (m_creature->GetVisibility() != VISIBILITY_ON) + m_creature->SetVisibility(VISIBILITY_ON); + + if (m_pInstance) + m_pInstance->SetData(TYPE_ARLOKK, DONE); + } + + void JustSummoned(Creature* pSummoned) override + { + // Just attack a random target. The Marked player will attract them automatically + if (Unit* pTarget = m_creature->SelectAttackingTarget(ATTACKING_TARGET_RANDOM, 0)) + pSummoned->AI()->AttackStart(pTarget); + } + + void UpdateAI(const uint32 uiDiff) override + { + if (!m_creature->SelectHostileTarget() || !m_creature->getVictim()) + return; + + // Summon panters every 5 seconds + if (m_uiSummonTimer < uiDiff) + { + if (m_pInstance) + { + if (Creature* pTrigger = m_pInstance->SelectRandomPantherTrigger(true)) + m_creature->SummonCreature(NPC_ZULIAN_PROWLER, pTrigger->GetPositionX(), pTrigger->GetPositionY(), pTrigger->GetPositionZ(), 0, TEMPSUMMON_TIMED_OOC_DESPAWN, 30000); + if (Creature* pTrigger = m_pInstance->SelectRandomPantherTrigger(false)) + m_creature->SummonCreature(NPC_ZULIAN_PROWLER, pTrigger->GetPositionX(), pTrigger->GetPositionY(), pTrigger->GetPositionZ(), 0, TEMPSUMMON_TIMED_OOC_DESPAWN, 30000); + } + + m_uiSummonTimer = 5000; + } + else + m_uiSummonTimer -= uiDiff; + + if (m_uiVisibleTimer) + { + if (m_uiVisibleTimer <= uiDiff) + { + // Restore visibility + m_creature->SetVisibility(VISIBILITY_ON); + + if (Unit* pTarget = m_creature->SelectAttackingTarget(ATTACKING_TARGET_RANDOM, 0)) + AttackStart(pTarget); + + m_uiVisibleTimer = 0; + } + else + m_uiVisibleTimer -= uiDiff; + + // Do nothing while vanished + return; + } + + // Troll phase + if (!m_bIsPhaseTwo) + { + if (m_uiShadowWordPainTimer < uiDiff) + { + if (Unit* pTarget = m_creature->SelectAttackingTarget(ATTACKING_TARGET_RANDOM, 0)) + { + if (DoCastSpellIfCan(pTarget, SPELL_SHADOW_WORD_PAIN) == CAST_OK) + m_uiShadowWordPainTimer = 15000; + } + } + else + m_uiShadowWordPainTimer -= uiDiff; + + if (m_uiMarkTimer < uiDiff) + { + if (Unit* pTarget = m_creature->SelectAttackingTarget(ATTACKING_TARGET_RANDOM, 0, SPELL_MARK_ARLOKK, SELECT_FLAG_PLAYER)) + { + if (DoCastSpellIfCan(pTarget, SPELL_MARK_ARLOKK) == CAST_OK) + { + DoScriptText(SAY_FEAST_PANTHER, m_creature, pTarget); + m_uiMarkTimer = 30000; + } + } + } + else + m_uiMarkTimer -= uiDiff; + + if (m_uiGougeTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature, SPELL_GOUGE) == CAST_OK) + { + if (m_creature->GetThreatManager().getThreat(m_creature->getVictim())) + m_creature->GetThreatManager().modifyThreatPercent(m_creature->getVictim(), -80); + + m_uiGougeTimer = urand(17000, 27000); + } + } + else + m_uiGougeTimer -= uiDiff; + + // Transform to Panther + if (m_uiTransformTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature, SPELL_PANTHER_TRANSFORM) == CAST_OK) + { + m_uiTransformTimer = 80000; + m_bIsPhaseTwo = true; + } + } + else + m_uiTransformTimer -= uiDiff; + } + // Panther phase + else + { + if (m_uiRavageTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature->getVictim(), SPELL_RAVAGE) == CAST_OK) + m_uiRavageTimer = urand(10000, 15000); + } + else + m_uiRavageTimer -= uiDiff; + + if (m_uiTrashTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature->getVictim(), SPELL_TRASH) == CAST_OK) + m_uiTrashTimer = urand(13000, 15000); + } + else + m_uiTrashTimer -= uiDiff; + + if (m_uiWhirlwindTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature, SPELL_WHIRLWIND) == CAST_OK) + m_uiWhirlwindTimer = 15000; + } + else + m_uiWhirlwindTimer -= uiDiff; + + if (m_uiVanishTimer < uiDiff) + { + // Note: this is a workaround because we do not know the real vanish spell + m_creature->SetVisibility(VISIBILITY_OFF); + DoResetThreat(); + + m_uiVanishTimer = 85000; + m_uiVisibleTimer = 45000; + } + else + m_uiVanishTimer -= uiDiff; + + // Transform back + if (m_uiTransformTimer < uiDiff) + { + m_creature->RemoveAurasDueToSpell(SPELL_PANTHER_TRANSFORM); + m_uiTransformTimer = 30000; + m_bIsPhaseTwo = false; + } + else + m_uiTransformTimer -= uiDiff; + } + + DoMeleeAttackIfReady(); + } +}; + +CreatureAI* GetAI_boss_arlokk(Creature* pCreature) +{ + return new boss_arlokkAI(pCreature); +} + +bool GOUse_go_gong_of_bethekk(Player* /*pPlayer*/, GameObject* pGo) +{ + if (ScriptedInstance* pInstance = (ScriptedInstance*)pGo->GetInstanceData()) + { + if (pInstance->GetData(TYPE_ARLOKK) == DONE || pInstance->GetData(TYPE_ARLOKK) == IN_PROGRESS) + return true; + + pInstance->SetData(TYPE_ARLOKK, IN_PROGRESS); + } + + return false; +} + +void AddSC_boss_arlokk() +{ + Script* pNewScript; + + pNewScript = new Script; + pNewScript->Name = "boss_arlokk"; + pNewScript->GetAI = &GetAI_boss_arlokk; + pNewScript->RegisterSelf(); + + pNewScript = new Script; + pNewScript->Name = "go_gong_of_bethekk"; + pNewScript->pGOUse = &GOUse_go_gong_of_bethekk; + pNewScript->RegisterSelf(); +} diff --git a/src/modules/SD2/scripts/eastern_kingdoms/zulgurub/boss_hakkar.cpp b/src/modules/SD2/scripts/eastern_kingdoms/zulgurub/boss_hakkar.cpp new file mode 100644 index 000000000..79f946847 --- /dev/null +++ b/src/modules/SD2/scripts/eastern_kingdoms/zulgurub/boss_hakkar.cpp @@ -0,0 +1,244 @@ +/* Copyright (C) 2006 - 2013 ScriptDev2 + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with 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: 100 +SDComment: +SDCategory: Zul'Gurub +EndScriptData */ + +#include "precompiled.h" +#include "zulgurub.h" + +enum +{ + SAY_AGGRO = -1309020, + SAY_FLEEING = -1309021, + + SPELL_BLOOD_SIPHON = 24324, // triggers 24322 or 24323 on caster + SPELL_CORRUPTED_BLOOD = 24328, + SPELL_CAUSE_INSANITY = 24327, + SPELL_WILL_OF_HAKKAR = 24178, + SPELL_ENRAGE = 24318, + + // The Aspects of all High Priests + SPELL_ASPECT_OF_JEKLIK = 24687, + SPELL_ASPECT_OF_VENOXIS = 24688, + SPELL_ASPECT_OF_MARLI = 24686, + SPELL_ASPECT_OF_THEKAL = 24689, + SPELL_ASPECT_OF_ARLOKK = 24690 +}; + +struct boss_hakkarAI : public ScriptedAI +{ + boss_hakkarAI(Creature* pCreature) : ScriptedAI(pCreature) + { + m_pInstance = (ScriptedInstance*)pCreature->GetInstanceData(); + Reset(); + } + + ScriptedInstance* m_pInstance; + + uint32 m_uiBloodSiphonTimer; + uint32 m_uiCorruptedBloodTimer; + uint32 m_uiCauseInsanityTimer; + uint32 m_uiWillOfHakkarTimer; + uint32 m_uiEnrageTimer; + + uint32 m_uiAspectOfJeklikTimer; + uint32 m_uiAspectOfVenoxisTimer; + uint32 m_uiAspectOfMarliTimer; + uint32 m_uiAspectOfThekalTimer; + uint32 m_uiAspectOfArlokkTimer; + + void Reset() override + { + m_uiBloodSiphonTimer = 90000; + m_uiCorruptedBloodTimer = 25000; + m_uiCauseInsanityTimer = 17000; + m_uiWillOfHakkarTimer = 17000; + m_uiEnrageTimer = 10 * MINUTE * IN_MILLISECONDS; + + m_uiAspectOfJeklikTimer = 4000; + m_uiAspectOfVenoxisTimer = 7000; + m_uiAspectOfMarliTimer = 12000; + m_uiAspectOfThekalTimer = 8000; + m_uiAspectOfArlokkTimer = 18000; + } + + void Aggro(Unit* /*who*/) override + { + DoScriptText(SAY_AGGRO, m_creature); + + // check if the priest encounters are done + if (m_pInstance) + { + if (m_pInstance->GetData(TYPE_JEKLIK) == DONE) + m_uiAspectOfJeklikTimer = 0; + if (m_pInstance->GetData(TYPE_VENOXIS) == DONE) + m_uiAspectOfVenoxisTimer = 0; + if (m_pInstance->GetData(TYPE_MARLI) == DONE) + m_uiAspectOfMarliTimer = 0; + if (m_pInstance->GetData(TYPE_THEKAL) == DONE) + m_uiAspectOfThekalTimer = 0; + if (m_pInstance->GetData(TYPE_ARLOKK) == DONE) + m_uiAspectOfArlokkTimer = 0; + } + } + + void UpdateAI(const uint32 uiDiff) override + { + if (!m_creature->SelectHostileTarget() || !m_creature->getVictim()) + return; + + if (m_uiBloodSiphonTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature, SPELL_BLOOD_SIPHON) == CAST_OK) + m_uiBloodSiphonTimer = 90000; + } + else + m_uiBloodSiphonTimer -= uiDiff; + + // Corrupted Blood Timer + if (m_uiCorruptedBloodTimer < uiDiff) + { + if (Unit* pTarget = m_creature->SelectAttackingTarget(ATTACKING_TARGET_RANDOM, 0)) + { + if (DoCastSpellIfCan(pTarget, SPELL_CORRUPTED_BLOOD) == CAST_OK) + m_uiCorruptedBloodTimer = urand(30000, 45000); + } + } + else + m_uiCorruptedBloodTimer -= uiDiff; + + // Cause Insanity Timer + if (m_uiCauseInsanityTimer < uiDiff) + { + if (m_creature->GetThreatManager().getThreatList().size() > 1) + { + if (DoCastSpellIfCan(m_creature->getVictim(), SPELL_CAUSE_INSANITY) == CAST_OK) + m_uiCauseInsanityTimer = urand(10000, 15000); + } + else // Solo case, check again later + m_uiCauseInsanityTimer = urand(35000, 43000); + } + else + m_uiCauseInsanityTimer -= uiDiff; + + // Will Of Hakkar Timer + if (m_uiWillOfHakkarTimer < uiDiff) + { + if (Unit* pTarget = m_creature->SelectAttackingTarget(ATTACKING_TARGET_RANDOM, 1)) + { + if (DoCastSpellIfCan(pTarget, SPELL_WILL_OF_HAKKAR) == CAST_OK) + m_uiWillOfHakkarTimer = urand(25000, 35000); + } + else // solo attempt, try again later + m_uiWillOfHakkarTimer = 25000; + } + else + m_uiWillOfHakkarTimer -= uiDiff; + + if (m_uiEnrageTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature, SPELL_ENRAGE) == CAST_OK) + m_uiEnrageTimer = 10 * MINUTE * IN_MILLISECONDS; + } + else + m_uiEnrageTimer -= uiDiff; + + // Checking if Jeklik is dead. If not we cast her Aspect + if (m_uiAspectOfJeklikTimer) + { + if (m_uiAspectOfJeklikTimer <= uiDiff) + { + if (DoCastSpellIfCan(m_creature, SPELL_ASPECT_OF_JEKLIK) == CAST_OK) + m_uiAspectOfJeklikTimer = urand(10000, 14000); + } + else + m_uiAspectOfJeklikTimer -= uiDiff; + } + + // Checking if Venoxis is dead. If not we cast his Aspect + if (m_uiAspectOfVenoxisTimer) + { + if (m_uiAspectOfVenoxisTimer <= uiDiff) + { + if (DoCastSpellIfCan(m_creature, SPELL_ASPECT_OF_VENOXIS) == CAST_OK) + m_uiAspectOfVenoxisTimer = 8000; + } + else + m_uiAspectOfVenoxisTimer -= uiDiff; + } + + // Checking if Marli is dead. If not we cast her Aspect + if (m_uiAspectOfMarliTimer) + { + if (m_uiAspectOfMarliTimer <= uiDiff) + { + if (DoCastSpellIfCan(m_creature->getVictim(), SPELL_ASPECT_OF_MARLI) == CAST_OK) + m_uiAspectOfMarliTimer = 10000; + } + else + m_uiAspectOfMarliTimer -= uiDiff; + } + + // Checking if Thekal is dead. If not we cast his Aspect + if (m_uiAspectOfThekalTimer) + { + if (m_uiAspectOfThekalTimer <= uiDiff) + { + if (DoCastSpellIfCan(m_creature, SPELL_ASPECT_OF_THEKAL) == CAST_OK) + m_uiAspectOfThekalTimer = 15000; + } + else + m_uiAspectOfThekalTimer -= uiDiff; + } + + // Checking if Arlokk is dead. If yes we cast her Aspect + if (m_uiAspectOfArlokkTimer) + { + if (m_uiAspectOfArlokkTimer <= uiDiff) + { + if (DoCastSpellIfCan(m_creature->getVictim(), SPELL_ASPECT_OF_ARLOKK) == CAST_OK) + { + DoResetThreat(); + m_uiAspectOfArlokkTimer = urand(10000, 15000); + } + } + else + m_uiAspectOfArlokkTimer -= uiDiff; + } + + DoMeleeAttackIfReady(); + } +}; + +CreatureAI* GetAI_boss_hakkar(Creature* pCreature) +{ + return new boss_hakkarAI(pCreature); +} + +void AddSC_boss_hakkar() +{ + Script* pNewScript; + + pNewScript = new Script; + pNewScript->Name = "boss_hakkar"; + pNewScript->GetAI = &GetAI_boss_hakkar; + pNewScript->RegisterSelf(); +} diff --git a/src/modules/SD2/scripts/eastern_kingdoms/zulgurub/boss_hazzarah.cpp b/src/modules/SD2/scripts/eastern_kingdoms/zulgurub/boss_hazzarah.cpp new file mode 100644 index 000000000..3d651c22c --- /dev/null +++ b/src/modules/SD2/scripts/eastern_kingdoms/zulgurub/boss_hazzarah.cpp @@ -0,0 +1,126 @@ +/* Copyright (C) 2006 - 2013 ScriptDev2 + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with 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" + +enum +{ + SPELL_CHAIN_BURN = 24684, + SPELL_SLEEP = 24664, + SPELL_EARTH_SHOCK = 24685, + SPELL_SUMMON_ILLUSION_1 = 24681, + SPELL_SUMMON_ILLUSION_2 = 24728, + SPELL_SUMMON_ILLUSION_3 = 24729, +}; + +struct boss_hazzarahAI : public ScriptedAI +{ + boss_hazzarahAI(Creature* pCreature) : ScriptedAI(pCreature) { Reset(); } + + uint32 m_uiManaBurnTimer; + uint32 m_uiSleepTimer; + uint32 m_uiEarthShockTimer; + uint32 m_uiIllusionsTimer; + + void Reset() override + { + m_uiManaBurnTimer = urand(4000, 10000); + m_uiSleepTimer = urand(10000, 18000); + m_uiEarthShockTimer = urand(7000, 14000); + m_uiIllusionsTimer = urand(10000, 18000); + } + + void JustSummoned(Creature* pSummoned) override + { + if (Unit* pTarget = m_creature->SelectAttackingTarget(ATTACKING_TARGET_RANDOM, 0)) + pSummoned->AI()->AttackStart(pTarget); + } + + void UpdateAI(const uint32 uiDiff) override + { + if (!m_creature->SelectHostileTarget() || !m_creature->getVictim()) + return; + + // ManaBurn_Timer + if (m_uiManaBurnTimer < uiDiff) + { + if (Unit* pTarget = m_creature->SelectAttackingTarget(ATTACKING_TARGET_RANDOM, 0, SPELL_CHAIN_BURN, SELECT_FLAG_POWER_MANA)) + { + if (DoCastSpellIfCan(pTarget, SPELL_CHAIN_BURN) == CAST_OK) + m_uiManaBurnTimer = urand(8000, 16000); + } + } + else + m_uiManaBurnTimer -= uiDiff; + + // Sleep_Timer + if (m_uiSleepTimer < uiDiff) + { + if (Unit* pTarget = m_creature->SelectAttackingTarget(ATTACKING_TARGET_RANDOM, 0)) + { + if (DoCastSpellIfCan(pTarget, SPELL_SLEEP) == CAST_OK) + m_uiSleepTimer = urand(12000, 20000); + } + } + else + m_uiSleepTimer -= uiDiff; + + // Earthshock + if (m_uiEarthShockTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature, SPELL_EARTH_SHOCK) == CAST_OK) + m_uiEarthShockTimer = urand(9000, 16000); + } + else + m_uiEarthShockTimer -= uiDiff; + + // Illusions_Timer + if (m_uiIllusionsTimer < uiDiff) + { + DoCastSpellIfCan(m_creature, SPELL_SUMMON_ILLUSION_1, CAST_TRIGGERED); + DoCastSpellIfCan(m_creature, SPELL_SUMMON_ILLUSION_2, CAST_TRIGGERED); + DoCastSpellIfCan(m_creature, SPELL_SUMMON_ILLUSION_3, CAST_TRIGGERED); + + m_uiIllusionsTimer = urand(15000, 25000); + } + else + m_uiIllusionsTimer -= uiDiff; + + DoMeleeAttackIfReady(); + } +}; + +CreatureAI* GetAI_boss_hazzarah(Creature* pCreature) +{ + return new boss_hazzarahAI(pCreature); +} + +void AddSC_boss_hazzarah() +{ + Script* pNewScript; + + pNewScript = new Script; + pNewScript->Name = "boss_hazzarah"; + pNewScript->GetAI = &GetAI_boss_hazzarah; + pNewScript->RegisterSelf(); +} diff --git a/src/modules/SD2/scripts/eastern_kingdoms/zulgurub/boss_jeklik.cpp b/src/modules/SD2/scripts/eastern_kingdoms/zulgurub/boss_jeklik.cpp new file mode 100644 index 000000000..ded5807ff --- /dev/null +++ b/src/modules/SD2/scripts/eastern_kingdoms/zulgurub/boss_jeklik.cpp @@ -0,0 +1,413 @@ +/* Copyright (C) 2006 - 2013 ScriptDev2 + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with 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: Some minor improvements are required; Bat rider movement not implemented +SDCategory: Zul'Gurub +EndScriptData */ + +#include "precompiled.h" +#include "zulgurub.h" + +enum +{ + SAY_AGGRO = -1309002, + SAY_RAIN_FIRE = -1309003, + SAY_SHRIEK = -1309026, + SAY_HEAL = -1309027, + SAY_DEATH = -1309004, + + // Bat spells + SPELL_CHARGE = 22911, + SPELL_SONIC_BURST = 23918, + // SPELL_PSYHIC_SCREAM = 22884, // spell not confirmed - needs research + SPELL_SWOOP = 23919, + SPELL_SUMMON_FRENZIED_BATS = 23974, + + // Troll form spells + SPELL_SHADOW_WORD_PAIN = 23952, + SPELL_MIND_FLAY = 23953, + SPELL_BLOOD_LEECH = 22644, + SPELL_GREATERHEAL = 23954, + + // Common spells + SPELL_GREEN_CHANNELING = 13540, // visual for idle mode + SPELL_BAT_FORM = 23966, + + // Batriders Spell + SPELL_LIQUID_FIRE = 23968, // script effect - triggers 23971, + SPELL_UNSTABLE_CONCOCTION = 24024, + SPELL_TRASH = 8876, + SPELL_DEMORALIZING_SHOUT = 23511, + SPELL_BATTLE_COMMAND = 5115, + SPELL_INFECTED_BITE = 16128, + + // npcs + NPC_FRENZIED_BAT = 14965, + NPC_BAT_RIDER = 14750, +}; + +struct boss_jeklikAI : public ScriptedAI +{ + boss_jeklikAI(Creature* pCreature) : ScriptedAI(pCreature) + { + m_pInstance = (ScriptedInstance*)pCreature->GetInstanceData(); + Reset(); + } + + ScriptedInstance* m_pInstance; + + uint32 m_uiChargeTimer; + uint32 m_uiSwoopTimer; + uint32 m_uiSonicBurstTimer; + uint32 m_uiSpawnBatsTimer; + uint32 m_uiShadowWordPainTimer; + uint32 m_uiMindFlayTimer; + uint32 m_uiChainMindFlayTimer; + uint32 m_uiGreaterHealTimer; + uint32 m_uiFlyingBatsTimer; + + bool m_bIsPhaseOne; + + GuidList m_lBombRiderGuidsList; + + void Reset() override + { + m_uiChargeTimer = 20000; + m_uiSwoopTimer = 5000; + m_uiSonicBurstTimer = 8000; + m_uiSpawnBatsTimer = 50000; + m_uiShadowWordPainTimer = 6000; + m_uiMindFlayTimer = 11000; + m_uiChainMindFlayTimer = 26000; + m_uiGreaterHealTimer = 20000; + m_uiFlyingBatsTimer = 30000; + + m_bIsPhaseOne = true; + + DoCastSpellIfCan(m_creature, SPELL_GREEN_CHANNELING); + SetCombatMovement(false); + } + + void Aggro(Unit* /*pWho*/) override + { + DoScriptText(SAY_AGGRO, m_creature); + + // Note: on aggro the bats from the cave behind the boss should fly outside! + if (DoCastSpellIfCan(m_creature, SPELL_BAT_FORM) == CAST_OK) + { + m_creature->SetLevitate(true); + // override MMaps, by allowing the boss to fly up from the ledge + m_creature->SetWalk(false); + m_creature->GetMotionMaster()->MovePoint(1, -12281.58f, -1392.84f, 146.1f); + } + } + + void JustDied(Unit* /*pKiller*/) override + { + DoScriptText(SAY_DEATH, m_creature); + DoDespawnBombRiders(); + + if (m_pInstance) + m_pInstance->SetData(TYPE_JEKLIK, DONE); + } + + void JustReachedHome() override + { + DoDespawnBombRiders(); + + if (m_pInstance) + m_pInstance->SetData(TYPE_JEKLIK, FAIL); + } + + void JustSummoned(Creature* pSummoned) override + { + if (pSummoned->GetEntry() == NPC_FRENZIED_BAT) + { + if (Unit* pTarget = m_creature->SelectAttackingTarget(ATTACKING_TARGET_RANDOM, 0)) + pSummoned->AI()->AttackStart(pTarget); + } + else if (pSummoned->GetEntry() == NPC_BAT_RIDER) + { + pSummoned->CastSpell(pSummoned, SPELL_LIQUID_FIRE, true); + m_lBombRiderGuidsList.push_back(pSummoned->GetObjectGuid()); + } + + pSummoned->SetLevitate(true); + } + + void EnterEvadeMode() override + { + // Override MMaps, and teleport to original position + float fX, fY, fZ, fO; + m_creature->GetRespawnCoord(fX, fY, fZ, &fO); + m_creature->NearTeleportTo(fX, fY, fZ, fO); + + ScriptedAI::EnterEvadeMode(); + } + + void MovementInform(uint32 uiMoveType, uint32 uiPointId) override + { + if (uiMoveType != POINT_MOTION_TYPE || !uiPointId) + return; + + SetCombatMovement(true); + DoStartMovement(m_creature->getVictim()); + } + + // Wrapper to despawn the bomb riders on evade / death + void DoDespawnBombRiders() + { + if (m_lBombRiderGuidsList.empty()) + return; + + for (GuidList::const_iterator itr = m_lBombRiderGuidsList.begin(); itr != m_lBombRiderGuidsList.end(); ++itr) + { + if (Creature* pRider = m_creature->GetMap()->GetCreature(*itr)) + pRider->ForcedDespawn(); + } + } + + void UpdateAI(const uint32 uiDiff) override + { + if (!m_creature->SelectHostileTarget() || !m_creature->getVictim()) + return; + + // Bat phase + if (m_bIsPhaseOne) + { + // Phase Switch at 50% + if (m_creature->GetHealthPercent() < 50.0f) + { + m_creature->RemoveAurasDueToSpell(SPELL_BAT_FORM); + m_creature->SetLevitate(false); + DoResetThreat(); + m_bIsPhaseOne = false; + return; + } + + if (m_uiChargeTimer < uiDiff) + { + if (Unit* pTarget = m_creature->SelectAttackingTarget(ATTACKING_TARGET_RANDOM, 0)) + { + if (DoCastSpellIfCan(pTarget, SPELL_CHARGE) == CAST_OK) + m_uiChargeTimer = urand(15000, 30000); + } + } + else + m_uiChargeTimer -= uiDiff; + + if (m_uiSwoopTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature->getVictim(), SPELL_SWOOP) == CAST_OK) + m_uiSwoopTimer = urand(4000, 9000); + } + else + m_uiSwoopTimer -= uiDiff; + + if (m_uiSonicBurstTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature, SPELL_SONIC_BURST) == CAST_OK) + m_uiSonicBurstTimer = urand(8000, 13000); + } + else + m_uiSonicBurstTimer -= uiDiff; + + if (m_uiSpawnBatsTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature, SPELL_SUMMON_FRENZIED_BATS) == CAST_OK) + { + DoScriptText(SAY_SHRIEK, m_creature); + m_uiSpawnBatsTimer = 60000; + } + } + else + m_uiSpawnBatsTimer -= uiDiff; + } + // Troll phase + else + { + if (m_uiShadowWordPainTimer < uiDiff) + { + if (Unit* pTarget = m_creature->SelectAttackingTarget(ATTACKING_TARGET_RANDOM, 0)) + { + if (DoCastSpellIfCan(pTarget, SPELL_SHADOW_WORD_PAIN) == CAST_OK) + m_uiShadowWordPainTimer = urand(12000, 18000); + } + } + else + m_uiShadowWordPainTimer -= uiDiff; + + if (m_uiMindFlayTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature->getVictim(), SPELL_MIND_FLAY) == CAST_OK) + m_uiMindFlayTimer = 16000; + } + else + m_uiMindFlayTimer -= uiDiff; + + if (m_uiChainMindFlayTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature, SPELL_BLOOD_LEECH) == CAST_OK) + m_uiChainMindFlayTimer = urand(15000, 30000); + } + else + m_uiChainMindFlayTimer -= uiDiff; + + if (m_uiGreaterHealTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature, SPELL_GREATERHEAL, CAST_INTERRUPT_PREVIOUS) == CAST_OK) + { + DoScriptText(SAY_HEAL, m_creature); + m_uiGreaterHealTimer = urand(25000, 35000); + } + } + else + m_uiGreaterHealTimer -= uiDiff; + + if (m_uiFlyingBatsTimer) + { + if (m_uiFlyingBatsTimer <= uiDiff) + { + // Note: the bat riders summoning and movement may need additional research + for (uint8 i = 0; i < 3; ++i) + { + if (Unit* pTarget = m_creature->SelectAttackingTarget(ATTACKING_TARGET_RANDOM, 0)) + m_creature->SummonCreature(NPC_BAT_RIDER, pTarget->GetPositionX(), pTarget->GetPositionY(), pTarget->GetPositionZ() + 15.0f, 0, TEMPSUMMON_DEAD_DESPAWN, 0); + } + DoScriptText(SAY_RAIN_FIRE, m_creature); + + m_uiFlyingBatsTimer = 0; + } + else + m_uiFlyingBatsTimer -= uiDiff; + } + } + + DoMeleeAttackIfReady(); + } +}; + +struct npc_gurubashi_bat_riderAI : public ScriptedAI +{ + npc_gurubashi_bat_riderAI(Creature* pCreature) : ScriptedAI(pCreature) + { + m_bIsSummon = m_creature->IsTemporarySummon(); + Reset(); + } + + bool m_bIsSummon; + bool m_bHasDoneConcoction; + + uint32 m_uiInfectedBiteTimer; + uint32 m_uiBattleCommandTimer; + + void Reset() override + { + m_uiInfectedBiteTimer = 6500; + m_uiBattleCommandTimer = 8000; + + m_bHasDoneConcoction = false; + + DoCastSpellIfCan(m_creature, SPELL_TRASH); + } + + void Aggro(Unit* /*pWho*/) override + { + // Don't attack if is summoned by Jeklik - the npc gets aggro because of the Liquid Fire + if (m_bIsSummon) + return; + + DoCastSpellIfCan(m_creature, SPELL_DEMORALIZING_SHOUT); + // For normal mobs flag needs to be removed + m_creature->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE); + } + + void AttackStart(Unit* pWho) override + { + // Don't attack if is summoned by Jeklik + if (m_bIsSummon) + return; + + ScriptedAI::AttackStart(pWho); + } + + void MoveInLineOfSight(Unit* pWho) override + { + // Don't attack if is summoned by Jeklik + if (m_bIsSummon) + return; + + ScriptedAI::MoveInLineOfSight(pWho); + } + + void UpdateAI(const uint32 uiDiff) override + { + if (!m_creature->SelectHostileTarget() || !m_creature->getVictim()) + return; + + if (!m_bHasDoneConcoction && m_creature->GetHealthPercent() < 50.0f) + { + if (DoCastSpellIfCan(m_creature, SPELL_UNSTABLE_CONCOCTION) == CAST_OK) + m_bHasDoneConcoction = true; + } + + if (m_uiInfectedBiteTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature->getVictim(), SPELL_INFECTED_BITE) == CAST_OK) + m_uiInfectedBiteTimer = 6500; + } + else + m_uiInfectedBiteTimer -= uiDiff; + + if (m_uiBattleCommandTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature, SPELL_BATTLE_COMMAND) == CAST_OK) + m_uiBattleCommandTimer = 25000; + } + else + m_uiBattleCommandTimer -= uiDiff; + + DoMeleeAttackIfReady(); + } +}; + +CreatureAI* GetAI_boss_jeklik(Creature* pCreature) +{ + return new boss_jeklikAI(pCreature); +} + +CreatureAI* GetAI_npc_gurubashi_bat_rider(Creature* pCreature) +{ + return new npc_gurubashi_bat_riderAI(pCreature); +} + +void AddSC_boss_jeklik() +{ + Script* pNewScript; + + pNewScript = new Script; + pNewScript->Name = "boss_jeklik"; + pNewScript->GetAI = &GetAI_boss_jeklik; + pNewScript->RegisterSelf(); + + pNewScript = new Script; + pNewScript->Name = "npc_gurubashi_bat_rider"; + pNewScript->GetAI = &GetAI_npc_gurubashi_bat_rider; + pNewScript->RegisterSelf(); +} diff --git a/src/modules/SD2/scripts/eastern_kingdoms/zulgurub/boss_jindo.cpp b/src/modules/SD2/scripts/eastern_kingdoms/zulgurub/boss_jindo.cpp new file mode 100644 index 000000000..7ab4723d1 --- /dev/null +++ b/src/modules/SD2/scripts/eastern_kingdoms/zulgurub/boss_jindo.cpp @@ -0,0 +1,221 @@ +/* Copyright (C) 2006 - 2013 ScriptDev2 + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with 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 invisible is removed as of Attacking (core bug) - MANY HACKZ!! +SDCategory: Zul'Gurub +EndScriptData */ + +#include "precompiled.h" +#include "zulgurub.h" + +enum +{ + SAY_AGGRO = -1309014, + + SPELL_BRAINWASH_TOTEM = 24262, + SPELL_POWERFULL_HEALING_WARD = 24309, + SPELL_HEX = 24053, + SPELL_DELUSIONS_OF_JINDO = 24306, + SPELL_SHADE_OF_JINDO = 24308, // Spell was removed from DBC around TBC; will summon npcs manually! + + SPELL_HEALING_WARD_HEAL = 24311, + + // npcs + NPC_SHADE_OF_JINDO = 14986, + NPC_SACRIFICED_TROLL = 14826, + NPC_POWERFULL_HEALING_WARD = 14987, + + MAX_SKELETONS = 9, +}; + +static const float aPitTeleportLocs[4] = +{ + -11583.7783f, -1249.4278f, 77.5471f, 4.745f +}; + +struct boss_jindoAI : public ScriptedAI +{ + boss_jindoAI(Creature* pCreature) : ScriptedAI(pCreature) + { + m_pInstance = (ScriptedInstance*)pCreature->GetInstanceData(); + Reset(); + } + + ScriptedInstance* m_pInstance; + + uint32 m_uiBrainWashTotemTimer; + uint32 m_uiHealingWardTimer; + uint32 m_uiHexTimer; + uint32 m_uiDelusionsTimer; + uint32 m_uiTeleportTimer; + + void Reset() override + { + m_uiBrainWashTotemTimer = 20000; + m_uiHealingWardTimer = 16000; + m_uiHexTimer = 8000; + m_uiDelusionsTimer = 10000; + m_uiTeleportTimer = 5000; + } + + void Aggro(Unit* /*pWho*/) override + { + DoScriptText(SAY_AGGRO, m_creature); + } + + void SummonedCreatureJustDied(Creature* pSummoned) override + { + if (pSummoned->GetEntry() == NPC_POWERFULL_HEALING_WARD) + m_uiHealingWardTimer = 15000; // how long delay? + } + + void UpdateAI(const uint32 uiDiff) override + { + if (!m_creature->SelectHostileTarget() || !m_creature->getVictim()) + return; + + // Brain Wash Totem Timer + if (m_uiBrainWashTotemTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature, SPELL_BRAINWASH_TOTEM) == CAST_OK) + m_uiBrainWashTotemTimer = urand(18000, 26000); + } + else + m_uiBrainWashTotemTimer -= uiDiff; + + // Healing Ward Timer + if (m_uiHealingWardTimer) + { + if (m_uiHealingWardTimer <= uiDiff) + { + if (DoCastSpellIfCan(m_creature, SPELL_POWERFULL_HEALING_WARD) == CAST_OK) + m_uiHealingWardTimer = 0; + } + else + m_uiHealingWardTimer -= uiDiff; + } + + // Hex Timer + if (m_uiHexTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature->getVictim(), SPELL_HEX) == CAST_OK) + m_uiHexTimer = urand(12000, 20000); + } + else + m_uiHexTimer -= uiDiff; + + // Casting the delusion curse with a shade. So shade will attack the same target with the curse. + if (m_uiDelusionsTimer < uiDiff) + { + // random target except the tank + Unit* pTarget = m_creature->SelectAttackingTarget(ATTACKING_TARGET_RANDOM, 1); + if (!pTarget) + pTarget = m_creature->getVictim(); + + if (DoCastSpellIfCan(pTarget, SPELL_DELUSIONS_OF_JINDO) == CAST_OK) + { + float fX, fY, fZ; + m_creature->GetRandomPoint(pTarget->GetPositionX(), pTarget->GetPositionY(), pTarget->GetPositionZ(), 5.0f, fX, fY, fZ); + if (Creature* pSummoned = m_creature->SummonCreature(NPC_SHADE_OF_JINDO, fX, fY, fZ, 0, TEMPSUMMON_TIMED_OOC_DESPAWN, 15000)) + pSummoned->AI()->AttackStart(pTarget); + + m_uiDelusionsTimer = urand(4000, 12000); + } + } + else + m_uiDelusionsTimer -= uiDiff; + + // Teleporting a random player and spawning 9 skeletons that will attack this player + if (m_uiTeleportTimer < uiDiff) + { + if (Unit* pTarget = m_creature->SelectAttackingTarget(ATTACKING_TARGET_RANDOM, 0)) + { + DoTeleportPlayer(pTarget, aPitTeleportLocs[0], aPitTeleportLocs[1], aPitTeleportLocs[2], aPitTeleportLocs[3]); + + // summon 9 skeletons in the pit at random points + float fX, fY, fZ; + for (uint8 i = 0; i < MAX_SKELETONS; ++i) + { + m_creature->GetRandomPoint(aPitTeleportLocs[0], aPitTeleportLocs[1], aPitTeleportLocs[2], 4.0f, fX, fY, fZ); + if (Creature* pSummoned = m_creature->SummonCreature(NPC_SACRIFICED_TROLL, fX, fY, fZ, 0.0f, TEMPSUMMON_TIMED_OOC_DESPAWN, 15000)) + pSummoned->AI()->AttackStart(pTarget); + } + + m_uiTeleportTimer = urand(15000, 23000); + } + } + else + m_uiTeleportTimer -= uiDiff; + + DoMeleeAttackIfReady(); + } +}; + +// HACK script! Should not need to have totems in sd2 +struct mob_healing_wardAI : public ScriptedAI +{ + mob_healing_wardAI(Creature* pCreature) : ScriptedAI(pCreature) { Reset(); } + + uint32 m_uiHealTimer; + + void Reset() override + { + m_uiHealTimer = 3000; // Timer unknown, sources go over 1s, per tick to 3s, keep 3s as in original script + } + + void AttackStart(Unit* /*pWho*/) override {} + void MoveInLineOfSight(Unit* /*pWho*/) override {} + + void UpdateAI(const uint32 uiDiff) override + { + // Heal Timer + if (m_uiHealTimer < uiDiff) + { + DoCastSpellIfCan(m_creature, SPELL_HEALING_WARD_HEAL); + m_uiHealTimer = 3000; + } + else + m_uiHealTimer -= uiDiff; + } +}; + +CreatureAI* GetAI_boss_jindo(Creature* pCreature) +{ + return new boss_jindoAI(pCreature); +} + +CreatureAI* GetAI_mob_healing_ward(Creature* pCreature) +{ + return new mob_healing_wardAI(pCreature); +} + +void AddSC_boss_jindo() +{ + Script* pNewScript; + + pNewScript = new Script; + pNewScript->Name = "boss_jindo"; + pNewScript->GetAI = &GetAI_boss_jindo; + pNewScript->RegisterSelf(); + + pNewScript = new Script; + pNewScript->Name = "mob_healing_ward"; + pNewScript->GetAI = &GetAI_mob_healing_ward; + pNewScript->RegisterSelf(); +} diff --git a/src/modules/SD2/scripts/eastern_kingdoms/zulgurub/boss_mandokir.cpp b/src/modules/SD2/scripts/eastern_kingdoms/zulgurub/boss_mandokir.cpp new file mode 100644 index 000000000..944de5d44 --- /dev/null +++ b/src/modules/SD2/scripts/eastern_kingdoms/zulgurub/boss_mandokir.cpp @@ -0,0 +1,407 @@ +/* Copyright (C) 2006 - 2013 ScriptDev2 + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with 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: 80 +SDComment: test Threating Gaze. Script depends on ACID script for Vilebranch Speaker +SDCategory: Zul'Gurub +EndScriptData */ + +#include "precompiled.h" +#include "zulgurub.h" + +enum +{ + NPC_OHGAN = 14988, + NPC_CHAINED_SPIRIT = 15117, // resing spirits + + SAY_AGGRO = -1309015, + SAY_DING_KILL = -1309016, + SAY_GRATS_JINDO = -1309017, + SAY_WATCH = -1309018, + SAY_WATCH_WHISPER = -1309019, + + EMOTE_RAGE = -1309024, + + SPELL_CHARGE = 24315, + SPELL_CLEAVE = 20691, + SPELL_FEAR = 29321, + SPELL_WHIRLWIND = 24236, + SPELL_MORTAL_STRIKE = 24573, + SPELL_ENRAGE = 23537, + SPELL_WATCH = 24314, + SPELL_SUMMON_PLAYER = 25104, + SPELL_LEVEL_UP = 24312, + + // Ohgans Spells + SPELL_SUNDERARMOR = 24317, + + // Chained Spirit Spells + SPELL_REVIVE = 24341, + + POINT_DOWNSTAIRS = 1 +}; + +struct SpawnLocations +{ + float fX, fY, fZ, fAng; +}; + +static SpawnLocations aSpirits[] = +{ + { -12150.9f, -1956.24f, 133.407f, 2.57835f}, + { -12157.1f, -1972.78f, 133.947f, 2.64903f}, + { -12172.3f, -1982.63f, 134.061f, 1.48664f}, + { -12194.0f, -1979.54f, 132.194f, 1.45916f}, + { -12211.3f, -1978.49f, 133.580f, 1.35705f}, + { -12228.4f, -1977.10f, 132.728f, 1.25495f}, + { -12250.0f, -1964.78f, 135.066f, 0.92901f}, + { -12264.0f, -1953.08f, 134.072f, 0.62663f}, + { -12289.0f, -1924.00f, 132.620f, 5.37829f}, + { -12267.3f, -1902.26f, 131.328f, 5.32724f}, + { -12255.3f, -1893.53f, 134.026f, 5.06413f}, + { -12229.9f, -1891.39f, 134.704f, 4.40047f}, + { -12215.9f, -1889.09f, 137.273f, 4.70285f}, + { -12200.5f, -1890.69f, 135.777f, 4.84422f}, + { -12186.0f, -1890.12f, 134.261f, 4.36513f}, + { -12246.3f, -1890.09f, 135.475f, 4.73427f}, + { -12170.7f, -1894.85f, 133.852f, 3.51690f}, + { -12279.0f, -1931.92f, 136.130f, 0.04151f}, + { -12266.1f, -1940.72f, 132.606f, 0.70910f} +}; + +struct boss_mandokirAI : public ScriptedAI +{ + boss_mandokirAI(Creature* pCreature) : ScriptedAI(pCreature) + { + m_pInstance = (ScriptedInstance*)pCreature->GetInstanceData(); + Reset(); + } + + ScriptedInstance* m_pInstance; + + uint32 m_uiWatchTimer; + uint32 m_uiCleaveTimer; + uint32 m_uiWhirlwindTimer; + uint32 m_uiFearTimer; + uint32 m_uiMortalStrikeTimer; + uint32 m_uiCheckTimer; + + uint8 m_uiKillCount; + + float m_fTargetThreat; + ObjectGuid m_watchTargetGuid; + + void Reset() override + { + m_uiWatchTimer = 33000; + m_uiCleaveTimer = 7000; + m_uiWhirlwindTimer = 20000; + m_uiFearTimer = 1000; + m_uiMortalStrikeTimer = 1000; + m_uiCheckTimer = 1000; + + m_uiKillCount = 0; + + m_fTargetThreat = 0.0f; + } + + void Aggro(Unit* /*pWho*/) override + { + DoScriptText(SAY_AGGRO, m_creature); + + for (uint8 i = 0; i < countof(aSpirits); ++i) + m_creature->SummonCreature(NPC_CHAINED_SPIRIT, aSpirits[i].fX, aSpirits[i].fY, aSpirits[i].fZ, aSpirits[i].fAng, TEMPSUMMON_CORPSE_DESPAWN, 0); + + // At combat start Mandokir is mounted so we must unmount it first + m_creature->Unmount(); + + // And summon his raptor + m_creature->SummonCreature(NPC_OHGAN, 0.0f, 0.0f, 0.0f, 0.0f, TEMPSUMMON_TIMED_OOC_DESPAWN, 35000); + + if (m_pInstance) + m_pInstance->SetData(TYPE_OHGAN, IN_PROGRESS); + } + + void JustReachedHome() override + { + if (m_pInstance) + m_pInstance->SetData(TYPE_OHGAN, FAIL); + } + + void JustDied(Unit* /*pKiller*/) override + { + if (m_pInstance) + m_pInstance->SetData(TYPE_OHGAN, DONE); + } + + void EnterEvadeMode() override + { + m_creature->RemoveAllAurasOnEvade(); + m_creature->DeleteThreatList(); + m_creature->CombatStop(true); + m_creature->LoadCreatureAddon(true); + + // should evade to bottom of the stairs when raid fail + if (m_creature->IsAlive()) + m_creature->GetMotionMaster()->MovePoint(0, aMandokirDownstairsPos[0], aMandokirDownstairsPos[1], aMandokirDownstairsPos[2]); + + m_creature->SetLootRecipient(NULL); + + Reset(); + } + + void KilledUnit(Unit* pVictim) override + { + if (pVictim->GetTypeId() == TYPEID_PLAYER) + { + ++m_uiKillCount; + + if (m_uiKillCount == 3) + { + DoScriptText(SAY_DING_KILL, m_creature); + + if (m_pInstance) + { + if (Creature* pJindo = m_pInstance->GetSingleCreatureFromStorage(NPC_JINDO)) + { + if (pJindo->IsAlive()) + DoScriptText(SAY_GRATS_JINDO, pJindo); + } + } + + DoCastSpellIfCan(m_creature, SPELL_LEVEL_UP, CAST_TRIGGERED); + m_uiKillCount = 0; + } + + if (m_creature->IsInCombat()) + { + if (Creature* pSpirit = GetClosestCreatureWithEntry(pVictim, NPC_CHAINED_SPIRIT, 50.0f)) + pSpirit->CastSpell(pVictim, SPELL_REVIVE, false); + } + } + } + + void JustSummoned(Creature* pSummoned) override + { + if (pSummoned->GetEntry() == NPC_OHGAN) + { + if (m_creature->getVictim()) + pSummoned->AI()->AttackStart(m_creature->getVictim()); + } + } + + void SummonedCreatureJustDied(Creature* pSummoned) override + { + if (pSummoned->GetEntry() == NPC_OHGAN) + { + DoCastSpellIfCan(m_creature, SPELL_ENRAGE, CAST_TRIGGERED); + DoScriptText(EMOTE_RAGE, m_creature); + } + } + + void SpellHitTarget(Unit* pTarget, const SpellEntry* pSpell) override + { + if (pSpell->Id == SPELL_WATCH) + { + DoScriptText(SAY_WATCH, m_creature, pTarget); + DoScriptText(SAY_WATCH_WHISPER, m_creature, pTarget); + + m_watchTargetGuid = pTarget->GetObjectGuid(); + m_fTargetThreat = m_creature->GetThreatManager().getThreat(pTarget); + m_uiWatchTimer = 6000; + + // Could use this instead of hard coded timer for the above (but no script access), + // but would still a hack since we should better use the dummy, at aura removal + // SpellDurationEntry* const pDuration = sSpellDurationStore.LookupEntry(pSpell->DurationIndex); + } + } + + void MovementInform(uint32 uiMoveType, uint32 uiPointId) override + { + if (uiMoveType != POINT_MOTION_TYPE || !m_pInstance) + return; + + if (uiPointId == POINT_DOWNSTAIRS) + { + m_creature->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_PASSIVE); + m_creature->SetInCombatWithZone(); + } + } + + void UpdateAI(const uint32 uiDiff) override + { + if (!m_creature->SelectHostileTarget() || !m_creature->getVictim()) + return; + + if (m_uiWatchTimer < uiDiff) + { + // If someone is watched + if (m_watchTargetGuid) + { + Player* pWatchTarget = m_creature->GetMap()->GetPlayer(m_watchTargetGuid); + + // If threat is higher that previously saved, mandokir will act + if (pWatchTarget && pWatchTarget->IsAlive() && m_creature->GetThreatManager().getThreat(pWatchTarget) > m_fTargetThreat) + { + if (!m_creature->IsWithinLOSInMap(pWatchTarget)) + m_creature->CastSpell(pWatchTarget, SPELL_SUMMON_PLAYER, true); + + DoCastSpellIfCan(pWatchTarget, SPELL_CHARGE); + } + + m_watchTargetGuid.Clear(); + } + else + { + if (Unit* pTarget = m_creature->SelectAttackingTarget(ATTACKING_TARGET_RANDOM, 0)) + { + if (Player* pPlayer = pTarget->GetCharmerOrOwnerPlayerOrPlayerItself()) + m_creature->CastSpell(pPlayer, SPELL_WATCH, false); + } + } + + m_uiWatchTimer = 20000; + } + else + m_uiWatchTimer -= uiDiff; + + if (!m_watchTargetGuid) + { + // Cleave + if (m_uiCleaveTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature->getVictim(), SPELL_CLEAVE) == CAST_OK) + m_uiCleaveTimer = 7000; + } + else + m_uiCleaveTimer -= uiDiff; + + // Whirlwind + if (m_uiWhirlwindTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature, SPELL_WHIRLWIND) == CAST_OK) + m_uiWhirlwindTimer = 18000; + } + else + m_uiWhirlwindTimer -= uiDiff; + + // If more then 3 targets in melee range mandokir will cast fear + if (m_uiFearTimer < uiDiff) + { + uint8 uiTargetInRangeCount = 0; + + ThreatList const& tList = m_creature->GetThreatManager().getThreatList(); + for (ThreatList::const_iterator i = tList.begin(); i != tList.end(); ++i) + { + Unit* pTarget = m_creature->GetMap()->GetUnit((*i)->getUnitGuid()); + + if (pTarget && pTarget->GetTypeId() == TYPEID_PLAYER && m_creature->CanReachWithMeleeAttack(pTarget)) + ++uiTargetInRangeCount; + } + + if (uiTargetInRangeCount > 3) + DoCastSpellIfCan(m_creature, SPELL_FEAR); + + m_uiFearTimer = 4000; + } + else + m_uiFearTimer -= uiDiff; + + // Mortal Strike if target below 50% hp + if (m_creature->getVictim()->GetHealthPercent() < 50.0f) + { + if (m_uiMortalStrikeTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature->getVictim(), SPELL_MORTAL_STRIKE) == CAST_OK) + m_uiMortalStrikeTimer = 15000; + } + else + m_uiMortalStrikeTimer -= uiDiff; + } + } + + DoMeleeAttackIfReady(); + } +}; + +// Ohgan +struct mob_ohganAI : public ScriptedAI +{ + mob_ohganAI(Creature* pCreature) : ScriptedAI(pCreature) { Reset(); } + + uint32 m_uiSunderArmorTimer; + + void Reset() override + { + m_uiSunderArmorTimer = 5000; + } + + void KilledUnit(Unit* pVictim) override + { + if (pVictim->GetTypeId() == TYPEID_PLAYER) + { + if (m_creature->IsInCombat()) + { + if (Creature* pSpirit = GetClosestCreatureWithEntry(pVictim, NPC_CHAINED_SPIRIT, 50.0f)) + pSpirit->CastSpell(pVictim, SPELL_REVIVE, false); + } + } + } + + void UpdateAI(const uint32 uiDiff) override + { + if (!m_creature->SelectHostileTarget() || !m_creature->getVictim()) + return; + + // SunderArmor + if (m_uiSunderArmorTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature->getVictim(), SPELL_SUNDERARMOR) == CAST_OK) + m_uiSunderArmorTimer = urand(10000, 15000); + } + else + m_uiSunderArmorTimer -= uiDiff; + + 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* pNewScript; + + pNewScript = new Script; + pNewScript->Name = "boss_mandokir"; + pNewScript->GetAI = &GetAI_boss_mandokir; + pNewScript->RegisterSelf(); + + pNewScript = new Script; + pNewScript->Name = "mob_ohgan"; + pNewScript->GetAI = &GetAI_mob_ohgan; + pNewScript->RegisterSelf(); +} diff --git a/src/modules/SD2/scripts/eastern_kingdoms/zulgurub/boss_marli.cpp b/src/modules/SD2/scripts/eastern_kingdoms/zulgurub/boss_marli.cpp new file mode 100644 index 000000000..384f84d9e --- /dev/null +++ b/src/modules/SD2/scripts/eastern_kingdoms/zulgurub/boss_marli.cpp @@ -0,0 +1,251 @@ +/* Copyright (C) 2006 - 2013 ScriptDev2 + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with 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: 90 +SDComment: Enlarge for small spiders NYI +SDCategory: Zul'Gurub +EndScriptData */ + +#include "precompiled.h" +#include "zulgurub.h" + +enum +{ + SAY_AGGRO = -1309005, + SAY_TRANSFORM = -1309006, + SAY_TRANSFORM_BACK = -1309025, + SAY_SPIDER_SPAWN = -1309007, + SAY_DEATH = -1309008, + + // Spider form spells + SPELL_CORROSIVE_POISON = 24111, + SPELL_CHARGE = 22911, + SPELL_ENVELOPING_WEBS = 24110, + SPELL_POISON_SHOCK = 24112, // purpose of this spell is unk + + // Troll form spells + SPELL_POISON_VOLLEY = 24099, + SPELL_DRAIN_LIFE = 24300, + SPELL_ENLARGE = 24109, // purpose of this spell is unk + SPELL_SPIDER_EGG = 24082, // removed from DBC - should trigger 24081 which summons 15041 + + // common spells + SPELL_SPIDER_FORM = 24084, + SPELL_TRANSFORM_BACK = 24085, + SPELL_TRASH = 3391, + SPELL_HATCH_EGGS = 24083, // note this should only target 4 eggs! +}; + +struct boss_marliAI : public ScriptedAI +{ + boss_marliAI(Creature* pCreature) : ScriptedAI(pCreature) + { + m_pInstance = (ScriptedInstance*)pCreature->GetInstanceData(); + Reset(); + } + + ScriptedInstance* m_pInstance; + + uint32 m_uiPoisonVolleyTimer; + uint32 m_uiSpawnSpiderTimer; + uint32 m_uiChargeTimer; + uint32 m_uiTransformTimer; + uint32 m_uiDrainLifeTimer; + uint32 m_uiCorrosivePoisonTimer; + uint32 m_uiWebsTimer; + uint32 m_uiTrashTimer; + + bool m_bIsInPhaseTwo; + + void Reset() override + { + m_uiPoisonVolleyTimer = 15000; + m_uiSpawnSpiderTimer = 55000; + m_uiTransformTimer = 60000; + m_uiDrainLifeTimer = 30000; + m_uiCorrosivePoisonTimer = 1000; + m_uiWebsTimer = 5000; + m_uiTrashTimer = 5000; + + m_bIsInPhaseTwo = false; + } + + void Aggro(Unit* /*pWho*/) override + { + DoScriptText(SAY_AGGRO, m_creature); + + DoCastSpellIfCan(m_creature, SPELL_HATCH_EGGS); + } + + void JustDied(Unit* /*pKiller*/) override + { + DoScriptText(SAY_DEATH, m_creature); + + if (m_pInstance) + m_pInstance->SetData(TYPE_MARLI, DONE); + } + + void JustReachedHome() override + { + if (m_pInstance) + m_pInstance->SetData(TYPE_MARLI, FAIL); + } + + void UpdateAI(const uint32 uiDiff) override + { + if (!m_creature->SelectHostileTarget() || !m_creature->getVictim()) + return; + + // Troll phase + if (!m_bIsInPhaseTwo) + { + if (m_uiPoisonVolleyTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature, SPELL_POISON_VOLLEY) == CAST_OK) + m_uiPoisonVolleyTimer = urand(10000, 20000); + } + else + m_uiPoisonVolleyTimer -= uiDiff; + + if (m_uiDrainLifeTimer < uiDiff) + { + if (Unit* pTarget = m_creature->SelectAttackingTarget(ATTACKING_TARGET_RANDOM, 0)) + { + if (DoCastSpellIfCan(pTarget, SPELL_DRAIN_LIFE) == CAST_OK) + m_uiDrainLifeTimer = urand(20000, 50000); + } + } + else + m_uiDrainLifeTimer -= uiDiff; + + if (m_uiSpawnSpiderTimer < uiDiff) + { + // Workaround for missing spell 24082 - creature always selects the closest egg for hatch + if (m_pInstance) + { + if (GameObject* pEgg = GetClosestGameObjectWithEntry(m_creature, GO_SPIDER_EGG, 30.0f)) + { + if (urand(0, 1)) + DoScriptText(SAY_SPIDER_SPAWN, m_creature); + + pEgg->Use(m_creature); + m_uiSpawnSpiderTimer = 60000; + } + } + } + else + m_uiSpawnSpiderTimer -= uiDiff; + } + // Spider phase + else + { + if (m_uiWebsTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature, SPELL_ENVELOPING_WEBS) == CAST_OK) + { + m_uiWebsTimer = urand(15000, 20000); + m_uiChargeTimer = 1000; + } + } + else + m_uiWebsTimer -= uiDiff; + + if (m_uiChargeTimer) + { + if (m_uiChargeTimer < uiDiff) + { + // ToDo: research if the selected target shouldn't have the enveloping webs aura + if (Unit* pTarget = m_creature->SelectAttackingTarget(ATTACKING_TARGET_RANDOM, 0, SPELL_CHARGE, SELECT_FLAG_NOT_IN_MELEE_RANGE)) + { + if (DoCastSpellIfCan(pTarget, SPELL_CHARGE) == CAST_OK) + { + DoResetThreat(); + m_uiChargeTimer = 0; + } + } + } + else + m_uiChargeTimer -= uiDiff; + } + + if (m_uiCorrosivePoisonTimer < uiDiff) + { + if (Unit* pTarget = m_creature->SelectAttackingTarget(ATTACKING_TARGET_RANDOM, 0)) + { + if (DoCastSpellIfCan(pTarget, SPELL_CORROSIVE_POISON) == CAST_OK) + m_uiCorrosivePoisonTimer = urand(25000, 35000); + } + } + else + m_uiCorrosivePoisonTimer -= uiDiff; + } + + // Transform from Troll to Spider and back + if (m_uiTransformTimer < uiDiff) + { + if (!m_bIsInPhaseTwo) + { + if (DoCastSpellIfCan(m_creature, SPELL_SPIDER_FORM) == CAST_OK) + { + DoScriptText(SAY_TRANSFORM, m_creature); + DoResetThreat(); + m_uiTransformTimer = 60000; + m_uiWebsTimer = 5000; + m_bIsInPhaseTwo = true; + } + } + else + { + if (DoCastSpellIfCan(m_creature, SPELL_TRANSFORM_BACK) == CAST_OK) + { + DoScriptText(SAY_TRANSFORM_BACK, m_creature); + m_creature->RemoveAurasDueToSpell(SPELL_SPIDER_FORM); + m_bIsInPhaseTwo = false; + m_uiTransformTimer = 60000; + } + } + } + else + m_uiTransformTimer -= uiDiff; + + if (m_uiTrashTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature->getVictim(), SPELL_TRASH) == CAST_OK) + m_uiTrashTimer = urand(10000, 20000); + } + else + m_uiTrashTimer -= uiDiff; + + DoMeleeAttackIfReady(); + } +}; + +CreatureAI* GetAI_boss_marli(Creature* pCreature) +{ + return new boss_marliAI(pCreature); +} + +void AddSC_boss_marli() +{ + Script* pNewScript; + + pNewScript = new Script; + pNewScript->Name = "boss_marli"; + pNewScript->GetAI = &GetAI_boss_marli; + pNewScript->RegisterSelf(); +} diff --git a/src/modules/SD2/scripts/eastern_kingdoms/zulgurub/boss_renataki.cpp b/src/modules/SD2/scripts/eastern_kingdoms/zulgurub/boss_renataki.cpp new file mode 100644 index 000000000..45927b1cc --- /dev/null +++ b/src/modules/SD2/scripts/eastern_kingdoms/zulgurub/boss_renataki.cpp @@ -0,0 +1,132 @@ +/* Copyright (C) 2006 - 2013 ScriptDev2 + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with 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" + +enum +{ + SPELL_THOUSAND_BLADES = 24649, + SPELL_VANISH = 24699, + SPELL_GOUGE = 24698, + SPELL_TRASH = 3391 +}; + +struct boss_renatakiAI : public ScriptedAI +{ + boss_renatakiAI(Creature* pCreature) : ScriptedAI(pCreature) { Reset(); } + + uint32 m_uiVanishTimer; + uint32 m_uiAmbushTimer; + uint32 m_uiGougeTimer; + uint32 m_uiThousandBladesTimer; + + void Reset() override + { + m_uiVanishTimer = urand(25000, 30000); + m_uiAmbushTimer = 0; + m_uiGougeTimer = urand(15000, 25000); + m_uiThousandBladesTimer = urand(4000, 8000); + } + + void EnterEvadeMode() override + { + // If is vanished, don't evade + if (m_uiAmbushTimer) + return; + + ScriptedAI::EnterEvadeMode(); + } + + void UpdateAI(const uint32 uiDiff) override + { + if (!m_creature->SelectHostileTarget() || !m_creature->getVictim()) + return; + + // Note: because the Vanish spell adds invisibility effect on the target, the timers won't be decreased during the vanish phase + if (m_uiAmbushTimer) + { + if (m_uiAmbushTimer <= uiDiff) + { + if (DoCastSpellIfCan(m_creature->getVictim(), SPELL_TRASH) == CAST_OK) + m_uiAmbushTimer = 0; + } + else + m_uiAmbushTimer -= uiDiff; + + // don't do anything else while vanished + return; + } + + // Invisible_Timer + if (m_uiVanishTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature, SPELL_VANISH) == CAST_OK) + { + m_uiVanishTimer = urand(25000, 40000); + m_uiAmbushTimer = 2000; + } + } + else + m_uiVanishTimer -= uiDiff; + + // Resetting some aggro so he attacks other gamers + if (m_uiGougeTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature, SPELL_GOUGE) == CAST_OK) + { + if (m_creature->GetThreatManager().getThreat(m_creature->getVictim())) + m_creature->GetThreatManager().modifyThreatPercent(m_creature->getVictim(), -50); + + m_uiGougeTimer = urand(7000, 20000); + } + } + else + m_uiGougeTimer -= uiDiff; + + // Thausand Blades + if (m_uiThousandBladesTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature->getVictim(), SPELL_THOUSAND_BLADES) == CAST_OK) + m_uiThousandBladesTimer = urand(7000, 12000); + } + else + m_uiThousandBladesTimer -= uiDiff; + + DoMeleeAttackIfReady(); + } +}; + +CreatureAI* GetAI_boss_renataki(Creature* pCreature) +{ + return new boss_renatakiAI(pCreature); +} + +void AddSC_boss_renataki() +{ + Script* pNewScript; + + pNewScript = new Script; + pNewScript->Name = "boss_renataki"; + pNewScript->GetAI = &GetAI_boss_renataki; + pNewScript->RegisterSelf(); +} diff --git a/src/modules/SD2/scripts/eastern_kingdoms/zulgurub/boss_thekal.cpp b/src/modules/SD2/scripts/eastern_kingdoms/zulgurub/boss_thekal.cpp new file mode 100644 index 000000000..cc6175e24 --- /dev/null +++ b/src/modules/SD2/scripts/eastern_kingdoms/zulgurub/boss_thekal.cpp @@ -0,0 +1,682 @@ +/* Copyright (C) 2006 - 2013 ScriptDev2 + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with 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 "zulgurub.h" + +enum +{ + SAY_AGGRO = -1309009, + SAY_DEATH = -1309010, + + SPELL_MORTAL_CLEAVE = 22859, + SPELL_SILENCE = 23207, + SPELL_FRENZY = 23342, + SPELL_FORCE_PUNCH = 24189, + SPELL_CHARGE = 24408, + SPELL_ENRAGE = 23537, + SPELL_SUMMON_TIGERS = 24183, + SPELL_TIGER_FORM = 24169, + SPELL_RESURRECT = 24173, + + // Zealot Lor'Khan Spells + SPELL_SHIELD = 25020, + SPELL_BLOODLUST = 24185, + SPELL_GREATER_HEAL = 24208, + SPELL_DISARM = 22691, + + // Zealot Lor'Khan Spells + SPELL_SWEEPING_STRIKES = 18765, + SPELL_SINISTER_STRIKE = 15667, + SPELL_GOUGE = 24698, + SPELL_KICK = 15614, + SPELL_BLIND = 21060, + + PHASE_NORMAL = 1, + PHASE_FAKE_DEATH = 2, + PHASE_WAITING = 3, + PHASE_TIGER = 4, +}; + +// abstract base class for faking death +struct boss_thekalBaseAI : public ScriptedAI +{ + boss_thekalBaseAI(Creature* pCreature) : ScriptedAI(pCreature) + { + m_uiPhase = PHASE_NORMAL; + } + + uint8 m_uiPhase; + + virtual void OnFakeingDeath() {} + virtual void OnRevive() {} + + void DamageTaken(Unit* /*pKiller*/, uint32& uiDamage) override + { + if (uiDamage < m_creature->GetHealth()) + return; + + // Prevent glitch if in fake death + if (m_uiPhase == PHASE_FAKE_DEATH || m_uiPhase == PHASE_WAITING) + { + uiDamage = 0; + return; + } + + // Only init fake in normal phase + if (m_uiPhase != PHASE_NORMAL) + return; + + uiDamage = 0; + + m_creature->InterruptNonMeleeSpells(true); + m_creature->SetHealth(0); + m_creature->StopMoving(); + m_creature->ClearComboPointHolders(); + m_creature->RemoveAllAurasOnDeath(); + m_creature->ModifyAuraState(AURA_STATE_HEALTHLESS_20_PERCENT, false); + m_creature->ModifyAuraState(AURA_STATE_HEALTHLESS_35_PERCENT, false); + m_creature->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE); + m_creature->ClearAllReactives(); + m_creature->GetMotionMaster()->Clear(); + m_creature->GetMotionMaster()->MoveIdle(); + m_creature->SetStandState(UNIT_STAND_STATE_DEAD); + + m_uiPhase = PHASE_FAKE_DEATH; + + OnFakeingDeath(); + } + + void Revive(bool bOnlyFlags = false) + { + m_creature->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE); + m_creature->SetStandState(UNIT_STAND_STATE_STAND); + + if (bOnlyFlags) + return; + + m_creature->SetHealth(m_creature->GetMaxHealth()); + m_uiPhase = PHASE_NORMAL; + + DoResetThreat(); + Reset(); + + // Assume Attack + if (m_creature->getVictim()) + m_creature->GetMotionMaster()->MoveChase(m_creature->getVictim()); + + OnRevive(); + } + + void PreventRevive() + { + if (m_creature->IsNonMeleeSpellCasted(true)) + m_creature->InterruptNonMeleeSpells(true); + + m_uiPhase = PHASE_WAITING; + } +}; + +struct boss_thekalAI : public boss_thekalBaseAI +{ + boss_thekalAI(Creature* pCreature) : boss_thekalBaseAI(pCreature) + { + m_pInstance = (ScriptedInstance*)pCreature->GetInstanceData(); + Reset(); + } + + ScriptedInstance* m_pInstance; + + uint32 m_uiMortalCleaveTimer; + uint32 m_uiSilenceTimer; + uint32 m_uiFrenzyTimer; + uint32 m_uiForcePunchTimer; + uint32 m_uiChargeTimer; + uint32 m_uiEnrageTimer; + uint32 m_uiSummonTigersTimer; + uint32 m_uiResurrectTimer; + + bool m_bEnraged; + + void Reset() override + { + m_uiMortalCleaveTimer = 4000; + m_uiSilenceTimer = 9000; + m_uiFrenzyTimer = 30000; + m_uiForcePunchTimer = 4000; + m_uiChargeTimer = 12000; + m_uiEnrageTimer = 32000; + m_uiSummonTigersTimer = 25000; + m_uiResurrectTimer = 10000; + m_uiPhase = PHASE_NORMAL; + + m_bEnraged = false; + + // remove fake death + Revive(true); + } + + void Aggro(Unit* /*pWho*/) override + { + DoScriptText(SAY_AGGRO, m_creature); + } + + void JustDied(Unit* /*pKiller*/) override + { + DoScriptText(SAY_DEATH, m_creature); + + if (!m_pInstance) + return; + + m_pInstance->SetData(TYPE_THEKAL, DONE); + + // remove the two adds + if (Creature* pZath = m_pInstance->GetSingleCreatureFromStorage(NPC_ZATH)) + pZath->ForcedDespawn(); + if (Creature* pLorkhan = m_pInstance->GetSingleCreatureFromStorage(NPC_LORKHAN)) + pLorkhan->ForcedDespawn(); + } + + void JustReachedHome() override + { + if (m_pInstance) + m_pInstance->SetData(TYPE_THEKAL, FAIL); + } + + // Only call in context where m_pInstance is valid + bool CanPreventAddsResurrect() + { + // If any add is alive, return false + if (m_pInstance->GetData(TYPE_ZATH) != SPECIAL || m_pInstance->GetData(TYPE_LORKHAN) != SPECIAL) + return false; + + // Else Prevent them Resurrecting + if (Creature* pLorkhan = m_pInstance->GetSingleCreatureFromStorage(NPC_LORKHAN)) + { + if (boss_thekalBaseAI* pFakerAI = dynamic_cast(pLorkhan->AI())) + pFakerAI->PreventRevive(); + } + if (Creature* pZath = m_pInstance->GetSingleCreatureFromStorage(NPC_ZATH)) + { + if (boss_thekalBaseAI* pFakerAI = dynamic_cast(pZath->AI())) + pFakerAI->PreventRevive(); + } + + return true; + } + + void OnFakeingDeath() + { + m_uiResurrectTimer = 10000; + + if (m_pInstance) + { + m_pInstance->SetData(TYPE_THEKAL, SPECIAL); + + // If both Adds are already dead, don't wait 10 seconds + if (CanPreventAddsResurrect()) + m_uiResurrectTimer = 1000; + } + } + + void OnRevive() + { + if (!m_pInstance) + return; + + // Both Adds are 'dead' enter tiger phase + if (CanPreventAddsResurrect()) + { + DoCastSpellIfCan(m_creature, SPELL_TIGER_FORM, CAST_TRIGGERED); + m_uiPhase = PHASE_TIGER; + } + } + + void UpdateAI(const uint32 uiDiff) override + { + if (!m_creature->SelectHostileTarget() || !m_creature->getVictim()) + return; + + switch (m_uiPhase) + { + case PHASE_FAKE_DEATH: + if (m_uiResurrectTimer < uiDiff) + { + // resurrect him in any case + DoCastSpellIfCan(m_creature, SPELL_RESURRECT); + + m_uiPhase = PHASE_WAITING; + if (m_pInstance) + { + CanPreventAddsResurrect(); + m_pInstance->SetData(TYPE_THEKAL, IN_PROGRESS); + } + } + else + m_uiResurrectTimer -= uiDiff; + + // No break needed here + case PHASE_WAITING: + return; + + case PHASE_NORMAL: + if (m_uiMortalCleaveTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature->getVictim(), SPELL_MORTAL_CLEAVE) == CAST_OK) + m_uiMortalCleaveTimer = urand(15000, 20000); + } + else + m_uiMortalCleaveTimer -= uiDiff; + + if (m_uiSilenceTimer < uiDiff) + { + if (Unit* pTarget = m_creature->SelectAttackingTarget(ATTACKING_TARGET_RANDOM, 0)) + { + if (DoCastSpellIfCan(pTarget, SPELL_SILENCE) == CAST_OK) + m_uiSilenceTimer = urand(20000, 25000); + } + } + else + m_uiSilenceTimer -= uiDiff; + + break; + case PHASE_TIGER: + if (m_uiChargeTimer < uiDiff) + { + if (Unit* pTarget = m_creature->SelectAttackingTarget(ATTACKING_TARGET_RANDOM, 0)) + { + if (DoCastSpellIfCan(pTarget, SPELL_CHARGE) == CAST_OK) + { + DoResetThreat(); + AttackStart(pTarget); + m_uiChargeTimer = urand(15000, 22000); + } + } + } + else + m_uiChargeTimer -= uiDiff; + + if (m_uiFrenzyTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature, SPELL_FRENZY) == CAST_OK) + m_uiFrenzyTimer = 30000; + } + else + m_uiFrenzyTimer -= uiDiff; + + if (m_uiForcePunchTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature, SPELL_FORCE_PUNCH) == CAST_OK) + m_uiForcePunchTimer = urand(16000, 21000); + } + else + m_uiForcePunchTimer -= uiDiff; + + if (m_uiSummonTigersTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature, SPELL_SUMMON_TIGERS) == CAST_OK) + m_uiSummonTigersTimer = urand(10000, 14000); + } + else + m_uiSummonTigersTimer -= uiDiff; + + if (!m_bEnraged && m_creature->GetHealthPercent() < 11.0f) + { + if (DoCastSpellIfCan(m_creature, SPELL_ENRAGE) == CAST_OK) + m_bEnraged = true; + } + + break; + } + + DoMeleeAttackIfReady(); + } +}; + +/*###### +## mob_zealot_lorkhan +######*/ + +struct mob_zealot_lorkhanAI : public boss_thekalBaseAI +{ + mob_zealot_lorkhanAI(Creature* pCreature) : boss_thekalBaseAI(pCreature) + { + m_pInstance = (ScriptedInstance*)pCreature->GetInstanceData(); + Reset(); + } + + ScriptedInstance* m_pInstance; + + uint32 m_uiShieldTimer; + uint32 m_uiBloodLustTimer; + uint32 m_uiGreaterHealTimer; + uint32 m_uiDisarmTimer; + uint32 m_uiResurrectTimer; + + void Reset() override + { + m_uiShieldTimer = 1000; + m_uiBloodLustTimer = 16000; + m_uiGreaterHealTimer = 32000; + m_uiDisarmTimer = 6000; + m_uiPhase = PHASE_NORMAL; + + if (m_pInstance) + m_pInstance->SetData(TYPE_LORKHAN, NOT_STARTED); + + Revive(true); + } + + void Aggro(Unit* /*pWho*/) override + { + if (m_pInstance) + m_pInstance->SetData(TYPE_LORKHAN, IN_PROGRESS); + } + + void OnFakeingDeath() + { + m_uiResurrectTimer = 10000; + + if (m_pInstance) + m_pInstance->SetData(TYPE_LORKHAN, SPECIAL); + } + + void UpdateAI(const uint32 uiDiff) override + { + if (!m_creature->SelectHostileTarget() || !m_creature->getVictim()) + return; + + switch (m_uiPhase) + { + case PHASE_FAKE_DEATH: + if (m_uiResurrectTimer < uiDiff) + { + if (!m_pInstance) + return; + + if (m_pInstance->GetData(TYPE_THEKAL) != SPECIAL || m_pInstance->GetData(TYPE_ZATH) != SPECIAL) + { + DoCastSpellIfCan(m_creature, SPELL_RESURRECT); + m_pInstance->SetData(TYPE_LORKHAN, IN_PROGRESS); + } + + m_uiPhase = PHASE_WAITING; + } + else + m_uiResurrectTimer -= uiDiff; + + // no break needed here + case PHASE_WAITING: + return; + + case PHASE_NORMAL: + // Shield_Timer + if (m_uiShieldTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature, SPELL_SHIELD) == CAST_OK) + m_uiShieldTimer = 61000; + } + else + m_uiShieldTimer -= uiDiff; + + // BloodLust_Timer + if (m_uiBloodLustTimer < uiDiff) + { + // ToDo: research if this should be cast on Thekal or Zath + if (DoCastSpellIfCan(m_creature, SPELL_BLOODLUST) == CAST_OK) + m_uiBloodLustTimer = urand(20000, 28000); + } + else + m_uiBloodLustTimer -= uiDiff; + + // Casting Greaterheal to Thekal or Zath if they are in meele range. + // TODO - why this range check? + if (m_uiGreaterHealTimer < uiDiff) + { + if (m_pInstance) + { + Creature* pThekal = m_pInstance->GetSingleCreatureFromStorage(NPC_THEKAL); + Creature* pZath = m_pInstance->GetSingleCreatureFromStorage(NPC_ZATH); + + switch (urand(0, 1)) + { + case 0: + if (pThekal && m_creature->IsWithinDistInMap(pThekal, 3 * ATTACK_DISTANCE)) + DoCastSpellIfCan(pThekal, SPELL_GREATER_HEAL); + break; + case 1: + if (pZath && m_creature->IsWithinDistInMap(pZath, 3 * ATTACK_DISTANCE)) + DoCastSpellIfCan(pZath, SPELL_GREATER_HEAL); + break; + } + } + + m_uiGreaterHealTimer = urand(15000, 20000); + } + else + m_uiGreaterHealTimer -= uiDiff; + + // Disarm_Timer + if (m_uiDisarmTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature->getVictim(), SPELL_DISARM) == CAST_OK) + m_uiDisarmTimer = urand(15000, 25000); + } + else + m_uiDisarmTimer -= uiDiff; + + break; + } + + DoMeleeAttackIfReady(); + } +}; + +/*###### +## npc_zealot_zath +######*/ + +struct mob_zealot_zathAI : public boss_thekalBaseAI +{ + mob_zealot_zathAI(Creature* pCreature) : boss_thekalBaseAI(pCreature) + { + m_pInstance = (ScriptedInstance*)pCreature->GetInstanceData(); + Reset(); + } + + ScriptedInstance* m_pInstance; + + uint32 m_uiSweepingStrikesTimer; + uint32 m_uiSinisterStrikeTimer; + uint32 m_uiGougeTimer; + uint32 m_uiKickTimer; + uint32 m_uiBlindTimer; + uint32 m_uiResurrectTimer; + + void Reset() override + { + m_uiSweepingStrikesTimer = 13000; + m_uiSinisterStrikeTimer = 8000; + m_uiGougeTimer = 25000; + m_uiKickTimer = 18000; + m_uiBlindTimer = 5000; + m_uiPhase = PHASE_NORMAL; + + if (m_pInstance) + m_pInstance->SetData(TYPE_ZATH, NOT_STARTED); + + Revive(true); + } + + void Aggro(Unit* /*pWho*/) override + { + if (m_pInstance) + m_pInstance->SetData(TYPE_ZATH, IN_PROGRESS); + } + + void OnFakeingDeath() + { + m_uiResurrectTimer = 10000; + + if (m_pInstance) + m_pInstance->SetData(TYPE_ZATH, SPECIAL); + } + + void UpdateAI(const uint32 uiDiff) override + { + if (!m_creature->SelectHostileTarget() || !m_creature->getVictim()) + return; + + switch (m_uiPhase) + { + case PHASE_FAKE_DEATH: + if (m_uiResurrectTimer < uiDiff) + { + if (!m_pInstance) + return; + + if (m_pInstance->GetData(TYPE_THEKAL) != SPECIAL || m_pInstance->GetData(TYPE_LORKHAN) != SPECIAL) + { + DoCastSpellIfCan(m_creature, SPELL_RESURRECT); + m_pInstance->SetData(TYPE_ZATH, IN_PROGRESS); + } + + m_uiPhase = PHASE_WAITING; + } + else + m_uiResurrectTimer -= uiDiff; + + // no break needed here + case PHASE_WAITING: + return; + + case PHASE_NORMAL: + // SweepingStrikes_Timer + if (m_uiSweepingStrikesTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature, SPELL_SWEEPING_STRIKES) == CAST_OK) + m_uiSweepingStrikesTimer = urand(22000, 26000); + } + else + m_uiSweepingStrikesTimer -= uiDiff; + + // SinisterStrike_Timer + if (m_uiSinisterStrikeTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature->getVictim(), SPELL_SINISTER_STRIKE) == CAST_OK) + m_uiSinisterStrikeTimer = urand(8000, 16000); + } + else + m_uiSinisterStrikeTimer -= uiDiff; + + // Gouge_Timer + if (m_uiGougeTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature->getVictim(), SPELL_GOUGE) == CAST_OK) + { + if (m_creature->GetThreatManager().getThreat(m_creature->getVictim())) + m_creature->GetThreatManager().modifyThreatPercent(m_creature->getVictim(), -100); + + m_uiGougeTimer = urand(17000, 27000); + } + } + else + m_uiGougeTimer -= uiDiff; + + // Kick_Timer + if (m_uiKickTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature->getVictim(), SPELL_KICK) == CAST_OK) + m_uiKickTimer = urand(15000, 25000); + } + else + m_uiKickTimer -= uiDiff; + + // Blind_Timer + if (m_uiBlindTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature->getVictim(), SPELL_BLIND) == CAST_OK) + m_uiBlindTimer = urand(10000, 20000); + } + else + m_uiBlindTimer -= uiDiff; + + break; + } + + DoMeleeAttackIfReady(); + } +}; + +bool EffectDummyCreature_thekal_resurrection(Unit* /*pCaster*/, uint32 uiSpellId, SpellEffectIndex uiEffIndex, Creature* pCreatureTarget, ObjectGuid /*originalCasterGuid*/) +{ + // always check spellid and effectindex + if (uiSpellId == SPELL_RESURRECT && uiEffIndex == EFFECT_INDEX_0) + { + if (boss_thekalBaseAI* pFakerAI = dynamic_cast(pCreatureTarget->AI())) + pFakerAI->Revive(); + + // always return true when we are handling this spell and effect + return true; + } + + return false; +} + +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* pNewScript; + + pNewScript = new Script; + pNewScript->Name = "boss_thekal"; + pNewScript->GetAI = &GetAI_boss_thekal; + pNewScript->pEffectDummyNPC = &EffectDummyCreature_thekal_resurrection; + pNewScript->RegisterSelf(); + + pNewScript = new Script; + pNewScript->Name = "mob_zealot_lorkhan"; + pNewScript->GetAI = &GetAI_mob_zealot_lorkhan; + pNewScript->pEffectDummyNPC = &EffectDummyCreature_thekal_resurrection; + pNewScript->RegisterSelf(); + + pNewScript = new Script; + pNewScript->Name = "mob_zealot_zath"; + pNewScript->GetAI = &GetAI_mob_zealot_zath; + pNewScript->pEffectDummyNPC = &EffectDummyCreature_thekal_resurrection; + pNewScript->RegisterSelf(); +} diff --git a/src/modules/SD2/scripts/eastern_kingdoms/zulgurub/boss_venoxis.cpp b/src/modules/SD2/scripts/eastern_kingdoms/zulgurub/boss_venoxis.cpp new file mode 100644 index 000000000..70bfe62c2 --- /dev/null +++ b/src/modules/SD2/scripts/eastern_kingdoms/zulgurub/boss_venoxis.cpp @@ -0,0 +1,227 @@ +/* Copyright (C) 2006 - 2013 ScriptDev2 + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with 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 "zulgurub.h" + +enum +{ + SAY_TRANSFORM = -1309000, + SAY_DEATH = -1309001, + + // troll spells + SPELL_HOLY_FIRE = 23860, + SPELL_HOLY_WRATH = 23979, + SPELL_HOLY_NOVA = 23858, + SPELL_DISPELL = 23859, + SPELL_RENEW = 23895, + + // serpent spells + SPELL_VENOMSPIT = 23862, + SPELL_POISON_CLOUD = 23861, + SPELL_PARASITIC_SERPENT = 23867, + + // common spells + SPELL_SNAKE_FORM = 23849, + SPELL_FRENZY = 23537, + SPELL_TRASH = 3391 +}; + +struct boss_venoxisAI : public ScriptedAI +{ + boss_venoxisAI(Creature* pCreature) : ScriptedAI(pCreature) + { + m_pInstance = (ScriptedInstance*)pCreature->GetInstanceData(); + Reset(); + } + + ScriptedInstance* m_pInstance; + + uint32 m_uiHolyWrathTimer; + uint32 m_uiVenomSpitTimer; + uint32 m_uiRenewTimer; + uint32 m_uiPoisonCloudTimer; + uint32 m_uiHolySpellTimer; + uint32 m_uiDispellTimer; + uint32 m_uiTrashTimer; + + bool m_bPhaseTwo; + bool m_bInBerserk; + + void Reset() override + { + m_uiHolyWrathTimer = 40000; + m_uiVenomSpitTimer = 5500; + m_uiRenewTimer = 30000; + m_uiPoisonCloudTimer = 2000; + m_uiHolySpellTimer = 10000; + m_uiDispellTimer = 35000; + m_uiTrashTimer = 5000; + + m_bPhaseTwo = false; + m_bInBerserk = false; + } + + void JustReachedHome() override + { + if (m_pInstance) + m_pInstance->SetData(TYPE_VENOXIS, FAIL); + } + + void JustDied(Unit* /*pKiller*/) override + { + DoScriptText(SAY_DEATH, m_creature); + + if (m_pInstance) + m_pInstance->SetData(TYPE_VENOXIS, DONE); + } + + void UpdateAI(const uint32 uiDiff) override + { + if (!m_creature->SelectHostileTarget() || !m_creature->getVictim()) + return; + + // Troll phase + if (!m_bPhaseTwo) + { + if (m_uiDispellTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature, SPELL_DISPELL) == CAST_OK) + m_uiDispellTimer = urand(15000, 30000); + } + else + m_uiDispellTimer -= uiDiff; + + if (m_uiRenewTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature, SPELL_RENEW) == CAST_OK) + m_uiRenewTimer = urand(20000, 30000); + } + else + m_uiRenewTimer -= uiDiff; + + if (m_uiHolyWrathTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature->getVictim(), SPELL_HOLY_WRATH) == CAST_OK) + m_uiHolyWrathTimer = urand(15000, 25000); + } + else + m_uiHolyWrathTimer -= uiDiff; + + if (m_uiHolySpellTimer < uiDiff) + { + uint8 uiTargetsInRange = 0; + + // See how many targets are in melee range + ThreatList const& tList = m_creature->GetThreatManager().getThreatList(); + for (ThreatList::const_iterator iter = tList.begin(); iter != tList.end(); ++iter) + { + if (Unit* pTempTarget = m_creature->GetMap()->GetUnit((*iter)->getUnitGuid())) + { + if (pTempTarget->GetTypeId() == TYPEID_PLAYER && m_creature->CanReachWithMeleeAttack(pTempTarget)) + ++uiTargetsInRange; + } + } + + // If there are more targets in melee range cast holy nova, else holy fire + // not sure which is the minimum targets for holy nova + if (uiTargetsInRange > 3) + DoCastSpellIfCan(m_creature, SPELL_HOLY_NOVA); + else + { + if (Unit* pTarget = m_creature->SelectAttackingTarget(ATTACKING_TARGET_RANDOM, 0)) + DoCastSpellIfCan(pTarget, SPELL_HOLY_FIRE); + } + + m_uiHolySpellTimer = urand(4000, 8000); + } + else + m_uiHolySpellTimer -= uiDiff; + + // Transform at 50% hp + if (m_creature->GetHealthPercent() < 50.0f) + { + if (DoCastSpellIfCan(m_creature, SPELL_SNAKE_FORM, CAST_INTERRUPT_PREVIOUS) == CAST_OK) + { + DoCastSpellIfCan(m_creature, SPELL_PARASITIC_SERPENT, CAST_TRIGGERED); + DoScriptText(SAY_TRANSFORM, m_creature); + DoResetThreat(); + m_bPhaseTwo = true; + } + } + } + // Snake phase + else + { + if (m_uiPoisonCloudTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature, SPELL_POISON_CLOUD) == CAST_OK) + m_uiPoisonCloudTimer = 15000; + } + else + m_uiPoisonCloudTimer -= uiDiff; + + if (m_uiVenomSpitTimer < uiDiff) + { + if (Unit* pTarget = m_creature->SelectAttackingTarget(ATTACKING_TARGET_RANDOM, 0)) + { + if (DoCastSpellIfCan(pTarget, SPELL_VENOMSPIT) == CAST_OK) + m_uiVenomSpitTimer = urand(15000, 20000); + } + } + else + m_uiVenomSpitTimer -= uiDiff; + } + + if (m_uiTrashTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature->getVictim(), SPELL_TRASH) == CAST_OK) + m_uiTrashTimer = urand(10000, 20000); + } + else + m_uiTrashTimer -= uiDiff; + + if (!m_bInBerserk && m_creature->GetHealthPercent() < 11.0f) + { + if (DoCastSpellIfCan(m_creature, SPELL_FRENZY) == CAST_OK) + m_bInBerserk = true; + } + + DoMeleeAttackIfReady(); + } +}; + +CreatureAI* GetAI_boss_venoxis(Creature* pCreature) +{ + return new boss_venoxisAI(pCreature); +} + +void AddSC_boss_venoxis() +{ + Script* pNewScript; + + pNewScript = new Script; + pNewScript->Name = "boss_venoxis"; + pNewScript->GetAI = &GetAI_boss_venoxis; + pNewScript->RegisterSelf(); +} diff --git a/src/modules/SD2/scripts/eastern_kingdoms/zulgurub/instance_zulgurub.cpp b/src/modules/SD2/scripts/eastern_kingdoms/zulgurub/instance_zulgurub.cpp new file mode 100644 index 000000000..179418cb4 --- /dev/null +++ b/src/modules/SD2/scripts/eastern_kingdoms/zulgurub/instance_zulgurub.cpp @@ -0,0 +1,261 @@ +/* Copyright (C) 2006 - 2013 ScriptDev2 + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along 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 "zulgurub.h" + +instance_zulgurub::instance_zulgurub(Map* pMap) : ScriptedInstance(pMap), + m_bHasIntroYelled(false), + m_bHasAltarYelled(false) +{ + Initialize(); +} + +void instance_zulgurub::Initialize() +{ + memset(&m_auiEncounter, 0, sizeof(m_auiEncounter)); +} + +void instance_zulgurub::DoYellAtTriggerIfCan(uint32 uiTriggerId) +{ + if (uiTriggerId == AREATRIGGER_ENTER && !m_bHasIntroYelled) + { + DoOrSimulateScriptTextForThisInstance(SAY_HAKKAR_PROTECT, NPC_HAKKAR); + m_bHasIntroYelled = true; + } + else if (uiTriggerId == AREATRIGGER_ALTAR && !m_bHasAltarYelled) + { + DoOrSimulateScriptTextForThisInstance(SAY_MINION_DESTROY, NPC_HAKKAR); + m_bHasAltarYelled = true; + } +} + +void instance_zulgurub::OnCreatureCreate(Creature* pCreature) +{ + switch (pCreature->GetEntry()) + { + case NPC_LORKHAN: + case NPC_ZATH: + case NPC_THEKAL: + case NPC_JINDO: + case NPC_HAKKAR: + case NPC_BLOODLORD_MANDOKIR: + case NPC_MARLI: + m_mNpcEntryGuidStore[pCreature->GetEntry()] = pCreature->GetObjectGuid(); + break; + case NPC_PANTHER_TRIGGER: + if (pCreature->GetPositionY() < -1626) + m_lLeftPantherTriggerGUIDList.push_back(pCreature->GetObjectGuid()); + else + m_lRightPantherTriggerGUIDList.push_back(pCreature->GetObjectGuid()); + break; + } +} + +void instance_zulgurub::OnObjectCreate(GameObject* pGo) +{ + switch (pGo->GetEntry()) + { + case GO_GONG_OF_BETHEKK: + case GO_FORCEFIELD: + break; + case GO_SPIDER_EGG: + m_lSpiderEggGUIDList.push_back(pGo->GetObjectGuid()); + return; + } + + m_mGoEntryGuidStore[pGo->GetEntry()] = pGo->GetObjectGuid(); +} + +void instance_zulgurub::SetData(uint32 uiType, uint32 uiData) +{ + switch (uiType) + { + case TYPE_JEKLIK: + case TYPE_VENOXIS: + case TYPE_THEKAL: + m_auiEncounter[uiType] = uiData; + if (uiData == DONE) + DoLowerHakkarHitPoints(); + break; + case TYPE_MARLI: + m_auiEncounter[uiType] = uiData; + if (uiData == DONE) + DoLowerHakkarHitPoints(); + if (uiData == FAIL) + { + for (GuidList::const_iterator itr = m_lSpiderEggGUIDList.begin(); itr != m_lSpiderEggGUIDList.end(); ++itr) + { + if (GameObject* pEgg = instance->GetGameObject(*itr)) + { + // Note: this type of Gameobject needs to be respawned manually + pEgg->SetRespawnTime(2 * DAY); + pEgg->Respawn(); + } + } + } + break; + case TYPE_ARLOKK: + m_auiEncounter[uiType] = uiData; + DoUseDoorOrButton(GO_FORCEFIELD); + if (uiData == DONE) + DoLowerHakkarHitPoints(); + if (uiData == FAIL) + { + // Note: this gameobject should change flags - currently it despawns which isn't correct + if (GameObject* pGong = GetSingleGameObjectFromStorage(GO_GONG_OF_BETHEKK)) + { + pGong->SetRespawnTime(2 * DAY); + pGong->Respawn(); + } + } + break; + case TYPE_OHGAN: + // Note: SPECIAL instance data is set via ACID! + if (uiData == SPECIAL) + { + if (Creature* pMandokir = GetSingleCreatureFromStorage(NPC_BLOODLORD_MANDOKIR)) + { + pMandokir->SetWalk(false); + pMandokir->GetMotionMaster()->MovePoint(1, aMandokirDownstairsPos[0], aMandokirDownstairsPos[1], aMandokirDownstairsPos[2]); + } + } + m_auiEncounter[uiType] = uiData; + break; + case TYPE_LORKHAN: + case TYPE_ZATH: + m_auiEncounter[uiType] = uiData; + 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_strInstData = saveStream.str(); + + SaveToDB(); + OUT_SAVE_INST_DATA_COMPLETE; + } +} + +// Each time High Priest dies lower Hakkar's HP +void instance_zulgurub::DoLowerHakkarHitPoints() +{ + if (Creature* pHakkar = GetSingleCreatureFromStorage(NPC_HAKKAR)) + { + if (pHakkar->IsAlive() && pHakkar->GetMaxHealth() > HP_LOSS_PER_PRIEST) + { + pHakkar->SetMaxHealth(pHakkar->GetMaxHealth() - HP_LOSS_PER_PRIEST); + pHakkar->SetHealth(pHakkar->GetHealth() - HP_LOSS_PER_PRIEST); + } + } +} + +void instance_zulgurub::Load(const char* chrIn) +{ + if (!chrIn) + { + 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]; + + for (uint8 i = 0; i < MAX_ENCOUNTER; ++i) + { + if (m_auiEncounter[i] == IN_PROGRESS) + m_auiEncounter[i] = NOT_STARTED; + } + + OUT_LOAD_INST_DATA_COMPLETE; +} + +uint32 instance_zulgurub::GetData(uint32 uiType) const +{ + if (uiType < MAX_ENCOUNTER) + return m_auiEncounter[uiType]; + + return 0; +} + +Creature* instance_zulgurub::SelectRandomPantherTrigger(bool bIsLeft) +{ + GuidList* plTempList = bIsLeft ? &m_lLeftPantherTriggerGUIDList : &m_lRightPantherTriggerGUIDList; + std::vector vTriggers; + vTriggers.reserve(plTempList->size()); + + for (GuidList::const_iterator itr = plTempList->begin(); itr != plTempList->end(); ++itr) + { + if (Creature* pTemp = instance->GetCreature(*itr)) + vTriggers.push_back(pTemp); + } + + if (vTriggers.empty()) + return NULL; + + return vTriggers[urand(0, vTriggers.size() - 1)]; +} + +InstanceData* GetInstanceData_instance_zulgurub(Map* pMap) +{ + return new instance_zulgurub(pMap); +} + +bool AreaTrigger_at_zulgurub(Player* pPlayer, AreaTriggerEntry const* pAt) +{ + if (pAt->id == AREATRIGGER_ENTER || pAt->id == AREATRIGGER_ALTAR) + { + if (pPlayer->isGameMaster() || pPlayer->IsDead()) + return false; + + if (instance_zulgurub* pInstance = (instance_zulgurub*)pPlayer->GetInstanceData()) + pInstance->DoYellAtTriggerIfCan(pAt->id); + } + + return false; +} + +void AddSC_instance_zulgurub() +{ + Script* pNewScript; + + pNewScript = new Script; + pNewScript->Name = "instance_zulgurub"; + pNewScript->GetInstanceData = &GetInstanceData_instance_zulgurub; + pNewScript->RegisterSelf(); + + pNewScript = new Script; + pNewScript->Name = "at_zulgurub"; + pNewScript->pAreaTrigger = &AreaTrigger_at_zulgurub; + pNewScript->RegisterSelf(); +} diff --git a/src/modules/SD2/scripts/eastern_kingdoms/zulgurub/zulgurub.h b/src/modules/SD2/scripts/eastern_kingdoms/zulgurub/zulgurub.h new file mode 100644 index 000000000..dbfc16bf5 --- /dev/null +++ b/src/modules/SD2/scripts/eastern_kingdoms/zulgurub/zulgurub.h @@ -0,0 +1,82 @@ +/* Copyright (C) 2006 - 2013 ScriptDev2 + * This program is free software licensed under GPL version 2 + * Please see the included DOCS/LICENSE.TXT for more information */ + +#ifndef DEF_ZULGURUB_H +#define DEF_ZULGURUB_H + +enum +{ + MAX_ENCOUNTER = 8, + MAX_PRIESTS = 5, + + TYPE_JEKLIK = 0, + TYPE_VENOXIS = 1, + TYPE_MARLI = 2, + TYPE_THEKAL = 3, + TYPE_ARLOKK = 4, + TYPE_OHGAN = 5, // Do not change, used by Acid + TYPE_LORKHAN = 6, + TYPE_ZATH = 7, + + NPC_LORKHAN = 11347, + NPC_ZATH = 11348, + NPC_THEKAL = 14509, + NPC_JINDO = 11380, + NPC_HAKKAR = 14834, + NPC_PANTHER_TRIGGER = 15091, + NPC_BLOODLORD_MANDOKIR = 11382, + NPC_MARLI = 14510, + + GO_SPIDER_EGG = 179985, + GO_GONG_OF_BETHEKK = 180526, + GO_FORCEFIELD = 180497, + + SAY_MINION_DESTROY = -1309022, + SAY_HAKKAR_PROTECT = -1309023, + + HP_LOSS_PER_PRIEST = 60000, + + AREATRIGGER_ENTER = 3958, + AREATRIGGER_ALTAR = 3960, +}; + +static const float aMandokirDownstairsPos[3] = { -12196.30f, -1948.37f, 130.31f}; + +class instance_zulgurub : public ScriptedInstance +{ + public: + instance_zulgurub(Map* pMap); + ~instance_zulgurub() {} + + void Initialize() override; + // IsEncounterInProgress() const override { return false; } // not active in Zul'Gurub + + void OnCreatureCreate(Creature* pCreature) override; + void OnObjectCreate(GameObject* pGo) override; + + void SetData(uint32 uiType, uint32 uiData) override; + uint32 GetData(uint32 uiType) const override; + + const char* Save() const override { return m_strInstData.c_str(); } + void Load(const char* chrIn) override; + + void DoYellAtTriggerIfCan(uint32 uiTriggerId); + + Creature* SelectRandomPantherTrigger(bool bIsLeft); + + protected: + void DoLowerHakkarHitPoints(); + + uint32 m_auiEncounter[MAX_ENCOUNTER]; + std::string m_strInstData; + + GuidList m_lRightPantherTriggerGUIDList; + GuidList m_lLeftPantherTriggerGUIDList; + GuidList m_lSpiderEggGUIDList; + + bool m_bHasIntroYelled; + bool m_bHasAltarYelled; +}; + +#endif diff --git a/src/modules/SD2/scripts/kalimdor/ashenvale.cpp b/src/modules/SD2/scripts/kalimdor/ashenvale.cpp new file mode 100644 index 000000000..5eb538dd9 --- /dev/null +++ b/src/modules/SD2/scripts/kalimdor/ashenvale.cpp @@ -0,0 +1,631 @@ +/* Copyright (C) 2006 - 2013 ScriptDev2 + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along 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: 976, 6482, 6544, 6641 +SDCategory: Ashenvale Forest +EndScriptData */ + +/* ContentData +npc_muglash +npc_ruul_snowhoof +npc_torek +npc_feero_ironhand +EndContentData */ + +#include "precompiled.h" +#include "escort_ai.h" + +/*#### +# npc_muglash +####*/ + +enum +{ + SAY_MUG_START1 = -1000501, + SAY_MUG_START2 = -1000502, + SAY_MUG_BRAZIER = -1000503, + SAY_MUG_BRAZIER_WAIT = -1000504, + SAY_MUG_ON_GUARD = -1000505, + SAY_MUG_REST = -1000506, + SAY_MUG_DONE = -1000507, + SAY_MUG_GRATITUDE = -1000508, + SAY_MUG_PATROL = -1000509, + SAY_MUG_RETURN = -1000510, + + QUEST_VORSHA = 6641, + + GO_NAGA_BRAZIER = 178247, + NPC_MUGLASH = 12717, + + NPC_WRATH_RIDER = 3713, + NPC_WRATH_SORCERESS = 3717, + NPC_WRATH_RAZORTAIL = 3712, + + NPC_WRATH_PRIESTESS = 3944, + NPC_WRATH_MYRMIDON = 3711, + NPC_WRATH_SEAWITCH = 3715, + + NPC_VORSHA = 12940 +}; + +static float m_afFirstNagaCoord[3][3] = +{ + {3603.504150f, 1122.631104f, 1.635f}, // rider + {3589.293945f, 1148.664063f, 5.565f}, // sorceress + {3609.925537f, 1168.759521f, -1.168f} // razortail +}; + +static float m_afSecondNagaCoord[3][3] = +{ + {3609.925537f, 1168.759521f, -1.168f}, // witch + {3645.652100f, 1139.425415f, 1.322f}, // priest + {3583.602051f, 1128.405762f, 2.347f} // myrmidon +}; + +static float m_fVorshaCoord[] = {3633.056885f, 1172.924072f, -5.388f}; + +struct npc_muglashAI : public npc_escortAI +{ + npc_muglashAI(Creature* pCreature) : npc_escortAI(pCreature) + { + m_uiWaveId = 0; + m_bIsBrazierExtinguished = false; + Reset(); + } + + bool m_bIsBrazierExtinguished; + + uint32 m_uiWaveId; + uint32 m_uiEventTimer; + + void Reset() override + { + m_uiEventTimer = 10000; + + if (!HasEscortState(STATE_ESCORT_ESCORTING)) + { + m_uiWaveId = 0; + m_bIsBrazierExtinguished = false; + } + } + + void Aggro(Unit* /*pWho*/) override + { + if (HasEscortState(STATE_ESCORT_PAUSED)) + { + if (urand(0, 1)) + return; + + if (Player* pPlayer = GetPlayerForEscort()) + DoScriptText(SAY_MUG_ON_GUARD, m_creature, pPlayer); + } + } + + void WaypointReached(uint32 uiPointId) override + { + switch (uiPointId) + { + case 0: + if (Player* pPlayer = GetPlayerForEscort()) + DoScriptText(SAY_MUG_START2, m_creature, pPlayer); + break; + case 24: + if (Player* pPlayer = GetPlayerForEscort()) + DoScriptText(SAY_MUG_BRAZIER, m_creature, pPlayer); + + if (GameObject* pGo = GetClosestGameObjectWithEntry(m_creature, GO_NAGA_BRAZIER, INTERACTION_DISTANCE * 2)) + { + // some kind of event flag? Update to player/group only? + pGo->RemoveFlag(GAMEOBJECT_FLAGS, GO_FLAG_NO_INTERACT); + SetEscortPaused(true); + } + break; + case 25: + DoScriptText(SAY_MUG_GRATITUDE, m_creature); + + if (Player* pPlayer = GetPlayerForEscort()) + pPlayer->GroupEventHappens(QUEST_VORSHA, m_creature); + break; + case 26: + DoScriptText(SAY_MUG_PATROL, m_creature); + break; + case 27: + DoScriptText(SAY_MUG_RETURN, m_creature); + break; + } + } + + void DoWaveSummon() + { + switch (m_uiWaveId) + { + case 1: + m_creature->SummonCreature(NPC_WRATH_RIDER, m_afFirstNagaCoord[0][0], m_afFirstNagaCoord[0][1], m_afFirstNagaCoord[0][2], 0.0f, TEMPSUMMON_TIMED_OOC_DESPAWN, 60000); + m_creature->SummonCreature(NPC_WRATH_SORCERESS, m_afFirstNagaCoord[1][0], m_afFirstNagaCoord[1][1], m_afFirstNagaCoord[1][2], 0.0f, TEMPSUMMON_TIMED_OOC_DESPAWN, 60000); + m_creature->SummonCreature(NPC_WRATH_RAZORTAIL, m_afFirstNagaCoord[2][0], m_afFirstNagaCoord[2][1], m_afFirstNagaCoord[2][2], 0.0f, TEMPSUMMON_TIMED_OOC_DESPAWN, 60000); + break; + case 2: + m_creature->SummonCreature(NPC_WRATH_PRIESTESS, m_afSecondNagaCoord[0][0], m_afSecondNagaCoord[0][1], m_afSecondNagaCoord[0][2], 0.0f, TEMPSUMMON_TIMED_OOC_DESPAWN, 60000); + m_creature->SummonCreature(NPC_WRATH_MYRMIDON, m_afSecondNagaCoord[1][0], m_afSecondNagaCoord[1][1], m_afSecondNagaCoord[1][2], 0.0f, TEMPSUMMON_TIMED_OOC_DESPAWN, 60000); + m_creature->SummonCreature(NPC_WRATH_SEAWITCH, m_afSecondNagaCoord[2][0], m_afSecondNagaCoord[2][1], m_afSecondNagaCoord[2][2], 0.0f, TEMPSUMMON_TIMED_OOC_DESPAWN, 60000); + break; + case 3: + m_creature->SummonCreature(NPC_VORSHA, m_fVorshaCoord[0], m_fVorshaCoord[1], m_fVorshaCoord[2], 0.0f, TEMPSUMMON_TIMED_OOC_DESPAWN, 60000); + break; + case 4: + SetEscortPaused(false); + DoScriptText(SAY_MUG_DONE, m_creature); + break; + } + } + + void JustSummoned(Creature* pSummoned) override + { + pSummoned->AI()->AttackStart(m_creature); + } + + void UpdateEscortAI(const uint32 uiDiff) override + { + if (!m_creature->SelectHostileTarget() || !m_creature->getVictim()) + { + if (HasEscortState(STATE_ESCORT_PAUSED) && m_bIsBrazierExtinguished) + { + if (m_uiEventTimer < uiDiff) + { + ++m_uiWaveId; + DoWaveSummon(); + m_uiEventTimer = 10000; + } + else + m_uiEventTimer -= uiDiff; + } + + return; + } + + DoMeleeAttackIfReady(); + } +}; + +bool QuestAccept_npc_muglash(Player* pPlayer, Creature* pCreature, const Quest* pQuest) +{ + if (pQuest->GetQuestId() == QUEST_VORSHA) + { + if (npc_muglashAI* pEscortAI = dynamic_cast(pCreature->AI())) + { + DoScriptText(SAY_MUG_START1, pCreature); + pCreature->SetFactionTemporary(FACTION_ESCORT_H_PASSIVE, TEMPFACTION_RESTORE_RESPAWN); + + pEscortAI->Start(false, pPlayer, pQuest); + } + } + + return true; +} + +CreatureAI* GetAI_npc_muglash(Creature* pCreature) +{ + return new npc_muglashAI(pCreature); +} + +bool GOUse_go_naga_brazier(Player* /*pPlayer*/, GameObject* pGo) +{ + if (Creature* pCreature = GetClosestCreatureWithEntry(pGo, NPC_MUGLASH, INTERACTION_DISTANCE * 2)) + { + if (npc_muglashAI* pEscortAI = dynamic_cast(pCreature->AI())) + { + DoScriptText(SAY_MUG_BRAZIER_WAIT, pCreature); + + pEscortAI->m_bIsBrazierExtinguished = true; + return false; + } + } + + return true; +} + +/*#### +# npc_ruul_snowhoof +####*/ + +enum +{ + QUEST_FREEDOM_TO_RUUL = 6482, + NPC_T_URSA = 3921, + NPC_T_TOTEMIC = 3922, + NPC_T_PATHFINDER = 3926 +}; + +struct npc_ruul_snowhoofAI : public npc_escortAI +{ + npc_ruul_snowhoofAI(Creature* pCreature) : npc_escortAI(pCreature) { Reset(); } + + void Reset() override {} + + void WaypointReached(uint32 uiPointId) override + { + switch (uiPointId) + { + case 13: + m_creature->SummonCreature(NPC_T_TOTEMIC, 3449.218018f, -587.825073f, 174.978867f, 4.714445f, TEMPSUMMON_DEAD_DESPAWN, 60000); + m_creature->SummonCreature(NPC_T_URSA, 3446.384521f, -587.830872f, 175.186279f, 4.714445f, TEMPSUMMON_DEAD_DESPAWN, 60000); + m_creature->SummonCreature(NPC_T_PATHFINDER, 3444.218994f, -587.835327f, 175.380600f, 4.714445f, TEMPSUMMON_DEAD_DESPAWN, 60000); + break; + case 19: + m_creature->SummonCreature(NPC_T_TOTEMIC, 3508.344482f, -492.024261f, 186.929031f, 4.145029f, TEMPSUMMON_DEAD_DESPAWN, 60000); + m_creature->SummonCreature(NPC_T_URSA, 3506.265625f, -490.531006f, 186.740128f, 4.239277f, TEMPSUMMON_DEAD_DESPAWN, 60000); + m_creature->SummonCreature(NPC_T_PATHFINDER, 3503.682373f, -489.393799f, 186.629684f, 4.349232f, TEMPSUMMON_DEAD_DESPAWN, 60000); + break; + case 21: + if (Player* pPlayer = GetPlayerForEscort()) + pPlayer->GroupEventHappens(QUEST_FREEDOM_TO_RUUL, m_creature); + break; + } + } + + void JustSummoned(Creature* summoned) override + { + summoned->AI()->AttackStart(m_creature); + } +}; + +bool QuestAccept_npc_ruul_snowhoof(Player* pPlayer, Creature* pCreature, const Quest* pQuest) +{ + if (pQuest->GetQuestId() == QUEST_FREEDOM_TO_RUUL) + { + pCreature->SetFactionTemporary(FACTION_ESCORT_N_NEUTRAL_PASSIVE, TEMPFACTION_RESTORE_RESPAWN); + pCreature->SetStandState(UNIT_STAND_STATE_STAND); + + if (npc_ruul_snowhoofAI* pEscortAI = dynamic_cast(pCreature->AI())) + pEscortAI->Start(false, pPlayer, pQuest); + } + return true; +} + +CreatureAI* GetAI_npc_ruul_snowhoofAI(Creature* pCreature) +{ + return new npc_ruul_snowhoofAI(pCreature); +} + +/*#### +# npc_torek +####*/ + +enum +{ + SAY_READY = -1000106, + SAY_MOVE = -1000107, + SAY_PREPARE = -1000108, + SAY_WIN = -1000109, + SAY_END = -1000110, + + SPELL_REND = 11977, + SPELL_THUNDERCLAP = 8078, + + QUEST_TOREK_ASSULT = 6544, + + NPC_SPLINTERTREE_RAIDER = 12859, + NPC_DURIEL = 12860, + NPC_SILVERWING_SENTINEL = 12896, + NPC_SILVERWING_WARRIOR = 12897 +}; + +struct npc_torekAI : public npc_escortAI +{ + npc_torekAI(Creature* pCreature) : npc_escortAI(pCreature) {Reset();} + + uint32 m_uiRend_Timer; + uint32 m_uiThunderclap_Timer; + + void Reset() override + { + m_uiRend_Timer = 5000; + m_uiThunderclap_Timer = 8000; + } + + void WaypointReached(uint32 uiPointId) override + { + Player* pPlayer = GetPlayerForEscort(); + + if (!pPlayer) + return; + + switch (uiPointId) + { + case 1: + DoScriptText(SAY_MOVE, m_creature, pPlayer); + break; + case 8: + DoScriptText(SAY_PREPARE, m_creature, pPlayer); + break; + case 19: + // TODO: verify location and creatures amount. + m_creature->SummonCreature(NPC_DURIEL, 1776.73f, -2049.06f, 109.83f, 1.54f, TEMPSUMMON_TIMED_OOC_DESPAWN, 25000); + m_creature->SummonCreature(NPC_SILVERWING_SENTINEL, 1774.64f, -2049.41f, 109.83f, 1.40f, TEMPSUMMON_TIMED_OOC_DESPAWN, 25000); + m_creature->SummonCreature(NPC_SILVERWING_WARRIOR, 1778.73f, -2049.50f, 109.83f, 1.67f, TEMPSUMMON_TIMED_OOC_DESPAWN, 25000); + break; + case 20: + DoScriptText(SAY_WIN, m_creature, pPlayer); + pPlayer->GroupEventHappens(QUEST_TOREK_ASSULT, m_creature); + break; + case 21: + DoScriptText(SAY_END, m_creature, pPlayer); + break; + } + } + + void JustSummoned(Creature* pSummoned) override + { + pSummoned->AI()->AttackStart(m_creature); + } + + void UpdateEscortAI(const uint32 uiDiff) override + { + if (!m_creature->SelectHostileTarget() || !m_creature->getVictim()) + return; + + if (m_uiRend_Timer < uiDiff) + { + DoCastSpellIfCan(m_creature->getVictim(), SPELL_REND); + m_uiRend_Timer = 20000; + } + else + m_uiRend_Timer -= uiDiff; + + if (m_uiThunderclap_Timer < uiDiff) + { + DoCastSpellIfCan(m_creature, SPELL_THUNDERCLAP); + m_uiThunderclap_Timer = 30000; + } + else + m_uiThunderclap_Timer -= uiDiff; + + DoMeleeAttackIfReady(); + } +}; + +bool QuestAccept_npc_torek(Player* pPlayer, Creature* pCreature, const Quest* pQuest) +{ + if (pQuest->GetQuestId() == QUEST_TOREK_ASSULT) + { + // TODO: find companions, make them follow Torek, at any time (possibly done by mangos/database in future?) + DoScriptText(SAY_READY, pCreature, pPlayer); + + if (npc_torekAI* pEscortAI = dynamic_cast(pCreature->AI())) + pEscortAI->Start(true, pPlayer, pQuest); + } + + return true; +} + +CreatureAI* GetAI_npc_torek(Creature* pCreature) +{ + return new npc_torekAI(pCreature); +} + +/*#### +# npc_feero_ironhand +####*/ + +enum +{ + SAY_QUEST_START = -1000771, + SAY_FIRST_AMBUSH_START = -1000772, + SAY_FIRST_AMBUSH_END = -1000773, + SAY_SECOND_AMBUSH_START = -1000774, + SAY_SCOUT_SECOND_AMBUSH = -1000775, + SAY_SECOND_AMBUSH_END = -1000776, + SAY_FINAL_AMBUSH_START = -1000777, + SAY_BALIZAR_FINAL_AMBUSH = -1000778, + SAY_FINAL_AMBUSH_ATTACK = -1000779, + SAY_QUEST_END = -1000780, + + QUEST_SUPPLIES_TO_AUBERDINE = 976, + + NPC_DARK_STRAND_ASSASSIN = 3879, + NPC_FORSAKEN_SCOUT = 3893, + + NPC_ALIGAR_THE_TORMENTOR = 3898, + NPC_BALIZAR_THE_UMBRAGE = 3899, + NPC_CAEDAKAR_THE_VICIOUS = 3900, +}; + +/* + * Notes about the event: + * The summon coords and event sequence are guesswork based on the comments from wowhead and wowwiki + */ + +// Distance, Angle or Offset +static const float aSummonPositions[2][2] = +{ + {30.0f, 1.25f}, + {30.0f, 0.95f} +}; + +// Hardcoded positions for the last 3 mobs +static const float aEliteSummonPositions[3][4] = +{ + {4243.12f, 108.22f, 38.12f, 3.62f}, + {4240.95f, 114.04f, 38.35f, 3.56f}, + {4235.78f, 118.09f, 38.08f, 4.12f} +}; + +struct npc_feero_ironhandAI : public npc_escortAI +{ + npc_feero_ironhandAI(Creature* pCreature) : npc_escortAI(pCreature) + { + Reset(); + } + + uint8 m_uiCreaturesCount; + bool m_bIsAttacked; + + void Reset() override + { + if (!HasEscortState(STATE_ESCORT_ESCORTING)) + { + m_uiCreaturesCount = 0; + m_bIsAttacked = false; + } + } + + void WaypointReached(uint32 uiPointId) override + { + switch (uiPointId) + { + case 14: + // Prepare the first ambush + DoScriptText(SAY_FIRST_AMBUSH_START, m_creature); + for (uint8 i = 0; i < 4; ++i) + DoSpawnMob(NPC_DARK_STRAND_ASSASSIN, aSummonPositions[0][0], aSummonPositions[0][1] - M_PI_F / 4 * i); + break; + case 20: + // Prepare the second ambush + DoScriptText(SAY_SECOND_AMBUSH_START, m_creature); + for (uint8 i = 0; i < 3; ++i) + DoSpawnMob(NPC_FORSAKEN_SCOUT, aSummonPositions[1][0], aSummonPositions[1][1] - M_PI_F / 3 * i); + break; + case 29: + // Final ambush + DoScriptText(SAY_FINAL_AMBUSH_START, m_creature); + m_creature->SummonCreature(NPC_BALIZAR_THE_UMBRAGE, aEliteSummonPositions[0][0], aEliteSummonPositions[0][1], aEliteSummonPositions[0][2], aEliteSummonPositions[0][3], TEMPSUMMON_TIMED_OOC_DESPAWN, 20000); + m_creature->SummonCreature(NPC_ALIGAR_THE_TORMENTOR, aEliteSummonPositions[1][0], aEliteSummonPositions[1][1], aEliteSummonPositions[1][2], aEliteSummonPositions[1][3], TEMPSUMMON_TIMED_OOC_DESPAWN, 20000); + m_creature->SummonCreature(NPC_CAEDAKAR_THE_VICIOUS, aEliteSummonPositions[2][0], aEliteSummonPositions[2][1], aEliteSummonPositions[2][2], aEliteSummonPositions[2][3], TEMPSUMMON_TIMED_OOC_DESPAWN, 20000); + break; + case 30: + // Complete the quest + if (Player* pPlayer = GetPlayerForEscort()) + pPlayer->GroupEventHappens(QUEST_SUPPLIES_TO_AUBERDINE, m_creature); + break; + } + } + + void AttackedBy(Unit* pWho) override + { + // Yell only at the first attack + if (!m_bIsAttacked) + { + if (((Creature*)pWho)->GetEntry() == NPC_BALIZAR_THE_UMBRAGE) + { + DoScriptText(SAY_FINAL_AMBUSH_ATTACK, m_creature); + m_bIsAttacked = true; + } + } + } + + // Summon mobs at calculated points + void DoSpawnMob(uint32 uiEntry, float fDistance, float fAngle) + { + float fX, fY, fZ; + m_creature->GetNearPoint(m_creature, fX, fY, fZ, 0, fDistance, fAngle); + + m_creature->SummonCreature(uiEntry, fX, fY, fZ, 0, TEMPSUMMON_TIMED_OOC_DESPAWN, 20000); + } + + void SummonedCreatureJustDied(Creature* pSummoned) override + { + --m_uiCreaturesCount; + + if (!m_uiCreaturesCount) + { + switch (pSummoned->GetEntry()) + { + case NPC_DARK_STRAND_ASSASSIN: + DoScriptText(SAY_FIRST_AMBUSH_END, m_creature); + break; + case NPC_FORSAKEN_SCOUT: + DoScriptText(SAY_SECOND_AMBUSH_END, m_creature); + break; + case NPC_ALIGAR_THE_TORMENTOR: + case NPC_BALIZAR_THE_UMBRAGE: + case NPC_CAEDAKAR_THE_VICIOUS: + DoScriptText(SAY_QUEST_END, m_creature); + break; + } + } + } + + void JustSummoned(Creature* pSummoned) override + { + if (pSummoned->GetEntry() == NPC_FORSAKEN_SCOUT) + { + // Only one of the scouts yells + if (m_uiCreaturesCount == 1) + DoScriptText(SAY_SCOUT_SECOND_AMBUSH, pSummoned, m_creature); + } + else if (pSummoned->GetEntry() == NPC_BALIZAR_THE_UMBRAGE) + DoScriptText(SAY_BALIZAR_FINAL_AMBUSH, pSummoned); + + ++m_uiCreaturesCount; + pSummoned->AI()->AttackStart(m_creature); + } +}; + +CreatureAI* GetAI_npc_feero_ironhand(Creature* pCreature) +{ + return new npc_feero_ironhandAI(pCreature); +} + +bool QuestAccept_npc_feero_ironhand(Player* pPlayer, Creature* pCreature, const Quest* pQuest) +{ + if (pQuest->GetQuestId() == QUEST_SUPPLIES_TO_AUBERDINE) + { + DoScriptText(SAY_QUEST_START, pCreature, pPlayer); + pCreature->SetFactionTemporary(FACTION_ESCORT_A_NEUTRAL_PASSIVE, TEMPFACTION_RESTORE_RESPAWN); + + if (npc_feero_ironhandAI* pEscortAI = dynamic_cast(pCreature->AI())) + pEscortAI->Start(true, pPlayer, pQuest, true); + } + + return true; +} + +void AddSC_ashenvale() +{ + Script* pNewScript; + + pNewScript = new Script; + pNewScript->Name = "npc_muglash"; + pNewScript->GetAI = &GetAI_npc_muglash; + pNewScript->pQuestAcceptNPC = &QuestAccept_npc_muglash; + pNewScript->RegisterSelf(); + + pNewScript = new Script; + pNewScript->Name = "go_naga_brazier"; + pNewScript->pGOUse = &GOUse_go_naga_brazier; + pNewScript->RegisterSelf(); + + pNewScript = new Script; + pNewScript->Name = "npc_ruul_snowhoof"; + pNewScript->GetAI = &GetAI_npc_ruul_snowhoofAI; + pNewScript->pQuestAcceptNPC = &QuestAccept_npc_ruul_snowhoof; + pNewScript->RegisterSelf(); + + pNewScript = new Script; + pNewScript->Name = "npc_torek"; + pNewScript->GetAI = &GetAI_npc_torek; + pNewScript->pQuestAcceptNPC = &QuestAccept_npc_torek; + pNewScript->RegisterSelf(); + + pNewScript = new Script; + pNewScript->Name = "npc_feero_ironhand"; + pNewScript->GetAI = &GetAI_npc_feero_ironhand; + pNewScript->pQuestAcceptNPC = &QuestAccept_npc_feero_ironhand; + pNewScript->RegisterSelf(); +} diff --git a/src/modules/SD2/scripts/kalimdor/azshara.cpp b/src/modules/SD2/scripts/kalimdor/azshara.cpp new file mode 100644 index 000000000..200dc2f76 --- /dev/null +++ b/src/modules/SD2/scripts/kalimdor/azshara.cpp @@ -0,0 +1,490 @@ +/* Copyright (C) 2006 - 2013 ScriptDev2 + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along 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 +npc_rizzle_sprysprocket +npc_depth_charge +go_southfury_moonstone +mobs_spitelashes +npc_loramus_thalipedes +EndContentData */ + +#include "precompiled.h" +#include "escort_ai.h" + +/*##### +## npc_rizzle_sprysprocket +#####*/ + +enum +{ + SAY_START = -1000351, + EMOTE_START = -1000352, + SAY_WHISPER_CHILL = -1000353, + SAY_GRENADE_FAIL = -1000354, + SAY_END = -1000355, + + QUEST_MOONSTONE = 10994, + NPC_RIZZLE = 23002, + NPC_DEPTH_CHARGE = 23025, + + SPELL_SUMMON_RIZZLE = 39866, + SPELL_BLACKJACK = 39865, // stuns player + SPELL_ESCAPE = 39871, // teleports to water + SPELL_SWIM_SPEED = 40596, + + SPELL_FROST_TRAP = 39902, // not used? + + SPELL_PERIODIC_GRENADE = 40553, // cannot tell who are supposed to have this aura + SPELL_FROST_GRENADE = 40525, // triggered by periodic grenade + + SPELL_SUMMON_DEPTH_CHARGE = 39907, // summons the bomb creature + SPELL_TRAP = 39899, // knockback + + SPELL_PERIODIC_CHECK = 39888, + SPELL_SURRENDER = 39889, // should be triggered by periodic check, if player comes in certain distance with quest incomplete + + SPELL_GIVE_MOONSTONE = 39886 +}; + +#define GOSSIP_ITEM_MOONSTONE "Hand over the Southfury moonstone and I'll let you go." + +struct npc_rizzle_sprysprocketAI : public npc_escortAI +{ + npc_rizzle_sprysprocketAI(Creature* pCreature) : npc_escortAI(pCreature) + { + pCreature->SetActiveObjectState(true); + m_bIsIntro = true; + m_uiIntroPhase = 0; + m_uiIntroTimer = 0; + m_uiDepthChargeTimer = 10000; + Reset(); + } + + bool m_bIsIntro; + uint8 m_uiIntroPhase; + uint32 m_uiIntroTimer; + uint32 m_uiDepthChargeTimer; + + void MoveInLineOfSight(Unit* pUnit) override + { + if (HasEscortState(STATE_ESCORT_ESCORTING) && pUnit->GetTypeId() == TYPEID_PLAYER) + { + if (!HasEscortState(STATE_ESCORT_PAUSED) && m_creature->IsWithinDistInMap(pUnit, INTERACTION_DISTANCE) && m_creature->IsWithinLOSInMap(pUnit)) + { + if (((Player*)pUnit)->GetQuestStatus(QUEST_MOONSTONE) == QUEST_STATUS_INCOMPLETE) + m_creature->CastSpell(m_creature, SPELL_SURRENDER, true); + } + } + + npc_escortAI::MoveInLineOfSight(pUnit); + } + + void WaypointReached(uint32 uiPointId) override + { + switch (uiPointId) + { + case 0: + m_creature->CastSpell(m_creature, SPELL_PERIODIC_CHECK, true); + break; + } + } + + void Reset() override { } + + void SpellHit(Unit* /*pCaster*/, const SpellEntry* pSpell) override + { + if (pSpell->Id == SPELL_SURRENDER) + { + SetEscortPaused(true); + DoScriptText(SAY_END, m_creature); + m_creature->SetUInt32Value(UNIT_NPC_FLAGS, UNIT_NPC_FLAG_GOSSIP); + } + } + + // this may be wrong (and doesn't work) + void SpellHitTarget(Unit* pTarget, const SpellEntry* pSpell) override + { + if (pTarget->GetTypeId() == TYPEID_PLAYER && pSpell->Id == SPELL_FROST_GRENADE) + DoScriptText(SAY_WHISPER_CHILL, m_creature, pTarget); + } + + // this may be wrong + void JustSummoned(Creature* /*pSummoned*/) override + { + // pSummoned->CastSpell(pSummoned,SPELL_PERIODIC_GRENADE,false,NULL,NULL,m_creature->GetObjectGuid()); + } + + void UpdateEscortAI(const uint32 uiDiff) override + { + if (m_bIsIntro) + { + if (m_uiIntroTimer < uiDiff) + m_uiIntroTimer = 1500; + else + { + m_uiIntroTimer -= uiDiff; + return; + } + + switch (m_uiIntroPhase) + { + case 0: + DoScriptText(SAY_START, m_creature); + DoScriptText(EMOTE_START, m_creature); + break; + case 1: + // teleports to water _before_ we Start() + m_creature->CastSpell(m_creature, SPELL_ESCAPE, false); + break; + case 2: + m_creature->CastSpell(m_creature, SPELL_SWIM_SPEED, false); + m_bIsIntro = false; + Start(true); + break; + } + + ++m_uiIntroPhase; + return; + } + + if (m_uiDepthChargeTimer < uiDiff) + { + if (!HasEscortState(STATE_ESCORT_PAUSED)) + m_creature->CastSpell(m_creature, SPELL_SUMMON_DEPTH_CHARGE, false); + + m_uiDepthChargeTimer = urand(10000, 15000); + } + else + m_uiDepthChargeTimer -= uiDiff; + } +}; + +CreatureAI* GetAI_npc_rizzle_sprysprocket(Creature* pCreature) +{ + return new npc_rizzle_sprysprocketAI(pCreature); +} + +bool GossipHello_npc_rizzle_sprysprocket(Player* pPlayer, Creature* pCreature) +{ + if (pPlayer->GetQuestStatus(QUEST_MOONSTONE) == QUEST_STATUS_INCOMPLETE) + pPlayer->ADD_GOSSIP_ITEM(GOSSIP_ICON_CHAT, GOSSIP_ITEM_MOONSTONE, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 1); + + pPlayer->SEND_GOSSIP_MENU(pPlayer->GetGossipTextId(pCreature), pCreature->GetObjectGuid()); + return true; +} + +bool GossipSelect_npc_rizzle_sprysprocket(Player* pPlayer, Creature* /*pCreature*/, uint32 /*uiSender*/, uint32 uiAction) +{ + if (uiAction == GOSSIP_ACTION_INFO_DEF + 1) + { + pPlayer->CLOSE_GOSSIP_MENU(); + pPlayer->CastSpell(pPlayer, SPELL_GIVE_MOONSTONE, false); + } + + return true; +} + +struct npc_depth_chargeAI : public ScriptedAI +{ + npc_depth_chargeAI(Creature* pCreature) : ScriptedAI(pCreature) { Reset(); } + + void MoveInLineOfSight(Unit* pUnit) override + { + if (pUnit->GetTypeId() != TYPEID_PLAYER) + return; + + if (m_creature->IsWithinDistInMap(pUnit, INTERACTION_DISTANCE) && m_creature->IsWithinLOSInMap(pUnit)) + m_creature->CastSpell(pUnit, SPELL_TRAP, false); + } + + void Reset() override { } +}; + +CreatureAI* GetAI_npc_depth_charge(Creature* pCreature) +{ + return new npc_depth_chargeAI(pCreature); +} + +/*###### +## go_southfury_moonstone +######*/ + +bool GOUse_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; +} + +/*###### +## mobs_spitelashes +######*/ +enum +{ + // quest related + SPELL_POLYMORPH_BACKFIRE = 28406, // summons npc 16479 + QUEST_FRAGMENTED_MAGIC = 9364, + + // npc spells + SPELL_DISARM = 6713, // warrior + SPELL_SCREECH = 3589, // screamer + SPELL_FROST_SHOCK = 12548, // serpent guard + SPELL_RENEW = 11640, // siren + SPELL_SHOOT = 6660, + SPELL_FROST_SHOT = 12551, + SPELL_FROST_NOVA = 11831, + SPELL_STRIKE = 11976, // myrmidon + + NPC_SPITELASH_WARRIOR = 6190, + NPC_SPITELASH_SCREAMER = 6193, + NPC_SPITELASH_GUARD = 6194, + NPC_SPITELASH_SIREN = 6195, + NPC_SPITELASH_MYRMIDON = 6196, + + TARGET_TYPE_RANDOM = 0, + TARGET_TYPE_VICTIM = 1, + TARGET_TYPE_SELF = 2, + TARGET_TYPE_FRIENDLY = 3, +}; + +struct SpitelashAbilityStruct +{ + uint32 m_uiCreatureEntry, m_uiSpellId; + uint8 m_uiTargetType; + uint32 m_uiInitialTimer, m_uiCooldown; +}; + +static SpitelashAbilityStruct m_aSpitelashAbility[8] = +{ + {NPC_SPITELASH_WARRIOR, SPELL_DISARM, TARGET_TYPE_VICTIM, 4000, 10000}, + {NPC_SPITELASH_SCREAMER, SPELL_SCREECH, TARGET_TYPE_SELF, 7000, 15000}, + {NPC_SPITELASH_GUARD, SPELL_FROST_SHOCK, TARGET_TYPE_VICTIM, 7000, 13000}, + {NPC_SPITELASH_SIREN, SPELL_RENEW, TARGET_TYPE_FRIENDLY, 4000, 7000}, + {NPC_SPITELASH_SIREN, SPELL_SHOOT, TARGET_TYPE_RANDOM, 3000, 9000}, + {NPC_SPITELASH_SIREN, SPELL_FROST_SHOT, TARGET_TYPE_RANDOM, 7000, 10000}, + {NPC_SPITELASH_SIREN, SPELL_FROST_NOVA, TARGET_TYPE_SELF, 10000, 15000}, + {NPC_SPITELASH_MYRMIDON, SPELL_STRIKE, TARGET_TYPE_VICTIM, 3000, 7000} +}; + +struct mobs_spitelashesAI : public ScriptedAI +{ + mobs_spitelashesAI(Creature* pCreature) : ScriptedAI(pCreature) + { + for (uint8 i = 0; i < countof(m_aSpitelashAbility); ++i) + { + if (m_aSpitelashAbility[i].m_uiCreatureEntry == m_creature->GetEntry()) + m_mSpellTimers[i] = m_aSpitelashAbility[i].m_uiInitialTimer; + } + + Reset(); + } + + uint32 m_uiMorphTimer; + + UNORDERED_MAP m_mSpellTimers; + + void Reset() override + { + m_uiMorphTimer = 0; + + for (UNORDERED_MAP::iterator itr = m_mSpellTimers.begin(); itr != m_mSpellTimers.end(); ++itr) + itr->second = m_aSpitelashAbility[itr->first].m_uiInitialTimer; + } + + void SpellHit(Unit* pCaster, const SpellEntry* pSpell) override + { + // If already hit by the polymorph return + if (m_uiMorphTimer) + return; + + // Creature get polymorphed into a sheep and after 5 secs despawns + if (pCaster->GetTypeId() == TYPEID_PLAYER && ((Player*)pCaster)->GetQuestStatus(QUEST_FRAGMENTED_MAGIC) == QUEST_STATUS_INCOMPLETE && + (pSpell->Id == 118 || pSpell->Id == 12824 || pSpell->Id == 12825 || pSpell->Id == 12826)) + m_uiMorphTimer = 5000; + } + + bool CanUseSpecialAbility(uint32 uiIndex) + { + Unit* pTarget = NULL; + + switch (m_aSpitelashAbility[uiIndex].m_uiTargetType) + { + case TARGET_TYPE_SELF: + pTarget = m_creature; + break; + case TARGET_TYPE_VICTIM: + pTarget = m_creature->getVictim(); + break; + case TARGET_TYPE_RANDOM: + pTarget = m_creature->SelectAttackingTarget(ATTACKING_TARGET_RANDOM, 0, m_aSpitelashAbility[uiIndex].m_uiSpellId, SELECT_FLAG_IN_LOS); + break; + case TARGET_TYPE_FRIENDLY: + pTarget = DoSelectLowestHpFriendly(10.0f); + break; + } + + if (pTarget) + { + if (DoCastSpellIfCan(pTarget, m_aSpitelashAbility[uiIndex].m_uiSpellId) == CAST_OK) + return true; + } + + return false; + } + + void UpdateAI(const uint32 uiDiff) override + { + if (!m_creature->SelectHostileTarget() || !m_creature->getVictim()) + return; + + if (m_uiMorphTimer) + { + if (m_uiMorphTimer <= uiDiff) + { + if (DoCastSpellIfCan(m_creature, SPELL_POLYMORPH_BACKFIRE, CAST_TRIGGERED) == CAST_OK) + { + m_uiMorphTimer = 0; + m_creature->ForcedDespawn(); + } + } + else + m_uiMorphTimer -= uiDiff; + } + + for (UNORDERED_MAP::iterator itr = m_mSpellTimers.begin(); itr != m_mSpellTimers.end(); ++itr) + { + if (itr->second < uiDiff) + { + if (CanUseSpecialAbility(itr->first)) + { + itr->second = m_aSpitelashAbility[itr->first].m_uiCooldown; + break; + } + } + else + itr->second -= uiDiff; + } + + DoMeleeAttackIfReady(); + } +}; + +CreatureAI* GetAI_mobs_spitelashes(Creature* pCreature) +{ + return new mobs_spitelashesAI(pCreature); +} + +/*###### +## npc_loramus_thalipedes +######*/ + +bool GossipHello_npc_loramus_thalipedes(Player* pPlayer, Creature* pCreature) +{ + if (pCreature->IsQuestGiver()) + pPlayer->PrepareQuestMenu(pCreature->GetObjectGuid()); + + if (pPlayer->GetQuestStatus(2744) == QUEST_STATUS_INCOMPLETE) + pPlayer->ADD_GOSSIP_ITEM(GOSSIP_ICON_CHAT, "Can you help me?", GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 1); + + if (pPlayer->GetQuestStatus(3141) == QUEST_STATUS_INCOMPLETE) + pPlayer->ADD_GOSSIP_ITEM(GOSSIP_ICON_CHAT, "Tell me your story", GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 2); + + pPlayer->SEND_GOSSIP_MENU(pPlayer->GetGossipTextId(pCreature), pCreature->GetObjectGuid()); + + return true; +} + +bool GossipSelect_npc_loramus_thalipedes(Player* pPlayer, Creature* pCreature, uint32 /*uiSender*/, uint32 uiAction) +{ + switch (uiAction) + { + case GOSSIP_ACTION_INFO_DEF+1: + pPlayer->CLOSE_GOSSIP_MENU(); + pPlayer->AreaExploredOrEventHappens(2744); + break; + + case GOSSIP_ACTION_INFO_DEF+2: + pPlayer->ADD_GOSSIP_ITEM(GOSSIP_ICON_CHAT, "Please continue", GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 21); + pPlayer->SEND_GOSSIP_MENU(1813, pCreature->GetObjectGuid()); + break; + case GOSSIP_ACTION_INFO_DEF+21: + pPlayer->ADD_GOSSIP_ITEM(GOSSIP_ICON_CHAT, "I do not understand", GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 22); + pPlayer->SEND_GOSSIP_MENU(1814, pCreature->GetObjectGuid()); + break; + case GOSSIP_ACTION_INFO_DEF+22: + pPlayer->ADD_GOSSIP_ITEM(GOSSIP_ICON_CHAT, "Indeed", GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 23); + pPlayer->SEND_GOSSIP_MENU(1815, pCreature->GetObjectGuid()); + break; + case GOSSIP_ACTION_INFO_DEF+23: + pPlayer->ADD_GOSSIP_ITEM(GOSSIP_ICON_CHAT, "I will do this with or your help, Loramus", GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 24); + pPlayer->SEND_GOSSIP_MENU(1816, pCreature->GetObjectGuid()); + break; + case GOSSIP_ACTION_INFO_DEF+24: + pPlayer->ADD_GOSSIP_ITEM(GOSSIP_ICON_CHAT, "Yes", GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 25); + pPlayer->SEND_GOSSIP_MENU(1817, pCreature->GetObjectGuid()); + break; + case GOSSIP_ACTION_INFO_DEF+25: + pPlayer->CLOSE_GOSSIP_MENU(); + pPlayer->AreaExploredOrEventHappens(3141); + break; + } + return true; +} + +void AddSC_azshara() +{ + Script* pNewScript; + + pNewScript = new Script; + pNewScript->Name = "npc_rizzle_sprysprocket"; + pNewScript->GetAI = &GetAI_npc_rizzle_sprysprocket; + pNewScript->pGossipHello = &GossipHello_npc_rizzle_sprysprocket; + pNewScript->pGossipSelect = &GossipSelect_npc_rizzle_sprysprocket; + pNewScript->RegisterSelf(); + + pNewScript = new Script; + pNewScript->Name = "npc_depth_charge"; + pNewScript->GetAI = &GetAI_npc_depth_charge; + pNewScript->RegisterSelf(); + + pNewScript = new Script; + pNewScript->Name = "go_southfury_moonstone"; + pNewScript->pGOUse = &GOUse_go_southfury_moonstone; + pNewScript->RegisterSelf(); + + pNewScript = new Script; + pNewScript->Name = "mobs_spitelashes"; + pNewScript->GetAI = &GetAI_mobs_spitelashes; + pNewScript->RegisterSelf(); + + pNewScript = new Script; + pNewScript->Name = "npc_loramus_thalipedes"; + pNewScript->pGossipHello = &GossipHello_npc_loramus_thalipedes; + pNewScript->pGossipSelect = &GossipSelect_npc_loramus_thalipedes; + pNewScript->RegisterSelf(); +} diff --git a/src/modules/SD2/scripts/kalimdor/azuremyst_isle.cpp b/src/modules/SD2/scripts/kalimdor/azuremyst_isle.cpp new file mode 100644 index 000000000..9b178a6b1 --- /dev/null +++ b/src/modules/SD2/scripts/kalimdor/azuremyst_isle.cpp @@ -0,0 +1,412 @@ +/* Copyright (C) 2006 - 2013 ScriptDev2 + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along 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, 9528, 9537, Injured Draenei cosmetic only +SDCategory: Azuremyst Isle +EndScriptData */ + +/* ContentData +npc_draenei_survivor +npc_engineer_spark_overgrind +npc_injured_draenei +npc_magwin +EndContentData */ + +#include "precompiled.h" +#include "escort_ai.h" +#include + +/*###### +## npc_draenei_survivor +######*/ + +enum +{ + SAY_HEAL1 = -1000176, + SAY_HEAL2 = -1000177, + SAY_HEAL3 = -1000178, + SAY_HEAL4 = -1000179, + SAY_HELP1 = -1000180, + SAY_HELP2 = -1000181, + SAY_HELP3 = -1000182, + SAY_HELP4 = -1000183, + + SPELL_IRRIDATION = 35046, + SPELL_STUNNED = 28630 +}; + +struct npc_draenei_survivorAI : public ScriptedAI +{ + npc_draenei_survivorAI(Creature* pCreature) : ScriptedAI(pCreature) {Reset();} + + ObjectGuid m_casterGuid; + + uint32 m_uiSayThanksTimer; + uint32 m_uiRunAwayTimer; + uint32 m_uiSayHelpTimer; + + bool m_bCanSayHelp; + + void Reset() override + { + m_casterGuid.Clear(); + + m_uiSayThanksTimer = 0; + m_uiRunAwayTimer = 0; + m_uiSayHelpTimer = 10000; + + m_bCanSayHelp = 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 MoveInLineOfSight(Unit* pWho) override + { + if (m_bCanSayHelp && pWho->GetTypeId() == TYPEID_PLAYER && m_creature->IsFriendlyTo(pWho) && + m_creature->IsWithinDistInMap(pWho, 25.0f)) + { + // Random switch between 4 texts + switch (urand(0, 3)) + { + case 0: DoScriptText(SAY_HELP1, m_creature, pWho); break; + case 1: DoScriptText(SAY_HELP2, m_creature, pWho); break; + case 2: DoScriptText(SAY_HELP3, m_creature, pWho); break; + case 3: DoScriptText(SAY_HELP4, m_creature, pWho); break; + } + + m_uiSayHelpTimer = 20000; + m_bCanSayHelp = false; + } + } + + void SpellHit(Unit* pCaster, const SpellEntry* pSpell) override + { + if (pSpell->IsFitToFamilyMask(UI64LIT(0x0000000000000000), 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); + + m_casterGuid = pCaster->GetObjectGuid(); + + m_uiSayThanksTimer = 5000; + } + } + + void UpdateAI(const uint32 uiDiff) override + { + if (m_uiSayThanksTimer) + { + if (m_uiSayThanksTimer <= uiDiff) + { + m_creature->RemoveAurasDueToSpell(SPELL_IRRIDATION); + + if (Player* pPlayer = m_creature->GetMap()->GetPlayer(m_casterGuid)) + { + if (pPlayer->GetTypeId() != TYPEID_PLAYER) + return; + + switch (urand(0, 3)) + { + case 0: DoScriptText(SAY_HEAL1, m_creature, pPlayer); break; + case 1: DoScriptText(SAY_HEAL2, m_creature, pPlayer); break; + case 2: DoScriptText(SAY_HEAL3, m_creature, pPlayer); break; + case 3: DoScriptText(SAY_HEAL4, m_creature, pPlayer); break; + } + + pPlayer->TalkedToCreature(m_creature->GetEntry(), m_creature->GetObjectGuid()); + } + + m_creature->GetMotionMaster()->Clear(); + m_creature->GetMotionMaster()->MovePoint(0, -4115.053711f, -13754.831055f, 73.508949f); + + m_uiRunAwayTimer = 10000; + m_uiSayThanksTimer = 0; + } + else m_uiSayThanksTimer -= uiDiff; + + return; + } + + if (m_uiRunAwayTimer) + { + if (m_uiRunAwayTimer <= uiDiff) + m_creature->ForcedDespawn(); + else + m_uiRunAwayTimer -= uiDiff; + + return; + } + + if (m_uiSayHelpTimer < uiDiff) + { + m_bCanSayHelp = true; + m_uiSayHelpTimer = 20000; + } + else m_uiSayHelpTimer -= uiDiff; + } +}; + +CreatureAI* GetAI_npc_draenei_survivor(Creature* pCreature) +{ + return new npc_draenei_survivorAI(pCreature); +} + +/*###### +## npc_engineer_spark_overgrind +######*/ + +enum +{ + SAY_TEXT = -1000184, + EMOTE_SHELL = -1000185, + SAY_ATTACK = -1000186, + + 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 npc_engineer_spark_overgrindAI : public ScriptedAI +{ + npc_engineer_spark_overgrindAI(Creature* pCreature) : ScriptedAI(pCreature) + { + m_uiNpcFlags = pCreature->GetUInt32Value(UNIT_NPC_FLAGS); + Reset(); + + if (pCreature->GetAreaId() == AREA_COVE || pCreature->GetAreaId() == AREA_ISLE) + m_bIsTreeEvent = true; + } + + uint32 m_uiNpcFlags; + + uint32 m_uiDynamiteTimer; + uint32 m_uiEmoteTimer; + + bool m_bIsTreeEvent; + + void Reset() override + { + m_creature->SetUInt32Value(UNIT_NPC_FLAGS, m_uiNpcFlags); + + m_uiDynamiteTimer = 8000; + m_uiEmoteTimer = urand(120000, 150000); + + m_bIsTreeEvent = false; + } + + void Aggro(Unit* who) override + { + DoScriptText(SAY_ATTACK, m_creature, who); + } + + void UpdateAI(const uint32 diff) override + { + if (!m_creature->IsInCombat() && !m_bIsTreeEvent) + { + if (m_uiEmoteTimer < diff) + { + DoScriptText(SAY_TEXT, m_creature); + DoScriptText(EMOTE_SHELL, m_creature); + m_uiEmoteTimer = urand(120000, 150000); + } + else m_uiEmoteTimer -= diff; + } + else if (m_bIsTreeEvent) + { + // nothing here yet + return; + } + + if (!m_creature->SelectHostileTarget() || !m_creature->getVictim()) + return; + + if (m_uiDynamiteTimer < diff) + { + DoCastSpellIfCan(m_creature->getVictim(), SPELL_DYNAMITE); + m_uiDynamiteTimer = 8000; + } + else m_uiDynamiteTimer -= 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(pPlayer->GetGossipTextId(pCreature), pCreature->GetObjectGuid()); + return true; +} + +bool GossipSelect_npc_engineer_spark_overgrind(Player* pPlayer, Creature* pCreature, uint32 /*uiSender*/, uint32 uiAction) +{ + if (uiAction == GOSSIP_ACTION_INFO_DEF) + { + pPlayer->CLOSE_GOSSIP_MENU(); + pCreature->SetFactionTemporary(FACTION_HOSTILE, TEMPFACTION_RESTORE_COMBAT_STOP | TEMPFACTION_RESTORE_RESPAWN); + pCreature->AI()->AttackStart(pPlayer); + } + return true; +} + +/*###### +## npc_injured_draenei +######*/ + +struct npc_injured_draeneiAI : public ScriptedAI +{ + npc_injured_draeneiAI(Creature* pCreature) : ScriptedAI(pCreature) {Reset();} + + void Reset() override + { + m_creature->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_IN_COMBAT); + m_creature->SetHealth(int(m_creature->GetMaxHealth()*.15)); + switch (urand(0, 1)) + { + case 0: m_creature->SetStandState(UNIT_STAND_STATE_SIT); break; + case 1: m_creature->SetStandState(UNIT_STAND_STATE_SLEEP); break; + } + } + + void MoveInLineOfSight(Unit* /*pWho*/) override {} // ignore everyone around them (won't aggro anything) + + void UpdateAI(const uint32 /*uiDiff*/) override {} +}; + +CreatureAI* GetAI_npc_injured_draenei(Creature* pCreature) +{ + return new npc_injured_draeneiAI(pCreature); +} + +/*###### +## npc_magwin +######*/ + +enum +{ + SAY_START = -1000111, + SAY_AGGRO = -1000112, + SAY_PROGRESS = -1000113, + SAY_END1 = -1000114, + SAY_END2 = -1000115, + EMOTE_HUG = -1000116, + + QUEST_A_CRY_FOR_HELP = 9528 +}; + +struct npc_magwinAI : public npc_escortAI +{ + npc_magwinAI(Creature* pCreature) : npc_escortAI(pCreature) { Reset(); } + + void WaypointReached(uint32 uiPointId) override + { + Player* pPlayer = GetPlayerForEscort(); + + if (!pPlayer) + return; + + switch (uiPointId) + { + case 0: + DoScriptText(SAY_START, m_creature, pPlayer); + break; + case 17: + DoScriptText(SAY_PROGRESS, m_creature, pPlayer); + break; + case 28: + DoScriptText(SAY_END1, m_creature, pPlayer); + break; + case 29: + DoScriptText(EMOTE_HUG, m_creature, pPlayer); + DoScriptText(SAY_END2, m_creature, pPlayer); + pPlayer->GroupEventHappens(QUEST_A_CRY_FOR_HELP, m_creature); + break; + } + } + + void Aggro(Unit* pWho) override + { + DoScriptText(SAY_AGGRO, m_creature, pWho); + } + + void Reset() override { } +}; + +bool QuestAccept_npc_magwin(Player* pPlayer, Creature* pCreature, const Quest* pQuest) +{ + if (pQuest->GetQuestId() == QUEST_A_CRY_FOR_HELP) + { + pCreature->SetFactionTemporary(FACTION_ESCORT_A_NEUTRAL_PASSIVE, TEMPFACTION_RESTORE_RESPAWN); + + if (npc_magwinAI* pEscortAI = dynamic_cast(pCreature->AI())) + pEscortAI->Start(false, pPlayer, pQuest); + } + return true; +} + +CreatureAI* GetAI_npc_magwinAI(Creature* pCreature) +{ + return new npc_magwinAI(pCreature); +} + +void AddSC_azuremyst_isle() +{ + Script* pNewScript; + + pNewScript = new Script; + pNewScript->Name = "npc_draenei_survivor"; + pNewScript->GetAI = &GetAI_npc_draenei_survivor; + pNewScript->RegisterSelf(); + + pNewScript = new Script; + pNewScript->Name = "npc_engineer_spark_overgrind"; + pNewScript->GetAI = &GetAI_npc_engineer_spark_overgrind; + pNewScript->pGossipHello = &GossipHello_npc_engineer_spark_overgrind; + pNewScript->pGossipSelect = &GossipSelect_npc_engineer_spark_overgrind; + pNewScript->RegisterSelf(); + + pNewScript = new Script; + pNewScript->Name = "npc_injured_draenei"; + pNewScript->GetAI = &GetAI_npc_injured_draenei; + pNewScript->RegisterSelf(); + + pNewScript = new Script; + pNewScript->Name = "npc_magwin"; + pNewScript->GetAI = &GetAI_npc_magwinAI; + pNewScript->pQuestAcceptNPC = &QuestAccept_npc_magwin; + pNewScript->RegisterSelf(); +} diff --git a/src/modules/SD2/scripts/kalimdor/blackfathom_deeps/blackfathom_deeps.h b/src/modules/SD2/scripts/kalimdor/blackfathom_deeps/blackfathom_deeps.h new file mode 100644 index 000000000..550992174 --- /dev/null +++ b/src/modules/SD2/scripts/kalimdor/blackfathom_deeps/blackfathom_deeps.h @@ -0,0 +1,121 @@ +/* Copyright (C) 2006 - 2013 ScriptDev2 + * 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 +{ + MAX_ENCOUNTER = 2, + MAX_FIRES = 4, + MAX_COUNT_POS = 3, + + TYPE_KELRIS = 1, + TYPE_SHRINE = 2, + + NPC_KELRIS = 4832, + + // Shrine event + NPC_AKUMAI_SERVANT = 4978, + NPC_AKUMAI_SNAPJAW = 4825, + NPC_BARBED_CRUSTACEAN = 4823, + NPC_MURKSHALLOW_SOFTSHELL = 4977, + + GO_PORTAL_DOOR = 21117, + GO_SHRINE_1 = 21118, + GO_SHRINE_2 = 21119, + GO_SHRINE_3 = 21120, + GO_SHRINE_4 = 21121, +}; + +/* This is the spawn pattern for the event mobs +* D +* 0 3 +* 1 S 4 +* 2 5 +* E + +* This event spawns 4 sets of mobs +* The order in whitch the fires are lit doesn't matter + +* First: 3 Snapjaws: Positions 0, 1, 5 +* Second: 2 Servants: Positions 1, 4 +* Third: 4 Crabs: Positions 0, 2, 3, 4 +* Fourth: 10 Murkshallows: Positions 2*0, 1, 2*2; 3, 2*4, 2*5 + +* On wipe the mobs don't despawn; they stay there until player returns +*/ + +struct Locations +{ + float m_fX, m_fY, m_fZ, m_fO; +}; + +static const Locations aSpawnLocations[6] = // Should be near the correct positions +{ + { -768.949f, -174.413f, -25.87f, 3.09f}, // Left side + { -768.888f, -164.238f, -25.87f, 3.09f}, + { -768.951f, -153.911f, -25.88f, 3.09f}, + { -867.782f, -174.352f, -25.87f, 6.27f}, // Right side + { -867.875f, -164.089f, -25.87f, 6.27f}, + { -867.859f, -153.927f, -25.88f, 6.27f} +}; + +struct PosCount +{ + uint8 m_uiCount, m_uiSummonPosition; +}; + +struct SummonInformation +{ + uint8 m_uiWaveIndex; + uint32 m_uiNpcEntry; + PosCount m_aCountAndPos[MAX_COUNT_POS]; +}; + +// ASSERT m_uiSummonPosition < 6 (see aSpawnLocations) +static const SummonInformation aWaveSummonInformation[] = +{ + {0, NPC_AKUMAI_SNAPJAW, {{1, 0}, {1, 1}, {1, 5}}}, + {1, NPC_AKUMAI_SERVANT, {{1, 1}, {1, 4}, {0, 0}}}, + {2, NPC_BARBED_CRUSTACEAN, {{1, 0}, {1, 2}, {0, 0}}}, + {2, NPC_BARBED_CRUSTACEAN, {{1, 3}, {1, 4}, {0, 0}}}, + {3, NPC_MURKSHALLOW_SOFTSHELL, {{2, 0}, {1, 1}, {2, 2}}}, + {3, NPC_MURKSHALLOW_SOFTSHELL, {{1, 3}, {2, 4}, {2, 5}}} +}; + +class instance_blackfathom_deeps : public ScriptedInstance +{ + public: + instance_blackfathom_deeps(Map* pMap); + ~instance_blackfathom_deeps() {} + + void Initialize() override; + + void OnCreatureCreate(Creature* pCreature) override; + void OnObjectCreate(GameObject* pGo) override; + void OnCreatureDeath(Creature* pCreature) override; + + void Update(uint32 uiDiff) override; + + void SetData(uint32 uiType, uint32 uiData) override; + uint32 GetData(uint32 uiType) const override; + + const char* Save() const override { return m_strInstData.c_str(); } + void Load(const char* chrIn) override; + + protected: + void DoSpawnMobs(uint8 uiWaveIndex); + bool IsWaveEventFinished(); + + uint32 m_auiEncounter[MAX_ENCOUNTER]; + std::string m_strInstData; + + uint32 m_uiSpawnMobsTimer[MAX_FIRES]; + uint8 m_uiWaveCounter; + + std::list m_lWaveMobsGuids[MAX_FIRES]; +}; + +#endif diff --git a/src/modules/SD2/scripts/kalimdor/blackfathom_deeps/instance_blackfathom_deeps.cpp b/src/modules/SD2/scripts/kalimdor/blackfathom_deeps/instance_blackfathom_deeps.cpp new file mode 100644 index 000000000..25c466cf8 --- /dev/null +++ b/src/modules/SD2/scripts/kalimdor/blackfathom_deeps/instance_blackfathom_deeps.cpp @@ -0,0 +1,278 @@ +/* Copyright (C) 2006 - 2013 ScriptDev2 + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along 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 "blackfathom_deeps.h" + +/* Encounter 0 = Twilight Lord Kelris + Encounter 1 = Shrine event + Must kill twilight lord for shrine event to be possible + */ + +instance_blackfathom_deeps::instance_blackfathom_deeps(Map* pMap) : ScriptedInstance(pMap), + m_uiWaveCounter(0) +{ + Initialize(); +} + +void instance_blackfathom_deeps::Initialize() +{ + memset(&m_auiEncounter, 0, sizeof(m_auiEncounter)); + memset(&m_uiSpawnMobsTimer, 0, sizeof(m_uiSpawnMobsTimer)); +} + +void instance_blackfathom_deeps::OnCreatureCreate(Creature* pCreature) +{ + if (pCreature->GetEntry() == NPC_KELRIS) + m_mNpcEntryGuidStore[NPC_KELRIS] = pCreature->GetObjectGuid(); +} + +void instance_blackfathom_deeps::OnObjectCreate(GameObject* pGo) +{ + switch (pGo->GetEntry()) + { + case GO_PORTAL_DOOR: + if (m_auiEncounter[1] == DONE) + pGo->SetGoState(GO_STATE_ACTIVE); + + m_mGoEntryGuidStore[GO_PORTAL_DOOR] = pGo->GetObjectGuid(); + break; + case GO_SHRINE_1: + case GO_SHRINE_2: + case GO_SHRINE_3: + case GO_SHRINE_4: + if (m_auiEncounter[1] == DONE) + pGo->SetGoState(GO_STATE_ACTIVE); + break; + } +} + +void instance_blackfathom_deeps::DoSpawnMobs(uint8 uiWaveIndex) +{ + Creature* pKelris = GetSingleCreatureFromStorage(NPC_KELRIS); + if (!pKelris) + return; + + float fX_resp, fY_resp, fZ_resp; + + pKelris->GetRespawnCoord(fX_resp, fY_resp, fZ_resp); + + for (uint8 i = 0; i < countof(aWaveSummonInformation); ++i) + { + if (aWaveSummonInformation[i].m_uiWaveIndex != uiWaveIndex) + continue; + + // Summon mobs at positions + for (uint8 j = 0; j < MAX_COUNT_POS; ++j) + { + for (uint8 k = 0; k < aWaveSummonInformation[i].m_aCountAndPos[j].m_uiCount; ++k) + { + uint8 uiPos = aWaveSummonInformation[i].m_aCountAndPos[j].m_uiSummonPosition; + float fPosX = aSpawnLocations[uiPos].m_fX; + float fPosY = aSpawnLocations[uiPos].m_fY; + float fPosZ = aSpawnLocations[uiPos].m_fZ; + float fPosO = aSpawnLocations[uiPos].m_fO; + + // Adapt fPosY slightly in case of higher summon-counts + if (aWaveSummonInformation[i].m_aCountAndPos[j].m_uiCount > 1) + fPosY = fPosY - INTERACTION_DISTANCE / 2 + k * INTERACTION_DISTANCE / aWaveSummonInformation[i].m_aCountAndPos[j].m_uiCount; + + if (Creature* pSummoned = pKelris->SummonCreature(aWaveSummonInformation[i].m_uiNpcEntry, fPosX, fPosY, fPosZ, fPosO, TEMPSUMMON_DEAD_DESPAWN, 0)) + { + pSummoned->GetMotionMaster()->MovePoint(0, fX_resp, fY_resp, fZ_resp); + m_lWaveMobsGuids[uiWaveIndex].push_back(pSummoned->GetGUIDLow()); + } + } + } + } +} + +void instance_blackfathom_deeps::SetData(uint32 uiType, uint32 uiData) +{ + switch (uiType) + { + case TYPE_KELRIS: // EventAI must set instance data (1,3) at his death + if (m_auiEncounter[0] != DONE && uiData == DONE) + m_auiEncounter[0] = uiData; + break; + case TYPE_SHRINE: + m_auiEncounter[1] = uiData; + if (uiData == IN_PROGRESS) + { + m_uiSpawnMobsTimer[m_uiWaveCounter] = 3000; + ++m_uiWaveCounter; + } + else if (uiData == DONE) + DoUseDoorOrButton(GO_PORTAL_DOOR); + break; + } + + if (uiData == DONE) + { + OUT_SAVE_INST_DATA; + + std::ostringstream saveStream; + + saveStream << m_auiEncounter[0] << " " << m_auiEncounter[1]; + + m_strInstData = saveStream.str(); + SaveToDB(); + OUT_SAVE_INST_DATA_COMPLETE; + } +} + +uint32 instance_blackfathom_deeps::GetData(uint32 uiType) const +{ + switch (uiType) + { + case TYPE_KELRIS: return m_auiEncounter[0]; + case TYPE_SHRINE: return m_auiEncounter[1]; + default: + return 0; + } +} + +void instance_blackfathom_deeps::Load(const char* chrIn) +{ + if (!chrIn) + { + OUT_LOAD_INST_DATA_FAIL; + return; + } + + OUT_LOAD_INST_DATA(chrIn); + + std::istringstream loadStream(chrIn); + loadStream >> m_auiEncounter[0] >> m_auiEncounter[1]; + + for (uint8 i = 0; i < MAX_ENCOUNTER; ++i) + { + if (m_auiEncounter[i] == IN_PROGRESS) + m_auiEncounter[i] = NOT_STARTED; + } + + OUT_LOAD_INST_DATA_COMPLETE; +} + +void instance_blackfathom_deeps::OnCreatureDeath(Creature* pCreature) +{ + // Only use this function if shrine event is in progress + if (m_auiEncounter[1] != IN_PROGRESS) + return; + + switch (pCreature->GetEntry()) + { + case NPC_AKUMAI_SERVANT: + m_lWaveMobsGuids[1].remove(pCreature->GetGUIDLow()); + break; + case NPC_AKUMAI_SNAPJAW: + m_lWaveMobsGuids[0].remove(pCreature->GetGUIDLow()); + break; + case NPC_BARBED_CRUSTACEAN: + m_lWaveMobsGuids[2].remove(pCreature->GetGUIDLow()); + break; + case NPC_MURKSHALLOW_SOFTSHELL: + m_lWaveMobsGuids[3].remove(pCreature->GetGUIDLow()); + break; + } + + if (IsWaveEventFinished()) + SetData(TYPE_SHRINE, DONE); +} + +// Check if all the summoned event mobs are dead +bool instance_blackfathom_deeps::IsWaveEventFinished() +{ + // If not all fires are lighted return + if (m_uiWaveCounter < MAX_FIRES) + return false; + + // Check if all mobs are dead + for (uint8 i = 0; i < MAX_FIRES; ++i) + { + if (!m_lWaveMobsGuids[i].empty()) + return false; + } + + return true; +} + +void instance_blackfathom_deeps::Update(uint32 uiDiff) +{ + // Only use this function if shrine event is in progress + if (m_auiEncounter[1] != IN_PROGRESS) + return; + + for (uint8 i = 0; i < MAX_FIRES; ++i) + { + if (m_uiSpawnMobsTimer[i]) + { + if (m_uiSpawnMobsTimer[i] <= uiDiff) + { + DoSpawnMobs(i); + m_uiSpawnMobsTimer[i] = 0; + } + else + m_uiSpawnMobsTimer[i] -= uiDiff; + } + } +} + +InstanceData* GetInstanceData_instance_blackfathom_deeps(Map* pMap) +{ + return new instance_blackfathom_deeps(pMap); +} + +bool GOUse_go_fire_of_akumai(Player* /*pPlayer*/, GameObject* pGo) +{ + instance_blackfathom_deeps* pInstance = (instance_blackfathom_deeps*)pGo->GetInstanceData(); + + if (!pInstance) + return true; + + if (pInstance->GetData(TYPE_SHRINE) == DONE) + return true; + + if (pInstance->GetData(TYPE_KELRIS) == DONE) + { + pInstance->SetData(TYPE_SHRINE, IN_PROGRESS); + return false; + } + + return true; +} + +void AddSC_instance_blackfathom_deeps() +{ + Script* pNewScript; + + pNewScript = new Script; + pNewScript->Name = "instance_blackfathom_deeps"; + pNewScript->GetInstanceData = &GetInstanceData_instance_blackfathom_deeps; + pNewScript->RegisterSelf(); + + pNewScript = new Script; + pNewScript->Name = "go_fire_of_akumai"; + pNewScript->pGOUse = &GOUse_go_fire_of_akumai; + pNewScript->RegisterSelf(); +} diff --git a/src/modules/SD2/scripts/kalimdor/bloodmyst_isle.cpp b/src/modules/SD2/scripts/kalimdor/bloodmyst_isle.cpp new file mode 100644 index 000000000..cf56a405c --- /dev/null +++ b/src/modules/SD2/scripts/kalimdor/bloodmyst_isle.cpp @@ -0,0 +1,85 @@ +/* Copyright (C) 2006 - 2013 ScriptDev2 + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along 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 +SDCategory: Bloodmyst Isle +EndScriptData */ + +/* ContentData +mob_webbed_creature +EndContentData */ + +#include "precompiled.h" + +/*###### +## mob_webbed_creature +######*/ + +enum +{ + NPC_EXPEDITION_RESEARCHER = 17681, +}; + +// possible creatures to be spawned (too many to be added to enum) +const uint32 possibleSpawns[31] = {17322, 17661, 17496, 17522, 17340, 17352, 17333, 17524, 17654, 17348, 17339, 17345, 17359, 17353, 17336, 17550, 17330, 17701, 17321, 17325, 17320, 17683, 17342, 17715, 17334, 17341, 17338, 17337, 17346, 17344, 17327}; + +struct mob_webbed_creatureAI : public Scripted_NoMovementAI +{ + mob_webbed_creatureAI(Creature* pCreature) : Scripted_NoMovementAI(pCreature) { Reset(); } + + void Reset() override { } + void AttackStart(Unit* /*pWho*/) override { } + void MoveInLineOfSight(Unit* /*pWho*/) override { } + + void JustDied(Unit* pKiller) override + { + uint32 uiSpawnCreatureEntry = 0; + + switch (urand(0, 2)) + { + case 0: + uiSpawnCreatureEntry = NPC_EXPEDITION_RESEARCHER; + if (pKiller->GetTypeId() == TYPEID_PLAYER) + ((Player*)pKiller)->KilledMonsterCredit(uiSpawnCreatureEntry, m_creature->GetObjectGuid()); + break; + case 1: + case 2: + uiSpawnCreatureEntry = possibleSpawns[urand(0, 30)]; + break; + } + + if (uiSpawnCreatureEntry) + m_creature->SummonCreature(uiSpawnCreatureEntry, 0.0f, 0.0f, 0.0f, m_creature->GetOrientation(), TEMPSUMMON_TIMED_OOC_DESPAWN, 25000); + } +}; + +CreatureAI* GetAI_mob_webbed_creature(Creature* pCreature) +{ + return new mob_webbed_creatureAI(pCreature); +} + +void AddSC_bloodmyst_isle() +{ + Script* pNewScript; + + pNewScript = new Script; + pNewScript->Name = "mob_webbed_creature"; + pNewScript->GetAI = &GetAI_mob_webbed_creature; + pNewScript->RegisterSelf(); +} diff --git a/src/modules/SD2/scripts/kalimdor/boss_azuregos.cpp b/src/modules/SD2/scripts/kalimdor/boss_azuregos.cpp new file mode 100644 index 000000000..674f23801 --- /dev/null +++ b/src/modules/SD2/scripts/kalimdor/boss_azuregos.cpp @@ -0,0 +1,166 @@ +/* Copyright (C) 2006 - 2013 ScriptDev2 + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with 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: Spell reflect not effecting dots (Core problem) +SDCategory: Azshara +EndScriptData */ + +#include "precompiled.h" + +enum +{ + SAY_TELEPORT = -1000100, + + SPELL_ARCANE_VACUUM = 21147, + SPELL_MARK_OF_FROST_PLAYER = 23182, + SPELL_MARK_OF_FROST_AURA = 23184, // Triggers 23186 on players that have 23182; unfortunatelly 23183 is missing from dbc + SPELL_MANA_STORM = 21097, + SPELL_CHILL = 21098, + SPELL_FROST_BREATH = 21099, + SPELL_REFLECT = 22067, + SPELL_CLEAVE = 19983, // Was 8255; this one is from wowhead and seems to be the correct one + SPELL_ENRAGE = 23537, +}; + +struct boss_azuregosAI : public ScriptedAI +{ + boss_azuregosAI(Creature* pCreature) : ScriptedAI(pCreature) {Reset();} + + uint32 m_uiManaStormTimer; + uint32 m_uiChillTimer; + uint32 m_uiBreathTimer; + uint32 m_uiTeleportTimer; + uint32 m_uiReflectTimer; + uint32 m_uiCleaveTimer; + bool m_bEnraged; + + void Reset() override + { + m_uiManaStormTimer = urand(5000, 17000); + m_uiChillTimer = urand(10000, 30000); + m_uiBreathTimer = urand(2000, 8000); + m_uiTeleportTimer = 30000; + m_uiReflectTimer = urand(15000, 30000); + m_uiCleaveTimer = 7000; + m_bEnraged = false; + } + + void KilledUnit(Unit* pVictim) override + { + // Mark killed players with Mark of Frost + if (pVictim->GetTypeId() == TYPEID_PLAYER) + pVictim->CastSpell(pVictim, SPELL_MARK_OF_FROST_PLAYER, true, NULL, NULL, m_creature->GetObjectGuid()); + } + + void Aggro(Unit* /*pWho*/) override + { + // Boss aura which triggers the stun effect on dead players who resurrect + DoCastSpellIfCan(m_creature, SPELL_MARK_OF_FROST_AURA); + } + + void UpdateAI(const uint32 uiDiff) override + { + // Return since we have no target + if (!m_creature->SelectHostileTarget() || !m_creature->getVictim()) + return; + + if (m_uiTeleportTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature, SPELL_ARCANE_VACUUM) == CAST_OK) + { + DoScriptText(SAY_TELEPORT, m_creature); + m_uiTeleportTimer = 30000; + } + } + else + m_uiTeleportTimer -= uiDiff; + + // Chill Timer + if (m_uiChillTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature, SPELL_CHILL) == CAST_OK) + m_uiChillTimer = urand(13000, 25000); + } + else + m_uiChillTimer -= uiDiff; + + // Breath Timer + if (m_uiBreathTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature, SPELL_FROST_BREATH) == CAST_OK) + m_uiBreathTimer = urand(10000, 15000); + } + else + m_uiBreathTimer -= uiDiff; + + // Mana Storm Timer + if (m_uiManaStormTimer < uiDiff) + { + if (Unit* pTarget = m_creature->SelectAttackingTarget(ATTACKING_TARGET_RANDOM, 0)) + { + if (DoCastSpellIfCan(pTarget, SPELL_MANA_STORM) == CAST_OK) + m_uiManaStormTimer = urand(7500, 12500); + } + } + else + m_uiManaStormTimer -= uiDiff; + + // Reflect Timer + if (m_uiReflectTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature, SPELL_REFLECT) == CAST_OK) + m_uiReflectTimer = urand(20000, 35000); + } + else + m_uiReflectTimer -= uiDiff; + + // Cleave Timer + if (m_uiCleaveTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature->getVictim(), SPELL_CLEAVE) == CAST_OK) + m_uiCleaveTimer = 7000; + } + else + m_uiCleaveTimer -= uiDiff; + + // EnrageTimer + if (!m_bEnraged && m_creature->GetHealthPercent() < 26.0f) + { + if (DoCastSpellIfCan(m_creature, SPELL_ENRAGE) == CAST_OK) + m_bEnraged = true; + } + + DoMeleeAttackIfReady(); + } +}; + +CreatureAI* GetAI_boss_azuregos(Creature* pCreature) +{ + return new boss_azuregosAI(pCreature); +} + +void AddSC_boss_azuregos() +{ + Script* pNewScript; + + pNewScript = new Script; + pNewScript->Name = "boss_azuregos"; + pNewScript->GetAI = &GetAI_boss_azuregos; + pNewScript->RegisterSelf(); +} diff --git a/src/modules/SD2/scripts/kalimdor/caverns_of_time/culling_of_stratholme/culling_of_stratholme.cpp b/src/modules/SD2/scripts/kalimdor/caverns_of_time/culling_of_stratholme/culling_of_stratholme.cpp new file mode 100644 index 000000000..b3a6f772c --- /dev/null +++ b/src/modules/SD2/scripts/kalimdor/caverns_of_time/culling_of_stratholme/culling_of_stratholme.cpp @@ -0,0 +1,243 @@ +/* Copyright (C) 2006 - 2013 ScriptDev2 + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +/* ScriptData +SDName: culling_of_stratholme +SD%Complete: 5% +SDComment: Placeholder +SDCategory: Culling of Stratholme +EndScriptData */ + +#include "precompiled.h" +#include "culling_of_stratholme.h" + +/* ************* +** npc_chromie (gossip, quest-accept) +************* */ + +enum +{ + QUEST_DISPELLING_ILLUSIONS = 13149, + QUEST_A_ROYAL_ESCORT = 13151, + + ITEM_ARCANE_DISRUPTOR = 37888, + + GOSSIP_ITEM_ENTRANCE_1 = -3595000, + GOSSIP_ITEM_ENTRANCE_2 = -3595001, + GOSSIP_ITEM_ENTRANCE_3 = -3595002, + + TEXT_ID_ENTRANCE_1 = 12992, + TEXT_ID_ENTRANCE_2 = 12993, + TEXT_ID_ENTRANCE_3 = 12994, + TEXT_ID_ENTRANCE_4 = 12995, + + GOSSIP_ITEM_INN_1 = -3595003, + GOSSIP_ITEM_INN_2 = -3595004, + GOSSIP_ITEM_INN_3 = -3595005, + + TEXT_ID_INN_1 = 12939, + TEXT_ID_INN_2 = 12949, + TEXT_ID_INN_3 = 12950, + TEXT_ID_INN_4 = 12952, +}; + +bool GossipHello_npc_chromie(Player* pPlayer, Creature* pCreature) +{ + if (pCreature->IsQuestGiver()) + pPlayer->PrepareQuestMenu(pCreature->GetObjectGuid()); + + if (instance_culling_of_stratholme* m_pInstance = (instance_culling_of_stratholme*)pCreature->GetInstanceData()) + { + switch (pCreature->GetEntry()) + { + case NPC_CHROMIE_INN: + if (m_pInstance->GetData(TYPE_GRAIN_EVENT) != DONE) + { + if (pPlayer->GetQuestRewardStatus(QUEST_DISPELLING_ILLUSIONS) && !pPlayer->HasItemCount(ITEM_ARCANE_DISRUPTOR, 1)) + pPlayer->ADD_GOSSIP_ITEM_ID(GOSSIP_ICON_CHAT, GOSSIP_ITEM_INN_1, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 1); + } + pPlayer->SEND_GOSSIP_MENU(TEXT_ID_INN_1, pCreature->GetObjectGuid()); + break; + case NPC_CHROMIE_ENTRANCE: + if (m_pInstance->GetData(TYPE_GRAIN_EVENT) == DONE && m_pInstance->GetData(TYPE_ARTHAS_INTRO_EVENT) == NOT_STARTED && pPlayer->GetQuestRewardStatus(QUEST_A_ROYAL_ESCORT)) + pPlayer->ADD_GOSSIP_ITEM_ID(GOSSIP_ICON_CHAT, GOSSIP_ITEM_ENTRANCE_1, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 1); + pPlayer->SEND_GOSSIP_MENU(TEXT_ID_ENTRANCE_1, pCreature->GetObjectGuid()); + break; + } + } + return true; +} + +bool GossipSelect_npc_chromie(Player* pPlayer, Creature* pCreature, uint32 /*sender*/, uint32 uiAction) +{ + switch (pCreature->GetEntry()) + { + case NPC_CHROMIE_INN: + switch (uiAction) + { + case GOSSIP_ACTION_INFO_DEF+1: + pPlayer->ADD_GOSSIP_ITEM_ID(GOSSIP_ICON_CHAT, GOSSIP_ITEM_INN_2, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 2); + pPlayer->SEND_GOSSIP_MENU(TEXT_ID_INN_2, pCreature->GetObjectGuid()); + break; + case GOSSIP_ACTION_INFO_DEF+2: + pPlayer->ADD_GOSSIP_ITEM_ID(GOSSIP_ICON_CHAT, GOSSIP_ITEM_INN_3, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 3); + pPlayer->SEND_GOSSIP_MENU(TEXT_ID_INN_3, pCreature->GetObjectGuid()); + break; + case GOSSIP_ACTION_INFO_DEF+3: + pPlayer->SEND_GOSSIP_MENU(TEXT_ID_INN_4, pCreature->GetObjectGuid()); + if (!pPlayer->HasItemCount(ITEM_ARCANE_DISRUPTOR, 1)) + { + if (Item* pItem = pPlayer->StoreNewItemInInventorySlot(ITEM_ARCANE_DISRUPTOR, 1)) + { + pPlayer->SendNewItem(pItem, 1, true, false); + if (instance_culling_of_stratholme* pInstance = (instance_culling_of_stratholme*)pCreature->GetInstanceData()) + { + if (pInstance->GetData(TYPE_GRAIN_EVENT) == NOT_STARTED) + pInstance->SetData(TYPE_GRAIN_EVENT, SPECIAL); + } + } + } + break; + } + break; + case NPC_CHROMIE_ENTRANCE: + switch (uiAction) + { + case GOSSIP_ACTION_INFO_DEF+1: + pPlayer->ADD_GOSSIP_ITEM_ID(GOSSIP_ICON_CHAT, GOSSIP_ITEM_ENTRANCE_2, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 2); + pPlayer->SEND_GOSSIP_MENU(TEXT_ID_ENTRANCE_2, pCreature->GetObjectGuid()); + break; + case GOSSIP_ACTION_INFO_DEF+2: + pPlayer->ADD_GOSSIP_ITEM_ID(GOSSIP_ICON_CHAT, GOSSIP_ITEM_ENTRANCE_3, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 3); + pPlayer->SEND_GOSSIP_MENU(TEXT_ID_ENTRANCE_3, pCreature->GetObjectGuid()); + break; + case GOSSIP_ACTION_INFO_DEF+3: + pPlayer->SEND_GOSSIP_MENU(TEXT_ID_ENTRANCE_4, pCreature->GetObjectGuid()); + if (instance_culling_of_stratholme* pInstance = (instance_culling_of_stratholme*)pCreature->GetInstanceData()) + { + if (pInstance->GetData(TYPE_ARTHAS_INTRO_EVENT) == NOT_STARTED) + pInstance->DoSpawnArthasIfNeeded(); + } + break; + } + break; + } + return true; +} + +bool QuestAccept_npc_chromie(Player* /*pPlayer*/, Creature* pCreature, const Quest* pQuest) +{ + switch (pQuest->GetQuestId()) + { + case QUEST_DISPELLING_ILLUSIONS: + if (instance_culling_of_stratholme* pInstance = (instance_culling_of_stratholme*)pCreature->GetInstanceData()) + { + if (pInstance->GetData(TYPE_GRAIN_EVENT) == NOT_STARTED) + pInstance->SetData(TYPE_GRAIN_EVENT, SPECIAL); + } + break; + case QUEST_A_ROYAL_ESCORT: + if (instance_culling_of_stratholme* pInstance = (instance_culling_of_stratholme*)pCreature->GetInstanceData()) + { + if (pInstance->GetData(TYPE_ARTHAS_INTRO_EVENT) == NOT_STARTED) + pInstance->DoSpawnArthasIfNeeded(); + } + break; + } + return true; +} + +/* ************* +** npc_crates_bunny (spell aura effect dummy) +************* */ + +enum +{ + SPELL_ARCANE_DISRUPTION = 49590, + SPELL_CRATES_KILL_CREDIT = 58109, +}; + +bool EffectAuraDummy_spell_aura_dummy_npc_crates_dummy(const Aura* pAura, bool bApply) +{ + if (pAura->GetId() == SPELL_ARCANE_DISRUPTION && pAura->GetEffIndex() == EFFECT_INDEX_0 && bApply) + { + if (Creature* pTarget = (Creature*)pAura->GetTarget()) + { + std::list lCrateBunnyList; + if (instance_culling_of_stratholme* pInstance = (instance_culling_of_stratholme*)pTarget->GetInstanceData()) + { + pInstance->GetCratesBunnyOrderedList(lCrateBunnyList); + uint8 i = 0; + for (std::list::const_iterator itr = lCrateBunnyList.begin(); itr != lCrateBunnyList.end(); ++itr) + { + ++i; + if (*itr == pTarget) + break; + } + + switch (i) + { + case 1: + // Start NPC_ROGER_OWENS Event + break; + case 2: + // Start NPC_SERGEANT_MORIGAN Event + break; + case 3: + // Start NPC_JENA_ANDERSON Event + break; + case 4: + // Start NPC_MALCOM_MOORE Event + break; + case 5: + // Start NPC_BARTLEBY_BATTSON Event + break; + } + + if (pInstance->GetData(TYPE_GRAIN_EVENT) != DONE) + { + pInstance->SetData(TYPE_GRAIN_EVENT, IN_PROGRESS); + + // Finished event, give killcredit + if (pInstance->GetData(TYPE_GRAIN_EVENT) == DONE) + pTarget->CastSpell(pTarget, SPELL_CRATES_KILL_CREDIT, true); + } + + // pTarget->ForcedDespawn(); // direct despawn has influence on visual effects, + // but despawning makes it impossible to multi-use the spell at the same place + // perhaps some add. GO-Visual + } + } + } + return true; +} + +void AddSC_culling_of_stratholme() +{ + Script* pNewScript; + + pNewScript = new Script; + pNewScript->Name = "npc_chromie"; + pNewScript->pGossipHello = &GossipHello_npc_chromie; + pNewScript->pGossipSelect = &GossipSelect_npc_chromie; + pNewScript->pQuestAcceptNPC = &QuestAccept_npc_chromie; + pNewScript->RegisterSelf(); + + pNewScript = new Script; + pNewScript->Name = "spell_dummy_npc_crates_bunny"; + pNewScript->pEffectAuraDummy = &EffectAuraDummy_spell_aura_dummy_npc_crates_dummy; + pNewScript->RegisterSelf(); +} diff --git a/src/modules/SD2/scripts/kalimdor/caverns_of_time/culling_of_stratholme/culling_of_stratholme.h b/src/modules/SD2/scripts/kalimdor/caverns_of_time/culling_of_stratholme/culling_of_stratholme.h new file mode 100644 index 000000000..27d0ea877 --- /dev/null +++ b/src/modules/SD2/scripts/kalimdor/caverns_of_time/culling_of_stratholme/culling_of_stratholme.h @@ -0,0 +1,161 @@ +/* Copyright (C) 2006 - 2013 ScriptDev2 + * This program is free software licensed under GPL version 2 + * Please see the included DOCS/LICENSE.TXT for more information */ + +#ifndef DEF_CULLING_OF_STRATHOLME_H +#define DEF_CULLING_OF_STRATHOLME_H + +enum +{ + MAX_ENCOUNTER = 9, + + TYPE_GRAIN_EVENT = 0, // crates with plagued grain identified + TYPE_ARTHAS_INTRO_EVENT = 1, // Arhas Speech and Walk to Gates and short intro with MalGanis + TYPE_MEATHOOK_EVENT = 2, // Waves 1-5 + TYPE_SALRAMM_EVENT = 3, // Waves 6-10 + TYPE_EPOCH_EVENT = 4, // Townhall Event, Boss Killed + TYPE_ARTHAS_ESCORT_EVENT = 5, // Townhall to Malganis + TYPE_MALGANIS_EVENT = 6, // Malganis + TYPE_INFINITE_CORRUPTER_TIME = 7, // Time for 25min Timer + TYPE_INFINITE_CORRUPTER = 8, + + // Main Encounter NPCs + NPC_CHROMIE_INN = 26527, + NPC_CHROMIE_ENTRANCE = 27915, + NPC_CHROMIE_END = 30997, + // NPC_HOURGLASS = 28656, + NPC_ARTHAS = 26499, + // NPC_MEATHOOK = 26529, + // NPC_SALRAMM_THE_FLESHCRAFTER = 26530, + // NPC_CHRONO_LORD_EPOCH = 26532, + // NPC_MALGANIS = 26533, + NPC_INFINITE_CORRUPTER = 32273, + NPC_LORDAERON_CRIER = 27913, + NPC_ZOMBIE = 27737, + + // Inn Event related NPC + NPC_MICHAEL_BELFAST = 30571, + NPC_HEARTHSINGER_FORRESTEN = 30551, + NPC_FRAS_SIABI = 30552, + NPC_FOOTMAN_JAMES = 30553, + NPC_MAL_CORRICKS = 31017, + NPC_GRYAN_STOUTMANTLE = 30561, + + // Grain Event NPCs + NPC_ROGER_OWENS = 27903, + NPC_SERGEANT_MORIGAN = 27877, + NPC_JENA_ANDERSON = 27885, + NPC_MALCOM_MOORE = 27891, // Not (yet?) spawned + NPC_BARTLEBY_BATTSON = 27907, + NPC_CRATES_BUNNY = 30996, + + // Intro Event NPCs + NPC_LORDAERON_FOOTMAN = 27745, + NPC_STRATHOLME_CITIZEN = 28167, + NPC_STRATHOLME_RESIDENT = 28169, + + // Mobs in Stratholme (to despawn) -- only here for sake of completeness handling remains open (mangos feature) + NPC_MAGISTRATE_BARTHILAS = 30994, + NPC_STEPHANIE_SINDREE = 31019, + NPC_LEEKA_TURNER = 31027, + NPC_SOPHIE_AAREN = 31021, + NPC_ROBERT_PIERCE = 31025, + NPC_GEORGE_GOODMAN = 31022, + + // Others NPCs in Stratholme + NPC_EMERY_NEILL = 30570, + NPC_EDWARD_ORRICK = 31018, + NPC_OLIVIA_ZENITH = 31020, + + // Townhall Event NPCs + NPC_AGIATED_STRATHOLME_CITIZEN = 31126, + NPC_AGIATED_STRATHOLME_RESIDENT = 31127, + NPC_PATRICIA_O_REILLY = 31028, + + // Gameobjects + GO_DOOR_BOOKCASE = 188686, + GO_DARK_RUNED_CHEST = 190663, + GO_DARK_RUNED_CHEST_H = 193597, + + // World States + WORLD_STATE_CRATES = 3479, + WORLD_STATE_CRATES_COUNT = 3480, + WORLD_STATE_WAVE = 3504, + WORLD_STATE_TIME = 3932, + WORLD_STATE_TIME_COUNTER = 3931, + + // Areatrigger + AREATRIGGER_INN = 5291, + /* + 5085 before bridge - could be Uther SpawnPos + 5148 ini entrance + 5181 ini exit + 5249 fras siabis store + 5250 leeking shields...(store) + 5251 bar in stratholme + 5252 Aaren flowers + 5253 Angelicas boutique + 5256 townhall + 5291 Inn */ +}; + +enum eInstancePosition +{ + POS_ARTHAS_INTRO = 1, + POS_ARTHAS_WAVES = 2, + POS_ARTHAS_TOWNHALL = 3, + POS_ARTHAS_ESCORTING = 4, + POS_ARTHAS_MALGANIS = 5, + POS_INSTANCE_FINISHED = 6 +}; + +class instance_culling_of_stratholme : public ScriptedInstance +{ + public: + instance_culling_of_stratholme(Map* pMap); + ~instance_culling_of_stratholme() {} + + void Initialize() override; + + void OnCreatureCreate(Creature* pCreature) override; + void OnObjectCreate(GameObject* pGo) override; + + void SetData(uint32 uiType, uint32 uiData) override; + uint32 GetData(uint32 uiType) const override; + + const char* Save() const override { return m_strInstData.c_str(); } + void Load(const char* chrIn) override; + + void Update(uint32 uiDiff) override; + + void GetStratAgiatedCitizenList(GuidList& lList) { lList = m_lAgiatedCitizenGUIDList; }; + void GetStratAgiatedResidentList(GuidList& lList) { lList = m_lAgiatedResidentGUIDList; }; + + void GetCratesBunnyOrderedList(std::list& lList); + Creature* GetStratIntroFootman(); + void GetResidentOrderedList(std::list& lList); + void DoSpawnArthasIfNeeded(); + void DoSpawnChromieIfNeeded(); + uint8 GetInstancePosition(); + void ArthasJustDied(); + + protected: + void OnPlayerEnter(Player* pPlayer) override; + void DoChromieHurrySpeech(); + + uint32 m_auiEncounter[MAX_ENCOUNTER]; + std::string m_strInstData; + + uint8 m_uiGrainCrateCount; + uint32 m_uiRemoveCrateStateTimer; + uint32 m_uiArthasRespawnTimer; + + GuidList m_luiCratesBunnyGUIDs; + GuidList m_luiFootmanGUIDs; + GuidList m_luiResidentGUIDs; + + GuidList m_lAgiatedCitizenGUIDList; + GuidList m_lAgiatedResidentGUIDList; +}; + +#endif diff --git a/src/modules/SD2/scripts/kalimdor/caverns_of_time/culling_of_stratholme/instance_culling_of_stratholme.cpp b/src/modules/SD2/scripts/kalimdor/caverns_of_time/culling_of_stratholme/instance_culling_of_stratholme.cpp new file mode 100644 index 000000000..0a8edce7a --- /dev/null +++ b/src/modules/SD2/scripts/kalimdor/caverns_of_time/culling_of_stratholme/instance_culling_of_stratholme.cpp @@ -0,0 +1,485 @@ +/* Copyright (C) 2006 - 2013 ScriptDev2 + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along 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_culling_of_stratholme +SD%Complete: 80% +SDComment: +SDCategory: Culling of Stratholme +EndScriptData */ + +#include "precompiled.h" +#include "culling_of_stratholme.h" + +enum +{ + MAX_ARTHAS_SPAWN_POS = 5, + SAY_CHROMIE_HURRY = -1000000 // TODO +}; + +struct sSpawnLocation +{ + float m_fX, m_fY, m_fZ, m_fO; +}; + +static sSpawnLocation m_aArthasSpawnLocs[] = // need tuning +{ + {1969.73f, 1287.12f, 145.48f, 3.14f}, + {2049.43f, 1287.43f, 142.75f, 0.06f}, + {2365.54f, 1194.85f, 131.98f, 0.47f}, + {2534.46f, 1125.99f, 130.75f, 0.27f}, + {2363.77f, 1406.31f, 128.64f, 3.23f} +}; + +static sSpawnLocation m_aChromieSpawnLocs[] = // need tuning, escpecially EndPositions! +{ + {1814.46f, 1283.97f, 142.30f, 4.32f}, // near bridge + {2311.0f, 1502.4f, 127.9f, 0.0f}, // End + {1811.52f, 1285.92f, 142.37f, 4.47f}, // Hourglass, near bridge + {2186.42f, 1323.77f, 129.91f, 0.0f}, // Hourglass, End +}; + +instance_culling_of_stratholme::instance_culling_of_stratholme(Map* pMap) : ScriptedInstance(pMap), + m_uiGrainCrateCount(0), + m_uiRemoveCrateStateTimer(0), + m_uiArthasRespawnTimer(0) +{ + Initialize(); +} + +void instance_culling_of_stratholme::Initialize() +{ + memset(&m_auiEncounter, 0, sizeof(m_auiEncounter)); +} + +void instance_culling_of_stratholme::OnCreatureCreate(Creature* pCreature) +{ + switch (pCreature->GetEntry()) + { + case NPC_CHROMIE_ENTRANCE: + case NPC_CHROMIE_END: + case NPC_ARTHAS: + case NPC_MICHAEL_BELFAST: + case NPC_HEARTHSINGER_FORRESTEN: + case NPC_FRAS_SIABI: + case NPC_FOOTMAN_JAMES: + case NPC_MAL_CORRICKS: + case NPC_GRYAN_STOUTMANTLE: + case NPC_ROGER_OWENS: + case NPC_SERGEANT_MORIGAN: + case NPC_JENA_ANDERSON: + case NPC_MALCOM_MOORE: + case NPC_BARTLEBY_BATTSON: + case NPC_PATRICIA_O_REILLY: + case NPC_LORDAERON_CRIER: + case NPC_INFINITE_CORRUPTER: + m_mNpcEntryGuidStore[pCreature->GetEntry()] = pCreature->GetObjectGuid(); + break; + + case NPC_CRATES_BUNNY: m_luiCratesBunnyGUIDs.push_back(pCreature->GetObjectGuid()); break; + case NPC_LORDAERON_FOOTMAN: m_luiFootmanGUIDs.push_back(pCreature->GetObjectGuid()); break; + + case NPC_STRATHOLME_CITIZEN: + case NPC_STRATHOLME_RESIDENT: + if (m_auiEncounter[TYPE_ARTHAS_INTRO_EVENT] == DONE) + pCreature->UpdateEntry(NPC_ZOMBIE); + else + m_luiResidentGUIDs.push_back(pCreature->GetObjectGuid()); + break; + case NPC_AGIATED_STRATHOLME_CITIZEN: m_lAgiatedCitizenGUIDList.push_back(pCreature->GetObjectGuid()); break; + case NPC_AGIATED_STRATHOLME_RESIDENT: m_lAgiatedResidentGUIDList.push_back(pCreature->GetObjectGuid()); break; + } +} + +void instance_culling_of_stratholme::OnObjectCreate(GameObject* pGo) +{ + switch (pGo->GetEntry()) + { + case GO_DOOR_BOOKCASE: + if (m_auiEncounter[TYPE_EPOCH_EVENT] == DONE) + pGo->SetGoState(GO_STATE_ACTIVE); + break; + case GO_DARK_RUNED_CHEST: + case GO_DARK_RUNED_CHEST_H: + break; + + default: + return; + } + m_mGoEntryGuidStore[pGo->GetEntry()] = pGo->GetObjectGuid(); +} + +void instance_culling_of_stratholme::DoChromieHurrySpeech() +{ + if (Creature* pChromie = GetSingleCreatureFromStorage(NPC_CHROMIE_ENTRANCE)) + { + Map::PlayerList const& players = instance->GetPlayers(); + if (!players.isEmpty()) + { + for (Map::PlayerList::const_iterator itr = players.begin(); itr != players.end(); ++itr) + { + if (Player* pPlayer = itr->getSource()) + DoScriptText(SAY_CHROMIE_HURRY, pChromie, pPlayer); + } + } + } +} + +void instance_culling_of_stratholme::SetData(uint32 uiType, uint32 uiData) +{ + switch (uiType) + { + case TYPE_GRAIN_EVENT: + m_auiEncounter[uiType] = uiData; + if (uiData == SPECIAL) + DoUpdateWorldState(WORLD_STATE_CRATES, 1); + else if (uiData == IN_PROGRESS) + { + if (m_uiGrainCrateCount >= 5) + return; + + ++m_uiGrainCrateCount; + DoUpdateWorldState(WORLD_STATE_CRATES_COUNT, m_uiGrainCrateCount); + + if (m_uiGrainCrateCount == 5) + { + m_uiRemoveCrateStateTimer = 20000; + SetData(TYPE_GRAIN_EVENT, DONE); + } + } + break; + case TYPE_ARTHAS_INTRO_EVENT: + m_auiEncounter[uiType] = uiData; + break; + case TYPE_ARTHAS_ESCORT_EVENT: + m_auiEncounter[uiType] = uiData; + break; + case TYPE_MEATHOOK_EVENT: + m_auiEncounter[uiType] = uiData; + if (uiData == DONE) + SetData(TYPE_SALRAMM_EVENT, IN_PROGRESS); + break; + case TYPE_SALRAMM_EVENT: + m_auiEncounter[uiType] = uiData; + if (uiData == DONE) + DoUpdateWorldState(WORLD_STATE_WAVE, 0); // Remove WaveCounter + break; + case TYPE_EPOCH_EVENT: + m_auiEncounter[uiType] = uiData; + break; + case TYPE_MALGANIS_EVENT: + m_auiEncounter[uiType] = uiData; + if (uiData == DONE) + { + DoRespawnGameObject(instance->IsRegularDifficulty() ? GO_DARK_RUNED_CHEST : GO_DARK_RUNED_CHEST_H, 30 * MINUTE); + DoSpawnChromieIfNeeded(); + } + break; + case TYPE_INFINITE_CORRUPTER_TIME: + m_auiEncounter[uiType] = uiData; + if (!uiData) + { + DoUpdateWorldState(WORLD_STATE_TIME, 0); // Remove Timer + DoUpdateWorldState(WORLD_STATE_TIME_COUNTER, 0); + } + else + DoUpdateWorldState(WORLD_STATE_TIME_COUNTER, uiData / (MINUTE * IN_MILLISECONDS)); + break; + case TYPE_INFINITE_CORRUPTER: + m_auiEncounter[uiType] = uiData; + switch (uiData) + { + case IN_PROGRESS: + if (!m_auiEncounter[TYPE_INFINITE_CORRUPTER_TIME]) + SetData(TYPE_INFINITE_CORRUPTER_TIME, MINUTE * 25 * IN_MILLISECONDS); + DoUpdateWorldState(WORLD_STATE_TIME, 1);// Show Timer + break; + case DONE: + SetData(TYPE_INFINITE_CORRUPTER_TIME, 0); + break; + case SPECIAL: + DoChromieHurrySpeech(); + break; + case FAIL: + SetData(TYPE_INFINITE_CORRUPTER_TIME, 0); + if (Creature* pCorrupter = GetSingleCreatureFromStorage(NPC_INFINITE_CORRUPTER)) + if (pCorrupter->IsAlive()) + pCorrupter->ForcedDespawn(); + break; + } + break; + } + + if (uiData == DONE || (uiType == TYPE_INFINITE_CORRUPTER && uiData == FAIL)) + { + 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_strInstData = saveStream.str(); + + SaveToDB(); + OUT_SAVE_INST_DATA_COMPLETE; + } +} + +void instance_culling_of_stratholme::Load(const char* chrIn) +{ + if (!chrIn) + { + 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]; + + for (uint8 i = 0; i < MAX_ENCOUNTER; ++i) + { + if (i != TYPE_INFINITE_CORRUPTER_TIME) + { + if (m_auiEncounter[i] == IN_PROGRESS) + m_auiEncounter[i] = NOT_STARTED; + } + } + + // If already started counting down time, the event is "in progress" + if (m_auiEncounter[TYPE_INFINITE_CORRUPTER_TIME]) + m_auiEncounter[TYPE_INFINITE_CORRUPTER] = IN_PROGRESS; + + OUT_LOAD_INST_DATA_COMPLETE; +} + +void instance_culling_of_stratholme::OnPlayerEnter(Player* /*pPlayer*/) +{ + if (instance->GetPlayersCountExceptGMs() == 0) + { + DoSpawnArthasIfNeeded(); + DoSpawnChromieIfNeeded(); + + // Show World States if needed, TODO verify if needed and if this is the right way + if (m_auiEncounter[TYPE_GRAIN_EVENT] == IN_PROGRESS || m_auiEncounter[TYPE_GRAIN_EVENT] == SPECIAL) + DoUpdateWorldState(WORLD_STATE_CRATES, 1); // Show Crates Counter + else + DoUpdateWorldState(WORLD_STATE_CRATES, 0); // Remove Crates Counter + + if (m_auiEncounter[TYPE_MEATHOOK_EVENT] == IN_PROGRESS) + DoUpdateWorldState(WORLD_STATE_WAVE, 1); // Add WaveCounter + else if (m_auiEncounter[TYPE_SALRAMM_EVENT] == IN_PROGRESS) + DoUpdateWorldState(WORLD_STATE_WAVE, 6); // Add WaveCounter + else + DoUpdateWorldState(WORLD_STATE_WAVE, 0); // Remove WaveCounter + + if (m_auiEncounter[TYPE_INFINITE_CORRUPTER_TIME]) + DoUpdateWorldState(WORLD_STATE_TIME, 1); // Show Timer + else + DoUpdateWorldState(WORLD_STATE_TIME, 0); // Remove Timer + } +} + +uint32 instance_culling_of_stratholme::GetData(uint32 uiType) const +{ + if (uiType < MAX_ENCOUNTER) + return m_auiEncounter[uiType]; + + return 0; +} + +uint8 instance_culling_of_stratholme::GetInstancePosition() +{ + if (m_auiEncounter[TYPE_MALGANIS_EVENT] == DONE) + return POS_INSTANCE_FINISHED; + else if (m_auiEncounter[TYPE_ARTHAS_ESCORT_EVENT] == DONE) + return POS_ARTHAS_MALGANIS; + else if (m_auiEncounter[TYPE_EPOCH_EVENT] == DONE) + return POS_ARTHAS_ESCORTING; + else if (m_auiEncounter[TYPE_SALRAMM_EVENT] == DONE) + return POS_ARTHAS_TOWNHALL; + else if (m_auiEncounter[TYPE_MEATHOOK_EVENT] == DONE) + return POS_ARTHAS_WAVES; + else if (m_auiEncounter[TYPE_ARTHAS_INTRO_EVENT] == DONE) + return POS_ARTHAS_WAVES; + else if (m_auiEncounter[TYPE_GRAIN_EVENT] == DONE) + return POS_ARTHAS_INTRO; + else + return 0; +} + +static bool sortFromEastToWest(Creature* pFirst, Creature* pSecond) +{ + return pFirst && pSecond && pFirst->GetPositionY() < pSecond->GetPositionY(); +} + +static bool sortFromSouthToNorth(Creature* pFirst, Creature* pSecond) +{ + return pFirst && pSecond && pFirst->GetPositionX() < pSecond->GetPositionX(); +} + +void instance_culling_of_stratholme::GetCratesBunnyOrderedList(std::list& lList) +{ + std::list lCratesBunnyList; + for (GuidList::const_iterator itr = m_luiCratesBunnyGUIDs.begin(); itr != m_luiCratesBunnyGUIDs.end(); ++itr) + { + if (Creature* pBunny = instance->GetCreature(*itr)) + lCratesBunnyList.push_back(pBunny); + } + if (lCratesBunnyList.empty()) + return; + + lCratesBunnyList.sort(sortFromEastToWest); + lList = lCratesBunnyList; +} + +Creature* instance_culling_of_stratholme::GetStratIntroFootman() +{ + std::list lFootmanList; + for (GuidList::const_iterator itr = m_luiFootmanGUIDs.begin(); itr != m_luiFootmanGUIDs.end(); ++itr) + { + if (Creature* pFootman = instance->GetCreature(*itr)) + lFootmanList.push_back(pFootman); + } + + if (lFootmanList.empty()) + return NULL; + else + { + lFootmanList.sort(sortFromSouthToNorth); + return *lFootmanList.begin(); + } +} + +void instance_culling_of_stratholme::GetResidentOrderedList(std::list& lList) +{ + std::list lResidentList; + for (GuidList::const_iterator itr = m_luiResidentGUIDs.begin(); itr != m_luiResidentGUIDs.end(); ++itr) + { + if (Creature* pResident = instance->GetCreature(*itr)) + lResidentList.push_back(pResident); + } + if (lResidentList.empty()) + return; + + lResidentList.sort(sortFromSouthToNorth); + lList = lResidentList; +} + +void instance_culling_of_stratholme::ArthasJustDied() +{ + m_uiArthasRespawnTimer = 10000; // TODO, could be instant +} + +void instance_culling_of_stratholme::DoSpawnArthasIfNeeded() +{ + Creature* pArthas = GetSingleCreatureFromStorage(NPC_ARTHAS, true); + if (pArthas && pArthas->IsAlive()) + return; + + uint8 uiPosition = GetInstancePosition(); + if (uiPosition && uiPosition <= MAX_ARTHAS_SPAWN_POS) + { + if (Player* pPlayer = GetPlayerInMap()) + pPlayer->SummonCreature(NPC_ARTHAS, m_aArthasSpawnLocs[uiPosition - 1].m_fX, m_aArthasSpawnLocs[uiPosition - 1].m_fY, m_aArthasSpawnLocs[uiPosition - 1].m_fZ, m_aArthasSpawnLocs[uiPosition - 1].m_fO, TEMPSUMMON_CORPSE_TIMED_DESPAWN, 10000); + } +} + +// Atm here only new Chromies are spawned - despawning depends on Mangos featuring such a thing +// The hourglass also is not yet spawned/ relocated. +void instance_culling_of_stratholme::DoSpawnChromieIfNeeded() +{ + Player* pPlayer = GetPlayerInMap(); + if (!pPlayer) + return; + + if (GetInstancePosition() == POS_INSTANCE_FINISHED) + { + Creature* pChromie = GetSingleCreatureFromStorage(NPC_CHROMIE_END, true); + if (!pChromie) + pPlayer->SummonCreature(NPC_CHROMIE_END, m_aChromieSpawnLocs[1].m_fX, m_aChromieSpawnLocs[1].m_fY, m_aChromieSpawnLocs[1].m_fZ, m_aChromieSpawnLocs[1].m_fO, TEMPSUMMON_CORPSE_TIMED_DESPAWN, 10000); + } + else if (GetInstancePosition() >= POS_ARTHAS_INTRO) + { + Creature* pChromie = GetSingleCreatureFromStorage(NPC_CHROMIE_ENTRANCE, true); + if (!pChromie) + pPlayer->SummonCreature(NPC_CHROMIE_ENTRANCE, m_aChromieSpawnLocs[0].m_fX, m_aChromieSpawnLocs[0].m_fY, m_aChromieSpawnLocs[0].m_fZ, m_aChromieSpawnLocs[0].m_fO, TEMPSUMMON_CORPSE_TIMED_DESPAWN, 10000); + } +} + +void instance_culling_of_stratholme::Update(uint32 uiDiff) +{ + // 25min Run - decrease time, update worldstate every ~20s + // as the time is always saved by m_auiEncounter[TYPE_INFINITE_CORRUPTER_TIME], there is no need for an extra timer + if (m_auiEncounter[TYPE_INFINITE_CORRUPTER_TIME]) + { + if (m_auiEncounter[TYPE_INFINITE_CORRUPTER_TIME] <= uiDiff) + SetData(TYPE_INFINITE_CORRUPTER, FAIL); + else + { + m_auiEncounter[TYPE_INFINITE_CORRUPTER_TIME] -= uiDiff; + if (m_auiEncounter[TYPE_INFINITE_CORRUPTER_TIME] / IN_MILLISECONDS % 20 == 0) + SetData(TYPE_INFINITE_CORRUPTER_TIME, m_auiEncounter[TYPE_INFINITE_CORRUPTER_TIME]); + } + + // This part is needed for a small "hurry up guys" note, TODO, verify 20min + if (m_auiEncounter[TYPE_INFINITE_CORRUPTER] == IN_PROGRESS && m_auiEncounter[TYPE_INFINITE_CORRUPTER_TIME] <= 24 * MINUTE * IN_MILLISECONDS) + SetData(TYPE_INFINITE_CORRUPTER, SPECIAL); + } + + // Small Timer, to remove Grain-Crate WorldState and Spawn Second Chromie + if (m_uiRemoveCrateStateTimer) + { + if (m_uiRemoveCrateStateTimer <= uiDiff) + { + DoUpdateWorldState(WORLD_STATE_CRATES, 0); + DoSpawnChromieIfNeeded(); + m_uiRemoveCrateStateTimer = 0; + } + else + m_uiRemoveCrateStateTimer -= uiDiff; + } + + // Respawn Arthas after some time + if (m_uiArthasRespawnTimer) + { + if (m_uiArthasRespawnTimer <= uiDiff) + { + DoSpawnArthasIfNeeded(); + m_uiArthasRespawnTimer = 0; + } + else + m_uiArthasRespawnTimer -= uiDiff; + } +} + +InstanceData* GetInstanceData_instance_culling_of_stratholme(Map* pMap) +{ + return new instance_culling_of_stratholme(pMap); +} + +void AddSC_instance_culling_of_stratholme() +{ + Script* pNewScript; + + pNewScript = new Script; + pNewScript->Name = "instance_culling_of_stratholme"; + pNewScript->GetInstanceData = &GetInstanceData_instance_culling_of_stratholme; + pNewScript->RegisterSelf(); +} diff --git a/src/modules/SD2/scripts/kalimdor/caverns_of_time/dark_portal/boss_aeonus.cpp b/src/modules/SD2/scripts/kalimdor/caverns_of_time/dark_portal/boss_aeonus.cpp new file mode 100644 index 000000000..780deb118 --- /dev/null +++ b/src/modules/SD2/scripts/kalimdor/caverns_of_time/dark_portal/boss_aeonus.cpp @@ -0,0 +1,160 @@ +/* Copyright (C) 2006 - 2013 ScriptDev2 + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with 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: 90 +SDComment: Small adjustments; Timers +SDCategory: Caverns of Time, The Dark Portal +EndScriptData */ + +#include "precompiled.h" +#include "dark_portal.h" + +enum +{ + SAY_AGGRO = -1269013, + SAY_BANISH = -1269014, + SAY_SLAY1 = -1269015, + SAY_SLAY2 = -1269016, + SAY_DEATH = -1269017, + EMOTE_GENERIC_FRENZY = -1000002, + + SPELL_CLEAVE = 40504, + SPELL_TIME_STOP = 31422, + SPELL_ENRAGE = 37605, + SPELL_SAND_BREATH = 31473, + SPELL_SAND_BREATH_H = 39049 +}; + +struct boss_aeonusAI : public ScriptedAI +{ + boss_aeonusAI(Creature* pCreature) : ScriptedAI(pCreature) + { + m_pInstance = (ScriptedInstance*)pCreature->GetInstanceData(); + m_bIsRegularMode = pCreature->GetMap()->IsRegularDifficulty(); + Reset(); + } + + ScriptedInstance* m_pInstance; + bool m_bIsRegularMode; + + uint32 m_uiSandBreathTimer; + uint32 m_uiTimeStopTimer; + uint32 m_uiFrenzyTimer; + uint32 m_uiCleaveTimer; + + void Reset() override + { + m_uiSandBreathTimer = urand(15000, 30000); + m_uiTimeStopTimer = urand(10000, 15000); + m_uiFrenzyTimer = urand(30000, 45000); + m_uiCleaveTimer = urand(5000, 9000); + } + + void Aggro(Unit* /*pWho*/) override + { + DoScriptText(SAY_AGGRO, m_creature); + } + + void MoveInLineOfSight(Unit* pWho) override + { + // Despawn Time Keeper + if (pWho->GetTypeId() == TYPEID_UNIT && pWho->GetEntry() == NPC_TIME_KEEPER) + { + if (m_creature->IsWithinDistInMap(pWho, 20.0f)) + { + if (DoCastSpellIfCan(pWho, SPELL_BANISH_HELPER) == CAST_OK) + DoScriptText(SAY_BANISH, m_creature); + } + } + + ScriptedAI::MoveInLineOfSight(pWho); + } + + void JustDied(Unit* /*pKiller*/) override + { + DoScriptText(SAY_DEATH, m_creature); + } + + void KilledUnit(Unit* /*pVictim*/) override + { + DoScriptText(urand(0, 1) ? SAY_SLAY1 : SAY_SLAY2, m_creature); + } + + void UpdateAI(const uint32 uiDiff) override + { + // Return since we have no target + if (!m_creature->SelectHostileTarget() || !m_creature->getVictim()) + return; + + // Sand Breath + if (m_uiSandBreathTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature, m_bIsRegularMode ? SPELL_SAND_BREATH : SPELL_SAND_BREATH_H) == CAST_OK) + m_uiSandBreathTimer = urand(15000, 25000); + } + else + m_uiSandBreathTimer -= uiDiff; + + // Time Stop + if (m_uiTimeStopTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature, SPELL_TIME_STOP) == CAST_OK) + m_uiTimeStopTimer = urand(20000, 35000); + } + else + m_uiTimeStopTimer -= uiDiff; + + // Cleave + if (m_uiCleaveTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature->getVictim(), SPELL_CLEAVE) == CAST_OK) + m_uiCleaveTimer = urand(7000, 12000); + } + else + m_uiCleaveTimer -= uiDiff; + + // Frenzy + if (m_uiFrenzyTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature, SPELL_ENRAGE) == CAST_OK) + { + DoScriptText(EMOTE_GENERIC_FRENZY, m_creature); + m_uiFrenzyTimer = urand(20000, 35000); + } + } + else + m_uiFrenzyTimer -= uiDiff; + + DoMeleeAttackIfReady(); + } +}; + +CreatureAI* GetAI_boss_aeonus(Creature* pCreature) +{ + return new boss_aeonusAI(pCreature); +} + +void AddSC_boss_aeonus() +{ + Script* pNewScript; + + pNewScript = new Script; + pNewScript->Name = "boss_aeonus"; + pNewScript->GetAI = &GetAI_boss_aeonus; + pNewScript->RegisterSelf(); +} diff --git a/src/modules/SD2/scripts/kalimdor/caverns_of_time/dark_portal/boss_chrono_lord_deja.cpp b/src/modules/SD2/scripts/kalimdor/caverns_of_time/dark_portal/boss_chrono_lord_deja.cpp new file mode 100644 index 000000000..2aee9259e --- /dev/null +++ b/src/modules/SD2/scripts/kalimdor/caverns_of_time/dark_portal/boss_chrono_lord_deja.cpp @@ -0,0 +1,160 @@ +/* Copyright (C) 2006 - 2013 ScriptDev2 + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with 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: 90 +SDComment: Small adjustments; Timers +SDCategory: Caverns of Time, The Dark Portal +EndScriptData */ + +#include "precompiled.h" +#include "dark_portal.h" + +enum +{ + SAY_AGGRO = -1269007, + SAY_BANISH = -1269008, + SAY_SLAY1 = -1269009, + SAY_SLAY2 = -1269010, + SAY_DEATH = -1269011, + + SPELL_ARCANE_BLAST = 31457, + SPELL_ARCANE_BLAST_H = 38538, + SPELL_ARCANE_DISCHARGE = 31472, + SPELL_ARCANE_DISCHARGE_H = 38539, + SPELL_TIME_LAPSE = 31467, + SPELL_ATTRACTION = 38540 +}; + +struct boss_chrono_lord_dejaAI : public ScriptedAI +{ + boss_chrono_lord_dejaAI(Creature* pCreature) : ScriptedAI(pCreature) + { + m_pInstance = (ScriptedInstance*)pCreature->GetInstanceData(); + m_bIsRegularMode = pCreature->GetMap()->IsRegularDifficulty(); + Reset(); + } + + ScriptedInstance* m_pInstance; + bool m_bIsRegularMode; + + uint32 m_uiArcaneBlastTimer; + uint32 m_uiTimeLapseTimer; + uint32 m_uiAttractionTimer; + uint32 m_uiArcaneDischargeTimer; + + void Reset() override + { + m_uiArcaneBlastTimer = urand(18000, 23000); + m_uiTimeLapseTimer = urand(10000, 15000); + m_uiArcaneDischargeTimer = urand(20000, 30000); + m_uiAttractionTimer = urand(25000, 35000); + } + + void Aggro(Unit* /*pWho*/) override + { + DoScriptText(SAY_AGGRO, m_creature); + } + + void MoveInLineOfSight(Unit* pWho) override + { + // Despawn Time Keeper + if (pWho->GetTypeId() == TYPEID_UNIT && pWho->GetEntry() == NPC_TIME_KEEPER) + { + if (m_creature->IsWithinDistInMap(pWho, 20.0f)) + { + if (DoCastSpellIfCan(pWho, SPELL_BANISH_HELPER) == CAST_OK) + DoScriptText(SAY_BANISH, m_creature); + } + } + + ScriptedAI::MoveInLineOfSight(pWho); + } + + void KilledUnit(Unit* /*pVictim*/) override + { + DoScriptText(urand(0, 1) ? SAY_SLAY1 : SAY_SLAY2, m_creature); + } + + void JustDied(Unit* /*pVictim*/) override + { + DoScriptText(SAY_DEATH, m_creature); + } + + void UpdateAI(const uint32 uiDiff) override + { + // Return since we have no target + if (!m_creature->SelectHostileTarget() || !m_creature->getVictim()) + return; + + // Arcane Blast + if (m_uiArcaneBlastTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature->getVictim(), m_bIsRegularMode ? SPELL_ARCANE_BLAST : SPELL_ARCANE_BLAST_H) == CAST_OK) + m_uiArcaneBlastTimer = urand(15000, 25000); + } + else + m_uiArcaneBlastTimer -= uiDiff; + + // Arcane Discharge + if (m_uiArcaneDischargeTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature, m_bIsRegularMode ? SPELL_ARCANE_DISCHARGE : SPELL_ARCANE_DISCHARGE_H) == CAST_OK) + m_uiArcaneDischargeTimer = urand(20000, 30000); + } + else + m_uiArcaneDischargeTimer -= uiDiff; + + // Time Lapse + if (m_uiTimeLapseTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature, SPELL_TIME_LAPSE) == CAST_OK) + m_uiTimeLapseTimer = urand(15000, 25000); + } + else + m_uiTimeLapseTimer -= uiDiff; + + // Attraction + if (!m_bIsRegularMode) + { + if (m_uiAttractionTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature, SPELL_ATTRACTION) == CAST_OK) + m_uiAttractionTimer = urand(25000, 35000); + } + else + m_uiAttractionTimer -= uiDiff; + } + + DoMeleeAttackIfReady(); + } +}; + +CreatureAI* GetAI_boss_chrono_lord_deja(Creature* pCreature) +{ + return new boss_chrono_lord_dejaAI(pCreature); +} + +void AddSC_boss_chrono_lord_deja() +{ + Script* pNewScript; + + pNewScript = new Script; + pNewScript->Name = "boss_chrono_lord_deja"; + pNewScript->GetAI = &GetAI_boss_chrono_lord_deja; + pNewScript->RegisterSelf(); +} diff --git a/src/modules/SD2/scripts/kalimdor/caverns_of_time/dark_portal/boss_temporus.cpp b/src/modules/SD2/scripts/kalimdor/caverns_of_time/dark_portal/boss_temporus.cpp new file mode 100644 index 000000000..67fa4f750 --- /dev/null +++ b/src/modules/SD2/scripts/kalimdor/caverns_of_time/dark_portal/boss_temporus.cpp @@ -0,0 +1,159 @@ +/* Copyright (C) 2006 - 2013 ScriptDev2 + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with 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: 90 +SDComment: Small adjustments; Timers +SDCategory: Caverns of Time, The Dark Portal +EndScriptData */ + +#include "precompiled.h" +#include "dark_portal.h" + +enum +{ + SAY_AGGRO = -1269001, + SAY_BANISH = -1269002, + SAY_SLAY1 = -1269003, + SAY_SLAY2 = -1269004, + SAY_DEATH = -1269005, + + SPELL_HASTE = 31458, + SPELL_MORTAL_WOUND = 31464, + SPELL_WING_BUFFET = 31475, + SPELL_WING_BUFFET_H = 38593, + SPELL_REFLECT = 38592 +}; + +struct boss_temporusAI : public ScriptedAI +{ + boss_temporusAI(Creature* pCreature) : ScriptedAI(pCreature) + { + m_pInstance = (ScriptedInstance*)pCreature->GetInstanceData(); + m_bIsRegularMode = pCreature->GetMap()->IsRegularDifficulty(); + Reset(); + } + + ScriptedInstance* m_pInstance; + bool m_bIsRegularMode; + + uint32 m_uiHasteTimer; + uint32 m_uiSpellReflectionTimer; + uint32 m_uiMortalWoundTimer; + uint32 m_uiWingBuffetTimer; + + void Reset() override + { + m_uiHasteTimer = urand(15000, 23000); + m_uiSpellReflectionTimer = 30000; + m_uiMortalWoundTimer = 8000; + m_uiWingBuffetTimer = urand(25000, 35000); + } + + void Aggro(Unit* /*pWho*/) override + { + DoScriptText(SAY_AGGRO, m_creature); + } + + void KilledUnit(Unit* /*pVictim*/) override + { + DoScriptText(urand(0, 1) ? SAY_SLAY1 : SAY_SLAY2, m_creature); + } + + void JustDied(Unit* /*pKiller*/) override + { + DoScriptText(SAY_DEATH, m_creature); + } + + void MoveInLineOfSight(Unit* pWho) override + { + // Despawn Time Keeper + if (pWho->GetTypeId() == TYPEID_UNIT && pWho->GetEntry() == NPC_TIME_KEEPER) + { + if (m_creature->IsWithinDistInMap(pWho, 20.0f)) + { + if (DoCastSpellIfCan(pWho, SPELL_BANISH_HELPER) == CAST_OK) + DoScriptText(SAY_BANISH, m_creature); + } + } + + ScriptedAI::MoveInLineOfSight(pWho); + } + + void UpdateAI(const uint32 uiDiff) override + { + // Return since we have no target + if (!m_creature->SelectHostileTarget() || !m_creature->getVictim()) + return; + + // Attack Haste + if (m_uiHasteTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature, SPELL_HASTE) == CAST_OK) + m_uiHasteTimer = urand(20000, 25000); + } + else + m_uiHasteTimer -= uiDiff; + + // MortalWound_Timer + if (m_uiMortalWoundTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature->getVictim(), SPELL_MORTAL_WOUND) == CAST_OK) + m_uiMortalWoundTimer = urand(10000, 20000); + } + else + m_uiMortalWoundTimer -= uiDiff; + + // Wing ruffet + if (m_uiWingBuffetTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature, m_bIsRegularMode ? SPELL_WING_BUFFET : SPELL_WING_BUFFET_H) == CAST_OK) + m_uiWingBuffetTimer = urand(20000, 30000); + } + else + m_uiWingBuffetTimer -= uiDiff; + + // Spell reflection + if (!m_bIsRegularMode) + { + if (m_uiSpellReflectionTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature, SPELL_REFLECT) == CAST_OK) + m_uiSpellReflectionTimer = urand(25000, 35000); + } + else + m_uiSpellReflectionTimer -= uiDiff; + } + + DoMeleeAttackIfReady(); + } +}; + +CreatureAI* GetAI_boss_temporus(Creature* pCreature) +{ + return new boss_temporusAI(pCreature); +} + +void AddSC_boss_temporus() +{ + Script* pNewScript; + + pNewScript = new Script; + pNewScript->Name = "boss_temporus"; + pNewScript->GetAI = &GetAI_boss_temporus; + pNewScript->RegisterSelf(); +} diff --git a/src/modules/SD2/scripts/kalimdor/caverns_of_time/dark_portal/dark_portal.cpp b/src/modules/SD2/scripts/kalimdor/caverns_of_time/dark_portal/dark_portal.cpp new file mode 100644 index 000000000..c4334f196 --- /dev/null +++ b/src/modules/SD2/scripts/kalimdor/caverns_of_time/dark_portal/dark_portal.cpp @@ -0,0 +1,366 @@ +/* Copyright (C) 2006 - 2013 ScriptDev2 + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along 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: 80 +SDComment: Some things may be still missing from here +SDCategory: Caverns of Time, The Dark Portal +EndScriptData */ + +/* ContentData +npc_medivh_bm +npc_time_rift +EndContentData */ + +#include "precompiled.h" +#include "dark_portal.h" + +/*###### +## npc_medivh_black_morass +######*/ + +enum +{ + SAY_DEATH = -1269025, + + SPELL_CORRUPT = 31326, +}; + +struct npc_medivh_black_morassAI : public ScriptedAI +{ + npc_medivh_black_morassAI(Creature* pCreature) : ScriptedAI(pCreature) + { + m_pInstance = (ScriptedInstance*)pCreature->GetInstanceData(); + Reset(); + } + + ScriptedInstance* m_pInstance; + + void Reset() override { } + + void AttackStart(Unit* /*pWho*/) override { } + + void JustSummoned(Creature* pSummoned) override + { + // The rift trash mobs are summoned by Medivh, so we can control the movement + if (pSummoned->GetEntry() != NPC_TIME_RIFT && pSummoned->GetEntry() != NPC_COUNCIL_ENFORCER) + { + float fX, fY, fZ; + m_creature->GetNearPoint(m_creature, fX, fY, fZ, 0, 20.0f, m_creature->GetAngle(pSummoned)); + pSummoned->SetWalk(false); + pSummoned->GetMotionMaster()->MovePoint(1, fX, fY, fZ); + } + } + + void SummonedMovementInform(Creature* pSummoned, uint32 uiMotionType, uint32 uiPointId) override + { + if (uiMotionType != POINT_MOTION_TYPE || !uiPointId) + return; + + pSummoned->CastSpell(m_creature, SPELL_CORRUPT, false); + } + + void JustDied(Unit* /*pKiller*/) override + { + if (m_pInstance) + m_pInstance->SetData(TYPE_MEDIVH, FAIL); + + DoScriptText(SAY_DEATH, m_creature); + } + + void UpdateAI(const uint32 /*uiDiff*/) override { } +}; + +CreatureAI* GetAI_npc_medivh_black_morass(Creature* pCreature) +{ + return new npc_medivh_black_morassAI(pCreature); +} + +bool EffectDummyCreature_npc_medivh_black_morass(Unit* /*pCaster*/, uint32 uiSpellId, SpellEffectIndex uiEffIndex, Creature* pCreatureTarget, ObjectGuid /*originalCasterGuid*/) +{ + // always check spellid and effectindex + if ((uiSpellId == SPELL_CORRUPT && uiEffIndex == EFFECT_INDEX_0) || (uiSpellId == SPELL_CORRUPT_AEONUS && uiEffIndex == EFFECT_INDEX_0)) + { + if (instance_dark_portal* pInstance = (instance_dark_portal*)pCreatureTarget->GetInstanceData()) + pInstance->SetData(TYPE_SHIELD, SPECIAL); + + // always return true when we are handling this spell and effect + return true; + } + + return false; +} + +/*###### +## npc_time_rift +######*/ + +enum +{ + SPELL_RIFT_PERIODIC = 31320, // should trigger 31388 + + // Boss spawn yells + SAY_CHRONO_LORD_ENTER = -1269006, + SAY_TEMPORUS_ENTER = -1269000, + SAY_AEONUS_ENTER = -1269012, +}; + +struct RiftWaveData +{ + uint32 uiPortalMob[4]; // spawns for portal waves (in order) +}; + +static const RiftWaveData aPortalWaves[] = +{ + {{NPC_ASSASSIN, NPC_WHELP, NPC_CHRONOMANCER, 0}}, + {{NPC_EXECUTIONER, NPC_CHRONOMANCER, NPC_WHELP, NPC_ASSASSIN}}, + {{NPC_EXECUTIONER, NPC_VANQUISHER, NPC_CHRONOMANCER, NPC_ASSASSIN}} +}; + +struct npc_time_riftAI : public ScriptedAI +{ + npc_time_riftAI(Creature* pCreature) : ScriptedAI(pCreature) + { + m_pInstance = (instance_dark_portal*)pCreature->GetInstanceData(); + m_bIsRegularMode = pCreature->GetMap()->IsRegularDifficulty(); + m_bIsFirstSummon = true; + m_uiRiftNumber = 0; + m_uiRiftWaveId = 0; + Reset(); + } + + instance_dark_portal* m_pInstance; + + bool m_bIsRegularMode; + bool m_bIsFirstSummon; + + uint8 m_uiRiftWaveCount; + uint8 m_uiRiftNumber; + uint8 m_uiRiftWaveId; + + void Reset() override + { + DoCastSpellIfCan(m_creature, SPELL_RIFT_PERIODIC); + + m_uiRiftWaveCount = 0; + m_uiRiftNumber = 0; + + if (m_pInstance) + { + m_uiRiftNumber = m_pInstance->GetCurrentRiftId(); + + if (m_uiRiftNumber < 6) + m_uiRiftWaveId = 0; + else if (m_uiRiftNumber > 12) + m_uiRiftWaveId = 2; + else + m_uiRiftWaveId = 1; + } + } + + void DoSummonCreatureAtRift(uint32 uiCreatureEntry, Creature* pSummoner) + { + if (!uiCreatureEntry) + return; + + float fX, fY, fZ; + m_creature->GetRandomPoint(m_creature->GetPositionX(), m_creature->GetPositionY(), m_creature->GetPositionZ(), 10.0f, fX, fY, fZ); + pSummoner->SummonCreature(uiCreatureEntry, fX, fY, fZ, m_creature->GetOrientation(), TEMPSUMMON_DEAD_DESPAWN, 0); + } + + void DoSummon() + { + if (!m_pInstance) + return; + + uint32 uiSummonEntry = 0; + + if (m_bIsFirstSummon) + { + // Select portal keeper / boss to summon + // On Heroic Mode if Chrono Lord and Temporus are already killed, we need to summon the replacement + switch (m_uiRiftNumber) + { + case 6: + uiSummonEntry = (m_pInstance->GetData(TYPE_CHRONO_LORD) == DONE && !m_bIsRegularMode) ? NPC_CHRONO_LORD : NPC_CHRONO_LORD_DEJA; + break; + case 12: + uiSummonEntry = (m_pInstance->GetData(TYPE_TEMPORUS) == DONE && !m_bIsRegularMode) ? NPC_TIMEREAVER : NPC_TEMPORUS; + break; + case 18: + uiSummonEntry = NPC_AEONUS; + break; + default: + uiSummonEntry = urand(0, 1) ? NPC_RIFT_KEEPER : NPC_RIFT_LORD; + break; + } + + // Set the next rift delay + if (uiSummonEntry != NPC_AEONUS) + m_pInstance->SetData(TYPE_TIME_RIFT, SPECIAL); + + DoSummonCreatureAtRift(uiSummonEntry, m_creature); + m_bIsFirstSummon = false; + } + else + { + // Some creatures are summoned by Medivh, because we can better handle the movement this way + Creature* pMedivh = m_pInstance->GetSingleCreatureFromStorage(NPC_MEDIVH); + if (!pMedivh) + return; + + // Reset the RiftWaveCount if we reached the maximum number of the currentRiftWave is 0 + if ((m_uiRiftWaveCount > 2 && !m_uiRiftWaveId) || m_uiRiftWaveCount > 3) + m_uiRiftWaveCount = 0; + + uiSummonEntry = aPortalWaves[m_uiRiftWaveId].uiPortalMob[m_uiRiftWaveCount]; + ++m_uiRiftWaveCount; + + // Summon the trash waves by Medivh, so we can better handle the movement + // For Whelps we need to summon them in packs of 3 + if (uiSummonEntry == NPC_WHELP) + { + for (uint8 i = 0; i < 3; ++i) + DoSummonCreatureAtRift(uiSummonEntry, pMedivh); + } + else + DoSummonCreatureAtRift(uiSummonEntry, pMedivh); + } + } + + void JustSummoned(Creature* pSummoned) override + { + switch (pSummoned->GetEntry()) + { + case NPC_CHRONO_LORD_DEJA: + DoCastSpellIfCan(pSummoned, SPELL_RIFT_CHANNEL); + DoScriptText(SAY_CHRONO_LORD_ENTER, pSummoned); + break; + case NPC_TEMPORUS: + DoCastSpellIfCan(pSummoned, SPELL_RIFT_CHANNEL); + DoScriptText(SAY_TEMPORUS_ENTER, pSummoned); + break; + case NPC_CHRONO_LORD: + case NPC_TIMEREAVER: + case NPC_RIFT_KEEPER: + case NPC_RIFT_LORD: + DoCastSpellIfCan(pSummoned, SPELL_RIFT_CHANNEL); + break; + case NPC_AEONUS: + DoScriptText(SAY_AEONUS_ENTER, pSummoned); + // Remove Time Rift aura so it won't spawn other mobs + m_creature->RemoveAurasDueToSpell(SPELL_RIFT_PERIODIC); + // Move to Medivh and cast Corrupt on him + pSummoned->SetWalk(false); + if (m_pInstance) + { + if (Creature* pMedivh = m_pInstance->GetSingleCreatureFromStorage(NPC_MEDIVH)) + { + float fX, fY, fZ; + pMedivh->GetNearPoint(pMedivh, fX, fY, fZ, 0, 20.0f, pMedivh->GetAngle(pSummoned)); + pSummoned->GetMotionMaster()->MovePoint(1, fX, fY, fZ); + } + } + break; + } + } + + void SummonedCreatureJustDied(Creature* pSummoned) override + { + switch (pSummoned->GetEntry()) + { + case NPC_AEONUS: + m_creature->ForcedDespawn(); + break; + case NPC_CHRONO_LORD_DEJA: + case NPC_TEMPORUS: + case NPC_CHRONO_LORD: + case NPC_TIMEREAVER: + case NPC_RIFT_KEEPER: + case NPC_RIFT_LORD: + m_creature->ForcedDespawn(3000); + // No need to set the data to DONE if there is a new portal spawned already + if (m_pInstance && m_uiRiftNumber == m_pInstance->GetCurrentRiftId()) + m_pInstance->SetData(TYPE_TIME_RIFT, DONE); + break; + } + } + + void SummonedCreatureDespawn(Creature* pSummoned) override + { + switch (pSummoned->GetEntry()) + { + case NPC_AEONUS: + case NPC_CHRONO_LORD_DEJA: + case NPC_TEMPORUS: + case NPC_CHRONO_LORD: + case NPC_TIMEREAVER: + case NPC_RIFT_KEEPER: + case NPC_RIFT_LORD: + // Despawn in case of event reset + m_creature->ForcedDespawn(); + break; + } + } + + void SummonedMovementInform(Creature* pSummoned, uint32 uiMotionType, uint32 uiPointId) override + { + if (uiMotionType != POINT_MOTION_TYPE || !uiPointId || pSummoned->GetEntry() != NPC_AEONUS) + return; + + pSummoned->CastSpell(pSummoned, SPELL_CORRUPT_AEONUS, false); + } + + void UpdateAI(const uint32 /*uiDiff*/) override { } +}; + +CreatureAI* GetAI_npc_time_rift(Creature* pCreature) +{ + return new npc_time_riftAI(pCreature); +} + +bool EffectDummyCreature_npc_time_rift_channel(Unit* /*pCaster*/, uint32 uiSpellId, SpellEffectIndex uiEffIndex, Creature* pCreatureTarget, ObjectGuid /*originalCasterGuid*/) +{ + // always check spellid and effectindex + if (uiSpellId == SPELL_RIFT_PERIODIC && uiEffIndex == EFFECT_INDEX_0) + { + if (npc_time_riftAI* pTimeRiftAI = dynamic_cast(pCreatureTarget->AI())) + pTimeRiftAI->DoSummon(); + + // always return true when we are handling this spell and effect + return true; + } + + return false; +} + +void AddSC_dark_portal() +{ + Script* pNewScript; + + pNewScript = new Script; + pNewScript->Name = "npc_medivh_black_morass"; + pNewScript->GetAI = &GetAI_npc_medivh_black_morass; + pNewScript->pEffectDummyNPC = &EffectDummyCreature_npc_medivh_black_morass; + pNewScript->RegisterSelf(); + + pNewScript = new Script; + pNewScript->Name = "npc_time_rift"; + pNewScript->GetAI = &GetAI_npc_time_rift; + pNewScript->pEffectDummyNPC = &EffectDummyCreature_npc_time_rift_channel; + pNewScript->RegisterSelf(); +} diff --git a/src/modules/SD2/scripts/kalimdor/caverns_of_time/dark_portal/dark_portal.h b/src/modules/SD2/scripts/kalimdor/caverns_of_time/dark_portal/dark_portal.h new file mode 100644 index 000000000..735839c26 --- /dev/null +++ b/src/modules/SD2/scripts/kalimdor/caverns_of_time/dark_portal/dark_portal.h @@ -0,0 +1,147 @@ +/* Copyright (C) 2006 - 2013 ScriptDev2 + * 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 + +enum +{ + MAX_ENCOUNTER = 6, + + TYPE_MEDIVH = 0, + TYPE_SHIELD = 1, + TYPE_TIME_RIFT = 2, + TYPE_CHRONO_LORD = 3, + TYPE_TEMPORUS = 4, + TYPE_AEONUS = 5, + + WORLD_STATE_ID = 2541, + WORLD_STATE_SHIELD = 2540, + WORLD_STATE_RIFT = 2784, + + QUEST_OPENING_PORTAL = 10297, + QUEST_MASTER_TOUCH = 9836, + + // event controlers + NPC_TIME_RIFT = 17838, + NPC_MEDIVH = 15608, + + // main bosses + NPC_CHRONO_LORD_DEJA = 17879, + NPC_TEMPORUS = 17880, + NPC_AEONUS = 17881, + + // boss replacements for heroic + NPC_CHRONO_LORD = 21697, + NPC_TIMEREAVER = 21698, + + // portal guardians + NPC_RIFT_KEEPER = 21104, + NPC_RIFT_LORD = 17839, + + // portal summons + NPC_ASSASSIN = 17835, + NPC_WHELP = 21818, + NPC_CHRONOMANCER = 17892, + NPC_EXECUTIONER = 18994, + NPC_VANQUISHER = 18995, + + // additional npcs + NPC_COUNCIL_ENFORCER = 17023, + NPC_TIME_KEEPER = 17918, + NPC_SAAT = 20201, + NPC_DARK_PORTAL_DUMMY = 18625, // cast spell 32564 on coordinates + NPC_DARK_PORTAL_BEAM = 18555, // purple beams which travel from Medivh to the Dark Portal + + // event spells + SPELL_RIFT_CHANNEL = 31387, // Aura channeled by the Time Rifts on the Rift Keepers + + SPELL_BANISH_HELPER = 31550, // used by the main bosses to banish the time keeprs + SPELL_CORRUPT_AEONUS = 37853, // used by Aeonus to corrupt Medivh + + // cosmetic spells + SPELL_PORTAL_CRYSTAL = 32564, // summons 18553 - Dark Portal Crystal stalker + SPELL_BANISH_GREEN = 32567, + + // yells during the event + SAY_SAAT_WELCOME = -1269019, + + SAY_MEDIVH_INTRO = -1269021, + SAY_MEDIVH_ENTER = -1269020, + SAY_MEDIVH_WIN = -1269026, + SAY_MEDIVH_WEAK75 = -1269022, + SAY_MEDIVH_WEAK50 = -1269023, + SAY_MEDIVH_WEAK25 = -1269024, + SAY_ORCS_ENTER = -1269027, + SAY_ORCS_ANSWER = -1269028, + + AREATRIGGER_MEDIVH = 4288, + AREATRIGGER_ENTER = 4485, +}; + +struct PortalData +{ + float fX, fY, fZ, fOrient; +}; + +static const PortalData afPortalLocation[] = +{ + { -2030.832f, 7024.944f, 23.07182f, 3.141593f}, + { -1961.734f, 7029.528f, 21.8114f, 2.129302f}, + { -1887.695f, 7106.557f, 22.0495f, 4.956735f}, + { -1930.911f, 7183.597f, 23.00764f, 3.595378f} +}; + +// Dark Crystal summon location +static const float fDarkPortalCrystalLoc[3] = { -2024.31f, 7127.75f, 22.65419f}; + +static const int32 uiMedivhWeakYell[3] = {SAY_MEDIVH_WEAK75, SAY_MEDIVH_WEAK50, SAY_MEDIVH_WEAK25}; + +class instance_dark_portal : public ScriptedInstance +{ + public: + instance_dark_portal(Map* pMap); + + void Initialize() override; + + void OnPlayerEnter(Player* pPlayer) override; + void OnCreatureCreate(Creature* pCreature) override; + + void OnCreatureEnterCombat(Creature* pCreature) override; + void OnCreatureEvade(Creature* pCreature); + void OnCreatureDeath(Creature* pCreature) override; + + void SetData(uint32 uiType, uint32 uiData) override; + uint32 GetData(uint32 uiType) const override; + + const char* Save() const override { return m_strInstData.c_str(); } + void Load(const char* chrIn) override; + + void DoHandleAreaTrigger(uint32 uiTriggerId); + + uint32 GetCurrentRiftId() { return m_uiWorldStateRiftCount; } + + void Update(uint32 uiDiff) override; + + private: + bool IsBossTimeRift() { return m_uiWorldStateRiftCount == 6 || m_uiWorldStateRiftCount == 12; } + void UpdateWorldState(bool bEnable = true); + void DoSpawnNextPortal(); + void DoResetEvent(); + + uint32 m_auiEncounter[MAX_ENCOUNTER]; + std::string m_strInstData; + + uint32 m_uiWorldState; + uint32 m_uiWorldStateRiftCount; + uint32 m_uiWorldStateShieldCount; + + bool m_bHasIntroYelled; + uint32 m_uiMedivhYellCount; + + uint32 m_uiNextPortalTimer; + uint8 m_uiCurrentRiftId; +}; + +#endif diff --git a/src/modules/SD2/scripts/kalimdor/caverns_of_time/dark_portal/instance_dark_portal.cpp b/src/modules/SD2/scripts/kalimdor/caverns_of_time/dark_portal/instance_dark_portal.cpp new file mode 100644 index 000000000..d35de7b6f --- /dev/null +++ b/src/modules/SD2/scripts/kalimdor/caverns_of_time/dark_portal/instance_dark_portal.cpp @@ -0,0 +1,410 @@ +/* Copyright (C) 2006 - 2013 ScriptDev2 + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along 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: 75 +SDComment: Quest support: 9836, 10297. Some visuals for the event are missing; Event epilogue NYI. +SDCategory: Caverns of Time, The Dark Portal +EndScriptData */ + +#include "precompiled.h" +#include "dark_portal.h" + +instance_dark_portal::instance_dark_portal(Map* pMap) : ScriptedInstance(pMap), + m_uiWorldState(0), + m_uiWorldStateRiftCount(0), + m_uiWorldStateShieldCount(100), + + m_bHasIntroYelled(false), + m_uiMedivhYellCount(1), + + m_uiNextPortalTimer(0), + m_uiCurrentRiftId(0) +{ + Initialize(); +} + +void instance_dark_portal::Initialize() +{ + memset(&m_auiEncounter, 0, sizeof(m_auiEncounter)); +} + +void instance_dark_portal::DoResetEvent() +{ + UpdateWorldState(false); + + m_uiWorldStateShieldCount = 100; + m_uiWorldStateRiftCount = 0; + + m_uiCurrentRiftId = 0; + m_uiNextPortalTimer = 0; + m_uiMedivhYellCount = 1; +} + +void instance_dark_portal::UpdateWorldState(bool bEnable) +{ + m_uiWorldState = bEnable ? 1 : 0; + + DoUpdateWorldState(WORLD_STATE_ID, m_uiWorldState); + DoUpdateWorldState(WORLD_STATE_SHIELD, m_uiWorldStateShieldCount); + DoUpdateWorldState(WORLD_STATE_RIFT, m_uiWorldStateRiftCount); +} + +void instance_dark_portal::OnPlayerEnter(Player* /*pPlayer*/) +{ + UpdateWorldState(m_auiEncounter[TYPE_MEDIVH] == IN_PROGRESS ? true : false); +} + +void instance_dark_portal::DoHandleAreaTrigger(uint32 uiTriggerId) +{ + if (uiTriggerId == AREATRIGGER_ENTER) + { + // Yell at instance entrance + if (!m_bHasIntroYelled) + { + if (Creature* pSaat = GetSingleCreatureFromStorage(NPC_SAAT)) + DoScriptText(SAY_SAAT_WELCOME, pSaat); + m_bHasIntroYelled = true; + } + } + else if (uiTriggerId == AREATRIGGER_MEDIVH) + { + // Start Dark Portal event + if (GetData(TYPE_MEDIVH) == NOT_STARTED || GetData(TYPE_MEDIVH) == FAIL) + SetData(TYPE_MEDIVH, IN_PROGRESS); + // Start Epilogue + else if (GetData(TYPE_AEONUS) == DONE && GetData(TYPE_MEDIVH) != DONE) + SetData(TYPE_MEDIVH, DONE); + } +} + +void instance_dark_portal::OnCreatureCreate(Creature* pCreature) +{ + switch (pCreature->GetEntry()) + { + case NPC_MEDIVH: + case NPC_SAAT: + case NPC_DARK_PORTAL_DUMMY: + m_mNpcEntryGuidStore[pCreature->GetEntry()] = pCreature->GetObjectGuid(); + break; + } +} + +void instance_dark_portal::SetData(uint32 uiType, uint32 uiData) +{ + switch (uiType) + { + case TYPE_MEDIVH: + { + if (uiData == IN_PROGRESS) + { + if (Creature* pMedivh = GetSingleCreatureFromStorage(NPC_MEDIVH)) + { + if (pMedivh->IsAlive()) + DoScriptText(SAY_MEDIVH_ENTER, pMedivh); + // If Medivh is not available the do not store the uiData; + else + return; + } + + // ToDo: + // Start the Portal Crystal casting - by the Dark Portal Dumm Npc + // Also Start summoning the Dark Portal Beams + + UpdateWorldState(); + m_uiNextPortalTimer = 3000; + } + if (uiData == DONE) + { + // Yell for event finished + if (Creature* pMedivh = GetSingleCreatureFromStorage(NPC_MEDIVH)) + { + DoScriptText(SAY_MEDIVH_WIN, pMedivh); + pMedivh->SetFacingTo(6.15f); + pMedivh->InterruptNonMeleeSpells(false); + pMedivh->SetFlag(UNIT_NPC_FLAGS, UNIT_NPC_FLAG_QUESTGIVER); + } + + // this may be completed further out in the post-event + Map::PlayerList const& players = instance->GetPlayers(); + + if (!players.isEmpty()) + { + 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); + } + } + } + } + if (uiData == FAIL) + DoResetEvent(); + m_auiEncounter[uiType] = uiData; + break; + } + case TYPE_SHIELD: + if (uiData == SPECIAL) + { + --m_uiWorldStateShieldCount; + DoUpdateWorldState(WORLD_STATE_SHIELD, m_uiWorldStateShieldCount); + + // Yell at 75%, 50% and 25% shield + if (m_uiWorldStateShieldCount < 100 - 25 * m_uiMedivhYellCount) + { + if (Creature* pMedivh = GetSingleCreatureFromStorage(NPC_MEDIVH)) + { + DoScriptText(uiMedivhWeakYell[m_uiMedivhYellCount - 1], pMedivh); + ++m_uiMedivhYellCount; + } + } + + // Kill the npc when the shield is broken + if (!m_uiWorldStateShieldCount) + { + if (Creature* pMedivh = GetSingleCreatureFromStorage(NPC_MEDIVH)) + { + if (pMedivh->IsAlive()) + pMedivh->DealDamage(pMedivh, pMedivh->GetHealth(), NULL, DIRECT_DAMAGE, SPELL_SCHOOL_MASK_NORMAL, NULL, false); + } + } + } + m_auiEncounter[uiType] = uiData; + return; + case TYPE_TIME_RIFT: + { + // Set the delay to the next time rift from the point the rift despawns + if (uiData == DONE) + m_uiNextPortalTimer = IsBossTimeRift() ? 125000 : 3000; + // Set the delay to the next time rift from the point the rift summons it's guardian + // ToDo: research if these timers are correct + else if (uiData == SPECIAL) + m_uiNextPortalTimer = IsBossTimeRift() ? 0 : m_uiWorldStateRiftCount > 12 ? 90000 : 2 * MINUTE * IN_MILLISECONDS; + + m_auiEncounter[uiType] = uiData; + return; + } + case TYPE_CHRONO_LORD: + case TYPE_TEMPORUS: + if (m_auiEncounter[uiType] != DONE) // Keep the DONE-information stored + m_auiEncounter[uiType] = uiData; + break; + case TYPE_AEONUS: + if (uiData == DONE) + UpdateWorldState(false); + m_auiEncounter[uiType] = uiData; + break; + default: + return; + } + + 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_strInstData = saveStream.str(); + + SaveToDB(); + OUT_SAVE_INST_DATA_COMPLETE; + } +} + +uint32 instance_dark_portal::GetData(uint32 uiType) const +{ + if (uiType < MAX_ENCOUNTER) + return m_auiEncounter[uiType]; + + return 0; +} + +void instance_dark_portal::Load(const char* chrIn) +{ + if (!chrIn) + { + 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]; + + for (uint8 i = 0; i < MAX_ENCOUNTER; ++i) + { + if (m_auiEncounter[i] == IN_PROGRESS) + m_auiEncounter[i] = NOT_STARTED; + } + + OUT_LOAD_INST_DATA_COMPLETE; +} + +void instance_dark_portal::OnCreatureEnterCombat(Creature* pCreature) +{ + switch (pCreature->GetEntry()) + { + case NPC_CHRONO_LORD_DEJA: + case NPC_CHRONO_LORD: + SetData(TYPE_CHRONO_LORD, IN_PROGRESS); + break; + case NPC_TEMPORUS: + case NPC_TIMEREAVER: + SetData(TYPE_TEMPORUS, IN_PROGRESS); + break; + case NPC_AEONUS: + SetData(TYPE_AEONUS, IN_PROGRESS); + // no break + case NPC_ASSASSIN: + case NPC_WHELP: + case NPC_CHRONOMANCER: + case NPC_EXECUTIONER: + case NPC_VANQUISHER: + pCreature->InterruptNonMeleeSpells(false); + break; + } +} + +void instance_dark_portal::OnCreatureEvade(Creature* pCreature) +{ + switch (pCreature->GetEntry()) + { + case NPC_CHRONO_LORD_DEJA: + case NPC_CHRONO_LORD: + SetData(TYPE_CHRONO_LORD, FAIL); + break; + case NPC_TEMPORUS: + case NPC_TIMEREAVER: + SetData(TYPE_TEMPORUS, FAIL); + break; + case NPC_AEONUS: + SetData(TYPE_AEONUS, FAIL); + // no break; + // Allow these guys to go and finish off Medivh + case NPC_ASSASSIN: + case NPC_WHELP: + case NPC_CHRONOMANCER: + case NPC_EXECUTIONER: + case NPC_VANQUISHER: + if (Creature* pMedivh = GetSingleCreatureFromStorage(NPC_MEDIVH)) + { + float fX, fY, fZ; + pMedivh->GetNearPoint(pMedivh, fX, fY, fZ, 0, 20.0f, pMedivh->GetAngle(pCreature)); + pCreature->GetMotionMaster()->MovePoint(1, fX, fY, fZ); + } + break; + } +} + +void instance_dark_portal::OnCreatureDeath(Creature* pCreature) +{ + switch (pCreature->GetEntry()) + { + case NPC_CHRONO_LORD_DEJA: + case NPC_CHRONO_LORD: + SetData(TYPE_CHRONO_LORD, DONE); + break; + case NPC_TEMPORUS: + case NPC_TIMEREAVER: + SetData(TYPE_TEMPORUS, DONE); + break; + case NPC_AEONUS: + SetData(TYPE_AEONUS, DONE); + break; + } +} + +void instance_dark_portal::DoSpawnNextPortal() +{ + if (Creature* pMedivh = GetSingleCreatureFromStorage(NPC_MEDIVH)) + { + // Randomize portal locations + uint8 uiTmp = urand(0, 2); + + if (uiTmp >= m_uiCurrentRiftId) + ++uiTmp; + + debug_log("SD2: instance_dark_portal: SetRiftId %u, old was id %u.", uiTmp, m_uiCurrentRiftId); + + m_uiCurrentRiftId = uiTmp; + + // Summon next portal + pMedivh->SummonCreature(NPC_TIME_RIFT, afPortalLocation[uiTmp].fX, afPortalLocation[uiTmp].fY, afPortalLocation[uiTmp].fZ, afPortalLocation[uiTmp].fOrient, TEMPSUMMON_CORPSE_DESPAWN, 0); + } +} + +void instance_dark_portal::Update(uint32 uiDiff) +{ + if (GetData(TYPE_MEDIVH) != IN_PROGRESS) + return; + + if (m_uiNextPortalTimer) + { + if (m_uiNextPortalTimer <= uiDiff) + { + DoUpdateWorldState(WORLD_STATE_RIFT, ++m_uiWorldStateRiftCount); + + DoSpawnNextPortal(); + m_uiNextPortalTimer = 0; + } + else + m_uiNextPortalTimer -= uiDiff; + } +} + +InstanceData* GetInstanceData_instance_dark_portal(Map* pMap) +{ + return new instance_dark_portal(pMap); +} + +bool AreaTrigger_at_dark_portal(Player* pPlayer, AreaTriggerEntry const* pAt) +{ + if (pAt->id == AREATRIGGER_MEDIVH || pAt->id == AREATRIGGER_ENTER) + { + if (pPlayer->isGameMaster() || pPlayer->IsDead()) + return false; + + if (instance_dark_portal* pInstance = (instance_dark_portal*)pPlayer->GetInstanceData()) + pInstance->DoHandleAreaTrigger(pAt->id); + } + + return false; +} + +void AddSC_instance_dark_portal() +{ + Script* pNewScript; + + pNewScript = new Script; + pNewScript->Name = "instance_dark_portal"; + pNewScript->GetInstanceData = &GetInstanceData_instance_dark_portal; + pNewScript->RegisterSelf(); + + pNewScript = new Script; + pNewScript->Name = "at_dark_portal"; + pNewScript->pAreaTrigger = &AreaTrigger_at_dark_portal; + pNewScript->RegisterSelf(); +} diff --git a/src/modules/SD2/scripts/kalimdor/caverns_of_time/dragon_soul/boss_deathwing.cpp b/src/modules/SD2/scripts/kalimdor/caverns_of_time/dragon_soul/boss_deathwing.cpp new file mode 100644 index 000000000..fa18fc733 --- /dev/null +++ b/src/modules/SD2/scripts/kalimdor/caverns_of_time/dragon_soul/boss_deathwing.cpp @@ -0,0 +1,28 @@ +/* Copyright (C) 2006 - 2013 ScriptDev2 + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +/* ScriptData +SDName: boss_deathwing +SD%Complete: 0 +SDComment: Placeholder +SDCategory: Dragon Soul +EndScriptData */ + +#include "precompiled.h" + +void AddSC_boss_deathwing() +{ +} diff --git a/src/modules/SD2/scripts/kalimdor/caverns_of_time/dragon_soul/boss_hagara.cpp b/src/modules/SD2/scripts/kalimdor/caverns_of_time/dragon_soul/boss_hagara.cpp new file mode 100644 index 000000000..9dea65f7f --- /dev/null +++ b/src/modules/SD2/scripts/kalimdor/caverns_of_time/dragon_soul/boss_hagara.cpp @@ -0,0 +1,28 @@ +/* Copyright (C) 2006 - 2013 ScriptDev2 + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +/* ScriptData +SDName: boss_hagara +SD%Complete: 0 +SDComment: Placeholder +SDCategory: Dragon Soul +EndScriptData */ + +#include "precompiled.h" + +void AddSC_boss_hagara() +{ +} diff --git a/src/modules/SD2/scripts/kalimdor/caverns_of_time/dragon_soul/boss_morchok.cpp b/src/modules/SD2/scripts/kalimdor/caverns_of_time/dragon_soul/boss_morchok.cpp new file mode 100644 index 000000000..3eee34595 --- /dev/null +++ b/src/modules/SD2/scripts/kalimdor/caverns_of_time/dragon_soul/boss_morchok.cpp @@ -0,0 +1,28 @@ +/* Copyright (C) 2006 - 2013 ScriptDev2 + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +/* ScriptData +SDName: boss_morchok +SD%Complete: 0 +SDComment: Placeholder +SDCategory: Dragon Soul +EndScriptData */ + +#include "precompiled.h" + +void AddSC_boss_morchok() +{ +} diff --git a/src/modules/SD2/scripts/kalimdor/caverns_of_time/dragon_soul/boss_ultraxion.cpp b/src/modules/SD2/scripts/kalimdor/caverns_of_time/dragon_soul/boss_ultraxion.cpp new file mode 100644 index 000000000..7c7879332 --- /dev/null +++ b/src/modules/SD2/scripts/kalimdor/caverns_of_time/dragon_soul/boss_ultraxion.cpp @@ -0,0 +1,28 @@ +/* Copyright (C) 2006 - 2013 ScriptDev2 + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +/* ScriptData +SDName: boss_ultraxion +SD%Complete: 0 +SDComment: Placeholder +SDCategory: Dragon Soul +EndScriptData */ + +#include "precompiled.h" + +void AddSC_boss_ultraxion() +{ +} diff --git a/src/modules/SD2/scripts/kalimdor/caverns_of_time/dragon_soul/boss_warlord_zonozz.cpp b/src/modules/SD2/scripts/kalimdor/caverns_of_time/dragon_soul/boss_warlord_zonozz.cpp new file mode 100644 index 000000000..8e60d577a --- /dev/null +++ b/src/modules/SD2/scripts/kalimdor/caverns_of_time/dragon_soul/boss_warlord_zonozz.cpp @@ -0,0 +1,28 @@ +/* Copyright (C) 2006 - 2013 ScriptDev2 + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with 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_zonozz +SD%Complete: 0 +SDComment: Placeholder +SDCategory: Dragon Soul +EndScriptData */ + +#include "precompiled.h" + +void AddSC_boss_warlord_zonozz() +{ +} diff --git a/src/modules/SD2/scripts/kalimdor/caverns_of_time/dragon_soul/boss_warmaster_blackhorn.cpp b/src/modules/SD2/scripts/kalimdor/caverns_of_time/dragon_soul/boss_warmaster_blackhorn.cpp new file mode 100644 index 000000000..2a258f7d6 --- /dev/null +++ b/src/modules/SD2/scripts/kalimdor/caverns_of_time/dragon_soul/boss_warmaster_blackhorn.cpp @@ -0,0 +1,28 @@ +/* Copyright (C) 2006 - 2013 ScriptDev2 + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with 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_blackhorn +SD%Complete: 0 +SDComment: Placeholder +SDCategory: Dragon Soul +EndScriptData */ + +#include "precompiled.h" + +void AddSC_boss_warmaster_blackhorn() +{ +} diff --git a/src/modules/SD2/scripts/kalimdor/caverns_of_time/dragon_soul/boss_yorsahj.cpp b/src/modules/SD2/scripts/kalimdor/caverns_of_time/dragon_soul/boss_yorsahj.cpp new file mode 100644 index 000000000..6c7cc5502 --- /dev/null +++ b/src/modules/SD2/scripts/kalimdor/caverns_of_time/dragon_soul/boss_yorsahj.cpp @@ -0,0 +1,28 @@ +/* Copyright (C) 2006 - 2013 ScriptDev2 + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +/* ScriptData +SDName: boss_yorsahj +SD%Complete: 0 +SDComment: Placeholder +SDCategory: Dragon Soul +EndScriptData */ + +#include "precompiled.h" + +void AddSC_boss_yorsahj() +{ +} diff --git a/src/modules/SD2/scripts/kalimdor/caverns_of_time/dragon_soul/dragon_soul.cpp b/src/modules/SD2/scripts/kalimdor/caverns_of_time/dragon_soul/dragon_soul.cpp new file mode 100644 index 000000000..2f834c217 --- /dev/null +++ b/src/modules/SD2/scripts/kalimdor/caverns_of_time/dragon_soul/dragon_soul.cpp @@ -0,0 +1,28 @@ +/* Copyright (C) 2006 - 2013 ScriptDev2 + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +/* ScriptData +SDName: dragon_soul +SD%Complete: 0 +SDComment: Placeholder +SDCategory: Dragon Soul +EndScriptData */ + +#include "precompiled.h" + +void AddSC_dragon_soul() +{ +} diff --git a/src/modules/SD2/scripts/kalimdor/caverns_of_time/dragon_soul/dragon_soul.h b/src/modules/SD2/scripts/kalimdor/caverns_of_time/dragon_soul/dragon_soul.h new file mode 100644 index 000000000..6b5f9ef47 --- /dev/null +++ b/src/modules/SD2/scripts/kalimdor/caverns_of_time/dragon_soul/dragon_soul.h @@ -0,0 +1,3 @@ +/* Copyright (C) 2006 - 2013 ScriptDev2 + * This program is free software licensed under GPL version 2 + * Please see the included DOCS/LICENSE.TXT for more information */ diff --git a/src/modules/SD2/scripts/kalimdor/caverns_of_time/dragon_soul/instance_dragon_soul.cpp b/src/modules/SD2/scripts/kalimdor/caverns_of_time/dragon_soul/instance_dragon_soul.cpp new file mode 100644 index 000000000..bce483b6c --- /dev/null +++ b/src/modules/SD2/scripts/kalimdor/caverns_of_time/dragon_soul/instance_dragon_soul.cpp @@ -0,0 +1,28 @@ +/* Copyright (C) 2006 - 2013 ScriptDev2 + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along 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_dragon_soul +SD%Complete: 0 +SDComment: Placeholder +SDCategory: Dragon Soul +EndScriptData */ + +#include "precompiled.h" + +void AddSC_instance_dragon_soul() +{ +} diff --git a/src/modules/SD2/scripts/kalimdor/caverns_of_time/end_of_time/end_of_time.cpp b/src/modules/SD2/scripts/kalimdor/caverns_of_time/end_of_time/end_of_time.cpp new file mode 100644 index 000000000..6e2f44058 --- /dev/null +++ b/src/modules/SD2/scripts/kalimdor/caverns_of_time/end_of_time/end_of_time.cpp @@ -0,0 +1,28 @@ +/* Copyright (C) 2006 - 2013 ScriptDev2 + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +/* ScriptData +SDName: end_of_time +SD%Complete: 0 +SDComment: Placeholder +SDCategory: End of Time +EndScriptData */ + +#include "precompiled.h" + +void AddSC_end_of_time() +{ +} diff --git a/src/modules/SD2/scripts/kalimdor/caverns_of_time/end_of_time/end_of_time.h b/src/modules/SD2/scripts/kalimdor/caverns_of_time/end_of_time/end_of_time.h new file mode 100644 index 000000000..6b5f9ef47 --- /dev/null +++ b/src/modules/SD2/scripts/kalimdor/caverns_of_time/end_of_time/end_of_time.h @@ -0,0 +1,3 @@ +/* Copyright (C) 2006 - 2013 ScriptDev2 + * This program is free software licensed under GPL version 2 + * Please see the included DOCS/LICENSE.TXT for more information */ diff --git a/src/modules/SD2/scripts/kalimdor/caverns_of_time/end_of_time/instance_end_of_time.cpp b/src/modules/SD2/scripts/kalimdor/caverns_of_time/end_of_time/instance_end_of_time.cpp new file mode 100644 index 000000000..8f12d72bf --- /dev/null +++ b/src/modules/SD2/scripts/kalimdor/caverns_of_time/end_of_time/instance_end_of_time.cpp @@ -0,0 +1,28 @@ +/* Copyright (C) 2006 - 2013 ScriptDev2 + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along 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_end_of_time +SD%Complete: 0 +SDComment: Placeholder +SDCategory: End of Time +EndScriptData */ + +#include "precompiled.h" + +void AddSC_instance_end_of_time() +{ +} diff --git a/src/modules/SD2/scripts/kalimdor/caverns_of_time/hour_of_twilight/boss_archbishop_benedictus.cpp b/src/modules/SD2/scripts/kalimdor/caverns_of_time/hour_of_twilight/boss_archbishop_benedictus.cpp new file mode 100644 index 000000000..ff6137eb4 --- /dev/null +++ b/src/modules/SD2/scripts/kalimdor/caverns_of_time/hour_of_twilight/boss_archbishop_benedictus.cpp @@ -0,0 +1,28 @@ +/* Copyright (C) 2006 - 2013 ScriptDev2 + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +/* ScriptData +SDName: boss_archbishop_benedictus +SD%Complete: 0 +SDComment: Placeholder +SDCategory: Hour of Twilight +EndScriptData */ + +#include "precompiled.h" + +void AddSC_boss_archbishop_benedictus() +{ +} diff --git a/src/modules/SD2/scripts/kalimdor/caverns_of_time/hour_of_twilight/boss_arcurion.cpp b/src/modules/SD2/scripts/kalimdor/caverns_of_time/hour_of_twilight/boss_arcurion.cpp new file mode 100644 index 000000000..fd08d64d8 --- /dev/null +++ b/src/modules/SD2/scripts/kalimdor/caverns_of_time/hour_of_twilight/boss_arcurion.cpp @@ -0,0 +1,28 @@ +/* Copyright (C) 2006 - 2013 ScriptDev2 + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +/* ScriptData +SDName: boss_arcurion +SD%Complete: 0 +SDComment: Placeholder +SDCategory: Hour of Twilight +EndScriptData */ + +#include "precompiled.h" + +void AddSC_boss_arcurion() +{ +} diff --git a/src/modules/SD2/scripts/kalimdor/caverns_of_time/hour_of_twilight/boss_asira_dawnslayer.cpp b/src/modules/SD2/scripts/kalimdor/caverns_of_time/hour_of_twilight/boss_asira_dawnslayer.cpp new file mode 100644 index 000000000..a051485a0 --- /dev/null +++ b/src/modules/SD2/scripts/kalimdor/caverns_of_time/hour_of_twilight/boss_asira_dawnslayer.cpp @@ -0,0 +1,28 @@ +/* Copyright (C) 2006 - 2013 ScriptDev2 + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +/* ScriptData +SDName: boss_asira_dawnslayer +SD%Complete: 0 +SDComment: Placeholder +SDCategory: Hour of Twilight +EndScriptData */ + +#include "precompiled.h" + +void AddSC_boss_asira_dawnslayer() +{ +} diff --git a/src/modules/SD2/scripts/kalimdor/caverns_of_time/hour_of_twilight/hour_of_twilight.h b/src/modules/SD2/scripts/kalimdor/caverns_of_time/hour_of_twilight/hour_of_twilight.h new file mode 100644 index 000000000..6b5f9ef47 --- /dev/null +++ b/src/modules/SD2/scripts/kalimdor/caverns_of_time/hour_of_twilight/hour_of_twilight.h @@ -0,0 +1,3 @@ +/* Copyright (C) 2006 - 2013 ScriptDev2 + * This program is free software licensed under GPL version 2 + * Please see the included DOCS/LICENSE.TXT for more information */ diff --git a/src/modules/SD2/scripts/kalimdor/caverns_of_time/hour_of_twilight/instance_hour_of_twilight.cpp b/src/modules/SD2/scripts/kalimdor/caverns_of_time/hour_of_twilight/instance_hour_of_twilight.cpp new file mode 100644 index 000000000..61a23df59 --- /dev/null +++ b/src/modules/SD2/scripts/kalimdor/caverns_of_time/hour_of_twilight/instance_hour_of_twilight.cpp @@ -0,0 +1,28 @@ +/* Copyright (C) 2006 - 2013 ScriptDev2 + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along 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_hour_of_twilight +SD%Complete: 0 +SDComment: Placeholder +SDCategory: Hour of Twilight +EndScriptData */ + +#include "precompiled.h" + +void AddSC_instance_hour_of_twilight() +{ +} diff --git a/src/modules/SD2/scripts/kalimdor/caverns_of_time/hyjal_summit/boss_archimonde.cpp b/src/modules/SD2/scripts/kalimdor/caverns_of_time/hyjal_summit/boss_archimonde.cpp new file mode 100644 index 000000000..4a510892c --- /dev/null +++ b/src/modules/SD2/scripts/kalimdor/caverns_of_time/hyjal_summit/boss_archimonde.cpp @@ -0,0 +1,418 @@ +/* Copyright (C) 2006 - 2013 ScriptDev2 + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with 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: Timers; Some details may need adjustments. +SDCategory: Caverns of Time, Mount Hyjal +EndScriptData */ + +#include "precompiled.h" +#include "hyjal.h" + +enum +{ + SAY_INTRO = -1534018, + SAY_AGGRO = -1534019, + SAY_DOOMFIRE1 = -1534020, + SAY_DOOMFIRE2 = -1534021, + SAY_AIR_BURST1 = -1534022, + SAY_AIR_BURST2 = -1534023, + SAY_SLAY1 = -1534024, + SAY_SLAY2 = -1534025, + SAY_SLAY3 = -1534026, + SAY_ENRAGE = -1534027, + SAY_EPILOGUE = -1534028, + SAY_SOUL_CHARGE1 = -1534029, + SAY_SOUL_CHARGE2 = -1534030, + + // spells + SPELL_DRAIN_TREE = 39140, // intro cosmetic spell + // SPELL_DRAIN_TREE_DUMMY = 39141, // purpose unk + + SPELL_FINGER_DEATH = 31984, + SPELL_FINGER_DEATH_SCRIPT = 32111, // targets whisps + SPELL_FINGER_DEATH_DUMMY = 39369, // epilogue spell + SPELL_HAND_OF_DEATH = 35354, // hard enrage spell + SPELL_AIR_BURST = 32014, + SPELL_GRIP_OF_THE_LEGION = 31972, + SPELL_DOOMFIRE_STRIKE = 31903, // summons 18095 and 18104 + SPELL_SOUL_CHARGE_YELLOW = 32045, // procs 32054 + SPELL_SOUL_CHARGE_GREEN = 32051, // procs 32057 + SPELL_SOUL_CHARGE_RED = 32052, // procs 32053 + SPELL_FEAR = 31970, + + SPELL_PROTECTION_OF_ELUNE = 38528, // protect the players on epilogue + + // summoned creatures + NPC_DOOMFIRE = 18095, + // NPC_DOOMFIRE_SPIRIT = 18104, + NPC_ANCIENT_WISP = 17946, + NPC_CHANNEL_TARGET = 22418, // if he gets in range of 75.0f, then he gets enraged + + // doomfire spells + SPELL_DOOMFIRE_SPAWN = 32074, + SPELL_DOOMFIRE = 31945, // fire damage spell + + // wisp spells + SPELL_DENOUEMENT_WISP = 32124, + SPELL_ANCIENT_SPARK = 39349, +}; + +/* Finally, Archimonde's script. His script isn't extremely complex, most are simply spells on timers. + The only complicated aspect of the battle is Finger of Death and Doomfire, with Doomfire being the + hardest bit to code. Finger of Death is simply a distance check - if no one is in melee range, then + select a random target and cast the spell on them. However, if someone IS in melee range, and this + is NOT the main tank (creature's victim), then we aggro that player and they become the new victim. + For Doomfire, we summon a mob (Doomfire 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 boss_archimondeAI : public ScriptedAI +{ + boss_archimondeAI(Creature* pCreature) : ScriptedAI(pCreature) + { + m_pInstance = (ScriptedInstance*)pCreature->GetInstanceData(); + m_bHasIntro = false; + Reset(); + } + + ScriptedInstance* m_pInstance; + + uint32 m_uiDrainNordrassilTimer; + uint32 m_uiFearTimer; + uint32 m_uiAirBurstTimer; + uint32 m_uiGripOfTheLegionTimer; + uint32 m_uiDoomfireTimer; + uint32 m_uiFingerOfDeathTimer; + uint32 m_uiHandOfDeathTimer; + uint32 m_uiSummonWispTimer; + uint32 m_uiWispCount; + uint32 m_uiEnrageTimer; + + bool m_bHasIntro; + bool m_bIsEnraged; + bool m_bIsEpilogue; + bool m_bStartEpilogue; + + void Reset() override + { + m_uiDrainNordrassilTimer = 10000; + m_uiFearTimer = 40000; + m_uiAirBurstTimer = 30000; + m_uiGripOfTheLegionTimer = urand(5000, 25000); + m_uiDoomfireTimer = 15000; + m_uiFingerOfDeathTimer = 15000; + m_uiHandOfDeathTimer = 2000; + m_uiWispCount = 0; + m_uiEnrageTimer = 10 * MINUTE * IN_MILLISECONDS; + + m_bIsEnraged = false; + m_bIsEpilogue = false; + m_bStartEpilogue = false; + } + + void Aggro(Unit* /*pWho*/) override + { + DoScriptText(SAY_AGGRO, m_creature); + } + + void MoveInLineOfSight(Unit* pWho) override + { + // If the boss reaches the tree during the fight, then he enrages - the distance is not very clear + if (!m_bIsEnraged && pWho->GetEntry() == NPC_CHANNEL_TARGET && pWho->IsWithinDistInMap(m_creature, 75.0f)) + { + m_uiEnrageTimer = 1000; + m_bIsEnraged = true; + } + + ScriptedAI::MoveInLineOfSight(pWho); + } + + void KilledUnit(Unit* pVictim) override + { + if (pVictim->GetTypeId() != TYPEID_PLAYER) + return; + + switch (urand(0, 2)) + { + case 0: DoScriptText(SAY_SLAY1, m_creature); break; + case 1: DoScriptText(SAY_SLAY2, m_creature); break; + case 2: DoScriptText(SAY_SLAY3, m_creature); break; + } + + switch (pVictim->getClass()) + { + case CLASS_PRIEST: + case CLASS_PALADIN: + case CLASS_WARLOCK: + pVictim->CastSpell(m_creature, SPELL_SOUL_CHARGE_RED, true); + break; + case CLASS_MAGE: + case CLASS_ROGUE: + case CLASS_WARRIOR: + pVictim->CastSpell(m_creature, SPELL_SOUL_CHARGE_YELLOW, true); + break; + case CLASS_DRUID: + case CLASS_SHAMAN: + case CLASS_HUNTER: + pVictim->CastSpell(m_creature, SPELL_SOUL_CHARGE_GREEN, true); + break; + } + } + + void JustReachedHome() override + { + // Start epilogue at 10% hp + if (m_bIsEpilogue) + { + if (DoCastSpellIfCan(m_creature, SPELL_PROTECTION_OF_ELUNE) == CAST_OK) + { + m_uiFingerOfDeathTimer = 5000; + m_bStartEpilogue = true; + } + } + else if (m_pInstance) + m_pInstance->SetData(TYPE_ARCHIMONDE, FAIL); + } + + void JustSummoned(Creature* pSummoned) override + { + if (pSummoned->GetEntry() == NPC_ANCIENT_WISP) + { + pSummoned->AI()->AttackStart(m_creature); + ++m_uiWispCount; + + // When enough wisps have gathered or boss is low hp, then kill him + if (m_uiWispCount >= 45 || m_creature->GetHealthPercent() <= 1.0f) + pSummoned->CastSpell(pSummoned, SPELL_DENOUEMENT_WISP, false); + } + else if (pSummoned->GetEntry() == NPC_DOOMFIRE) + { + pSummoned->CastSpell(pSummoned, SPELL_DOOMFIRE_SPAWN, true); + pSummoned->CastSpell(pSummoned, SPELL_DOOMFIRE, true, NULL, NULL, m_creature->GetObjectGuid()); + } + } + + void UpdateAI(const uint32 uiDiff) override + { + // Intro timer + if (m_uiDrainNordrassilTimer) + { + if (m_uiDrainNordrassilTimer <= uiDiff) + { + if (DoCastSpellIfCan(m_creature, SPELL_DRAIN_TREE) == CAST_OK) + { + if (!m_bHasIntro) + { + DoScriptText(SAY_INTRO, m_creature); + m_bHasIntro = true; + } + m_uiDrainNordrassilTimer = 0; + } + } + else + m_uiDrainNordrassilTimer -= uiDiff; + } + + if (!m_creature->SelectHostileTarget() || !m_creature->getVictim()) + return; + + // Start epilogue - fight was won! + if (m_creature->GetHealthPercent() < 10.0f) + { + if (!m_bIsEpilogue) + { + DoScriptText(SAY_EPILOGUE, m_creature); + + // move at home position and start outro + m_creature->GetMotionMaster()->MoveTargetedHome(); + SetCombatMovement(false); + m_bIsEpilogue = true; + } + + if (m_bStartEpilogue) + { + // Spam Finger of Death on players and Wisps + if (m_uiFingerOfDeathTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature, urand(0, 1) ? SPELL_FINGER_DEATH_DUMMY : SPELL_FINGER_DEATH_SCRIPT) == CAST_OK) + m_uiFingerOfDeathTimer = 1000; + } + else + m_uiFingerOfDeathTimer -= uiDiff; + + if (m_uiSummonWispTimer < uiDiff) + { + float fX, fY, fZ; + m_creature->GetNearPoint(m_creature, fX, fY, fZ, 0, 75.0f, urand(0, 1) ? frand(0, 2.8f) : frand(4.3f, M_PI_F * 2)); + m_creature->SummonCreature(NPC_ANCIENT_WISP, fX, fY, fZ, 0, TEMPSUMMON_TIMED_OOC_DESPAWN, 15000); + m_uiSummonWispTimer = urand(1000, 1500); + } + else + m_uiSummonWispTimer -= uiDiff; + } + + // Stop using the other spells + return; + } + + if (m_uiEnrageTimer) + { + if (m_uiEnrageTimer <= uiDiff) + { + if (DoCastSpellIfCan(m_creature, SPELL_HAND_OF_DEATH) == CAST_OK) + { + DoScriptText(SAY_ENRAGE, m_creature); + m_uiEnrageTimer = 0; + } + } + else + m_uiEnrageTimer -= uiDiff; + } + + if (m_uiGripOfTheLegionTimer < uiDiff) + { + if (Unit* pTarget = m_creature->SelectAttackingTarget(ATTACKING_TARGET_RANDOM, 0)) + { + if (DoCastSpellIfCan(pTarget, SPELL_GRIP_OF_THE_LEGION) == CAST_OK) + m_uiGripOfTheLegionTimer = urand(5000, 25000); + } + } + else + m_uiGripOfTheLegionTimer -= uiDiff; + + if (m_uiAirBurstTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature->getVictim(), SPELL_AIR_BURST) == CAST_OK) + { + DoScriptText(urand(0, 1) ? SAY_AIR_BURST1 : SAY_AIR_BURST2, m_creature); + m_uiAirBurstTimer = urand(25000, 40000); + } + } + else + m_uiAirBurstTimer -= uiDiff; + + if (m_uiFearTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature, SPELL_FEAR) == CAST_OK) + m_uiFearTimer = 42000; + } + else + m_uiFearTimer -= uiDiff; + + if (m_uiDoomfireTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature, SPELL_DOOMFIRE_STRIKE) == CAST_OK) + { + DoScriptText(urand(0, 1) ? SAY_DOOMFIRE1 : SAY_DOOMFIRE2, m_creature); + m_uiDoomfireTimer = urand(10000, 15000); + } + } + else + m_uiDoomfireTimer -= uiDiff; + + // If we are within range melee the target + if (m_creature->CanReachWithMeleeAttack(m_creature->getVictim())) + DoMeleeAttackIfReady(); + // Else spam Finger of Death + else + { + if (!m_creature->IsNonMeleeSpellCasted(false)) + { + if (Unit* pTarget = m_creature->SelectAttackingTarget(ATTACKING_TARGET_RANDOM, 0)) + DoCastSpellIfCan(pTarget, SPELL_FINGER_DEATH); + } + } + } +}; + +/* This is the script for the Doomfire Spirit Mob. This mob controls the doomfire npc and allows it to move randomly around the map. */ +struct npc_doomfire_spiritAI : public ScriptedAI +{ + npc_doomfire_spiritAI(Creature* pCreature) : ScriptedAI(pCreature) { Reset(); } + + ObjectGuid m_doomfireGuid; + + uint32 m_uiDoomfireLoadTimer; + uint32 m_uiChangeTargetTimer; + float m_fAngle; + + void Reset() override + { + m_uiDoomfireLoadTimer = 1000; + m_uiChangeTargetTimer = 1500; + m_fAngle = urand(0, M_PI_F * 2); + } + + void UpdateAI(const uint32 uiDiff) override + { + if (m_uiDoomfireLoadTimer) + { + if (m_uiDoomfireLoadTimer <= uiDiff) + { + // Get the closest doomfire + if (Creature* pTemp = GetClosestCreatureWithEntry(m_creature, NPC_DOOMFIRE, 5.0f)) + m_doomfireGuid = pTemp->GetObjectGuid(); + + m_uiDoomfireLoadTimer = 0; + } + else + m_uiDoomfireLoadTimer -= uiDiff; + } + + // It's not very clear how should this one move. For the moment just move to random points around on timer + if (m_uiChangeTargetTimer < uiDiff) + { + if (Creature* pDoomfire = m_creature->GetMap()->GetCreature(m_doomfireGuid)) + { + float fX, fY, fZ; + pDoomfire->GetNearPoint(pDoomfire, fX, fY, fZ, 0, 30.0f, m_fAngle + frand(0, M_PI_F * .5)); + pDoomfire->GetMotionMaster()->MovePoint(0, fX, fY, fZ); + } + + m_uiChangeTargetTimer = 4000; + } + else + m_uiChangeTargetTimer -= uiDiff; + } +}; + +CreatureAI* GetAI_boss_archimonde(Creature* pCreature) +{ + return new boss_archimondeAI(pCreature); +} + +CreatureAI* GetAI_npc_doomfire_spirit(Creature* pCreature) +{ + return new npc_doomfire_spiritAI(pCreature); +} + +void AddSC_boss_archimonde() +{ + Script* pNewScript; + + pNewScript = new Script; + pNewScript->Name = "boss_archimonde"; + pNewScript->GetAI = &GetAI_boss_archimonde; + pNewScript->RegisterSelf(); + + pNewScript = new Script; + pNewScript->Name = "npc_doomfire_spirit"; + pNewScript->GetAI = &GetAI_npc_doomfire_spirit; + pNewScript->RegisterSelf(); +} diff --git a/src/modules/SD2/scripts/kalimdor/caverns_of_time/hyjal_summit/hyjal.cpp b/src/modules/SD2/scripts/kalimdor/caverns_of_time/hyjal_summit/hyjal.cpp new file mode 100644 index 000000000..d45e82c53 --- /dev/null +++ b/src/modules/SD2/scripts/kalimdor/caverns_of_time/hyjal_summit/hyjal.cpp @@ -0,0 +1,245 @@ +/* Copyright (C) 2006 - 2013 ScriptDev2 + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along 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" + +enum +{ + GOSSIP_ITEM_JAINA_BEGIN = -3534000, + GOSSIP_ITEM_JAINA_ANATHERON = -3534001, + GOSSIP_ITEM_JAINA_SUCCCESS = -3534002, + + GOSSIP_ITEM_THRALL_BEGIN = -3534003, + GOSSIP_ITEM_THRALL_AZGALOR = -3534004, + GOSSIP_ITEM_THRALL_SUCCESS = -3534005, + + GOSSIP_ITEM_TYRANDE_AID = -3534006, + + // Note: additional menu items include 9230 and 9398. + GOSSIP_MENU_ID_DEFAULT = 907, // this is wrong, but currently we don't know which are the right ids +}; + +CreatureAI* GetAI_npc_jaina_proudmoore(Creature* pCreature) +{ + hyjalAI* pTempAI = new hyjalAI(pCreature); + + pTempAI->m_aSpells[0].m_uiSpellId = SPELL_BLIZZARD; + pTempAI->m_aSpells[0].m_uiCooldown = urand(15000, 35000); + pTempAI->m_aSpells[0].m_pType = TARGETTYPE_RANDOM; + + pTempAI->m_aSpells[1].m_uiSpellId = SPELL_PYROBLAST; + pTempAI->m_aSpells[1].m_uiCooldown = urand(2000, 9000); + pTempAI->m_aSpells[1].m_pType = TARGETTYPE_RANDOM; + + pTempAI->m_aSpells[2].m_uiSpellId = SPELL_SUMMON_ELEMENTALS; + pTempAI->m_aSpells[2].m_uiCooldown = urand(15000, 45000); + pTempAI->m_aSpells[2].m_pType = TARGETTYPE_SELF; + + return pTempAI; +} + +bool GossipHello_npc_jaina_proudmoore(Player* pPlayer, Creature* pCreature) +{ + if (ScriptedInstance* pInstance = (ScriptedInstance*)pCreature->GetInstanceData()) + { + if (hyjalAI* pJainaAI = dynamic_cast(pCreature->AI())) + { + if (!pJainaAI->IsEventInProgress()) + { + // Should not happen that jaina is here now, but for safe we check + if (pInstance->GetData(TYPE_KAZROGAL) != DONE) + { + if (pInstance->GetData(TYPE_WINTERCHILL) == NOT_STARTED || pInstance->GetData(TYPE_WINTERCHILL) == FAIL) + pPlayer->ADD_GOSSIP_ITEM_ID(GOSSIP_ICON_CHAT, GOSSIP_ITEM_JAINA_BEGIN, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 1); + else if (pInstance->GetData(TYPE_WINTERCHILL) == DONE && (pInstance->GetData(TYPE_ANETHERON) == NOT_STARTED || pInstance->GetData(TYPE_ANETHERON) == FAIL)) + pPlayer->ADD_GOSSIP_ITEM_ID(GOSSIP_ICON_CHAT, GOSSIP_ITEM_JAINA_ANATHERON, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 2); + else if (pInstance->GetData(TYPE_ANETHERON) == DONE) + pPlayer->ADD_GOSSIP_ITEM_ID(GOSSIP_ICON_CHAT, GOSSIP_ITEM_JAINA_SUCCCESS, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 3); + + if (pPlayer->isGameMaster()) + pPlayer->ADD_GOSSIP_ITEM(GOSSIP_ICON_DOT, "[GM] Toggle Debug Timers", GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF); + } + } + } + } + + pPlayer->SEND_GOSSIP_MENU(GOSSIP_MENU_ID_DEFAULT, pCreature->GetObjectGuid()); + return true; +} + +bool GossipSelect_npc_jaina_proudmoore(Player* pPlayer, Creature* pCreature, uint32 /*uiSender*/, uint32 uiAction) +{ + if (hyjalAI* pJainaAI = dynamic_cast(pCreature->AI())) + { + switch (uiAction) + { + case GOSSIP_ACTION_INFO_DEF + 1: + pJainaAI->StartEvent(); + break; + case GOSSIP_ACTION_INFO_DEF + 2: + pJainaAI->m_bIsFirstBossDead = true; + pJainaAI->m_uiWaveCount = 9; + pJainaAI->StartEvent(); + break; + case GOSSIP_ACTION_INFO_DEF + 3: + pJainaAI->Retreat(); + break; + case GOSSIP_ACTION_INFO_DEF: + pJainaAI->m_bDebugMode = !pJainaAI->m_bDebugMode; + debug_log("SD2: HyjalAI - Debug mode has been toggled %s", pJainaAI->m_bDebugMode ? "on" : "off"); + break; + } + } + + pPlayer->CLOSE_GOSSIP_MENU(); + return true; +} + +CreatureAI* GetAI_npc_thrall(Creature* pCreature) +{ + hyjalAI* pTempAI = new hyjalAI(pCreature); + + pTempAI->m_aSpells[0].m_uiSpellId = SPELL_CHAIN_LIGHTNING; + pTempAI->m_aSpells[0].m_uiCooldown = urand(2000, 7000); + pTempAI->m_aSpells[0].m_pType = TARGETTYPE_VICTIM; + + pTempAI->m_aSpells[1].m_uiSpellId = SPELL_FERAL_SPIRIT; + pTempAI->m_aSpells[1].m_uiCooldown = urand(6000, 41000); + pTempAI->m_aSpells[1].m_pType = TARGETTYPE_RANDOM; + + return pTempAI; +} + +bool GossipHello_npc_thrall(Player* pPlayer, Creature* pCreature) +{ + if (ScriptedInstance* pInstance = (ScriptedInstance*)pCreature->GetInstanceData()) + { + if (hyjalAI* pThrallAI = dynamic_cast(pCreature->AI())) + { + if (!pThrallAI->IsEventInProgress()) + { + // Only let them start the Horde phases if Anetheron is dead. + if (pInstance->GetData(TYPE_ANETHERON) == DONE && pInstance->GetData(TYPE_ARCHIMONDE) != DONE) + { + if (pInstance->GetData(TYPE_KAZROGAL) == NOT_STARTED || pInstance->GetData(TYPE_KAZROGAL) == FAIL) + pPlayer->ADD_GOSSIP_ITEM_ID(GOSSIP_ICON_CHAT, GOSSIP_ITEM_THRALL_BEGIN, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 1); + else if (pInstance->GetData(TYPE_KAZROGAL) == DONE && (pInstance->GetData(TYPE_AZGALOR) == NOT_STARTED || pInstance->GetData(TYPE_AZGALOR) == FAIL)) + pPlayer->ADD_GOSSIP_ITEM_ID(GOSSIP_ICON_CHAT, GOSSIP_ITEM_THRALL_AZGALOR, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 2); + else if (pInstance->GetData(TYPE_AZGALOR) == DONE) + pPlayer->ADD_GOSSIP_ITEM_ID(GOSSIP_ICON_CHAT, GOSSIP_ITEM_THRALL_SUCCESS, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 3); + + if (pPlayer->isGameMaster()) + pPlayer->ADD_GOSSIP_ITEM(GOSSIP_ICON_DOT, "[GM] Toggle Debug Timers", GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF); + } + } + } + } + + pPlayer->SEND_GOSSIP_MENU(GOSSIP_MENU_ID_DEFAULT, pCreature->GetObjectGuid()); + return true; +} + +bool GossipSelect_npc_thrall(Player* pPlayer, Creature* pCreature, uint32 /*uiSender*/, uint32 uiAction) +{ + if (hyjalAI* pThrallAI = dynamic_cast(pCreature->AI())) + { + switch (uiAction) + { + case GOSSIP_ACTION_INFO_DEF + 1: + pThrallAI->StartEvent(); + break; + case GOSSIP_ACTION_INFO_DEF + 2: + pThrallAI->m_bIsFirstBossDead = true; + pThrallAI->m_uiWaveCount = 9; + pThrallAI->StartEvent(); + break; + case GOSSIP_ACTION_INFO_DEF + 3: + pThrallAI->Retreat(); + break; + case GOSSIP_ACTION_INFO_DEF: + pThrallAI->m_bDebugMode = !pThrallAI->m_bDebugMode; + debug_log("SD2: HyjalAI - Debug mode has been toggled %s", pThrallAI->m_bDebugMode ? "on" : "off"); + break; + } + } + + pPlayer->CLOSE_GOSSIP_MENU(); + return true; +} + +bool GossipHello_npc_tyrande_whisperwind(Player* pPlayer, Creature* pCreature) +{ + if (ScriptedInstance* pInstance = (ScriptedInstance*)pCreature->GetInstanceData()) + { + // Only let them get item if Azgalor is dead. + if (pInstance->GetData(TYPE_AZGALOR) == DONE && !pPlayer->HasItemCount(ITEM_TEAR_OF_GODDESS, 1)) + pPlayer->ADD_GOSSIP_ITEM_ID(GOSSIP_ICON_CHAT, GOSSIP_ITEM_TYRANDE_AID, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF); + } + + pPlayer->SEND_GOSSIP_MENU(GOSSIP_MENU_ID_DEFAULT, pCreature->GetObjectGuid()); + return true; +} + +bool GossipSelect_npc_tyrande_whisperwind(Player* pPlayer, Creature* /*pCreature*/, uint32 /*uiSender*/, uint32 uiAction) +{ + if (uiAction == GOSSIP_ACTION_INFO_DEF) + { + if (Item* pItem = pPlayer->StoreNewItemInInventorySlot(ITEM_TEAR_OF_GODDESS, 1)) + pPlayer->SendNewItem(pItem, 1, true, false); + } + + pPlayer->CLOSE_GOSSIP_MENU(); + return true; +} + +void AddSC_hyjal() +{ + Script* pNewScript; + + pNewScript = new Script; + pNewScript->Name = "npc_jaina_proudmoore"; + pNewScript->GetAI = &GetAI_npc_jaina_proudmoore; + pNewScript->pGossipHello = &GossipHello_npc_jaina_proudmoore; + pNewScript->pGossipSelect = &GossipSelect_npc_jaina_proudmoore; + pNewScript->RegisterSelf(); + + pNewScript = new Script; + pNewScript->Name = "npc_thrall"; + pNewScript->GetAI = &GetAI_npc_thrall; + pNewScript->pGossipHello = &GossipHello_npc_thrall; + pNewScript->pGossipSelect = &GossipSelect_npc_thrall; + pNewScript->RegisterSelf(); + + pNewScript = new Script; + pNewScript->Name = "npc_tyrande_whisperwind"; + pNewScript->pGossipHello = &GossipHello_npc_tyrande_whisperwind; + pNewScript->pGossipSelect = &GossipSelect_npc_tyrande_whisperwind; + pNewScript->RegisterSelf(); +} diff --git a/src/modules/SD2/scripts/kalimdor/caverns_of_time/hyjal_summit/hyjal.h b/src/modules/SD2/scripts/kalimdor/caverns_of_time/hyjal_summit/hyjal.h new file mode 100644 index 000000000..988badf5b --- /dev/null +++ b/src/modules/SD2/scripts/kalimdor/caverns_of_time/hyjal_summit/hyjal.h @@ -0,0 +1,89 @@ +/* Copyright (C) 2006 - 2013 ScriptDev2 + * This program is free software licensed under GPL version 2 + * Please see the included DOCS/LICENSE.TXT for more information */ + +#ifndef DEF_HYJAL_H +#define DEF_HYJAL_H + +enum +{ + MAX_ENCOUNTER = 5, + + TYPE_WINTERCHILL = 0, + TYPE_ANETHERON = 1, + TYPE_KAZROGAL = 2, + TYPE_AZGALOR = 3, + TYPE_ARCHIMONDE = 4, + + TYPE_TRASH_COUNT = 5, + TYPE_RETREAT = 6, + + WORLD_STATE_WAVES = 2842, + WORLD_STATE_ENEMY = 2453, + WORLD_STATE_ENEMYCOUNT = 2454, + + NPC_JAINA = 17772, + NPC_THRALL = 17852, + NPC_TYRANDE = 17948, + + // Bosses summoned after every 8 waves + NPC_WINTERCHILL = 17767, + NPC_ANETHERON = 17808, + NPC_KAZROGAL = 17888, + NPC_AZGALOR = 17842, + NPC_ARCHIMONDE = 17968, + + // Trash Mobs summoned in waves + NPC_NECRO = 17899, + NPC_ABOMI = 17898, + NPC_GHOUL = 17895, + NPC_BANSH = 17905, + NPC_CRYPT = 17897, + NPC_GARGO = 17906, + NPC_FROST = 17907, + NPC_GIANT = 17908, + NPC_STALK = 17916, + + NPC_WATER_ELEMENTAL = 18001, + NPC_DIRE_WOLF = 17854, + + GO_ANCIENT_GEM = 185557, +}; + +static const float aArchimondeSpawnLoc[4] = {5581.49f, -3445.63f, 1575.1f, 3.905f}; + +class instance_mount_hyjal : public ScriptedInstance +{ + public: + instance_mount_hyjal(Map* pMap); + + void Initialize() override; + bool IsEncounterInProgress() const override; + + void OnPlayerEnter(Player* pPlayer) override; + + void OnCreatureCreate(Creature* pCreature) override; + void OnObjectCreate(GameObject* pGo) override; + + void OnCreatureEnterCombat(Creature* pCreature) override; + void OnCreatureEvade(Creature* pCreature); + void OnCreatureDeath(Creature* pCreature) override; + + void SetData(uint32 uiType, uint32 uiData) override; + uint32 GetData(uint32 uiType) const override; + + const char* Save() const override { return m_strSaveData.c_str(); } + void Load(const char* chrIn) override; + + private: + void DoSpawnArchimonde(); + + uint32 m_auiEncounter[MAX_ENCOUNTER]; + std::string m_strSaveData; + + GuidList lAncientGemGUIDList; + + uint32 m_uiTrashCount; +}; + +#endif diff --git a/src/modules/SD2/scripts/kalimdor/caverns_of_time/hyjal_summit/hyjalAI.cpp b/src/modules/SD2/scripts/kalimdor/caverns_of_time/hyjal_summit/hyjalAI.cpp new file mode 100644 index 000000000..f22e1588f --- /dev/null +++ b/src/modules/SD2/scripts/kalimdor/caverns_of_time/hyjal_summit/hyjalAI.cpp @@ -0,0 +1,548 @@ +/* Copyright (C) 2006 - 2013 ScriptDev2 + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along 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" + +struct HyjalLocation +{ + eBaseArea m_pBaseArea; + float m_fX, m_fY, m_fZ; +}; + +// Locations for summoning waves +// Must be even number +static const HyjalLocation aHyjalSpawnLoc[] = +{ + {BASE_ALLY, 4979.010f, -1709.134f, 1339.674f}, + {BASE_ALLY, 4969.123f, -1705.904f, 1341.363f}, + {BASE_ALLY, 4970.260f, -1698.546f, 1341.200f}, + {BASE_ALLY, 4975.262f, -1698.239f, 1341.427f}, + {BASE_HORDE, 5557.582f, -2587.159f, 1481.644f}, + {BASE_HORDE, 5545.901f, -2582.246f, 1479.256f}, + {BASE_HORDE, 5565.642f, -2565.666f, 1481.635f}, + {BASE_HORDE, 5547.218f, -2574.589f, 1479.194f} +}; + +// used to inform the wave where to move +static const HyjalLocation aHyjalWaveMoveTo[] = +{ + {BASE_ALLY, 5018.654f, -1752.074f, 1322.203f}, + {BASE_HORDE, 5504.569f, -2688.489f, 1479.991f} +}; + +struct HyjalYells +{ + uint32 uiCreatureEntry; + YellType m_pYellType; // Used to determine the type of yell (attack, rally, etc) + int32 m_iTextId; // The text id to be yelled +}; + +static const HyjalYells aHyjalYell[] = +{ + {NPC_JAINA, ATTACKED, -1534000}, + {NPC_JAINA, ATTACKED, -1534001}, + {NPC_JAINA, INCOMING, -1534002}, + {NPC_JAINA, BEGIN, -1534003}, + {NPC_JAINA, RALLY, -1534004}, + {NPC_JAINA, RALLY, -1534005}, + {NPC_JAINA, FAILURE, -1534006}, + {NPC_JAINA, SUCCESS, -1534007}, + {NPC_JAINA, DEATH, -1534008}, + + {NPC_THRALL, ATTACKED, -1534009}, + {NPC_THRALL, ATTACKED, -1534010}, + {NPC_THRALL, INCOMING, -1534011}, + {NPC_THRALL, BEGIN, -1534012}, + {NPC_THRALL, RALLY, -1534013}, + {NPC_THRALL, RALLY, -1534014}, + {NPC_THRALL, FAILURE, -1534015}, + {NPC_THRALL, SUCCESS, -1534016}, + {NPC_THRALL, DEATH, -1534017} +}; + +struct HyjalWave +{ + uint32 m_auiMobEntry[MAX_WAVE_MOB]; // Stores Creature Entries to be summoned in Waves + uint32 m_uiWaveTimer; // The timer before the next wave is summoned + bool m_bIsBoss; // Simply used to inform the wave summoner that the next wave contains a boss to halt all waves after that +}; + +// Waves that will be summoned in the Alliance Base +static const HyjalWave aHyjalWavesAlliance[] = +{ + // Rage Winterchill Wave 1-8 + {{NPC_GHOUL, NPC_GHOUL, NPC_GHOUL, NPC_GHOUL, NPC_GHOUL, NPC_GHOUL, NPC_GHOUL, NPC_GHOUL, NPC_GHOUL, NPC_GHOUL, 0, 0, 0, 0, 0, 0, 0, 0}, 125000, false}, + {{NPC_GHOUL, NPC_GHOUL, NPC_GHOUL, NPC_GHOUL, NPC_GHOUL, NPC_GHOUL, NPC_GHOUL, NPC_GHOUL, NPC_GHOUL, NPC_GHOUL, NPC_CRYPT, NPC_CRYPT, 0, 0, 0, 0, 0, 0}, 125000, false}, + {{NPC_GHOUL, NPC_GHOUL, NPC_GHOUL, NPC_GHOUL, NPC_GHOUL, NPC_GHOUL, NPC_CRYPT, NPC_CRYPT, NPC_CRYPT, NPC_CRYPT, NPC_CRYPT, NPC_CRYPT, 0, 0, 0, 0, 0, 0}, 125000, false}, + {{NPC_GHOUL, NPC_GHOUL, NPC_GHOUL, NPC_GHOUL, NPC_GHOUL, NPC_GHOUL, NPC_CRYPT, NPC_CRYPT, NPC_CRYPT, NPC_CRYPT, NPC_NECRO, NPC_NECRO, 0, 0, 0, 0, 0, 0}, 125000, false}, + {{NPC_GHOUL, NPC_GHOUL, NPC_CRYPT, NPC_CRYPT, NPC_CRYPT, NPC_CRYPT, NPC_CRYPT, NPC_CRYPT, NPC_NECRO, NPC_NECRO, NPC_NECRO, NPC_NECRO, 0, 0, 0, 0, 0, 0}, 125000, false}, + {{NPC_GHOUL, NPC_GHOUL, NPC_GHOUL, NPC_GHOUL, NPC_GHOUL, NPC_GHOUL, NPC_ABOMI, NPC_ABOMI, NPC_ABOMI, NPC_ABOMI, NPC_ABOMI, NPC_ABOMI, 0, 0, 0, 0, 0, 0}, 125000, false}, + {{NPC_GHOUL, NPC_GHOUL, NPC_GHOUL, NPC_GHOUL, NPC_NECRO, NPC_NECRO, NPC_NECRO, NPC_NECRO, NPC_ABOMI, NPC_ABOMI, NPC_ABOMI, NPC_ABOMI, 0, 0, 0, 0, 0, 0}, 125000, false}, + {{NPC_GHOUL, NPC_GHOUL, NPC_GHOUL, NPC_GHOUL, NPC_GHOUL, NPC_GHOUL, NPC_CRYPT, NPC_CRYPT, NPC_CRYPT, NPC_CRYPT, NPC_ABOMI, NPC_ABOMI, NPC_NECRO, NPC_NECRO, 0, 0, 0, 0}, 140000, false}, + // All 8 Waves are summoned, summon Rage Winterchill, next few waves are for Anetheron + {{NPC_WINTERCHILL, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, 0, true}, + // Anetheron Wave 1-8 + {{NPC_GHOUL, NPC_GHOUL, NPC_GHOUL, NPC_GHOUL, NPC_GHOUL, NPC_GHOUL, NPC_GHOUL, NPC_GHOUL, NPC_GHOUL, NPC_GHOUL, 0, 0, 0, 0, 0, 0, 0, 0}, 125000, false}, + {{NPC_GHOUL, NPC_GHOUL, NPC_GHOUL, NPC_GHOUL, NPC_GHOUL, NPC_GHOUL, NPC_GHOUL, NPC_GHOUL, NPC_ABOMI, NPC_ABOMI, NPC_ABOMI, NPC_ABOMI, 0, 0, 0, 0, 0, 0}, 125000, false}, + {{NPC_GHOUL, NPC_GHOUL, NPC_GHOUL, NPC_GHOUL, NPC_CRYPT, NPC_CRYPT, NPC_CRYPT, NPC_CRYPT, NPC_NECRO, NPC_NECRO, NPC_NECRO, NPC_NECRO, 0, 0, 0, 0, 0, 0}, 125000, false}, + {{NPC_CRYPT, NPC_CRYPT, NPC_CRYPT, NPC_CRYPT, NPC_CRYPT, NPC_CRYPT, NPC_NECRO, NPC_NECRO, NPC_NECRO, NPC_NECRO, NPC_BANSH, NPC_BANSH, 0, 0, 0, 0, 0, 0}, 125000, false}, + {{NPC_GHOUL, NPC_GHOUL, NPC_GHOUL, NPC_GHOUL, NPC_GHOUL, NPC_GHOUL, NPC_BANSH, NPC_BANSH, NPC_BANSH, NPC_BANSH, NPC_NECRO, NPC_NECRO, 0, 0, 0, 0, 0, 0}, 125000, false}, + {{NPC_GHOUL, NPC_GHOUL, NPC_GHOUL, NPC_GHOUL, NPC_GHOUL, NPC_GHOUL, NPC_ABOMI, NPC_ABOMI, NPC_NECRO, NPC_NECRO, NPC_NECRO, NPC_NECRO, 0, 0, 0, 0, 0, 0}, 125000, false}, + {{NPC_GHOUL, NPC_GHOUL, NPC_CRYPT, NPC_CRYPT, NPC_CRYPT, NPC_CRYPT, NPC_ABOMI, NPC_ABOMI, NPC_ABOMI, NPC_ABOMI, NPC_BANSH, NPC_BANSH, 0, 0, 0, 0, 0, 0}, 125000, false}, + {{NPC_GHOUL, NPC_GHOUL, NPC_GHOUL, NPC_ABOMI, NPC_ABOMI, NPC_ABOMI, NPC_ABOMI, NPC_CRYPT, NPC_CRYPT, NPC_CRYPT, NPC_BANSH, NPC_BANSH, NPC_NECRO, NPC_NECRO, 0, 0, 0, 0}, 140000, false}, + // All 8 Waves are summoned, summon Anatheron + {{NPC_ANETHERON, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, 0, true} +}; + +// Waves that are summoned in the Horde base +static const HyjalWave aHyjalWavesHorde[] = +{ + // Kaz'Rogal Wave 1-8 + {{NPC_GHOUL, NPC_GHOUL, NPC_GHOUL, NPC_GHOUL, NPC_ABOMI, NPC_ABOMI, NPC_ABOMI, NPC_ABOMI, NPC_BANSH, NPC_BANSH, NPC_NECRO, NPC_NECRO, 0, 0, 0, 0, 0, 0}, 135000, false}, + {{NPC_GHOUL, NPC_GHOUL, NPC_GHOUL, NPC_GHOUL, NPC_GARGO, NPC_GARGO, NPC_GARGO, NPC_GARGO, NPC_GARGO, NPC_GARGO, NPC_GARGO, NPC_GARGO, NPC_GARGO, NPC_GARGO, 0, 0, 0, 0}, 165000, false}, + {{NPC_GHOUL, NPC_GHOUL, NPC_GHOUL, NPC_GHOUL, NPC_GHOUL, NPC_GHOUL, NPC_CRYPT, NPC_CRYPT, NPC_CRYPT, NPC_CRYPT, NPC_CRYPT, NPC_CRYPT, NPC_NECRO, NPC_NECRO, 0, 0, 0, 0}, 160000, false}, + {{NPC_CRYPT, NPC_CRYPT, NPC_CRYPT, NPC_CRYPT, NPC_CRYPT, NPC_CRYPT, NPC_GARGO, NPC_GARGO, NPC_GARGO, NPC_GARGO, NPC_GARGO, NPC_GARGO, NPC_NECRO, NPC_NECRO, 0, 0, 0, 0}, 165000, false}, + {{NPC_GHOUL, NPC_GHOUL, NPC_GHOUL, NPC_GHOUL, NPC_ABOMI, NPC_ABOMI, NPC_ABOMI, NPC_ABOMI, NPC_ABOMI, NPC_ABOMI, NPC_NECRO, NPC_NECRO, NPC_NECRO, NPC_NECRO, 0, 0, 0, 0}, 135000, false}, + {{NPC_GARGO, NPC_GARGO, NPC_GARGO, NPC_GARGO, NPC_GARGO, NPC_GARGO, NPC_GARGO, NPC_GARGO, NPC_FROST, 0, 0, 0, 0, 0, 0, 0, 0, 0}, 135000, false}, + {{NPC_GHOUL, NPC_GHOUL, NPC_GHOUL, NPC_GHOUL, NPC_GHOUL, NPC_GHOUL, NPC_ABOMI, NPC_ABOMI, NPC_ABOMI, NPC_ABOMI, NPC_FROST, 0, 0, 0, 0, 0, 0, 0}, 195000, false}, + {{NPC_GHOUL, NPC_GHOUL, NPC_GHOUL, NPC_GHOUL, NPC_GHOUL, NPC_GHOUL, NPC_CRYPT, NPC_CRYPT, NPC_ABOMI, NPC_ABOMI, NPC_ABOMI, NPC_ABOMI, NPC_NECRO, NPC_NECRO, NPC_BANSH, NPC_BANSH, 0, 0}, 225000, false}, + // All 8 Waves are summoned, summon Kaz'Rogal, next few waves are for Azgalor + {{NPC_KAZROGAL, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, 0, true}, + // Azgalor Wave 1-8 + {{NPC_ABOMI, NPC_ABOMI, NPC_ABOMI, NPC_ABOMI, NPC_ABOMI, NPC_ABOMI, NPC_NECRO, NPC_NECRO, NPC_NECRO, NPC_NECRO, NPC_NECRO, NPC_NECRO, 0, 0, 0, 0, 0, 0}, 135000, false}, + {{NPC_GHOUL, NPC_GHOUL, NPC_GHOUL, NPC_GHOUL, NPC_GHOUL, NPC_FROST, NPC_GARGO, NPC_GARGO, NPC_GARGO, NPC_GARGO, NPC_GARGO, NPC_GARGO, NPC_GARGO, NPC_GARGO, 0, 0, 0, 0}, 165000, false}, + {{NPC_GHOUL, NPC_GHOUL, NPC_GHOUL, NPC_GHOUL, NPC_GIANT, NPC_GIANT, NPC_GIANT, NPC_GIANT, NPC_GIANT, NPC_GIANT, NPC_GIANT, NPC_GIANT, 0, 0, 0, 0, 0, 0}, 160000, false}, + {{NPC_GIANT, NPC_GIANT, NPC_GIANT, NPC_GIANT, NPC_GIANT, NPC_GIANT, NPC_STALK, NPC_STALK, NPC_STALK, NPC_STALK, NPC_STALK, NPC_STALK, NPC_STALK, NPC_STALK, 0, 0, 0, 0}, 165000, false}, + {{NPC_STALK, NPC_STALK, NPC_STALK, NPC_STALK, NPC_STALK, NPC_STALK, NPC_ABOMI, NPC_ABOMI, NPC_ABOMI, NPC_ABOMI, NPC_NECRO, NPC_NECRO, NPC_NECRO, NPC_NECRO, 0, 0, 0, 0}, 135000, false}, + {{NPC_NECRO, NPC_NECRO, NPC_NECRO, NPC_NECRO, NPC_NECRO, NPC_NECRO, NPC_BANSH, NPC_BANSH, NPC_BANSH, NPC_BANSH, NPC_BANSH, NPC_BANSH, 0, 0, 0, 0, 0, 0}, 135000, false}, + {{NPC_GHOUL, NPC_GHOUL, NPC_CRYPT, NPC_CRYPT, NPC_STALK, NPC_STALK, NPC_GIANT, NPC_GIANT, NPC_GIANT, NPC_GIANT, NPC_GIANT, NPC_GIANT, NPC_GIANT, NPC_GIANT, 0, 0, 0, 0}, 195000, false}, + {{NPC_CRYPT, NPC_CRYPT, NPC_CRYPT, NPC_CRYPT, NPC_STALK, NPC_STALK, NPC_ABOMI, NPC_ABOMI, NPC_ABOMI, NPC_ABOMI, NPC_BANSH, NPC_BANSH, NPC_NECRO, NPC_NECRO, 0, 0, 0, 0}, 225000, false}, + // All 8 Waves are summoned, summon Azgalor + {{NPC_AZGALOR, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, 0, true} +}; + +void hyjalAI::Reset() +{ + // Timers + m_uiNextWaveTimer = 10000; + m_uiWaveMoveTimer = 15000; + m_uiRetreatTimer = 25000; + + // Misc + m_uiWaveCount = 0; + m_uiEnemyCount = 0; + + // Set base area based on creature entry + switch (m_creature->GetEntry()) + { + case NPC_JAINA: + m_uiBase = BASE_ALLY; + DoCastSpellIfCan(m_creature, SPELL_BRILLIANCE_AURA, CAST_TRIGGERED); + break; + case NPC_THRALL: + m_uiBase = BASE_HORDE; + break; + } + + // Bools + m_bIsEventInProgress = false; + m_bIsSummoningWaves = false; + + m_bIsRetreating = false; + m_bDebugMode = false; + + // Flags + m_creature->SetFlag(UNIT_NPC_FLAGS, UNIT_NPC_FLAG_GOSSIP); + + if (!m_pInstance) + return; + + // Reset World States + m_pInstance->DoUpdateWorldState(WORLD_STATE_WAVES, 0); + m_pInstance->DoUpdateWorldState(WORLD_STATE_ENEMY, 0); + m_pInstance->DoUpdateWorldState(WORLD_STATE_ENEMYCOUNT, 0); + + // Reset Instance Data for trash count + m_pInstance->SetData(TYPE_TRASH_COUNT, 0); + + m_bIsFirstBossDead = m_pInstance->GetData(m_uiBase ? TYPE_KAZROGAL : TYPE_WINTERCHILL) == DONE; + m_bIsSecondBossDead = m_pInstance->GetData(m_uiBase ? TYPE_AZGALOR : TYPE_ANETHERON) == DONE; +} + +bool hyjalAI::IsEventInProgress() const +{ + if (m_bIsEventInProgress) + return true; + + // The boss might still be around and alive + for (uint8 i = 0; i < 2; ++i) + { + Creature* pBoss = m_creature->GetMap()->GetCreature(m_aBossGuid[i]); + if (pBoss && pBoss->IsAlive()) + return true; + } + + return false; +} + +void hyjalAI::EnterEvadeMode() +{ + m_creature->RemoveAllAurasOnEvade(); + m_creature->DeleteThreatList(); + m_creature->CombatStop(true); + + if (m_creature->IsAlive()) + m_creature->GetMotionMaster()->MoveTargetedHome(); + + m_creature->SetLootRecipient(NULL); +} + +void hyjalAI::JustReachedHome() +{ + if (m_uiBase == BASE_ALLY) + DoCastSpellIfCan(m_creature, SPELL_BRILLIANCE_AURA, CAST_TRIGGERED); + + m_bIsFirstBossDead = m_uiBase ? m_pInstance->GetData(TYPE_KAZROGAL) == DONE : m_pInstance->GetData(TYPE_WINTERCHILL) == DONE; + m_bIsSecondBossDead = m_uiBase ? m_pInstance->GetData(TYPE_AZGALOR) == DONE : m_pInstance->GetData(TYPE_ANETHERON) == DONE; +} + +void hyjalAI::Aggro(Unit* /*who*/) +{ + for (uint8 i = 0; i < MAX_SPELL; ++i) + if (m_aSpells[i].m_uiCooldown) + m_uiSpellTimer[i] = m_aSpells[i].m_uiCooldown; + + DoTalk(ATTACKED); +} + +void hyjalAI::SpawnCreatureForWave(uint32 uiMobEntry) +{ + HyjalLocation const* pSpawn = NULL; + + uint32 uiMaxCount = countof(aHyjalSpawnLoc); + uint32 uiRandId = urand(1, uiMaxCount / 2); // unsafe, if array becomes uneven. + + uint32 uiJ = 0; + + for (uint32 i = 0; i < uiMaxCount; ++i) + { + if (aHyjalSpawnLoc[i].m_pBaseArea != (eBaseArea)m_uiBase) + continue; + + ++uiJ; + + if (uiJ == uiRandId) + { + pSpawn = &aHyjalSpawnLoc[i]; + break; + } + } + + if (pSpawn) + m_creature->SummonCreature(uiMobEntry, pSpawn->m_fX, pSpawn->m_fY, pSpawn->m_fZ, 0.0f, TEMPSUMMON_TIMED_OOC_OR_DEAD_DESPAWN, 120000); +} + +void hyjalAI::JustSummoned(Creature* pSummoned) +{ + // not interesting for us + if (pSummoned->GetEntry() == NPC_WATER_ELEMENTAL || pSummoned->GetEntry() == NPC_DIRE_WOLF) + return; + + // Increment Enemy Count to be used in World States and instance script + ++m_uiEnemyCount; + + HyjalLocation const* pMove = NULL; + + for (uint32 i = 0; i < countof(aHyjalWaveMoveTo); ++i) + { + if (aHyjalWaveMoveTo[i].m_pBaseArea != (eBaseArea)m_uiBase) + continue; + + pMove = &aHyjalWaveMoveTo[i]; + break; + } + + if (pMove) + { + float fX, fY, fZ; + pSummoned->GetRandomPoint(pMove->m_fX, pMove->m_fY, pMove->m_fZ, 10.0f, fX, fY, fZ); + + pSummoned->SetWalk(false); + pSummoned->GetMotionMaster()->MovePoint(0, fX, fY, fZ); + } + + // Check if creature is a boss. + if (pSummoned->IsWorldBoss()) + m_aBossGuid[!m_bIsFirstBossDead ? 0 : 1] = pSummoned->GetObjectGuid(); + else + lWaveMobGUIDList.push_back(pSummoned->GetObjectGuid()); +} + +void hyjalAI::SummonedCreatureJustDied(Creature* pSummoned) +{ + if (!pSummoned->IsWorldBoss()) // Only do stuff when bosses die + return; + + if (m_aBossGuid[0] == pSummoned->GetObjectGuid()) + { + DoTalk(INCOMING); + m_bIsFirstBossDead = true; + } + else if (m_aBossGuid[1] == pSummoned->GetObjectGuid()) + { + DoTalk(SUCCESS); + m_bIsSecondBossDead = true; + } + + m_bIsEventInProgress = false; + + m_creature->SetFlag(UNIT_NPC_FLAGS, UNIT_NPC_FLAG_GOSSIP); + + // Reset world state for enemies to disable it + m_pInstance->DoUpdateWorldState(WORLD_STATE_ENEMY, 0); + + m_creature->SetActiveObjectState(false); +} + +void hyjalAI::SummonNextWave() +{ + // 1 in 4 chance we give a rally yell. Not sure if the chance is offilike. + if (!urand(0, 3)) + DoTalk(RALLY); + + if (!m_pInstance) + return; + + HyjalWave const* pWaveData = m_uiBase ? &aHyjalWavesHorde[m_uiWaveCount] : &aHyjalWavesAlliance[m_uiWaveCount]; + + if (!pWaveData) + { + script_error_log("hyjalAI not able to obtain wavedata for SummonNextWave."); + return; + } + + m_uiEnemyCount = m_pInstance->GetData(TYPE_TRASH_COUNT); + + for (uint8 i = 0; i < MAX_WAVE_MOB; ++i) + { + if (pWaveData->m_auiMobEntry[i]) + SpawnCreatureForWave(pWaveData->m_auiMobEntry[i]); + } + + if (!pWaveData->m_bIsBoss) + { + uint32 stateValue = m_uiWaveCount + 1; + + if (m_bIsFirstBossDead) + stateValue -= MAX_WAVES; // Subtract 9 from it to give the proper wave number if we are greater than 8 + + // Set world state to our current wave number + m_pInstance->DoUpdateWorldState(WORLD_STATE_WAVES, stateValue); + // Enable world state + m_pInstance->DoUpdateWorldState(WORLD_STATE_ENEMY, 1); + + m_pInstance->SetData(TYPE_TRASH_COUNT, m_uiEnemyCount); // Send data for instance script to update count + + if (!m_bDebugMode) + m_uiNextWaveTimer = pWaveData->m_uiWaveTimer; + else + { + m_uiNextWaveTimer = 15000; + debug_log("SD2: HyjalAI: debug mode is enabled. Next Wave in 15 seconds"); + } + } + else + { + // Set world state for waves to 0 to disable it. + m_pInstance->DoUpdateWorldState(WORLD_STATE_WAVES, 0); + m_pInstance->DoUpdateWorldState(WORLD_STATE_ENEMY, 1); + + // Set World State for enemies invading to 1. + m_pInstance->DoUpdateWorldState(WORLD_STATE_ENEMYCOUNT, 1); + + m_bIsSummoningWaves = false; + } + + m_uiWaveMoveTimer = 15000; + ++m_uiWaveCount; +} + +void hyjalAI::StartEvent() +{ + if (!m_pInstance) + return; + + if (IsEventInProgress()) + return; + + DoTalk(BEGIN); + + m_bIsEventInProgress = true; + m_bIsSummoningWaves = true; + + m_uiNextWaveTimer = 10000; + + m_creature->RemoveFlag(UNIT_NPC_FLAGS, UNIT_NPC_FLAG_GOSSIP); + + m_pInstance->DoUpdateWorldState(WORLD_STATE_WAVES, 0); + m_pInstance->DoUpdateWorldState(WORLD_STATE_ENEMY, 0); + m_pInstance->DoUpdateWorldState(WORLD_STATE_ENEMYCOUNT, 0); + + m_creature->SetActiveObjectState(true); +} + +void hyjalAI::DoTalk(YellType pYellType) +{ + HyjalYells const* pYell = NULL; + + bool bGetNext = false; + + for (uint32 i = 0; i < countof(aHyjalYell); ++i) + { + if (aHyjalYell[i].uiCreatureEntry == m_creature->GetEntry() && aHyjalYell[i].m_pYellType == pYellType) + { + // this would not be safe unless we knew these had two entries in m_aYell + if (pYellType == ATTACKED || pYellType == RALLY) + { + if (!bGetNext && urand(0, 1)) + { + bGetNext = true; + continue; + } + } + + pYell = &aHyjalYell[i]; + break; + } + } + + if (pYell) + DoScriptText(pYell->m_iTextId, m_creature); +} + +void hyjalAI::SpellHitTarget(Unit* /*pTarget*/, const SpellEntry* /*pSpell*/) +{ + // TODO: this spell should cause misc mobs to despawn + // if (pSpell->Id == SPELL_MASS_TELEPORT && pTarget->GetTypeId() != TYPEID_PLAYER) + //{ + // despawn; + //} +} + +void hyjalAI::Retreat() +{ + // this will trigger ancient gem respawn + if (m_pInstance) + m_pInstance->SetData(TYPE_RETREAT, SPECIAL); + + DoCastSpellIfCan(m_creature, SPELL_MASS_TELEPORT); + + m_bIsRetreating = true; +} + +void hyjalAI::JustDied(Unit* /*pKiller*/) +{ + DoTalk(DEATH); + m_creature->SetActiveObjectState(false); + + // TODO: in case they die during boss encounter, then what? despawn boss? +} + +void hyjalAI::UpdateAI(const uint32 uiDiff) +{ + if (!m_bIsEventInProgress) + return; + + if (m_bIsSummoningWaves && m_pInstance) + { + if (m_uiWaveMoveTimer < uiDiff) + { + // Skip the master timer, and start next wave in 5. Clear the list, it should not be any here now. + if (!m_pInstance->GetData(TYPE_TRASH_COUNT)) + { + lWaveMobGUIDList.clear(); + m_uiNextWaveTimer = std::min(m_uiNextWaveTimer, (uint32)5000); + } + + for (GuidList::const_iterator itr = lWaveMobGUIDList.begin(); itr != lWaveMobGUIDList.end(); ++itr) + { + if (Creature* pTemp = m_pInstance->instance->GetCreature(*itr)) + { + if (!pTemp->IsAlive() || pTemp->getVictim()) + continue; + + pTemp->SetWalk(false); + pTemp->GetMotionMaster()->MovePoint(1, m_creature->GetPositionX(), m_creature->GetPositionY(), m_creature->GetPositionZ()); + } + } + m_uiWaveMoveTimer = 10000; + } + else + m_uiWaveMoveTimer -= uiDiff; + + if (m_uiNextWaveTimer < uiDiff) + SummonNextWave(); + else + m_uiNextWaveTimer -= uiDiff; + } + + if (!m_creature->SelectHostileTarget() || !m_creature->getVictim()) + return; + + for (uint8 i = 0; i < MAX_SPELL; ++i) + { + if (m_aSpells[i].m_uiSpellId) + { + if (m_uiSpellTimer[i] < uiDiff) + { + if (m_creature->IsNonMeleeSpellCasted(false)) + m_creature->InterruptNonMeleeSpells(false); + + Unit* pTarget = NULL; + + switch (m_aSpells[i].m_pType) + { + case TARGETTYPE_SELF: pTarget = m_creature; break; + case TARGETTYPE_RANDOM: pTarget = m_creature->SelectAttackingTarget(ATTACKING_TARGET_RANDOM, 0); break; + case TARGETTYPE_VICTIM: pTarget = m_creature->getVictim(); break; + } + + if (pTarget) + { + DoCastSpellIfCan(pTarget, m_aSpells[i].m_uiSpellId); + m_uiSpellTimer[i] = m_aSpells[i].m_uiCooldown; + } + } + else + m_uiSpellTimer[i] -= uiDiff; + } + } + + DoMeleeAttackIfReady(); +} + +void hyjalAI::JustRespawned() +{ + Reset(); +} diff --git a/src/modules/SD2/scripts/kalimdor/caverns_of_time/hyjal_summit/hyjalAI.h b/src/modules/SD2/scripts/kalimdor/caverns_of_time/hyjal_summit/hyjalAI.h new file mode 100644 index 000000000..ebb578eb9 --- /dev/null +++ b/src/modules/SD2/scripts/kalimdor/caverns_of_time/hyjal_summit/hyjalAI.h @@ -0,0 +1,142 @@ +/* Copyright (C) 2006 - 2013 ScriptDev2 + * This program is free software licensed under GPL version 2 + * Please see the included DOCS/LICENSE.TXT for more information */ + +#ifndef SC_HYJALAI_H +#define SC_HYJALAI_H + +#include "hyjal.h" + +enum eBaseArea +{ + BASE_ALLY = 0, + BASE_HORDE = 1 +}; + +enum eMisc +{ + MAX_SPELL = 3, + MAX_WAVES = 9, + MAX_WAVE_MOB = 18, + + ITEM_TEAR_OF_GODDESS = 24494 +}; + +enum eSpell +{ + SPELL_MASS_TELEPORT = 16807, + + // Spells for Jaina + SPELL_BRILLIANCE_AURA = 31260, + SPELL_BLIZZARD = 31266, + SPELL_PYROBLAST = 31263, + SPELL_SUMMON_ELEMENTALS = 31264, + + // Thrall spells + SPELL_CHAIN_LIGHTNING = 31330, + SPELL_FERAL_SPIRIT = 31331 +}; + +enum TargetType // Used in the spell cast system for the AI +{ + TARGETTYPE_SELF = 0, + TARGETTYPE_RANDOM = 1, + TARGETTYPE_VICTIM = 2, +}; + +enum YellType +{ + ATTACKED = 0, // Used when attacked and set in combat + BEGIN = 1, // Used when the event is begun + INCOMING = 2, // Used to warn the raid that another wave phase is coming + RALLY = 3, // Used to rally the raid and warn that the next wave has been summoned + FAILURE = 4, // Used when raid has failed (unsure where to place) + SUCCESS = 5, // Used when the raid has sucessfully defeated a wave phase + DEATH = 6, // Used on death +}; + +struct hyjalAI : public ScriptedAI +{ + hyjalAI(Creature* pCreature) : ScriptedAI(pCreature) + { + memset(m_aSpells, 0, sizeof(m_aSpells)); + m_pInstance = (ScriptedInstance*)pCreature->GetInstanceData(); + 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 Reset() override; + + // Send creature back to spawn location and evade. + void EnterEvadeMode() override; + + // Called when creature reached home location after evade. + void JustReachedHome() override; + + // Used to reset cooldowns for our spells and to inform the raid that we're under attack + void Aggro(Unit* pWho) override; + + // Called to summon waves, check for boss deaths and to cast our spells. + void UpdateAI(const uint32 uiDiff) override; + + // Called on death, informs the raid that they have failed. + void JustDied(Unit* /*pKiller*/) override; + + void JustRespawned() override; + + // "Teleport" all friendly creatures away from the base. + void Retreat(); + + // Summons a creature for that wave in that base + void SpawnCreatureForWave(uint32 uiMobEntry); + + void JustSummoned(Creature*) override; + + void SummonedCreatureJustDied(Creature* pSummoned) override; + + // Summons the next wave, calls SummonCreature + void SummonNextWave(); + + // Begins the event by gossip click + void StartEvent(); + + // Searches for the appropriate yell and sound and uses it to inform the raid of various things + void DoTalk(YellType pYellType); + + // Used to filter who to despawn after mass teleport + void SpellHitTarget(Unit*, const SpellEntry*) override; + + bool IsEventInProgress() const; + + public: + ScriptedInstance* m_pInstance; + + ObjectGuid m_aBossGuid[2]; + + uint32 m_uiNextWaveTimer; + uint32 m_uiWaveCount; + uint32 m_uiWaveMoveTimer; + uint32 m_uiEnemyCount; + uint32 m_uiRetreatTimer; + uint32 m_uiBase; + + bool m_bIsEventInProgress; + bool m_bIsFirstBossDead; + bool m_bIsSecondBossDead; + bool m_bIsSummoningWaves; + bool m_bIsRetreating; + bool m_bDebugMode; + + struct sSpells + { + uint32 m_uiSpellId; + uint32 m_uiCooldown; + TargetType m_pType; + } m_aSpells[MAX_SPELL]; + + private: + uint32 m_uiSpellTimer[MAX_SPELL]; + GuidList lWaveMobGUIDList; +}; + +#endif diff --git a/src/modules/SD2/scripts/kalimdor/caverns_of_time/hyjal_summit/instance_hyjal.cpp b/src/modules/SD2/scripts/kalimdor/caverns_of_time/hyjal_summit/instance_hyjal.cpp new file mode 100644 index 000000000..c6454eca9 --- /dev/null +++ b/src/modules/SD2/scripts/kalimdor/caverns_of_time/hyjal_summit/instance_hyjal.cpp @@ -0,0 +1,245 @@ +/* Copyright (C) 2006 - 2013 ScriptDev2 + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along 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 "hyjal.h" + +/* Battle of Mount Hyjal encounters: +0 - Rage Winterchill event +1 - Anetheron event +2 - Kaz'rogal event +3 - Azgalor event +4 - Archimonde event +*/ + +instance_mount_hyjal::instance_mount_hyjal(Map* pMap) : ScriptedInstance(pMap), + m_uiTrashCount(0) +{ + Initialize(); +} + +void instance_mount_hyjal::Initialize() +{ + memset(&m_auiEncounter, 0, sizeof(m_auiEncounter)); +} + +bool instance_mount_hyjal::IsEncounterInProgress() const +{ + for (uint8 i = 0; i < MAX_ENCOUNTER; ++i) + if (m_auiEncounter[i] == IN_PROGRESS) return true; + + return false; +} + +void instance_mount_hyjal::OnPlayerEnter(Player* /*pPlayer*/) +{ + if (GetData(TYPE_AZGALOR) == DONE) + DoSpawnArchimonde(); +} + +void instance_mount_hyjal::OnCreatureCreate(Creature* pCreature) +{ + if (pCreature->GetEntry() == NPC_ARCHIMONDE) + m_mNpcEntryGuidStore[NPC_ARCHIMONDE] = pCreature->GetObjectGuid(); +} + +void instance_mount_hyjal::OnObjectCreate(GameObject* pGo) +{ + if (pGo->GetEntry() == GO_ANCIENT_GEM) + lAncientGemGUIDList.push_back(pGo->GetObjectGuid()); +} + +void instance_mount_hyjal::OnCreatureEnterCombat(Creature* pCreature) +{ + switch (pCreature->GetEntry()) + { + case NPC_WINTERCHILL: SetData(TYPE_WINTERCHILL, IN_PROGRESS); break; + case NPC_ANETHERON: SetData(TYPE_ANETHERON, IN_PROGRESS); break; + case NPC_KAZROGAL: SetData(TYPE_KAZROGAL, IN_PROGRESS); break; + case NPC_AZGALOR: SetData(TYPE_AZGALOR, IN_PROGRESS); break; + case NPC_ARCHIMONDE: SetData(TYPE_ARCHIMONDE, IN_PROGRESS); break; + } +} + +void instance_mount_hyjal::OnCreatureEvade(Creature* pCreature) +{ + switch (pCreature->GetEntry()) + { + case NPC_WINTERCHILL: SetData(TYPE_WINTERCHILL, FAIL); break; + case NPC_ANETHERON: SetData(TYPE_ANETHERON, FAIL); break; + case NPC_KAZROGAL: SetData(TYPE_KAZROGAL, FAIL); break; + case NPC_AZGALOR: SetData(TYPE_AZGALOR, FAIL); break; + } +} + +void instance_mount_hyjal::OnCreatureDeath(Creature* pCreature) +{ + switch (pCreature->GetEntry()) + { + case NPC_WINTERCHILL: SetData(TYPE_WINTERCHILL, DONE); break; + case NPC_ANETHERON: SetData(TYPE_ANETHERON, DONE); break; + case NPC_KAZROGAL: SetData(TYPE_KAZROGAL, DONE); break; + case NPC_AZGALOR: SetData(TYPE_AZGALOR, DONE); break; + case NPC_ARCHIMONDE: SetData(TYPE_ARCHIMONDE, DONE); break; + + // Trash Mobs summoned in waves + case NPC_NECRO: + case NPC_ABOMI: + case NPC_GHOUL: + case NPC_BANSH: + case NPC_CRYPT: + case NPC_GARGO: + case NPC_FROST: + case NPC_GIANT: + case NPC_STALK: + // Decrease counter, and update world-state + if (m_uiTrashCount) + { + --m_uiTrashCount; + DoUpdateWorldState(WORLD_STATE_ENEMYCOUNT, m_uiTrashCount); + } + break; + } +} + +void instance_mount_hyjal::SetData(uint32 uiType, uint32 uiData) +{ + switch (uiType) + { + case TYPE_WINTERCHILL: + case TYPE_ANETHERON: + case TYPE_KAZROGAL: + m_auiEncounter[uiType] = uiData; + break; + case TYPE_AZGALOR: + m_auiEncounter[uiType] = uiData; + if (uiData == DONE) + DoSpawnArchimonde(); + break; + case TYPE_ARCHIMONDE: + m_auiEncounter[uiType] = uiData; + break; + + case TYPE_TRASH_COUNT: + m_uiTrashCount = uiData; + DoUpdateWorldState(WORLD_STATE_ENEMYCOUNT, m_uiTrashCount); + break; + + case TYPE_RETREAT: + if (uiData == SPECIAL) + { + if (!lAncientGemGUIDList.empty()) + { + for (GuidList::const_iterator itr = lAncientGemGUIDList.begin(); itr != lAncientGemGUIDList.end(); ++itr) + { + // don't know how long it expected + DoRespawnGameObject(*itr, DAY); + } + } + } + break; + } + + debug_log("SD2: Instance Hyjal: Instance data updated for event %u (Data=%u)", uiType, uiData); + + 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_strSaveData = saveStream.str(); + + SaveToDB(); + OUT_SAVE_INST_DATA_COMPLETE; + } +} + +void instance_mount_hyjal::DoSpawnArchimonde() +{ + // Don't spawn if already killed + if (GetData(TYPE_ARCHIMONDE) == DONE) + return; + + // Don't spawn him twice + if (GetSingleCreatureFromStorage(NPC_ARCHIMONDE, true)) + return; + + // Summon Archimonde + if (Player* pPlayer = GetPlayerInMap()) + pPlayer->SummonCreature(NPC_ARCHIMONDE, aArchimondeSpawnLoc[0], aArchimondeSpawnLoc[1], aArchimondeSpawnLoc[2], aArchimondeSpawnLoc[3], TEMPSUMMON_DEAD_DESPAWN, 0); +} + +uint32 instance_mount_hyjal::GetData(uint32 uiType) const +{ + switch (uiType) + { + case TYPE_WINTERCHILL: + case TYPE_ANETHERON: + case TYPE_KAZROGAL: + case TYPE_AZGALOR: + case TYPE_ARCHIMONDE: + return m_auiEncounter[uiType]; + case TYPE_TRASH_COUNT: + return m_uiTrashCount; + default: + return 0; + } +} + +void instance_mount_hyjal::Load(const char* chrIn) +{ + if (!chrIn) + { + 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]; + + 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* pNewScript; + + pNewScript = new Script; + pNewScript->Name = "instance_hyjal"; + pNewScript->GetInstanceData = &GetInstanceData_instance_mount_hyjal; + pNewScript->RegisterSelf(); +} diff --git a/src/modules/SD2/scripts/kalimdor/caverns_of_time/old_hillsbrad/instance_old_hillsbrad.cpp b/src/modules/SD2/scripts/kalimdor/caverns_of_time/old_hillsbrad/instance_old_hillsbrad.cpp new file mode 100644 index 000000000..148a2e879 --- /dev/null +++ b/src/modules/SD2/scripts/kalimdor/caverns_of_time/old_hillsbrad/instance_old_hillsbrad.cpp @@ -0,0 +1,369 @@ +/* Copyright (C) 2006 - 2013 ScriptDev2 + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along 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: Thrall reset on server restart is not supported, because of core limitation. +SDCategory: Caverns of Time, Old Hillsbrad Foothills +EndScriptData */ + +#include "precompiled.h" +#include "old_hillsbrad.h" + +instance_old_hillsbrad::instance_old_hillsbrad(Map* pMap) : ScriptedInstance(pMap), + m_uiBarrelCount(0), + m_uiThrallEventCount(0), + m_uiThrallResetTimer(0) +{ + Initialize(); +} + +void instance_old_hillsbrad::Initialize() +{ + memset(&m_auiEncounter, 0, sizeof(m_auiEncounter)); +} + +void instance_old_hillsbrad::OnPlayerEnter(Player* pPlayer) +{ + // ToDo: HandleThrallRelocation(); + // Note: this isn't yet supported because of the grid load / unload + + // Spawn Drake if necessary + if (GetData(TYPE_DRAKE) == DONE || GetData(TYPE_BARREL_DIVERSION) != DONE) + return; + + if (GetSingleCreatureFromStorage(NPC_DRAKE, true)) + return; + + pPlayer->SummonCreature(NPC_DRAKE, aDrakeSummonLoc[0], aDrakeSummonLoc[1], aDrakeSummonLoc[2], aDrakeSummonLoc[3], TEMPSUMMON_DEAD_DESPAWN, 0); +} + +void instance_old_hillsbrad::OnCreatureCreate(Creature* pCreature) +{ + switch (pCreature->GetEntry()) + { + case NPC_THRALL: + case NPC_TARETHA: + case NPC_EROZION: + case NPC_ARMORER: + case NPC_TARREN_MILL_PROTECTOR: + case NPC_TARREN_MILL_LOOKOUT: + case NPC_YOUNG_BLANCHY: + case NPC_DRAKE: + case NPC_SKARLOC: + case NPC_EPOCH: + m_mNpcEntryGuidStore[pCreature->GetEntry()] = pCreature->GetObjectGuid(); + break; + case NPC_ORC_PRISONER: + // Sort the orcs which are inside the houses + if (pCreature->GetPositionZ() > 53.4f) + { + if (pCreature->GetPositionY() > 150.0f) + m_lLeftPrisonersList.push_back(pCreature->GetObjectGuid()); + else + m_lRightPrisonersList.push_back(pCreature->GetObjectGuid()); + } + break; + } +} + +void instance_old_hillsbrad::OnCreatureDeath(Creature* pCreature) +{ + switch (pCreature->GetEntry()) + { + case NPC_DRAKE: SetData(TYPE_DRAKE, DONE); break; + case NPC_SKARLOC: SetData(TYPE_SKARLOC, DONE); break; + case NPC_EPOCH: SetData(TYPE_EPOCH, DONE); break; + } +} + +void instance_old_hillsbrad::OnCreatureEnterCombat(Creature* pCreature) +{ + switch (pCreature->GetEntry()) + { + case NPC_DRAKE: + SetData(TYPE_DRAKE, IN_PROGRESS); + DoUpdateWorldState(WORLD_STATE_OH, 0); + break; + case NPC_SKARLOC: SetData(TYPE_SKARLOC, IN_PROGRESS); break; + case NPC_EPOCH: SetData(TYPE_EPOCH, IN_PROGRESS); break; + } +} + +void instance_old_hillsbrad::OnCreatureEvade(Creature* pCreature) +{ + switch (pCreature->GetEntry()) + { + case NPC_DRAKE: SetData(TYPE_DRAKE, FAIL); break; + case NPC_SKARLOC: SetData(TYPE_SKARLOC, FAIL); break; + case NPC_EPOCH: SetData(TYPE_EPOCH, FAIL); break; + } +} + +void instance_old_hillsbrad::OnObjectCreate(GameObject* pGo) +{ + if (pGo->GetEntry() == GO_ROARING_FLAME) + m_lRoaringFlamesList.push_back(pGo->GetObjectGuid()); + else if (pGo->GetEntry() == GO_PRISON_DOOR) + m_mGoEntryGuidStore[GO_PRISON_DOOR] = pGo->GetObjectGuid(); +} + +void instance_old_hillsbrad::HandleThrallRelocation() +{ + // reset instance data + SetData(TYPE_THRALL_EVENT, IN_PROGRESS); + + if (Creature* pThrall = GetSingleCreatureFromStorage(NPC_THRALL)) + { + debug_log("SD2: Instance Old Hillsbrad: Thrall relocation"); + + if (!pThrall->IsAlive()) + pThrall->Respawn(); + + // epoch failed, reloc to inn + if (GetData(TYPE_ESCORT_INN) == DONE) + pThrall->GetMap()->CreatureRelocation(pThrall, 2660.57f, 659.173f, 61.9370f, 5.76f); + // barn to inn failed, reloc to barn + else if (GetData(TYPE_ESCORT_BARN) == DONE) + pThrall->GetMap()->CreatureRelocation(pThrall, 2486.91f, 626.356f, 58.0761f, 4.66f); + // keep to barn failed, reloc to keep + else if (GetData(TYPE_SKARLOC) == DONE) + pThrall->GetMap()->CreatureRelocation(pThrall, 2063.40f, 229.509f, 64.4883f, 2.23f); + // prison to keep failed, reloc to prison + else + pThrall->GetMap()->CreatureRelocation(pThrall, 2231.89f, 119.95f, 82.2979f, 4.21f); + } +} + +void instance_old_hillsbrad::SetData(uint32 uiType, uint32 uiData) +{ + switch (uiType) + { + case TYPE_BARREL_DIVERSION: + m_auiEncounter[uiType] = uiData; + if (uiData == IN_PROGRESS) + { + if (m_uiBarrelCount >= MAX_BARRELS) + return; + + // Update barrels used and world state + ++m_uiBarrelCount; + DoUpdateWorldState(WORLD_STATE_OH, m_uiBarrelCount); + + debug_log("SD2: Instance Old Hillsbrad: go_barrel_old_hillsbrad count %u", m_uiBarrelCount); + + // Set encounter to done, and spawn Liutenant Drake + if (m_uiBarrelCount == MAX_BARRELS) + { + UpdateLodgeQuestCredit(); + + if (Player* pPlayer = GetPlayerInMap()) + { + pPlayer->SummonCreature(NPC_DRAKE, aDrakeSummonLoc[0], aDrakeSummonLoc[1], aDrakeSummonLoc[2], aDrakeSummonLoc[3], TEMPSUMMON_DEAD_DESPAWN, 0); + + // set the houses on fire + for (GuidList::const_iterator itr = m_lRoaringFlamesList.begin(); itr != m_lRoaringFlamesList.end(); ++itr) + DoRespawnGameObject(*itr, 30 * MINUTE); + + // move the orcs outside the houses + float fX, fY, fZ; + for (GuidList::const_iterator itr = m_lRightPrisonersList.begin(); itr != m_lRightPrisonersList.end(); ++itr) + { + if (Creature* pOrc = instance->GetCreature(*itr)) + { + pOrc->GetRandomPoint(afInstanceLoc[0][0], afInstanceLoc[0][1], afInstanceLoc[0][2], 10.0f, fX, fY, fZ); + pOrc->SetWalk(false); + pOrc->GetMotionMaster()->MovePoint(0, fX, fY, fZ); + } + } + for (GuidList::const_iterator itr = m_lLeftPrisonersList.begin(); itr != m_lLeftPrisonersList.end(); ++itr) + { + if (Creature* pOrc = instance->GetCreature(*itr)) + { + pOrc->GetRandomPoint(afInstanceLoc[1][0], afInstanceLoc[1][1], afInstanceLoc[1][2], 10.0f, fX, fY, fZ); + pOrc->SetWalk(false); + pOrc->GetMotionMaster()->MovePoint(0, fX, fY, fZ); + } + } + } + else + debug_log("SD2: Instance Old Hillsbrad: SetData (Type: %u Data %u) cannot find any pPlayer.", uiType, uiData); + + SetData(TYPE_BARREL_DIVERSION, DONE); + } + } + break; + case TYPE_THRALL_EVENT: + // nothing to do if already done and thrall respawn + if (GetData(TYPE_THRALL_EVENT) == DONE) + return; + m_auiEncounter[uiType] = uiData; + if (uiData == FAIL) + { + // despawn the bosses if necessary + if (Creature* pSkarloc = GetSingleCreatureFromStorage(NPC_SKARLOC, true)) + pSkarloc->ForcedDespawn(); + if (Creature* pEpoch = GetSingleCreatureFromStorage(NPC_EPOCH, true)) + pEpoch->ForcedDespawn(); + + if (m_uiThrallEventCount <= MAX_WIPE_COUNTER) + { + ++m_uiThrallEventCount; + debug_log("SD2: Instance Old Hillsbrad: Thrall event failed %u times.", m_uiThrallEventCount); + + // reset Thrall on timer + m_uiThrallResetTimer = 30000; + } + // If we already respawned Thrall too many times, the event is failed for good + else if (m_uiThrallEventCount > MAX_WIPE_COUNTER) + debug_log("SD2: Instance Old Hillsbrad: Thrall event failed %u times. Reset instance required.", m_uiThrallEventCount); + } + break; + case TYPE_DRAKE: + case TYPE_SKARLOC: + case TYPE_ESCORT_BARN: + case TYPE_ESCORT_INN: + case TYPE_EPOCH: + m_auiEncounter[uiType] = uiData; + debug_log("SD2: Instance Old Hillsbrad: Thrall event type %u adjusted to data %u.", uiType, uiData); + 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_strInstData = saveStream.str(); + + SaveToDB(); + OUT_SAVE_INST_DATA_COMPLETE; + } +} + +uint32 instance_old_hillsbrad::GetData(uint32 uiType) const +{ + if (uiType < MAX_ENCOUNTER) + return m_auiEncounter[uiType]; + + return 0; +} + +void instance_old_hillsbrad::Load(const char* chrIn) +{ + if (!chrIn) + { + 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]; + + for (uint8 i = 0; i < MAX_ENCOUNTER; ++i) + { + if (m_auiEncounter[i] == IN_PROGRESS) + m_auiEncounter[i] = NOT_STARTED; + } + + // custom reload - if the escort event or the Epoch event are not done, then reset the escort + // this is done, because currently we cannot handle Thrall relocation on server reset + if (m_auiEncounter[5] != DONE) + { + m_auiEncounter[2] = NOT_STARTED; + m_auiEncounter[3] = NOT_STARTED; + m_auiEncounter[4] = NOT_STARTED; + } + + OUT_LOAD_INST_DATA_COMPLETE; +} + +void instance_old_hillsbrad::UpdateLodgeQuestCredit() +{ + Map::PlayerList const& players = instance->GetPlayers(); + + if (!players.isEmpty()) + { + for (Map::PlayerList::const_iterator itr = players.begin(); itr != players.end(); ++itr) + { + if (Player* pPlayer = itr->getSource()) + pPlayer->KilledMonsterCredit(NPC_LODGE_QUEST_TRIGGER); + } + } +} + +void instance_old_hillsbrad::Update(uint32 uiDiff) +{ + if (m_uiThrallResetTimer) + { + if (m_uiThrallResetTimer <= uiDiff) + { + HandleThrallRelocation(); + m_uiThrallResetTimer = 0; + } + else + m_uiThrallResetTimer -= uiDiff; + } +} + +InstanceData* GetInstanceData_instance_old_hillsbrad(Map* pMap) +{ + return new instance_old_hillsbrad(pMap); +} + +bool ProcessEventId_event_go_barrel_old_hillsbrad(uint32 /*uiEventId*/, Object* pSource, Object* pTarget, bool bIsStart) +{ + if (bIsStart && pSource->GetTypeId() == TYPEID_PLAYER) + { + if (instance_old_hillsbrad* pInstance = (instance_old_hillsbrad*)((Player*)pSource)->GetInstanceData()) + { + if (pInstance->GetData(TYPE_BARREL_DIVERSION) == DONE) + return true; + + pInstance->SetData(TYPE_BARREL_DIVERSION, IN_PROGRESS); + + // Don't allow players to use same object twice + if (pTarget->GetTypeId() == TYPEID_GAMEOBJECT) + ((GameObject*)pTarget)->SetFlag(GAMEOBJECT_FLAGS, GO_FLAG_NO_INTERACT); + + return true; + } + } + return false; +} + +void AddSC_instance_old_hillsbrad() +{ + Script* pNewScript; + + pNewScript = new Script; + pNewScript->Name = "instance_old_hillsbrad"; + pNewScript->GetInstanceData = &GetInstanceData_instance_old_hillsbrad; + pNewScript->RegisterSelf(); + + pNewScript = new Script; + pNewScript->Name = "event_go_barrel_old_hillsbrad"; + pNewScript->pProcessEventId = &ProcessEventId_event_go_barrel_old_hillsbrad; + pNewScript->RegisterSelf(); +} diff --git a/src/modules/SD2/scripts/kalimdor/caverns_of_time/old_hillsbrad/old_hillsbrad.cpp b/src/modules/SD2/scripts/kalimdor/caverns_of_time/old_hillsbrad/old_hillsbrad.cpp new file mode 100644 index 000000000..47db84b44 --- /dev/null +++ b/src/modules/SD2/scripts/kalimdor/caverns_of_time/old_hillsbrad/old_hillsbrad.cpp @@ -0,0 +1,1339 @@ +/* Copyright (C) 2006 - 2013 ScriptDev2 + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along 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: 90 +SDComment: Quest support: 10283, 10284. All friendly NPC's. Thrall escort event is complete, possible a few details are still missing. +SDCategory: Caverns of Time, Old Hillsbrad Foothills +EndScriptData */ + +/* ContentData +npc_erozion +npc_thrall_old_hillsbrad +npc_taretha +EndContentData */ + +#include "precompiled.h" +#include "old_hillsbrad.h" +#include "escort_ai.h" + +/*###### +## npc_erozion +######*/ + +enum +{ + GOSSIP_ITEM_NEED_BOMBS = -3560001, + TEXT_ID_DEFAULT = 9778, + TEXT_ID_GOT_ITEM = 9515, + + ITEM_ENTRY_BOMBS = 25853, +}; + +bool GossipHello_npc_erozion(Player* pPlayer, Creature* pCreature) +{ + if (pCreature->IsQuestGiver()) + pPlayer->PrepareQuestMenu(pCreature->GetObjectGuid()); + + ScriptedInstance* pInstance = (ScriptedInstance*)pCreature->GetInstanceData(); + + if (pInstance && pInstance->GetData(TYPE_BARREL_DIVERSION) != DONE && !pPlayer->HasItemCount(ITEM_ENTRY_BOMBS, 1)) + pPlayer->ADD_GOSSIP_ITEM_ID(GOSSIP_ICON_CHAT, GOSSIP_ITEM_NEED_BOMBS, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 1); + + // Need info, should have option to teleport or not + /*if (!pPlayer->GetQuestRewardStatus(QUEST_ENTRY_RETURN) && pPlayer->GetQuestStatus(QUEST_ENTRY_RETURN) == QUEST_STATUS_COMPLETE) + pPlayer->ADD_GOSSIP_ITEM(GOSSIP_ICON_CHAT, "[PH] Teleport please, i'm tired.", GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF+2);*/ + + pPlayer->SEND_GOSSIP_MENU(TEXT_ID_DEFAULT, pCreature->GetObjectGuid()); + + return true; +} + +bool GossipSelect_npc_erozion(Player* pPlayer, Creature* pCreature, uint32 /*uiSender*/, uint32 uiAction) +{ + if (uiAction == GOSSIP_ACTION_INFO_DEF + 1) + { + if (Item* pItem = pPlayer->StoreNewItemInInventorySlot(ITEM_ENTRY_BOMBS, 1)) + pPlayer->SendNewItem(pItem, 1, true, false); + + pPlayer->SEND_GOSSIP_MENU(TEXT_ID_GOT_ITEM, pCreature->GetObjectGuid()); + } + + if (uiAction == GOSSIP_ACTION_INFO_DEF + 2) + pPlayer->CLOSE_GOSSIP_MENU(); + + return true; +} + +/*###### +## npc_thrall_old_hillsbrad +######*/ + +enum +{ + // Thrall texts - part I + SAY_TH_START_EVENT_PART_1 = -1560023, + SAY_ARMORER_CALL_GUARDS = -1560003, + SAY_TH_KILL_ARMORER = -1560050, + SAY_TH_ARMORY_1 = -1560024, + SAY_TH_ARMORY_2 = -1560005, + SAY_TH_SKARLOC_MEET = -1560025, + SAY_SKARLOC_ENTER = -1560000, + SAY_TH_SKARLOC_TAUNT = -1560026, + + // Thrall texts - part II + SAY_TH_START_EVENT_PART2 = -1560027, + SAY_TH_MOUNTS_UP = -1560028, + EMOTE_TH_STARTLE_HORSE = -1560006, + + // Thrall texts part III (barn) + SAY_LOOKOUT_BARN_1 = -1560007, + SAY_PROTECTOR_BARN_2 = -1560008, + EMOTE_TH_CALM_HORSE = -1560009, + SAY_PROTECTOR_BARN_3 = -1560010, + SAY_TH_HEAD_TOWN = -1560011, + + // Thrall texts part III (church) + SAY_TH_CHURCH_ENTER = -1560012, + SAY_LOOKOUT_CHURCH = -1560016, + SAY_TH_CHURCH_END = -1560029, + + // Thrall texts part III (inn) + SAY_LOOKOUT_INN = -1560017, + SAY_TA_ESCAPED = -1560049, + SAY_TH_MEET_TARETHA = -1560030, + + SAY_EPOCH_ENTER1 = -1560013, + SAY_TH_EPOCH_WONDER = -1560031, + SAY_EPOCH_ENTER2 = -1560014, + SAY_TH_EPOCH_KILL_TARETHA = -1560032, + SAY_EPOCH_ENTER3 = -1560015, + + // infinite dragons texts + SAY_INFINITE_DRAGON_AGGRO_1 = -1560004, + SAY_INFINITE_DRAGON_AGGRO_2 = -1560018, + SAY_INFINITE_DRAGON_AGGRO_3 = -1560019, + SAY_INFINITE_DRAGON_AGGRO_4 = -1560020, + + // Thrall texts - misc + SAY_TH_RANDOM_LOW_HP1 = -1560034, + SAY_TH_RANDOM_LOW_HP2 = -1560035, + + SAY_TH_RANDOM_DIE1 = -1560036, + SAY_TH_RANDOM_DIE2 = -1560037, + + SAY_TH_RANDOM_AGGRO1 = -1560038, + SAY_TH_RANDOM_AGGRO2 = -1560039, + SAY_TH_RANDOM_AGGRO3 = -1560040, + SAY_TH_RANDOM_AGGRO4 = -1560041, + + SAY_TH_RANDOM_KILL1 = -1560042, + SAY_TH_RANDOM_KILL2 = -1560043, + SAY_TH_RANDOM_KILL3 = -1560044, + + SAY_TH_LEAVE_COMBAT1 = -1560045, + SAY_TH_LEAVE_COMBAT2 = -1560046, + SAY_TH_LEAVE_COMBAT3 = -1560047, + + // reset texts + SAY_ERONZION_RESET_THRALL = -1560001, + SAY_ERONZION_RESET_LAST = -1560002, + + // gossip - start item + GOSSIP_ITEM_START = -3560000, // "We are ready to get you out of here, Thrall" + TEXT_ID_START = 9568, + + // gossip - after Skarloc items + GOSSIP_ITEM_SKARLOC_1 = -3560002, // "Taretha cannot see you, Thrall." + TEXT_ID_SKARLOC_1 = 9578, // Thank you friends, I owe my freedom to you. Where is Taretha? I hoped to see her + GOSSIP_ITEM_SKARLOC_2 = -3560003, // "The situation is rather complicated, Thrall. It would be best for you..." + TEXT_ID_SKARLOC_2 = 9579, // What do you mean by this? Is Taretha in danger? + GOSSIP_ITEM_SKARLOC_3 = -3560007, + TEXT_ID_SKARLOC_3 = 9580, // I will do no such thing. I simply cannot leave Taretha... + + // gossip - barn + GOSSIP_ITEM_TARREN_1 = -3560004, // "We're ready, Thrall." + TEXT_ID_TARREN = 9597, // tarren mill is beyond these trees + + TEXT_ID_INN = 9614, // I'm glad Taretha is alive. We now must find a way to free her... + + // spells used by Thrall + SPELL_KNOCKOUT_ARMORER = 32890, // cast on the armorer + SPELL_STRIKE = 14516, + SPELL_SHIELD_BLOCK = 12169, + SPELL_SHADOW_SPIKE = 33125, // used to kill Taretha + SPELL_TRANSFORM = 33133, // transform infinite defilers + SPELL_SUMMON_EROZION_IMAGE = 33954, // if thrall dies during escort + SPELL_SPAWN_EROZION_IMAGE = 33955, + + // equipment + EQUIP_ID_WEAPON = 927, + EQUIP_ID_SHIELD = 1961, + + // display ids + MODEL_THRALL_UNEQUIPPED = 17292, + MODEL_THRALL_EQUIPPED = 18165, + MODEL_SKARLOC_MOUNT = 8469, + + // misc creature entries + NPC_IMAGE_OF_ERONZION = 19438, + NPC_SKARLOC_MOUNT = 18798, + NPC_THRALL_QUEST_TRIGGER = 20156, + + // part I and II ambush npcs + NPC_RIFLE = 17820, + NPC_WARDEN = 17833, + NPC_VETERAN = 17860, + NPC_MAGE = 18934, + NPC_WATCHMAN = 17814, + NPC_SENTRY = 17815, + + // part III ambush npcs + NPC_CHURCH_GUARDSMAN = 23175, + NPC_CHURCH_PROTECTOR = 23179, + NPC_CHURCH_LOOKOUT = 23177, + + NPC_INN_GUARDSMAN = 23176, + NPC_INN_PROTECTOR = 23180, + NPC_INN_LOOKOUT = 23178, + + NPC_INFINITE_DEFILER = 18171, + NPC_INFINITE_SABOTEOR = 18172, + NPC_INFINITE_SLAYER = 18170, +}; + +static const DialogueEntry aThrallDialogue[] = +{ + {SAY_LOOKOUT_BARN_1, NPC_TARREN_MILL_LOOKOUT, 5000}, + {SAY_PROTECTOR_BARN_2, NPC_TARREN_MILL_PROTECTOR, 3000}, + {NPC_YOUNG_BLANCHY, 0, 4000}, + {EMOTE_TH_CALM_HORSE, NPC_THRALL, 1000}, + {SAY_PROTECTOR_BARN_3, NPC_TARREN_MILL_LOOKOUT, 0}, + {NPC_EPOCH, 0, 8000}, + {SAY_TH_EPOCH_WONDER, NPC_THRALL, 4000}, + {SAY_EPOCH_ENTER2, NPC_EPOCH, 4000}, + {SAY_TH_EPOCH_KILL_TARETHA, NPC_THRALL, 2000}, + {NPC_THRALL, 0, 0}, + {0, 0, 0}, +}; + +struct npc_thrall_old_hillsbradAI : public npc_escortAI, private DialogueHelper +{ + npc_thrall_old_hillsbradAI(Creature* pCreature) : npc_escortAI(pCreature), + DialogueHelper(aThrallDialogue) + { + m_pInstance = (instance_old_hillsbrad*)pCreature->GetInstanceData(); + InitializeDialogueHelper(m_pInstance); + pCreature->SetActiveObjectState(true); // required for proper relocation + m_bHadMount = false; + Reset(); + } + + instance_old_hillsbrad* m_pInstance; + + bool m_bIsLowHp; + bool m_bHadMount; + bool m_bHasChurchYelled; + bool m_bHasInnYelled; + bool m_bHasEpochYelled; + + uint8 m_uiEpochWaveId; + + uint32 m_uiStrikeTimer; + uint32 m_uiShieldBlockTimer; + uint32 m_uiEpochAttackTimer; + + ObjectGuid m_skarlocMountGuid; + + GuidList m_lSkarlocAddsGuids; + GuidList m_lTarrenMillSoldiersGuids; + + void Reset() override + { + m_bIsLowHp = false; + m_uiStrikeTimer = urand(3000, 7000); + m_uiShieldBlockTimer = urand(6000, 11000); + + if (m_bHadMount) + m_creature->Mount(MODEL_SKARLOC_MOUNT); + + if (!HasEscortState(STATE_ESCORT_ESCORTING)) + { + m_bHadMount = false; + m_bHasChurchYelled = false; + m_bHasEpochYelled = false; + + m_uiEpochWaveId = 0; + m_uiEpochAttackTimer = 0; + + m_creature->Unmount(); + SetEquipmentSlots(true); + m_creature->SetDisplayId(MODEL_THRALL_UNEQUIPPED); + } + } + + void Aggro(Unit* /*pWho*/) override + { + switch (urand(0, 3)) + { + case 0: DoScriptText(SAY_TH_RANDOM_AGGRO1, m_creature); break; + case 1: DoScriptText(SAY_TH_RANDOM_AGGRO2, m_creature); break; + case 2: DoScriptText(SAY_TH_RANDOM_AGGRO3, m_creature); break; + case 3: DoScriptText(SAY_TH_RANDOM_AGGRO4, m_creature); break; + } + + if (m_creature->IsMounted()) + { + m_creature->Unmount(); + m_bHadMount = true; + } + } + + void KilledUnit(Unit* /*pVictim*/) override + { + switch (urand(0, 2)) + { + case 0: DoScriptText(SAY_TH_RANDOM_KILL1, m_creature); break; + case 1: DoScriptText(SAY_TH_RANDOM_KILL2, m_creature); break; + case 2: DoScriptText(SAY_TH_RANDOM_KILL3, m_creature); break; + } + } + + void JustDied(Unit* /*pKiller*/) override + { + // fail, and relocation handled in instance script + if (m_pInstance) + m_pInstance->SetData(TYPE_THRALL_EVENT, FAIL); + + DoScriptText(urand(0, 1) ? SAY_TH_RANDOM_DIE1 : SAY_TH_RANDOM_DIE2, m_creature); + DoCastSpellIfCan(m_creature, SPELL_SUMMON_EROZION_IMAGE, CAST_TRIGGERED); + + // despawn the summons which won't self despawn + for (GuidList::const_iterator itr = m_lSkarlocAddsGuids.begin(); itr != m_lSkarlocAddsGuids.end(); ++itr) + { + if (Creature* pTemp = m_creature->GetMap()->GetCreature(*itr)) + pTemp->ForcedDespawn(); + } + for (GuidList::const_iterator itr = m_lTarrenMillSoldiersGuids.begin(); itr != m_lTarrenMillSoldiersGuids.end(); ++itr) + { + if (Creature* pTemp = m_creature->GetMap()->GetCreature(*itr)) + pTemp->ForcedDespawn(); + } + } + + void CorpseRemoved(uint32& uiRespawnDelay) override + { + uiRespawnDelay = 0; + + // if we're done, just set some high so he never really respawn + if (m_pInstance && (m_pInstance->GetData(TYPE_THRALL_EVENT) == DONE || m_pInstance->GetData(TYPE_THRALL_EVENT) == FAIL)) + uiRespawnDelay = 12 * HOUR; + } + + void JustRespawned() override + { + npc_escortAI::JustRespawned(); + + if (!m_pInstance) + return; + + if (m_pInstance->GetData(TYPE_THRALL_EVENT) == IN_PROGRESS) + { + Start(true); + SetEscortPaused(true); + + m_bHadMount = false; + m_creature->Unmount(); + + m_creature->RemoveFlag(UNIT_NPC_FLAGS, UNIT_NPC_FLAG_QUESTGIVER); + + // check current states before fail and set spesific for the part + if (m_pInstance->GetData(TYPE_SKARLOC) != DONE) + { + SetCurrentWaypoint(1); // basement + + SetEquipmentSlots(true); + m_creature->SetDisplayId(MODEL_THRALL_UNEQUIPPED); + m_creature->SetFlag(UNIT_NPC_FLAGS, UNIT_NPC_FLAG_GOSSIP); + m_lSkarlocAddsGuids.clear(); + + // reset prison door + m_pInstance->DoUseDoorOrButton(GO_PRISON_DOOR); + // respawn the Armorer + if (Creature* pArmorer = m_pInstance->GetSingleCreatureFromStorage(NPC_ARMORER)) + pArmorer->Respawn(); + // despwn the horse + if (Creature* pHorse = m_creature->GetMap()->GetCreature(m_skarlocMountGuid)) + pHorse->ForcedDespawn(); + } + else if (m_pInstance->GetData(TYPE_ESCORT_BARN) != DONE) + { + SetCurrentWaypoint(35); // keep + + m_creature->SetDisplayId(MODEL_THRALL_EQUIPPED); + m_creature->SetFlag(UNIT_NPC_FLAGS, UNIT_NPC_FLAG_GOSSIP); + + // resummon the mount + m_creature->SummonCreature(NPC_SKARLOC_MOUNT, 2047.775f, 253.4088f, 62.91183f, 5.37f, TEMPSUMMON_DEAD_DESPAWN, 0); + } + else if (m_pInstance->GetData(TYPE_ESCORT_INN) != DONE) + { + SetCurrentWaypoint(67); // barn + m_lTarrenMillSoldiersGuids.clear(); + + m_creature->SetDisplayId(MODEL_THRALL_EQUIPPED); + m_creature->SetFlag(UNIT_NPC_FLAGS, UNIT_NPC_FLAG_GOSSIP); + } + else if (m_pInstance->GetData(TYPE_EPOCH) != DONE) + { + SetCurrentWaypoint(108); // inn + m_creature->SetDisplayId(MODEL_THRALL_EQUIPPED); + m_creature->SetFlag(UNIT_NPC_FLAGS, UNIT_NPC_FLAG_GOSSIP); + m_lTarrenMillSoldiersGuids.clear(); + m_uiEpochWaveId = 0; + + // Reset Taretha + if (Creature* pTaretha = m_pInstance->GetSingleCreatureFromStorage(NPC_TARETHA)) + { + pTaretha->SetFlag(UNIT_NPC_FLAGS, UNIT_NPC_FLAG_GOSSIP); + pTaretha->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE); + pTaretha->SetStandState(UNIT_STAND_STATE_STAND); + } + } + } + } + + void EnterEvadeMode() override + { + if (HasEscortState(STATE_ESCORT_ESCORTING)) + { + switch (urand(0, 2)) + { + case 0: DoScriptText(SAY_TH_LEAVE_COMBAT1, m_creature); break; + case 1: DoScriptText(SAY_TH_LEAVE_COMBAT2, m_creature); break; + case 2: DoScriptText(SAY_TH_LEAVE_COMBAT3, m_creature); break; + } + } + + npc_escortAI::EnterEvadeMode(); + } + + void JustSummoned(Creature* pSummoned) override + { + switch (pSummoned->GetEntry()) + { + // Barn soldiers - also used for the first wave of Epoch adds + case NPC_TARREN_MILL_GUARDSMAN: + case NPC_TARREN_MILL_PROTECTOR: + case NPC_TARREN_MILL_LOOKOUT: + m_lTarrenMillSoldiersGuids.push_back(pSummoned->GetObjectGuid()); + // For the summons corresponding to the Epoch event, handle movement + if (m_pInstance && m_pInstance->GetData(TYPE_ESCORT_INN) == DONE) + { + pSummoned->GetMotionMaster()->MovePoint(1, pSummoned->GetPositionX(), pSummoned->GetPositionY() - 10.0f, pSummoned->GetPositionZ()); + + // Transform on timer + if (!m_uiEpochAttackTimer) + m_uiEpochAttackTimer = 7000; + } + break; + // Epoch wave spawns + case NPC_INFINITE_DEFILER: + case NPC_INFINITE_SABOTEOR: + case NPC_INFINITE_SLAYER: + m_lTarrenMillSoldiersGuids.push_back(pSummoned->GetObjectGuid()); + pSummoned->AI()->AttackStart(m_creature); + if (!m_bHasEpochYelled) + { + switch (urand(0, 3)) + { + case 0: DoScriptText(SAY_INFINITE_DRAGON_AGGRO_1, pSummoned); break; + case 1: DoScriptText(SAY_INFINITE_DRAGON_AGGRO_2, pSummoned); break; + case 2: DoScriptText(SAY_INFINITE_DRAGON_AGGRO_3, pSummoned); break; + case 3: DoScriptText(SAY_INFINITE_DRAGON_AGGRO_4, pSummoned); break; + } + m_bHasEpochYelled = true; + } + break; + case NPC_SKARLOC_MOUNT: + m_skarlocMountGuid = pSummoned->GetObjectGuid(); + break; + // Church solider - used to yell + case NPC_CHURCH_LOOKOUT: + if (!m_bHasChurchYelled) + { + DoScriptText(SAY_LOOKOUT_CHURCH, pSummoned); + m_bHasChurchYelled = true; + } + pSummoned->AI()->AttackStart(m_creature); + break; + // Inn soldier - used to yell + case NPC_INN_LOOKOUT: + if (!m_bHasInnYelled) + { + DoScriptText(SAY_LOOKOUT_INN, pSummoned); + m_bHasInnYelled = true; + } + pSummoned->AI()->AttackStart(m_creature); + break; + // Spawned when Thrall is dead + case NPC_IMAGE_OF_ERONZION: + if (m_pInstance) + DoScriptText(m_pInstance->GetThrallEventCount() < MAX_WIPE_COUNTER ? SAY_ERONZION_RESET_THRALL : SAY_ERONZION_RESET_LAST, pSummoned); + pSummoned->CastSpell(pSummoned, SPELL_SPAWN_EROZION_IMAGE, false); + pSummoned->ForcedDespawn(30000); + break; + case NPC_SKARLOC: + pSummoned->SetWalk(false); + pSummoned->GetMotionMaster()->MovePoint(1, 2050.029f, 249.9696f, 63.0313f); + break; + case NPC_EPOCH: + pSummoned->SetLevitate(true); + DoScriptText(SAY_EPOCH_ENTER1, pSummoned); + break; + // Skarloc helpers - they have special behavior + case NPC_WARDEN: + case NPC_VETERAN: + if (m_pInstance && m_pInstance->GetData(TYPE_SKARLOC) == IN_PROGRESS) + { + // Allow these to follow Skarloc and attack only on command + if (Creature* pSkarloc = m_pInstance->GetSingleCreatureFromStorage(NPC_SKARLOC)) + pSummoned->GetMotionMaster()->MoveFollow(pSkarloc, 5.0f, pSummoned->GetAngle(pSkarloc) + M_PI_F); + + pSummoned->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_PASSIVE | UNIT_FLAG_OOC_NOT_ATTACKABLE); + m_lSkarlocAddsGuids.push_back(pSummoned->GetObjectGuid()); + } + else + pSummoned->AI()->AttackStart(m_creature); + break; + default: + pSummoned->AI()->AttackStart(m_creature); + break; + } + } + + void SummonedCreatureJustDied(Creature* pSummoned) override + { + switch (pSummoned->GetEntry()) + { + case NPC_EPOCH: + DoHandleQuestCredit(); + SetEscortPaused(false); + break; + case NPC_SKARLOC: + SetEscortPaused(false); + break; + case NPC_TARREN_MILL_PROTECTOR: + case NPC_TARREN_MILL_LOOKOUT: + case NPC_TARREN_MILL_GUARDSMAN: + // continue escort when all the barn soldiers are dead + m_lTarrenMillSoldiersGuids.remove(pSummoned->GetObjectGuid()); + if (m_lTarrenMillSoldiersGuids.empty()) + { + SetRun(); + SetEscortPaused(false); + } + break; + case NPC_INFINITE_DEFILER: + case NPC_INFINITE_SABOTEOR: + case NPC_INFINITE_SLAYER: + // Handle Epoch event waves - spawn another when the previous is dead + m_lTarrenMillSoldiersGuids.remove(pSummoned->GetObjectGuid()); + if (m_lTarrenMillSoldiersGuids.empty()) + { + m_lTarrenMillSoldiersGuids.clear(); + m_bHasEpochYelled = false; + switch (m_uiEpochWaveId) + { + case 1: + m_creature->SummonCreature(NPC_INFINITE_DEFILER, 2595.477f, 684.3738f, 55.95534f, 6.05f, TEMPSUMMON_DEAD_DESPAWN, 0); + m_creature->SummonCreature(NPC_INFINITE_SABOTEOR, 2602.208f, 678.2955f, 56.34682f, 6.07f, TEMPSUMMON_DEAD_DESPAWN, 0); + m_creature->SummonCreature(NPC_INFINITE_SLAYER, 2602.8f, 686.2845f, 55.79315f, 5.95f, TEMPSUMMON_DEAD_DESPAWN, 0); + ++m_uiEpochWaveId; + break; + case 2: + m_creature->SummonCreature(NPC_INFINITE_DEFILER, 2646.289f, 718.5257f, 57.90024f, 4.32f, TEMPSUMMON_DEAD_DESPAWN, 0); + m_creature->SummonCreature(NPC_INFINITE_SABOTEOR, 2641.788f, 719.7106f, 57.4023f, 4.46f, TEMPSUMMON_DEAD_DESPAWN, 0); + m_creature->SummonCreature(NPC_INFINITE_SLAYER, 2645.725f, 709.7153f, 56.69411f, 4.38f, TEMPSUMMON_DEAD_DESPAWN, 0); + m_creature->SummonCreature(NPC_INFINITE_SLAYER, 2639.641f, 710.5246f, 56.23582f, 4.60f, TEMPSUMMON_DEAD_DESPAWN, 0); + ++m_uiEpochWaveId; + break; + case 3: + if (m_pInstance) + { + if (Creature* pEpoch = m_pInstance->GetSingleCreatureFromStorage(NPC_EPOCH)) + { + pEpoch->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_PASSIVE); + pEpoch->AI()->AttackStart(m_creature); + AttackStart(pEpoch); + } + } + break; + } + } + break; + } + } + + void SummonedMovementInform(Creature* pSummoned, uint32 uiType, uint32 uiPointId) override + { + if (uiType != POINT_MOTION_TYPE) + return; + + switch (pSummoned->GetEntry()) + { + // Handle Skarloc movement for the intro part + case NPC_SKARLOC: + switch (uiPointId) + { + case 1: + // summon mount + pSummoned->Unmount(); + m_creature->SummonCreature(NPC_SKARLOC_MOUNT, 2047.775f, 253.4088f, 62.91183f, 5.37f, TEMPSUMMON_DEAD_DESPAWN, 0); + pSummoned->SetWalk(true); + pSummoned->GetMotionMaster()->MovePoint(2, 2059.899f, 234.2593f, 64.10809f); + break; + case 2: + // taunt Thrall + DoScriptText(SAY_SKARLOC_ENTER, pSummoned); + SetEscortPaused(false); + break; + } + break; + // Handle infinite dragons transform on point reaches + case NPC_TARREN_MILL_GUARDSMAN: + if (uiPointId) + { + pSummoned->CastSpell(pSummoned, SPELL_TRANSFORM, false); + pSummoned->UpdateEntry(NPC_INFINITE_SLAYER); + } + break; + case NPC_TARREN_MILL_PROTECTOR: + if (uiPointId) + { + pSummoned->CastSpell(pSummoned, SPELL_TRANSFORM, false); + pSummoned->UpdateEntry(NPC_INFINITE_SABOTEOR); + } + break; + case NPC_TARREN_MILL_LOOKOUT: + if (uiPointId) + { + pSummoned->CastSpell(pSummoned, SPELL_TRANSFORM, false); + pSummoned->UpdateEntry(NPC_INFINITE_DEFILER); + } + break; + } + } + + void JustDidDialogueStep(int32 iEntry) override + { + if (!m_pInstance) + return; + + switch (iEntry) + { + case NPC_YOUNG_BLANCHY: + // ToDo: deal with the horse animation! + break; + case EMOTE_TH_CALM_HORSE: + if (Creature* pHorse = m_pInstance->GetSingleCreatureFromStorage(NPC_YOUNG_BLANCHY)) + m_creature->SetFacingToObject(pHorse); + break; + case SAY_PROTECTOR_BARN_3: + // Move the soldiers inside + float fX, fY, fZ; + for (GuidList::const_iterator itr = m_lTarrenMillSoldiersGuids.begin(); itr != m_lTarrenMillSoldiersGuids.end(); ++itr) + { + if (Creature* pTemp = m_creature->GetMap()->GetCreature(*itr)) + { + pTemp->SetWalk(false); + pTemp->GetRandomPoint(2480.19f, 696.15f, 55.78f, 5.0f, fX, fY, fZ); + pTemp->GetMotionMaster()->MovePoint(0, fX, fY, fZ); + pTemp->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_PASSIVE); + } + } + break; + case SAY_TH_EPOCH_WONDER: + m_creature->SetFacingTo(2.69f); + break; + case SAY_EPOCH_ENTER2: + if (Creature* pTaretha = m_pInstance->GetSingleCreatureFromStorage(NPC_TARETHA)) + { + pTaretha->CastSpell(pTaretha, SPELL_SHADOW_SPIKE, true); + pTaretha->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE); + pTaretha->SetStandState(UNIT_STAND_STATE_DEAD); + } + break; + case SAY_TH_EPOCH_KILL_TARETHA: + if (Creature* pTaretha = m_pInstance->GetSingleCreatureFromStorage(NPC_TARETHA)) + m_creature->SetFacingToObject(pTaretha); + break; + case NPC_THRALL: + SetRun(); + SetEscortPaused(false); + break; + } + } + + void WaypointReached(uint32 uiPoint) override + { + if (!m_pInstance) + return; + + switch (uiPoint) + { + // *** Escort event - Part I - inside the keep *** + case 0: + m_pInstance->DoUseDoorOrButton(GO_PRISON_DOOR); + break; + case 8: + if (Creature* pArmorer = m_pInstance->GetSingleCreatureFromStorage(NPC_ARMORER)) + { + DoScriptText(SAY_ARMORER_CALL_GUARDS, pArmorer); + pArmorer->SetFacingToObject(m_creature); + } + break; + case 9: + DoScriptText(SAY_TH_KILL_ARMORER, m_creature); + DoCastSpellIfCan(m_creature, SPELL_KNOCKOUT_ARMORER); + // also kill the armorer + if (Creature* pArmorer = m_pInstance->GetSingleCreatureFromStorage(NPC_ARMORER)) + pArmorer->DealDamage(pArmorer, pArmorer->GetHealth(), NULL, DIRECT_DAMAGE, SPELL_SCHOOL_MASK_NONE, NULL, false); + break; + case 10: + DoScriptText(SAY_TH_ARMORY_1, m_creature); + m_creature->SetStandState(UNIT_STAND_STATE_KNEEL); + m_creature->SetDisplayId(MODEL_THRALL_EQUIPPED); + SetEquipmentSlots(false, EQUIP_ID_WEAPON, EQUIP_ID_SHIELD, EQUIP_NO_CHANGE); + break; + case 11: + m_creature->SetStandState(UNIT_STAND_STATE_STAND); + break; + case 12: + if (Creature* pArmorer = m_pInstance->GetSingleCreatureFromStorage(NPC_ARMORER)) + m_creature->SetFacingToObject(pArmorer); + DoScriptText(SAY_TH_ARMORY_2, m_creature); + break; + // *** Escort event - Part I - outside the keep *** + case 17: + m_creature->SummonCreature(NPC_MAGE, 2186.909f, 139.8108f, 88.21628f, 5.75f, TEMPSUMMON_TIMED_OOC_DESPAWN, 5000); + m_creature->SummonCreature(NPC_WARDEN, 2187.943f, 141.6124f, 88.21628f, 5.73f, TEMPSUMMON_TIMED_OOC_DESPAWN, 5000); + m_creature->SummonCreature(NPC_VETERAN, 2190.508f, 140.4597f, 88.21628f, 6.04f, TEMPSUMMON_TIMED_OOC_DESPAWN, 5000); + m_creature->SummonCreature(NPC_VETERAN, 2189.543f, 139.0996f, 88.23965f, 0.21f, TEMPSUMMON_TIMED_OOC_DESPAWN, 5000); + break; + case 20: + m_creature->SummonCreature(NPC_MAGE, 2149.463f, 104.9756f, 73.63239f, 1.71f, TEMPSUMMON_TIMED_OOC_DESPAWN, 5000); + m_creature->SummonCreature(NPC_SENTRY, 2147.642f, 105.0251f, 73.99422f, 1.52f, TEMPSUMMON_TIMED_OOC_DESPAWN, 5000); + m_creature->SummonCreature(NPC_VETERAN, 2149.212f, 107.2005f, 74.15676f, 1.71f, TEMPSUMMON_TIMED_OOC_DESPAWN, 5000); + m_creature->SummonCreature(NPC_WARDEN, 2147.328f, 106.7235f, 74.34447f, 1.69f, TEMPSUMMON_TIMED_OOC_DESPAWN, 5000); + break; + case 23: + m_creature->SummonCreature(NPC_MAGE, 2142.363f, 172.4260f, 66.30494f, 2.54f, TEMPSUMMON_TIMED_OOC_DESPAWN, 5000); + m_creature->SummonCreature(NPC_SENTRY, 2138.177f, 168.6046f, 66.30494f, 2.47f, TEMPSUMMON_TIMED_OOC_DESPAWN, 5000); + m_creature->SummonCreature(NPC_SENTRY, 2142.372f, 174.2907f, 66.30494f, 2.56f, TEMPSUMMON_TIMED_OOC_DESPAWN, 5000); + m_creature->SummonCreature(NPC_VETERAN, 2140.146f, 169.2364f, 66.30494f, 2.49f, TEMPSUMMON_TIMED_OOC_DESPAWN, 5000); + break; + case 25: + m_creature->SummonCreature(NPC_MAGE, 2107.938f, 192.0753f, 66.30494f, 2.54f, TEMPSUMMON_TIMED_OOC_DESPAWN, 5000); + m_creature->SummonCreature(NPC_MAGE, 2109.852f, 195.1403f, 66.30493f, 2.42f, TEMPSUMMON_TIMED_OOC_DESPAWN, 5000); + m_creature->SummonCreature(NPC_VETERAN, 2108.486f, 189.9346f, 66.30494f, 2.68f, TEMPSUMMON_TIMED_OOC_DESPAWN, 5000); + m_creature->SummonCreature(NPC_VETERAN, 2112.387f, 195.4947f, 66.30494f, 2.39f, TEMPSUMMON_TIMED_OOC_DESPAWN, 5000); + break; + // *** Escort event - Part I - meet Skarloc *** + case 31: + m_pInstance->SetData(TYPE_SKARLOC, IN_PROGRESS); + m_creature->SummonCreature(NPC_SKARLOC, 2000.201f, 277.9190f, 66.4911f, 6.11f, TEMPSUMMON_DEAD_DESPAWN, 0); + m_creature->SummonCreature(NPC_VETERAN, 1997.969f, 274.4247f, 66.6181f, 5.67f, TEMPSUMMON_DEAD_DESPAWN, 0); + m_creature->SummonCreature(NPC_WARDEN, 2000.002f, 282.0754f, 66.2986f, 6.02f, TEMPSUMMON_DEAD_DESPAWN, 0); + DoScriptText(SAY_TH_SKARLOC_MEET, m_creature); + SetEscortPaused(true); + break; + case 33: + // Allow the guards and Skarloc to attack + if (Creature* pSkarloc = m_pInstance->GetSingleCreatureFromStorage(NPC_SKARLOC)) + { + pSkarloc->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_PASSIVE); + pSkarloc->AI()->AttackStart(m_creature); + AttackStart(pSkarloc); + } + for (GuidList::const_iterator itr = m_lSkarlocAddsGuids.begin(); itr != m_lSkarlocAddsGuids.end(); ++itr) + { + if (Creature* pTemp = m_creature->GetMap()->GetCreature(*itr)) + { + pTemp->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_PASSIVE); + pTemp->AI()->AttackStart(m_creature); + } + } + break; + case 34: + // wait for player input + if (Creature* pMount = m_creature->GetMap()->GetCreature(m_skarlocMountGuid)) + m_creature->SetFacingToObject(pMount); + + SetEscortPaused(true); + m_creature->SetFlag(UNIT_NPC_FLAGS, UNIT_NPC_FLAG_GOSSIP); + break; + // *** Escort event - Part II - road *** + case 35: + if (Creature* pMount = m_creature->GetMap()->GetCreature(m_skarlocMountGuid)) + { + m_creature->SetFacingToObject(pMount); + pMount->ForcedDespawn(4000); + } + break; + case 36: + DoScriptText(SAY_TH_MOUNTS_UP, m_creature); + m_creature->SetFacingTo(5.33f); + m_creature->Mount(MODEL_SKARLOC_MOUNT); + break; + // *** Escort event - Part II - reached barn *** + case 64: + m_creature->SummonCreature(NPC_SKARLOC_MOUNT, 2488.779f, 623.9724f, 58.07383f, 1.37f, TEMPSUMMON_TIMED_DESPAWN, 30000); + m_creature->Unmount(); + m_bHadMount = false; + break; + case 65: + if (Creature* pMount = m_creature->GetMap()->GetCreature(m_skarlocMountGuid)) + m_creature->SetFacingToObject(pMount); + DoScriptText(EMOTE_TH_STARTLE_HORSE, m_creature); + break; + case 66: + if (Creature* pMount = m_creature->GetMap()->GetCreature(m_skarlocMountGuid)) + { + pMount->SetWalk(false); + pMount->GetMotionMaster()->MovePoint(0, 2517.504f, 506.253f, 42.329f); + } + m_creature->SetFacingTo(4.66f); + // wait for player input + SetEscortPaused(true); + m_creature->SetFlag(UNIT_NPC_FLAGS, UNIT_NPC_FLAG_GOSSIP); + m_pInstance->SetData(TYPE_ESCORT_BARN, DONE); + break; + // *** Escort event - Part III - barn *** + case 70: + SetRun(false); + break; + case 73: + m_creature->SummonCreature(NPC_TARREN_MILL_PROTECTOR, 2500.22f, 692.60f, 55.50f, 2.84f, TEMPSUMMON_DEAD_DESPAWN, 0); + m_creature->SummonCreature(NPC_TARREN_MILL_LOOKOUT, 2500.13f, 696.55f, 55.51f, 3.38f, TEMPSUMMON_DEAD_DESPAWN, 0); + m_creature->SummonCreature(NPC_TARREN_MILL_GUARDSMAN, 2500.55f, 693.64f, 55.50f, 3.14f, TEMPSUMMON_DEAD_DESPAWN, 0); + m_creature->SummonCreature(NPC_TARREN_MILL_GUARDSMAN, 2500.94f, 695.81f, 55.50f, 3.14f, TEMPSUMMON_DEAD_DESPAWN, 0); + break; + // *** Escort event - Part III - start barn dialogue *** + case 74: + StartNextDialogueText(SAY_LOOKOUT_BARN_1); + SetEscortPaused(true); + break; + case 75: + DoScriptText(SAY_TH_HEAD_TOWN, m_creature); + break; + // *** Escort event - Part III - church *** + case 92: + DoScriptText(SAY_TH_CHURCH_ENTER, m_creature); + m_creature->SetFacingTo(1.0f); + break; + case 93: + m_creature->SummonCreature(NPC_CHURCH_PROTECTOR, 2627.88f, 657.63f, 55.98f, 4.28f, TEMPSUMMON_TIMED_OOC_OR_DEAD_DESPAWN, 5000); + m_creature->SummonCreature(NPC_CHURCH_LOOKOUT, 2627.27f, 655.17f, 56.03f, 4.50f, TEMPSUMMON_TIMED_OOC_OR_DEAD_DESPAWN, 5000); + m_creature->SummonCreature(NPC_CHURCH_LOOKOUT, 2629.21f, 654.81f, 56.04f, 4.38f, TEMPSUMMON_TIMED_OOC_OR_DEAD_DESPAWN, 5000); + m_creature->SummonCreature(NPC_CHURCH_GUARDSMAN, 2629.98f, 656.96f, 55.96f, 4.34f, TEMPSUMMON_TIMED_OOC_OR_DEAD_DESPAWN, 5000); + break; + case 94: + DoScriptText(SAY_TH_CHURCH_END, m_creature); + break; + // *** Escort event - Part III - inside the inn *** + case 105: + m_creature->SummonCreature(NPC_INN_PROTECTOR, 2652.71f, 660.31f, 61.93f, 1.67f, TEMPSUMMON_TIMED_OOC_DESPAWN, 5000); + m_creature->SummonCreature(NPC_INN_LOOKOUT, 2648.96f, 662.59f, 61.93f, 0.79f, TEMPSUMMON_TIMED_OOC_DESPAWN, 5000); + m_creature->SummonCreature(NPC_INN_LOOKOUT, 2657.36f, 662.34f, 61.93f, 2.68f, TEMPSUMMON_TIMED_OOC_DESPAWN, 5000); + m_creature->SummonCreature(NPC_INN_GUARDSMAN, 2656.39f, 659.77f, 61.93f, 2.61f, TEMPSUMMON_TIMED_OOC_DESPAWN, 5000); + SetRun(false); + break; + // *** Escort event - Part III - meet Taretha *** + case 106: + if (Creature* pTaretha = m_pInstance->GetSingleCreatureFromStorage(NPC_TARETHA)) + DoScriptText(SAY_TA_ESCAPED, pTaretha, m_creature); + break; + case 107: + // wait for player input + DoScriptText(SAY_TH_MEET_TARETHA, m_creature); + m_pInstance->SetData(TYPE_ESCORT_INN, DONE); + m_creature->SetFlag(UNIT_NPC_FLAGS, UNIT_NPC_FLAG_GOSSIP); + if (Creature* pTaretha = m_pInstance->GetSingleCreatureFromStorage(NPC_TARETHA)) + pTaretha->SetFlag(UNIT_NPC_FLAGS, UNIT_NPC_FLAG_GOSSIP); + SetEscortPaused(true); + break; + // *** Escort event - Part IV - Epoch *** + case 108: + m_creature->SummonCreature(NPC_EPOCH, 2639.92f, 700.2587f, 65.13583f, 4.74f, TEMPSUMMON_DEAD_DESPAWN, 0); + StartNextDialogueText(NPC_EPOCH); + SetEscortPaused(true); + break; + // *** Escort event - Part IV - Epoch - begin fight *** + case 116: + if (Creature* pEpoch = m_pInstance->GetSingleCreatureFromStorage(NPC_EPOCH)) + { + DoScriptText(SAY_EPOCH_ENTER3, pEpoch); + m_creature->SetFacingToObject(pEpoch); + } + break; + case 117: + // begin fight + m_lTarrenMillSoldiersGuids.clear(); + m_creature->SummonCreature(NPC_TARREN_MILL_GUARDSMAN, 2630.318f, 704.3388f, 56.33701f, 4.73f, TEMPSUMMON_DEAD_DESPAWN, 0); + m_creature->SummonCreature(NPC_TARREN_MILL_LOOKOUT, 2639.1f, 707.3839f, 56.14664f, 4.49f, TEMPSUMMON_DEAD_DESPAWN, 0); + m_creature->SummonCreature(NPC_TARREN_MILL_PROTECTOR, 2653.135f, 698.6548f, 57.56876f, 3.17f, TEMPSUMMON_DEAD_DESPAWN, 0); + ++m_uiEpochWaveId; + SetEscortPaused(true); + break; + // *** Escort event - Epilogue - run off *** + case 118: + // return to position + SetEscortPaused(true); + break; + case 120: + m_creature->SetActiveObjectState(false); + break; + } + } + + // Wrapper to restart escort + void DoRestartEscortMovement() + { + m_creature->RemoveFlag(UNIT_NPC_FLAGS, UNIT_NPC_FLAG_GOSSIP); + SetEscortPaused(false); + } + + // Complete the quest for escorting + void DoHandleQuestCredit() + { + Map::PlayerList const& lPlayerList = m_pInstance->instance->GetPlayers(); + + if (!lPlayerList.isEmpty()) + { + for (Map::PlayerList::const_iterator itr = lPlayerList.begin(); itr != lPlayerList.end(); ++itr) + { + if (Player* pPlayer = itr->getSource()) + pPlayer->KilledMonsterCredit(NPC_THRALL_QUEST_TRIGGER, m_creature->GetObjectGuid()); + } + } + } + + // Wrapper to make the dragons attack + void DoStartDragonsAttack() + { + for (GuidList::const_iterator itr = m_lTarrenMillSoldiersGuids.begin(); itr != m_lTarrenMillSoldiersGuids.end(); ++itr) + { + if (Creature* pTemp = m_creature->GetMap()->GetCreature(*itr)) + { + // Only one will yell aggro + if (!m_bHasEpochYelled) + { + switch (urand(0, 3)) + { + case 0: DoScriptText(SAY_INFINITE_DRAGON_AGGRO_1, pTemp); break; + case 1: DoScriptText(SAY_INFINITE_DRAGON_AGGRO_2, pTemp); break; + case 2: DoScriptText(SAY_INFINITE_DRAGON_AGGRO_3, pTemp); break; + case 3: DoScriptText(SAY_INFINITE_DRAGON_AGGRO_4, pTemp); break; + } + m_bHasEpochYelled = true; + } + + // Attack Thrall + pTemp->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_PASSIVE); + pTemp->AI()->AttackStart(m_creature); + } + } + } + + void UpdateEscortAI(const uint32 uiDiff) override + { + DialogueUpdate(uiDiff); + + // Handle soldiers tranform to Infinite dragons + if (m_uiEpochAttackTimer) + { + if (m_uiEpochAttackTimer <= uiDiff) + { + DoStartDragonsAttack(); + m_uiEpochAttackTimer = 0; + } + else + m_uiEpochAttackTimer -= uiDiff; + } + + if (!m_creature->SelectHostileTarget() || !m_creature->getVictim()) + return; + + if (m_uiStrikeTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature->getVictim(), SPELL_STRIKE) == CAST_OK) + m_uiStrikeTimer = urand(4000, 7000); + } + else + m_uiStrikeTimer -= uiDiff; + + if (m_uiShieldBlockTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature, SPELL_SHIELD_BLOCK) == CAST_OK) + m_uiShieldBlockTimer = urand(8000, 15000); + } + else + m_uiShieldBlockTimer -= uiDiff; + + if (!m_bIsLowHp && m_creature->GetHealthPercent() < 20.0f) + { + DoScriptText(urand(0, 1) ? SAY_TH_RANDOM_LOW_HP1 : SAY_TH_RANDOM_LOW_HP2, m_creature); + m_bIsLowHp = true; + } + + DoMeleeAttackIfReady(); + } +}; + +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()) + { + pPlayer->PrepareQuestMenu(pCreature->GetObjectGuid()); + pPlayer->SendPreparedQuest(pCreature->GetObjectGuid()); + } + + if (instance_old_hillsbrad* pInstance = (instance_old_hillsbrad*)pCreature->GetInstanceData()) + { + // If the inn escort has started, skip the gossip + if (pInstance->GetData(TYPE_ESCORT_INN) == DONE) + pPlayer->SEND_GOSSIP_MENU(TEXT_ID_INN, pCreature->GetObjectGuid()); + // Escort - barn to inn + else if (pInstance->GetData(TYPE_ESCORT_BARN) == DONE) + { + pPlayer->ADD_GOSSIP_ITEM_ID(GOSSIP_ICON_CHAT, GOSSIP_ITEM_TARREN_1, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 3); + pPlayer->SEND_GOSSIP_MENU(TEXT_ID_TARREN, pCreature->GetObjectGuid()); + } + // Escort - after Skarloc is defeated + else if (pInstance->GetData(TYPE_SKARLOC) == DONE) + { + pPlayer->ADD_GOSSIP_ITEM_ID(GOSSIP_ICON_CHAT, GOSSIP_ITEM_SKARLOC_1, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 2); + pPlayer->SEND_GOSSIP_MENU(TEXT_ID_SKARLOC_1, pCreature->GetObjectGuid()); + } + // Event start - after Drake is defeated + else if (pInstance->GetData(TYPE_DRAKE) == DONE) + { + pPlayer->ADD_GOSSIP_ITEM_ID(GOSSIP_ICON_CHAT, GOSSIP_ITEM_START, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 1); + pPlayer->SEND_GOSSIP_MENU(TEXT_ID_START, pCreature->GetObjectGuid()); + } + } + return true; +} + +bool GossipSelect_npc_thrall_old_hillsbrad(Player* pPlayer, Creature* pCreature, uint32 /*uiSender*/, uint32 uiAction) +{ + instance_old_hillsbrad* pInstance = (instance_old_hillsbrad*)pCreature->GetInstanceData(); + + switch (uiAction) + { + // Event start + case GOSSIP_ACTION_INFO_DEF+1: + { + pPlayer->CLOSE_GOSSIP_MENU(); + + DoScriptText(SAY_TH_START_EVENT_PART_1, pCreature); + + if (pInstance) + pInstance->SetData(TYPE_THRALL_EVENT, IN_PROGRESS); + + if (npc_thrall_old_hillsbradAI* pThrallAI = dynamic_cast(pCreature->AI())) + pThrallAI->Start(true, pPlayer); + + break; + } + // Escort - after Skarloc + case GOSSIP_ACTION_INFO_DEF+2: + { + pPlayer->ADD_GOSSIP_ITEM_ID(GOSSIP_ICON_CHAT, GOSSIP_ITEM_SKARLOC_2, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 20); + pPlayer->SEND_GOSSIP_MENU(TEXT_ID_SKARLOC_2, pCreature->GetObjectGuid()); + break; + } + case GOSSIP_ACTION_INFO_DEF+20: + { + pPlayer->ADD_GOSSIP_ITEM_ID(GOSSIP_ICON_CHAT, GOSSIP_ITEM_SKARLOC_3, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 21); + pPlayer->SEND_GOSSIP_MENU(TEXT_ID_SKARLOC_3, pCreature->GetObjectGuid()); + break; + } + case GOSSIP_ACTION_INFO_DEF+21: + { + pPlayer->CLOSE_GOSSIP_MENU(); + + DoScriptText(SAY_TH_START_EVENT_PART2, pCreature); + + if (pInstance) + pInstance->SetData(TYPE_ESCORT_BARN, IN_PROGRESS); + + if (npc_thrall_old_hillsbradAI* pThrallAI = dynamic_cast(pCreature->AI())) + pThrallAI->DoRestartEscortMovement(); + + break; + } + // Escort - barn to inn + case GOSSIP_ACTION_INFO_DEF+3: + { + pPlayer->CLOSE_GOSSIP_MENU(); + + if (pInstance) + pInstance->SetData(TYPE_ESCORT_INN, IN_PROGRESS); + + if (npc_thrall_old_hillsbradAI* pThrallAI = dynamic_cast(pCreature->AI())) + pThrallAI->DoRestartEscortMovement(); + + break; + } + } + return true; +} + +/*###### +## npc_taretha +######*/ + +enum +{ + // end event texts and spells + SAY_TA_FREE = -1560048, + SAY_TR_GLAD_SAFE = -1560054, + SAY_TA_NEVER_MET = -1560055, + SAY_TR_THEN_WHO = -1560056, + SAY_PRE_WIPE = -1560057, + SAY_WIPE_MEMORY = -1560051, + SAY_ABOUT_TARETHA = -1560052, + SAY_TH_EVENT_COMPLETE = -1560033, + SAY_TA_FAREWELL = -1560053, + SAY_AFTER_WIPE = -1560058, // not sure when to use this one + + GOSSIP_ITEM_EPOCH_1 = -3560005, // "Strange wizard?" + TEXT_ID_EPOCH_1 = 9610, // Thank you for helping Thrall escape, friends. Now I only hope + + GOSSIP_ITEM_EPOCH_2 = -3560006, // "We'll get you out. Taretha. Don't worry. I doubt the wizard would wander too far away." + TEXT_ID_EPOCH_2 = 9613, // Yes, friends. This man was no wizard of + + SPELL_TELEPORT = 7791, + SPELL_MEMORY_WIPE = 33336, // hits Taretha and Thrall + SPELL_MEMORY_WP_RESUME = 33337, + SPELL_SHADOW_PRISON = 33071, // in creature_template_addon - remove from Taretha on event complete +}; + +static const DialogueEntry aTarethaDialogue[] = +{ + {SAY_TA_FREE, NPC_TARETHA, 4000}, + {SAY_TR_GLAD_SAFE, NPC_THRALL, 9000}, + {SAY_TA_NEVER_MET, NPC_TARETHA, 3000}, + {SAY_TR_THEN_WHO, NPC_THRALL, 6000}, + {SPELL_MEMORY_WIPE, 0, 3000}, + {SAY_WIPE_MEMORY, NPC_EROZION, 12000}, + {SAY_ABOUT_TARETHA, NPC_EROZION, 6000}, + {SAY_TH_EVENT_COMPLETE, NPC_THRALL, 3000}, + {NPC_THRALL, 0, 2000}, + {SAY_TA_FAREWELL, NPC_TARETHA, 3000}, + {NPC_TARETHA, 0, 0}, + {0, 0, 0}, +}; + +struct npc_tarethaAI : public npc_escortAI, private DialogueHelper +{ + npc_tarethaAI(Creature* pCreature) : npc_escortAI(pCreature), + DialogueHelper(aTarethaDialogue) + { + m_pInstance = (instance_old_hillsbrad*)pCreature->GetInstanceData(); + InitializeDialogueHelper(m_pInstance); + Reset(); + } + + instance_old_hillsbrad* m_pInstance; + + bool m_bHasStartedEpilogue; + + void Reset() override + { + m_bHasStartedEpilogue = false; + } + + void JustSummoned(Creature* pSummoned) override + { + // Remove flags from the npc - the quest will be handled by the entrance version + if (pSummoned->GetEntry() == NPC_EROZION) + { + DoScriptText(SAY_PRE_WIPE, pSummoned); + pSummoned->CastSpell(pSummoned, SPELL_TELEPORT, false); + pSummoned->RemoveFlag(UNIT_NPC_FLAGS, UNIT_NPC_FLAG_GOSSIP | UNIT_NPC_FLAG_QUESTGIVER); + } + } + + void WaypointReached(uint32 uiPoint) override + { + if (uiPoint == 7) + { + StartNextDialogueText(SAY_TA_FREE); + + if (m_pInstance) + { + if (Creature* pThrall = m_pInstance->GetSingleCreatureFromStorage(NPC_THRALL)) + pThrall->SetFacingToObject(m_creature); + } + + m_creature->HandleEmote(EMOTE_ONESHOT_CHEER); + SetEscortPaused(true); + SetRun(false); + } + } + + void JustDidDialogueStep(int32 iEntry) override + { + if (!m_pInstance) + return; + + switch (iEntry) + { + case SAY_TR_THEN_WHO: + m_creature->SummonCreature(NPC_EROZION, 2646.47f, 680.416f, 55.38f, 4.16f, TEMPSUMMON_TIMED_DESPAWN, 5 * MINUTE * IN_MILLISECONDS); + break; + case SPELL_MEMORY_WIPE: + if (Creature* pErozion = m_pInstance->GetSingleCreatureFromStorage(NPC_EROZION)) + pErozion->CastSpell(pErozion, SPELL_MEMORY_WIPE, false); + break; + case SAY_TH_EVENT_COMPLETE: + if (Creature* pErozion = m_pInstance->GetSingleCreatureFromStorage(NPC_EROZION)) + pErozion->CastSpell(pErozion, SPELL_MEMORY_WP_RESUME, false); + if (Creature* pThrall = m_pInstance->GetSingleCreatureFromStorage(NPC_THRALL)) + pThrall->RemoveAurasDueToSpell(SPELL_MEMORY_WIPE); + m_creature->RemoveAurasDueToSpell(SPELL_MEMORY_WIPE); + break; + case NPC_THRALL: + if (Creature* pThrall = m_pInstance->GetSingleCreatureFromStorage(NPC_THRALL)) + { + if (npc_thrall_old_hillsbradAI* pThrallAI = dynamic_cast(pThrall->AI())) + pThrallAI->SetEscortPaused(false); + } + break; + case SAY_TA_FAREWELL: + if (Creature* pThrall = m_pInstance->GetSingleCreatureFromStorage(NPC_THRALL)) + m_creature->SetFacingToObject(pThrall); + m_creature->HandleEmote(EMOTE_ONESHOT_WAVE); + break; + case NPC_TARETHA: + if (Creature* pErozion = m_pInstance->GetSingleCreatureFromStorage(NPC_EROZION)) + pErozion->SetFlag(UNIT_NPC_FLAGS, UNIT_NPC_FLAG_QUESTGIVER); + m_pInstance->SetData(TYPE_THRALL_EVENT, DONE); + SetEscortPaused(false); + break; + } + } + + void UpdateEscortAI(const uint32 uiDiff) override + { + DialogueUpdate(uiDiff); + + if (!m_bHasStartedEpilogue && m_pInstance) + { + // Start epilogue + if (m_pInstance->GetData(TYPE_EPOCH) == DONE && m_pInstance->GetData(TYPE_THRALL_EVENT) != DONE) + { + m_creature->RemoveAurasDueToSpell(SPELL_SHADOW_PRISON); + m_creature->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE); + m_creature->SetStandState(UNIT_STAND_STATE_STAND); + + Start(true); + m_bHasStartedEpilogue = true; + } + } + } +}; + +CreatureAI* GetAI_npc_taretha(Creature* pCreature) +{ + return new npc_tarethaAI(pCreature); +} + +bool GossipHello_npc_taretha(Player* pPlayer, Creature* pCreature) +{ + instance_old_hillsbrad* pInstance = (instance_old_hillsbrad*)pCreature->GetInstanceData(); + + if (pInstance && pInstance->GetData(TYPE_ESCORT_INN) == DONE && pInstance->GetData(TYPE_EPOCH) != DONE) + { + pPlayer->ADD_GOSSIP_ITEM_ID(GOSSIP_ICON_CHAT, GOSSIP_ITEM_EPOCH_1, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 1); + pPlayer->SEND_GOSSIP_MENU(TEXT_ID_EPOCH_1, pCreature->GetObjectGuid()); + } + + return true; +} + +bool GossipSelect_npc_taretha(Player* pPlayer, Creature* pCreature, uint32 /*uiSender*/, uint32 uiAction) +{ + instance_old_hillsbrad* pInstance = (instance_old_hillsbrad*)pCreature->GetInstanceData(); + + if (uiAction == GOSSIP_ACTION_INFO_DEF + 1) + { + pPlayer->ADD_GOSSIP_ITEM_ID(GOSSIP_ICON_CHAT, GOSSIP_ITEM_EPOCH_2, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 2); + pPlayer->SEND_GOSSIP_MENU(TEXT_ID_EPOCH_2, pCreature->GetObjectGuid()); + } + + if (uiAction == GOSSIP_ACTION_INFO_DEF + 2) + { + pPlayer->CLOSE_GOSSIP_MENU(); + + if (pInstance && pInstance->GetData(TYPE_THRALL_EVENT) == IN_PROGRESS) + { + pCreature->RemoveFlag(UNIT_NPC_FLAGS, UNIT_NPC_FLAG_GOSSIP); + + if (Creature* pThrall = pInstance->GetSingleCreatureFromStorage(NPC_THRALL)) + { + if (npc_thrall_old_hillsbradAI* pThrallAI = dynamic_cast(pThrall->AI())) + pThrallAI->DoRestartEscortMovement(); + } + } + } + + return true; +} + +void AddSC_old_hillsbrad() +{ + Script* pNewScript; + + pNewScript = new Script; + pNewScript->Name = "npc_erozion"; + pNewScript->pGossipHello = &GossipHello_npc_erozion; + pNewScript->pGossipSelect = &GossipSelect_npc_erozion; + pNewScript->RegisterSelf(); + + pNewScript = new Script; + pNewScript->Name = "npc_thrall_old_hillsbrad"; + pNewScript->GetAI = &GetAI_npc_thrall_old_hillsbrad; + pNewScript->pGossipHello = &GossipHello_npc_thrall_old_hillsbrad; + pNewScript->pGossipSelect = &GossipSelect_npc_thrall_old_hillsbrad; + pNewScript->RegisterSelf(); + + pNewScript = new Script; + pNewScript->Name = "npc_taretha"; + pNewScript->GetAI = &GetAI_npc_taretha; + pNewScript->pGossipHello = &GossipHello_npc_taretha; + pNewScript->pGossipSelect = &GossipSelect_npc_taretha; + pNewScript->RegisterSelf(); +} diff --git a/src/modules/SD2/scripts/kalimdor/caverns_of_time/old_hillsbrad/old_hillsbrad.h b/src/modules/SD2/scripts/kalimdor/caverns_of_time/old_hillsbrad/old_hillsbrad.h new file mode 100644 index 000000000..e9187be07 --- /dev/null +++ b/src/modules/SD2/scripts/kalimdor/caverns_of_time/old_hillsbrad/old_hillsbrad.h @@ -0,0 +1,101 @@ +/* Copyright (C) 2006 - 2013 ScriptDev2 + * This program is free software licensed under GPL version 2 + * Please see the included DOCS/LICENSE.TXT for more information */ + +#ifndef DEF_OLD_HILLSBRAD_H +#define DEF_OLD_HILLSBRAD_H + +enum +{ + MAX_ENCOUNTER = 7, + MAX_BARRELS = 5, + MAX_WIPE_COUNTER = 20, + + TYPE_BARREL_DIVERSION = 0, // barrel event + TYPE_DRAKE = 1, // first boss + TYPE_SKARLOC = 2, // prison to keep - boss + TYPE_ESCORT_BARN = 3, // keep to barn + TYPE_ESCORT_INN = 4, // barn to inn + TYPE_EPOCH = 5, // inn event + TYPE_THRALL_EVENT = 6, // global event + + // event npcs + NPC_THRALL = 17876, + NPC_TARETHA = 18887, + NPC_EROZION = 18723, + NPC_LODGE_QUEST_TRIGGER = 20155, + NPC_ARMORER = 18764, + NPC_ORC_PRISONER = 18598, + + NPC_TARREN_MILL_GUARDSMAN = 18092, + NPC_TARREN_MILL_PROTECTOR = 18093, + NPC_TARREN_MILL_LOOKOUT = 18094, + NPC_YOUNG_BLANCHY = 18651, + + // bosses + NPC_DRAKE = 17848, + NPC_SKARLOC = 17862, + NPC_EPOCH = 18096, + + GO_ROARING_FLAME = 182592, + GO_PRISON_DOOR = 184393, + + QUEST_ENTRY_HILLSBRAD = 10282, + QUEST_ENTRY_DIVERSION = 10283, + QUEST_ENTRY_ESCAPE = 10284, + QUEST_ENTRY_RETURN = 10285, + + WORLD_STATE_OH = 2436, +}; + +static const float afInstanceLoc[][4] = +{ + {2104.51f, 91.96f, 53.14f, 0}, // right orcs outside loc + {2192.58f, 238.44f, 52.44f, 0}, // left orcs outside loc +}; + +static const float aDrakeSummonLoc[4] = {2128.43f, 71.01f, 64.42f, 1.74f}; + +class instance_old_hillsbrad : public ScriptedInstance +{ + public: + instance_old_hillsbrad(Map* pMap); + ~instance_old_hillsbrad() {} + + void Initialize() override; + + void OnPlayerEnter(Player* pPlayer) override; + void OnCreatureCreate(Creature* pCreature) override; + void OnObjectCreate(GameObject* pGo) override; + + void OnCreatureEnterCombat(Creature* pCreature) override; + void OnCreatureEvade(Creature* pCreature); + void OnCreatureDeath(Creature* pCreature) override; + + void SetData(uint32 uiType, uint32 uiData) override; + uint32 GetData(uint32 uiType) const override; + + uint32 GetThrallEventCount() { return m_uiThrallEventCount; } + + const char* Save() const override { return m_strInstData.c_str(); } + void Load(const char* chrIn) override; + + void Update(uint32 uiDiff) override; + + protected: + void HandleThrallRelocation(); + void UpdateLodgeQuestCredit(); + + uint32 m_auiEncounter[MAX_ENCOUNTER]; + std::string m_strInstData; + + uint32 m_uiBarrelCount; + uint32 m_uiThrallEventCount; + uint32 m_uiThrallResetTimer; + + GuidList m_lRoaringFlamesList; + GuidList m_lLeftPrisonersList; + GuidList m_lRightPrisonersList; +}; + +#endif diff --git a/src/modules/SD2/scripts/kalimdor/caverns_of_time/well_of_eternity/boss_mannoroth_and_varothen.cpp b/src/modules/SD2/scripts/kalimdor/caverns_of_time/well_of_eternity/boss_mannoroth_and_varothen.cpp new file mode 100644 index 000000000..7d46435c5 --- /dev/null +++ b/src/modules/SD2/scripts/kalimdor/caverns_of_time/well_of_eternity/boss_mannoroth_and_varothen.cpp @@ -0,0 +1,28 @@ +/* Copyright (C) 2006 - 2013 ScriptDev2 + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +/* ScriptData +SDName: boss_mannoroth_and_varothen +SD%Complete: 0 +SDComment: Placeholder +SDCategory: Well of Eternity +EndScriptData */ + +#include "precompiled.h" + +void AddSC_boss_mannoroth_and_varothen() +{ +} diff --git a/src/modules/SD2/scripts/kalimdor/caverns_of_time/well_of_eternity/boss_perotharn.cpp b/src/modules/SD2/scripts/kalimdor/caverns_of_time/well_of_eternity/boss_perotharn.cpp new file mode 100644 index 000000000..6d31fe95a --- /dev/null +++ b/src/modules/SD2/scripts/kalimdor/caverns_of_time/well_of_eternity/boss_perotharn.cpp @@ -0,0 +1,28 @@ +/* Copyright (C) 2006 - 2013 ScriptDev2 + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +/* ScriptData +SDName: boss_perotharn +SD%Complete: 0 +SDComment: Placeholder +SDCategory: Well of Eternity +EndScriptData */ + +#include "precompiled.h" + +void AddSC_boss_perotharn() +{ +} diff --git a/src/modules/SD2/scripts/kalimdor/caverns_of_time/well_of_eternity/boss_queen_azshara.cpp b/src/modules/SD2/scripts/kalimdor/caverns_of_time/well_of_eternity/boss_queen_azshara.cpp new file mode 100644 index 000000000..cc3975e2e --- /dev/null +++ b/src/modules/SD2/scripts/kalimdor/caverns_of_time/well_of_eternity/boss_queen_azshara.cpp @@ -0,0 +1,28 @@ +/* Copyright (C) 2006 - 2013 ScriptDev2 + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +/* ScriptData +SDName: boss_queen_azshara +SD%Complete: 0 +SDComment: Placeholder +SDCategory: Well of Eternity +EndScriptData */ + +#include "precompiled.h" + +void AddSC_boss_queen_azshara() +{ +} diff --git a/src/modules/SD2/scripts/kalimdor/caverns_of_time/well_of_eternity/instance_well_of_eternity.cpp b/src/modules/SD2/scripts/kalimdor/caverns_of_time/well_of_eternity/instance_well_of_eternity.cpp new file mode 100644 index 000000000..f1ef70fc0 --- /dev/null +++ b/src/modules/SD2/scripts/kalimdor/caverns_of_time/well_of_eternity/instance_well_of_eternity.cpp @@ -0,0 +1,28 @@ +/* Copyright (C) 2006 - 2013 ScriptDev2 + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along 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_well_of_eternity +SD%Complete: 0 +SDComment: Placeholder +SDCategory: Well of Eternity +EndScriptData */ + +#include "precompiled.h" + +void AddSC_instance_well_of_eternity() +{ +} diff --git a/src/modules/SD2/scripts/kalimdor/caverns_of_time/well_of_eternity/well_of_eternity.h b/src/modules/SD2/scripts/kalimdor/caverns_of_time/well_of_eternity/well_of_eternity.h new file mode 100644 index 000000000..6b5f9ef47 --- /dev/null +++ b/src/modules/SD2/scripts/kalimdor/caverns_of_time/well_of_eternity/well_of_eternity.h @@ -0,0 +1,3 @@ +/* Copyright (C) 2006 - 2013 ScriptDev2 + * This program is free software licensed under GPL version 2 + * Please see the included DOCS/LICENSE.TXT for more information */ diff --git a/src/modules/SD2/scripts/kalimdor/darkshore.cpp b/src/modules/SD2/scripts/kalimdor/darkshore.cpp new file mode 100644 index 000000000..63a5212ed --- /dev/null +++ b/src/modules/SD2/scripts/kalimdor/darkshore.cpp @@ -0,0 +1,775 @@ +/* Copyright (C) 2006 - 2013 ScriptDev2 + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along 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, 945, 994, 995, 2078, 2118, 5321 +SDCategory: Darkshore +EndScriptData */ + +/* ContentData +npc_kerlonian +npc_prospector_remtravel +npc_threshwackonator +npc_volcor +npc_therylune +npc_rabid_bear +EndContentData */ + +#include "precompiled.h" +#include "escort_ai.h" +#include "follower_ai.h" + +/*#### +# npc_kerlonian +####*/ + +enum +{ + 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 +}; + +// TODO: make concept similar as "ringo" -escort. Find a way to run the scripted attacks, _if_ player are choosing road. +struct npc_kerlonianAI : public FollowerAI +{ + npc_kerlonianAI(Creature* pCreature) : FollowerAI(pCreature) { Reset(); } + + uint32 m_uiFallAsleepTimer; + + void Reset() override + { + m_uiFallAsleepTimer = urand(10000, 45000); + } + + void MoveInLineOfSight(Unit* pWho) override + { + FollowerAI::MoveInLineOfSight(pWho); + + if (!m_creature->getVictim() && !HasFollowState(STATE_FOLLOW_COMPLETE) && pWho->GetEntry() == NPC_LILADRIS) + { + if (m_creature->IsWithinDistInMap(pWho, INTERACTION_DISTANCE * 5)) + { + if (Player* pPlayer = GetLeaderForFollower()) + { + 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) override + { + if (HasFollowState(STATE_FOLLOW_INPROGRESS | STATE_FOLLOW_PAUSED) && pSpell->Id == SPELL_AWAKEN) + ClearSleeping(); + } + + void SetSleeping() + { + SetFollowPaused(true); + + switch (urand(0, 2)) + { + case 0: DoScriptText(EMOTE_KER_SLEEP_1, m_creature); break; + case 1: DoScriptText(EMOTE_KER_SLEEP_2, m_creature); break; + case 2: DoScriptText(EMOTE_KER_SLEEP_3, m_creature); break; + } + + switch (urand(0, 3)) + { + case 0: DoScriptText(SAY_KER_SLEEP_1, m_creature); break; + case 1: DoScriptText(SAY_KER_SLEEP_2, m_creature); break; + case 2: DoScriptText(SAY_KER_SLEEP_3, m_creature); break; + case 3: DoScriptText(SAY_KER_SLEEP_4, m_creature); break; + } + + 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) override + { + if (!m_creature->SelectHostileTarget() || !m_creature->getVictim()) + { + if (!HasFollowState(STATE_FOLLOW_INPROGRESS)) + return; + + if (!HasFollowState(STATE_FOLLOW_PAUSED)) + { + if (m_uiFallAsleepTimer < uiDiff) + { + SetSleeping(); + m_uiFallAsleepTimer = urand(25000, 90000); + } + 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) + { + if (npc_kerlonianAI* pKerlonianAI = dynamic_cast(pCreature->AI())) + { + pCreature->SetStandState(UNIT_STAND_STATE_STAND); + DoScriptText(SAY_KER_START, pCreature, pPlayer); + pKerlonianAI->StartFollow(pPlayer, FACTION_ESCORT_N_FRIEND_PASSIVE, pQuest); + } + } + + return true; +} + +/*#### +# npc_prospector_remtravel +####*/ + +enum +{ + SAY_REM_START = -1000327, + SAY_REM_AGGRO = -1000339, + SAY_REM_RAMP1_1 = -1000328, + SAY_REM_RAMP1_2 = -1000329, + SAY_REM_BOOK = -1000330, + SAY_REM_TENT1_1 = -1000331, + SAY_REM_TENT1_2 = -1000332, + SAY_REM_MOSS = -1000333, + EMOTE_REM_MOSS = -1000334, + SAY_REM_MOSS_PROGRESS = -1000335, + SAY_REM_PROGRESS = -1000336, + SAY_REM_REMEMBER = -1000337, + EMOTE_REM_END = -1000338, + + QUEST_ABSENT_MINDED_PT2 = 731, + NPC_GRAVEL_SCOUT = 2158, + NPC_GRAVEL_BONE = 2159, + NPC_GRAVEL_GEO = 2160 +}; + +struct npc_prospector_remtravelAI : public npc_escortAI +{ + npc_prospector_remtravelAI(Creature* pCreature) : npc_escortAI(pCreature) { Reset(); } + + void WaypointReached(uint32 uiPointId) override + { + Player* pPlayer = GetPlayerForEscort(); + + if (!pPlayer) + return; + + switch (uiPointId) + { + case 0: + DoScriptText(SAY_REM_START, m_creature, pPlayer); + break; + case 5: + DoScriptText(SAY_REM_RAMP1_1, m_creature, pPlayer); + break; + case 6: + DoSpawnCreature(NPC_GRAVEL_SCOUT, -10.0f, 5.0f, 0.0f, 0.0f, TEMPSUMMON_TIMED_OOC_DESPAWN, 30000); + DoSpawnCreature(NPC_GRAVEL_BONE, -10.0f, 7.0f, 0.0f, 0.0f, TEMPSUMMON_TIMED_OOC_DESPAWN, 30000); + break; + case 9: + DoScriptText(SAY_REM_RAMP1_2, m_creature, pPlayer); + break; + case 14: + // depend quest rewarded? + DoScriptText(SAY_REM_BOOK, m_creature, pPlayer); + break; + case 15: + DoScriptText(SAY_REM_TENT1_1, m_creature, pPlayer); + break; + case 16: + DoSpawnCreature(NPC_GRAVEL_SCOUT, -10.0f, 5.0f, 0.0f, 0.0f, TEMPSUMMON_TIMED_OOC_DESPAWN, 30000); + DoSpawnCreature(NPC_GRAVEL_BONE, -10.0f, 7.0f, 0.0f, 0.0f, TEMPSUMMON_TIMED_OOC_DESPAWN, 30000); + break; + case 17: + DoScriptText(SAY_REM_TENT1_2, m_creature, pPlayer); + break; + case 26: + DoScriptText(SAY_REM_MOSS, m_creature, pPlayer); + break; + case 27: + DoScriptText(EMOTE_REM_MOSS, m_creature, pPlayer); + break; + case 28: + DoScriptText(SAY_REM_MOSS_PROGRESS, m_creature, pPlayer); + break; + case 29: + DoSpawnCreature(NPC_GRAVEL_SCOUT, -15.0f, 3.0f, 0.0f, 0.0f, TEMPSUMMON_TIMED_OOC_DESPAWN, 30000); + DoSpawnCreature(NPC_GRAVEL_BONE, -15.0f, 5.0f, 0.0f, 0.0f, TEMPSUMMON_TIMED_OOC_DESPAWN, 30000); + DoSpawnCreature(NPC_GRAVEL_GEO, -15.0f, 7.0f, 0.0f, 0.0f, TEMPSUMMON_TIMED_OOC_DESPAWN, 30000); + break; + case 31: + DoScriptText(SAY_REM_PROGRESS, m_creature, pPlayer); + break; + case 41: + DoScriptText(SAY_REM_REMEMBER, m_creature, pPlayer); + break; + case 42: + DoScriptText(EMOTE_REM_END, m_creature, pPlayer); + pPlayer->GroupEventHappens(QUEST_ABSENT_MINDED_PT2, m_creature); + break; + } + } + + void Reset() override { } + + void Aggro(Unit* pWho) override + { + if (urand(0, 1)) + DoScriptText(SAY_REM_AGGRO, m_creature, pWho); + } + + void JustSummoned(Creature* /*pSummoned*/) override + { + // 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) + { + pCreature->SetFactionTemporary(FACTION_ESCORT_A_NEUTRAL_PASSIVE, TEMPFACTION_RESTORE_RESPAWN); + + if (npc_prospector_remtravelAI* pEscortAI = dynamic_cast(pCreature->AI())) + pEscortAI->Start(false, pPlayer, pQuest, true); + } + + return true; +} + +/*#### +# npc_threshwackonator +####*/ + +enum +{ + EMOTE_START = -1000325, + SAY_AT_CLOSE = -1000326, + QUEST_GYROMAST_REV = 2078, + NPC_GELKAK = 6667, + FACTION_HOSTILE = 14 +}; + +#define GOSSIP_ITEM_INSERT_KEY "[PH] Insert key" + +struct npc_threshwackonatorAI : public FollowerAI +{ + npc_threshwackonatorAI(Creature* pCreature) : FollowerAI(pCreature) { Reset(); } + + void Reset() override {} + + void MoveInLineOfSight(Unit* pWho) override + { + FollowerAI::MoveInLineOfSight(pWho); + + if (!m_creature->getVictim() && !HasFollowState(STATE_FOLLOW_COMPLETE) && pWho->GetEntry() == NPC_GELKAK) + { + if (m_creature->IsWithinDistInMap(pWho, 10.0f)) + { + DoScriptText(SAY_AT_CLOSE, pWho); + DoAtEnd(); + } + } + } + + void DoAtEnd() + { + m_creature->SetFactionTemporary(FACTION_HOSTILE, TEMPFACTION_RESTORE_RESPAWN); + + if (Player* pHolder = GetLeaderForFollower()) + m_creature->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(pPlayer->GetGossipTextId(pCreature), pCreature->GetObjectGuid()); + 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 = dynamic_cast(pCreature->AI())) + { + DoScriptText(EMOTE_START, pCreature); + pThreshAI->StartFollow(pPlayer); + } + } + + return true; +} + +/*###### +# npc_volcor +######*/ + +enum +{ + SAY_START = -1000789, + SAY_END = -1000790, + SAY_FIRST_AMBUSH = -1000791, + SAY_AGGRO_1 = -1000792, + SAY_AGGRO_2 = -1000793, + SAY_AGGRO_3 = -1000794, + + SAY_ESCAPE = -1000195, + + NPC_BLACKWOOD_SHAMAN = 2171, + NPC_BLACKWOOD_URSA = 2170, + + SPELL_MOONSTALKER_FORM = 10849, + + WAYPOINT_ID_QUEST_STEALTH = 16, + FACTION_FRIENDLY = 35, + + QUEST_ESCAPE_THROUGH_FORCE = 994, + QUEST_ESCAPE_THROUGH_STEALTH = 995, +}; + +struct SummonLocation +{ + float m_fX, m_fY, m_fZ, m_fO; +}; + +// Spawn locations +static const SummonLocation aVolcorSpawnLocs[] = +{ + {4630.2f, 22.6f, 70.1f, 2.4f}, + {4603.8f, 53.5f, 70.4f, 5.4f}, + {4627.5f, 100.4f, 62.7f, 5.8f}, + {4692.8f, 75.8f, 56.7f, 3.1f}, + {4747.8f, 152.8f, 54.6f, 2.4f}, + {4711.7f, 109.1f, 53.5f, 2.4f}, +}; + +struct npc_volcorAI : public npc_escortAI +{ + npc_volcorAI(Creature* pCreature) : npc_escortAI(pCreature) { Reset(); } + + uint32 m_uiQuestId; + + void Reset() override + { + if (!HasEscortState(STATE_ESCORT_ESCORTING)) + m_uiQuestId = 0; + } + + void Aggro(Unit* /*pWho*/) override + { + // shouldn't always use text on agro + switch (urand(0, 4)) + { + case 0: DoScriptText(SAY_AGGRO_1, m_creature); break; + case 1: DoScriptText(SAY_AGGRO_2, m_creature); break; + case 2: DoScriptText(SAY_AGGRO_3, m_creature); break; + } + } + + void MoveInLineOfSight(Unit* pWho) override + { + // No combat for this quest + if (m_uiQuestId == QUEST_ESCAPE_THROUGH_STEALTH) + return; + + npc_escortAI::MoveInLineOfSight(pWho); + } + + void JustSummoned(Creature* pSummoned) override + { + pSummoned->AI()->AttackStart(m_creature); + } + + // Wrapper to handle start function for both quests + void StartEscort(Player* pPlayer, const Quest* pQuest) + { + m_creature->SetStandState(UNIT_STAND_STATE_STAND); + m_creature->SetFacingToObject(pPlayer); + m_uiQuestId = pQuest->GetQuestId(); + + if (pQuest->GetQuestId() == QUEST_ESCAPE_THROUGH_STEALTH) + { + // Note: faction may not be correct, but only this way works fine + m_creature->SetFactionTemporary(FACTION_FRIENDLY, TEMPFACTION_RESTORE_RESPAWN); + + Start(true, pPlayer, pQuest); + SetEscortPaused(true); + SetCurrentWaypoint(WAYPOINT_ID_QUEST_STEALTH); + SetEscortPaused(false); + } + else + Start(false, pPlayer, pQuest); + } + + void WaypointReached(uint32 uiPointId) override + { + switch (uiPointId) + { + case 2: + DoScriptText(SAY_START, m_creature); + break; + case 5: + m_creature->SummonCreature(NPC_BLACKWOOD_SHAMAN, aVolcorSpawnLocs[0].m_fX, aVolcorSpawnLocs[0].m_fY, aVolcorSpawnLocs[0].m_fZ, aVolcorSpawnLocs[0].m_fO, TEMPSUMMON_TIMED_OOC_DESPAWN, 20000); + m_creature->SummonCreature(NPC_BLACKWOOD_URSA, aVolcorSpawnLocs[1].m_fX, aVolcorSpawnLocs[1].m_fY, aVolcorSpawnLocs[1].m_fZ, aVolcorSpawnLocs[1].m_fO, TEMPSUMMON_TIMED_OOC_DESPAWN, 20000); + break; + case 6: + DoScriptText(SAY_FIRST_AMBUSH, m_creature); + break; + case 11: + m_creature->SummonCreature(NPC_BLACKWOOD_SHAMAN, aVolcorSpawnLocs[2].m_fX, aVolcorSpawnLocs[2].m_fY, aVolcorSpawnLocs[2].m_fZ, aVolcorSpawnLocs[2].m_fO, TEMPSUMMON_TIMED_OOC_DESPAWN, 20000); + m_creature->SummonCreature(NPC_BLACKWOOD_URSA, aVolcorSpawnLocs[3].m_fX, aVolcorSpawnLocs[3].m_fY, aVolcorSpawnLocs[3].m_fZ, aVolcorSpawnLocs[3].m_fO, TEMPSUMMON_TIMED_OOC_DESPAWN, 20000); + case 13: + m_creature->SummonCreature(NPC_BLACKWOOD_URSA, aVolcorSpawnLocs[4].m_fX, aVolcorSpawnLocs[4].m_fY, aVolcorSpawnLocs[4].m_fZ, aVolcorSpawnLocs[4].m_fO, TEMPSUMMON_TIMED_OOC_DESPAWN, 20000); + m_creature->SummonCreature(NPC_BLACKWOOD_URSA, aVolcorSpawnLocs[5].m_fX, aVolcorSpawnLocs[5].m_fY, aVolcorSpawnLocs[5].m_fZ, aVolcorSpawnLocs[5].m_fO, TEMPSUMMON_TIMED_OOC_DESPAWN, 20000); + break; + case 15: + DoScriptText(SAY_END, m_creature); + if (Player* pPlayer = GetPlayerForEscort()) + pPlayer->GroupEventHappens(QUEST_ESCAPE_THROUGH_FORCE, m_creature); + SetEscortPaused(true); + m_creature->ForcedDespawn(10000); + break; + // Quest 995 waypoints + case 16: + m_creature->HandleEmote(EMOTE_ONESHOT_BOW); + break; + case 17: + if (Player* pPlayer = GetPlayerForEscort()) + DoScriptText(SAY_ESCAPE, m_creature, pPlayer); + break; + case 18: + DoCastSpellIfCan(m_creature, SPELL_MOONSTALKER_FORM); + break; + case 24: + if (Player* pPlayer = GetPlayerForEscort()) + pPlayer->GroupEventHappens(QUEST_ESCAPE_THROUGH_STEALTH, m_creature); + break; + } + } +}; + +CreatureAI* GetAI_npc_volcor(Creature* pCreature) +{ + return new npc_volcorAI(pCreature); +} + +bool QuestAccept_npc_volcor(Player* pPlayer, Creature* pCreature, const Quest* pQuest) +{ + if (pQuest->GetQuestId() == QUEST_ESCAPE_THROUGH_FORCE || pQuest->GetQuestId() == QUEST_ESCAPE_THROUGH_STEALTH) + { + if (npc_volcorAI* pEscortAI = dynamic_cast(pCreature->AI())) + pEscortAI->StartEscort(pPlayer, pQuest); + } + + return true; +} + +/*#### +# npc_therylune +####*/ + +enum +{ + SAY_THERYLUNE_START = -1000905, + SAY_THERYLUNE_FINISH = -1000906, + + NPC_THERYSIL = 3585, + + QUEST_ID_THERYLUNE_ESCAPE = 945, +}; + +struct npc_theryluneAI : public npc_escortAI +{ + npc_theryluneAI(Creature* pCreature) : npc_escortAI(pCreature) { Reset(); } + + + void Reset() override {} + + void WaypointReached(uint32 uiPointId) override + { + switch (uiPointId) + { + case 17: + if (Player* pPlayer = GetPlayerForEscort()) + pPlayer->GroupEventHappens(QUEST_ID_THERYLUNE_ESCAPE, m_creature); + break; + case 19: + if (Player* pPlayer = GetPlayerForEscort()) + DoScriptText(SAY_THERYLUNE_FINISH, m_creature, pPlayer); + SetRun(); + break; + } + } +}; + +CreatureAI* GetAI_npc_therylune(Creature* pCreature) +{ + return new npc_theryluneAI(pCreature); +} + +bool QuestAccept_npc_therylune(Player* pPlayer, Creature* pCreature, const Quest* pQuest) +{ + if (pQuest->GetQuestId() == QUEST_ID_THERYLUNE_ESCAPE) + { + if (npc_theryluneAI* pEscortAI = dynamic_cast(pCreature->AI())) + { + pEscortAI->Start(false, pPlayer, pQuest); + DoScriptText(SAY_THERYLUNE_START, pCreature, pPlayer); + } + } + + return true; +} + +/*###### +## npc_rabid_bear +######*/ + +enum +{ + QUEST_PLAGUED_LANDS = 2118, + + NPC_CAPTURED_RABID_THISTLE_BEAR = 11836, + NPC_THARNARIUN_TREETENDER = 3701, // Npc related to quest-outro + GO_NIGHT_ELVEN_BEAR_TRAP = 111148, // This is actually the (visual) spell-focus GO + + SPELL_RABIES = 3150, // Spell used in comabt +}; + +struct npc_rabid_bearAI : public ScriptedAI +{ + npc_rabid_bearAI(Creature* pCreature) : ScriptedAI(pCreature) + { + Reset(); + JustRespawned(); + } + + uint32 m_uiCheckTimer; + uint32 m_uiRabiesTimer; + uint32 m_uiDespawnTimer; + + void Reset() override + { + m_uiRabiesTimer = urand(12000, 18000); + } + + void JustRespawned() override + { + m_uiCheckTimer = 1000; + m_uiDespawnTimer = 0; + } + + void MoveInLineOfSight(Unit* pWho) override + { + if (!m_uiCheckTimer && pWho->GetTypeId() == TYPEID_UNIT && pWho->GetEntry() == NPC_THARNARIUN_TREETENDER && + pWho->IsWithinDist(m_creature, 2 * INTERACTION_DISTANCE, false)) + { + // Possible related spell: 9455 9372 + m_creature->ForcedDespawn(1000); + m_creature->GetMotionMaster()->MoveIdle(); + + return; + } + + ScriptedAI::MoveInLineOfSight(pWho); + } + + void UpdateAI(const uint32 uiDiff) override + { + if (m_uiCheckTimer && m_creature->IsInCombat()) + { + if (m_uiCheckTimer <= uiDiff) + { + // Trap nearby? + if (GameObject* pTrap = GetClosestGameObjectWithEntry(m_creature, GO_NIGHT_ELVEN_BEAR_TRAP, 0.5f)) + { + // Despawn trap + pTrap->Use(m_creature); + // "Evade" + m_creature->RemoveAllAurasOnEvade(); + m_creature->DeleteThreatList(); + m_creature->CombatStop(true); + m_creature->SetLootRecipient(NULL); + Reset(); + // Update Entry and start following player + m_creature->UpdateEntry(NPC_CAPTURED_RABID_THISTLE_BEAR); + // get player + Unit* pTrapOwner = pTrap->GetOwner(); + if (pTrapOwner && pTrapOwner->GetTypeId() == TYPEID_PLAYER && + ((Player*)pTrapOwner)->GetQuestStatus(QUEST_PLAGUED_LANDS) == QUEST_STATUS_INCOMPLETE) + { + ((Player*)pTrapOwner)->KilledMonsterCredit(m_creature->GetEntry(), m_creature->GetObjectGuid()); + m_creature->GetMotionMaster()->MoveFollow(pTrapOwner, PET_FOLLOW_DIST, PET_FOLLOW_ANGLE); + } + else // Something unexpected happened + m_creature->ForcedDespawn(1000); + + // No need to check any more + m_uiCheckTimer = 0; + // Despawn after a while (delay guesswork) + m_uiDespawnTimer = 3 * MINUTE * IN_MILLISECONDS; + + return; + } + else + m_uiCheckTimer = 1000; + } + else + m_uiCheckTimer -= uiDiff; + } + + if (m_uiDespawnTimer && !m_creature->IsInCombat()) + { + if (m_uiDespawnTimer <= uiDiff) + { + m_creature->ForcedDespawn(); + return; + } + else + m_uiDespawnTimer -= uiDiff; + } + + if (!m_creature->SelectHostileTarget() || !m_creature->getVictim()) + return; + + if (m_uiRabiesTimer < uiDiff) + { + if (Unit* pTarget = m_creature->SelectAttackingTarget(ATTACKING_TARGET_TOPAGGRO, 0, SPELL_RABIES)) + DoCastSpellIfCan(pTarget, SPELL_RABIES); + m_uiRabiesTimer = urand(12000, 18000); + } + else + m_uiRabiesTimer -= uiDiff; + + DoMeleeAttackIfReady(); + } +}; + +CreatureAI* GetAI_npc_rabid_bear(Creature* pCreature) +{ + return new npc_rabid_bearAI(pCreature); +} + +void AddSC_darkshore() +{ + Script* pNewScript; + + pNewScript = new Script; + pNewScript->Name = "npc_kerlonian"; + pNewScript->GetAI = &GetAI_npc_kerlonian; + pNewScript->pQuestAcceptNPC = &QuestAccept_npc_kerlonian; + pNewScript->RegisterSelf(); + + pNewScript = new Script; + pNewScript->Name = "npc_prospector_remtravel"; + pNewScript->GetAI = &GetAI_npc_prospector_remtravel; + pNewScript->pQuestAcceptNPC = &QuestAccept_npc_prospector_remtravel; + pNewScript->RegisterSelf(); + + pNewScript = new Script; + pNewScript->Name = "npc_threshwackonator"; + pNewScript->GetAI = &GetAI_npc_threshwackonator; + pNewScript->pGossipHello = &GossipHello_npc_threshwackonator; + pNewScript->pGossipSelect = &GossipSelect_npc_threshwackonator; + pNewScript->RegisterSelf(); + + pNewScript = new Script; + pNewScript->Name = "npc_volcor"; + pNewScript->GetAI = &GetAI_npc_volcor; + pNewScript->pQuestAcceptNPC = &QuestAccept_npc_volcor; + pNewScript->RegisterSelf(); + + pNewScript = new Script; + pNewScript->Name = "npc_therylune"; + pNewScript->GetAI = &GetAI_npc_therylune; + pNewScript->pQuestAcceptNPC = &QuestAccept_npc_therylune; + pNewScript->RegisterSelf(); + + pNewScript = new Script; + pNewScript->Name = "npc_rabid_bear"; + pNewScript->GetAI = &GetAI_npc_rabid_bear; + pNewScript->RegisterSelf(); +} diff --git a/src/modules/SD2/scripts/kalimdor/desolace.cpp b/src/modules/SD2/scripts/kalimdor/desolace.cpp new file mode 100644 index 000000000..b29955f30 --- /dev/null +++ b/src/modules/SD2/scripts/kalimdor/desolace.cpp @@ -0,0 +1,417 @@ +/* Copyright (C) 2006 - 2013 ScriptDev2 + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along 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: 1440, 5561, 6132 +SDCategory: Desolace +EndScriptData */ + +/* ContentData +npc_aged_dying_ancient_kodo +npc_dalinda_malem +npc_melizza_brimbuzzle +EndContentData */ + +#include "precompiled.h" +#include "escort_ai.h" + +/*###### +## npc_aged_dying_ancient_kodo +######*/ + +enum +{ + SAY_SMEED_HOME_1 = -1000348, + SAY_SMEED_HOME_2 = -1000349, + SAY_SMEED_HOME_3 = -1000350, + + 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 npc_aged_dying_ancient_kodoAI : public ScriptedAI +{ + npc_aged_dying_ancient_kodoAI(Creature* pCreature) : ScriptedAI(pCreature) { Reset(); } + + uint32 m_uiDespawnTimer; + + void Reset() override + { + m_uiDespawnTimer = 0; + } + + void MoveInLineOfSight(Unit* pWho) override + { + if (pWho->GetEntry() == NPC_SMEED) + { + if (m_creature->HasFlag(UNIT_NPC_FLAGS, UNIT_NPC_FLAG_GOSSIP)) + return; + + if (m_creature->IsWithinDistInMap(pWho, 10.0f)) + { + switch (urand(0, 2)) + { + case 0: DoScriptText(SAY_SMEED_HOME_1, pWho); break; + case 1: DoScriptText(SAY_SMEED_HOME_2, pWho); break; + case 2: DoScriptText(SAY_SMEED_HOME_3, pWho); break; + } + + // 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) override + { + if (pSpell->Id == SPELL_KODO_KOMBO_GOSSIP) + { + m_creature->SetFlag(UNIT_NPC_FLAGS, UNIT_NPC_FLAG_GOSSIP); + m_uiDespawnTimer = 60000; + } + } + + void UpdateAI(const uint32 diff) override + { + // timer should always be == 0 unless we already updated entry of creature. Then not expect this updated to ever be in combat. + if (m_uiDespawnTimer && m_uiDespawnTimer <= diff) + { + if (!m_creature->getVictim() && m_creature->IsAlive()) + { + Reset(); + m_creature->SetDeathState(JUST_DIED); + m_creature->Respawn(); + return; + } + } + else m_uiDespawnTimer -= diff; + + if (!m_creature->SelectHostileTarget() || !m_creature->getVictim()) + 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, SpellEffectIndex effIndex, Creature* pCreatureTarget, ObjectGuid /*originalCasterGuid*/) +{ + // always check spellid and effectindex + if (spellId == SPELL_KODO_KOMBO_ITEM && effIndex == EFFECT_INDEX_0) + { + // 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, PET_FOLLOW_ANGLE); + } + + // 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->GetObjectGuid()); + + pPlayer->RemoveAurasDueToSpell(SPELL_KODO_KOMBO_PLAYER_BUFF); + pCreature->GetMotionMaster()->MoveIdle(); + } + + pPlayer->SEND_GOSSIP_MENU(pPlayer->GetGossipTextId(pCreature), pCreature->GetObjectGuid()); + return true; +} + +/*###### +## npc_dalinda_malem +######*/ + +enum +{ + QUEST_RETURN_TO_VAHLARRIEL = 1440, +}; + +struct npc_dalinda_malemAI : public npc_escortAI +{ + npc_dalinda_malemAI(Creature* m_creature) : npc_escortAI(m_creature) { Reset(); } + + void Reset() override {} + + void JustStartedEscort() override + { + m_creature->SetStandState(UNIT_STAND_STATE_STAND); + } + + void WaypointReached(uint32 uiPointId) override + { + if (uiPointId == 18) + { + if (Player* pPlayer = GetPlayerForEscort()) + pPlayer->GroupEventHappens(QUEST_RETURN_TO_VAHLARRIEL, m_creature); + } + } +}; + +CreatureAI* GetAI_npc_dalinda_malem(Creature* pCreature) +{ + return new npc_dalinda_malemAI(pCreature); +} + +bool QuestAccept_npc_dalinda_malem(Player* pPlayer, Creature* pCreature, const Quest* pQuest) +{ + if (pQuest->GetQuestId() == QUEST_RETURN_TO_VAHLARRIEL) + { + if (npc_dalinda_malemAI* pEscortAI = dynamic_cast(pCreature->AI())) + { + // TODO This faction change needs confirmation, also possible that we need to drop her PASSIVE flag + pCreature->SetFactionTemporary(FACTION_ESCORT_A_NEUTRAL_PASSIVE, TEMPFACTION_RESTORE_RESPAWN | TEMPFACTION_TOGGLE_PASSIVE); + pEscortAI->Start(false, pPlayer, pQuest); + } + } + return true; +} + +/*###### +## npc_melizza_brimbuzzle +######*/ + +enum +{ + QUEST_GET_ME_OUT_OF_HERE = 6132, + + GO_MELIZZAS_CAGE = 177706, + + SAY_MELIZZA_START = -1000784, + SAY_MELIZZA_FINISH = -1000785, + SAY_MELIZZA_1 = -1000786, + SAY_MELIZZA_2 = -1000787, + SAY_MELIZZA_3 = -1000788, + + NPC_MARAUDINE_MARAUDER = 4659, + NPC_MARAUDINE_BONEPAW = 4660, + NPC_MARAUDINE_WRANGLER = 4655, + + NPC_MELIZZA = 12277, + + POINT_ID_QUEST_COMPLETE = 1, + POINT_ID_EVENT_COMPLETE = 2, + + MAX_MARAUDERS = 2, + MAX_WRANGLERS = 3, +}; + +static const DialogueEntry aIntroDialogue[] = +{ + {POINT_ID_QUEST_COMPLETE, 0, 1000}, + {QUEST_GET_ME_OUT_OF_HERE, NPC_MELIZZA, 0}, + {POINT_ID_EVENT_COMPLETE, 0, 2000}, + {SAY_MELIZZA_1, NPC_MELIZZA, 4000}, + {SAY_MELIZZA_2, NPC_MELIZZA, 5000}, + {SAY_MELIZZA_3, NPC_MELIZZA, 4000}, + {NPC_MELIZZA, 0, 0}, + {0, 0, 0}, +}; + +struct SummonLocation +{ + float m_fX, m_fY, m_fZ; +}; + +static const SummonLocation aMarauderSpawn[] = +{ + { -1291.492f, 2644.650f, 111.556f}, + { -1306.730f, 2675.163f, 111.561f}, +}; + +static const SummonLocation wranglerSpawn = { -1393.194f, 2429.465f, 88.689f }; + +struct npc_melizza_brimbuzzleAI : public npc_escortAI, private DialogueHelper +{ + npc_melizza_brimbuzzleAI(Creature* m_creature) : npc_escortAI(m_creature), + DialogueHelper(aIntroDialogue) + { + Reset(); + } + + void Reset() override {} + + void JustStartedEscort() override + { + if (GameObject* pCage = GetClosestGameObjectWithEntry(m_creature, GO_MELIZZAS_CAGE, INTERACTION_DISTANCE)) + pCage->UseDoorOrButton(); + } + + Creature* GetSpeakerByEntry(uint32 uiEntry) override + { + if (uiEntry == NPC_MELIZZA) + return m_creature; + + return NULL; + } + + void WaypointReached(uint32 uiPointId) override + { + switch (uiPointId) + { + case 1: + if (Player* pPlayer = GetPlayerForEscort()) + DoScriptText(SAY_MELIZZA_START, m_creature, pPlayer); + + m_creature->SetFactionTemporary(FACTION_ESCORT_N_NEUTRAL_PASSIVE, TEMPFACTION_RESTORE_RESPAWN); + break; + case 4: + for (uint8 i = 0; i < MAX_MARAUDERS; ++i) + { + for (uint8 j = 0; j < MAX_MARAUDERS; ++j) + { + // Summon 2 Marauders on each point + float fX, fY, fZ; + m_creature->GetRandomPoint(aMarauderSpawn[i].m_fX, aMarauderSpawn[i].m_fY, aMarauderSpawn[i].m_fZ, 7.0f, fX, fY, fZ); + m_creature->SummonCreature(NPC_MARAUDINE_MARAUDER, fX, fY, fZ, 0.0f, TEMPSUMMON_DEAD_DESPAWN, 0); + } + } + break; + case 9: + for (uint8 i = 0; i < MAX_WRANGLERS; ++i) + { + float fX, fY, fZ; + m_creature->GetRandomPoint(wranglerSpawn.m_fX, wranglerSpawn.m_fY, wranglerSpawn.m_fZ, 10.0f, fX, fY, fZ); + m_creature->SummonCreature(NPC_MARAUDINE_BONEPAW, fX, fY, fZ, 0.0f, TEMPSUMMON_DEAD_DESPAWN, 0); + + m_creature->GetRandomPoint(wranglerSpawn.m_fX, wranglerSpawn.m_fY, wranglerSpawn.m_fZ, 10.0f, fX, fY, fZ); + m_creature->SummonCreature(NPC_MARAUDINE_WRANGLER, fX, fY, fZ, 0.0f, TEMPSUMMON_DEAD_DESPAWN, 0); + } + break; + case 12: + StartNextDialogueText(POINT_ID_QUEST_COMPLETE); + break; + case 19: + StartNextDialogueText(POINT_ID_EVENT_COMPLETE); + break; + } + } + + void JustDidDialogueStep(int32 iEntry) override + { + switch (iEntry) + { + case POINT_ID_QUEST_COMPLETE: + SetEscortPaused(true); + break; + case QUEST_GET_ME_OUT_OF_HERE: + if (Player* pPlayer = GetPlayerForEscort()) + { + DoScriptText(SAY_MELIZZA_FINISH, m_creature, pPlayer); + pPlayer->GroupEventHappens(QUEST_GET_ME_OUT_OF_HERE, m_creature); + } + + m_creature->ClearTemporaryFaction(); + SetRun(true); + SetEscortPaused(false); + break; + case POINT_ID_EVENT_COMPLETE: + SetEscortPaused(true); + m_creature->SetFacingTo(4.71f); + break; + case NPC_MELIZZA: + SetEscortPaused(false); + break; + } + } + + void UpdateEscortAI(const uint32 uiDiff) override + { + DialogueUpdate(uiDiff); + + if (!m_creature->SelectHostileTarget() || !m_creature->getVictim()) + return; + + DoMeleeAttackIfReady(); + } +}; + +CreatureAI* GetAI_npc_melizza_brimbuzzle(Creature* pCreature) +{ + return new npc_melizza_brimbuzzleAI(pCreature); +} + +bool QuestAccept_npc_melizza_brimbuzzle(Player* pPlayer, Creature* pCreature, const Quest* pQuest) +{ + if (pQuest->GetQuestId() == QUEST_GET_ME_OUT_OF_HERE) + { + if (npc_melizza_brimbuzzleAI* pEscortAI = dynamic_cast(pCreature->AI())) + pEscortAI->Start(false, pPlayer, pQuest); + } + + return true; +} + +void AddSC_desolace() +{ + Script* pNewScript; + + pNewScript = new Script; + pNewScript->Name = "npc_aged_dying_ancient_kodo"; + pNewScript->GetAI = &GetAI_npc_aged_dying_ancient_kodo; + pNewScript->pEffectDummyNPC = &EffectDummyCreature_npc_aged_dying_ancient_kodo; + pNewScript->pGossipHello = &GossipHello_npc_aged_dying_ancient_kodo; + pNewScript->RegisterSelf(); + + pNewScript = new Script; + pNewScript->Name = "npc_dalinda_malem"; + pNewScript->GetAI = &GetAI_npc_dalinda_malem; + pNewScript->pQuestAcceptNPC = &QuestAccept_npc_dalinda_malem; + pNewScript->RegisterSelf(); + + pNewScript = new Script; + pNewScript->Name = "npc_melizza_brimbuzzle"; + pNewScript->GetAI = &GetAI_npc_melizza_brimbuzzle; + pNewScript->pQuestAcceptNPC = &QuestAccept_npc_melizza_brimbuzzle; + pNewScript->RegisterSelf(); +} diff --git a/src/modules/SD2/scripts/kalimdor/dire_maul/dire_maul.cpp b/src/modules/SD2/scripts/kalimdor/dire_maul/dire_maul.cpp new file mode 100644 index 000000000..28d5ed68d --- /dev/null +++ b/src/modules/SD2/scripts/kalimdor/dire_maul/dire_maul.cpp @@ -0,0 +1,28 @@ +/* Copyright (C) 2006 - 2013 ScriptDev2 + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +/* ScriptData +SDName: dire_maul +SD%Complete: 0 +SDComment: Placeholder +SDCategory: Dire Maul +EndScriptData */ + +#include "precompiled.h" + +void AddSC_dire_maul() +{ +} diff --git a/src/modules/SD2/scripts/kalimdor/dire_maul/dire_maul.h b/src/modules/SD2/scripts/kalimdor/dire_maul/dire_maul.h new file mode 100644 index 000000000..29d5c44b2 --- /dev/null +++ b/src/modules/SD2/scripts/kalimdor/dire_maul/dire_maul.h @@ -0,0 +1,135 @@ +/* Copyright (C) 2006 - 2013 ScriptDev2 + * This program is free software licensed under GPL version 2 + * Please see the included DOCS/LICENSE.TXT for more information */ + +#ifndef DEF_DM_H +#define DEF_DM_H + +enum +{ + MAX_ENCOUNTER = 16, + MAX_GENERATORS = 5, + + // East + TYPE_ALZZIN = 0, // Do not change - Handled with Acid + TYPE_ZEVRIM = 1, + TYPE_IRONBARK = 2, + + // West + TYPE_WARPWOOD = 3, + TYPE_IMMOLTHAR = 4, + TYPE_PRINCE = 5, + TYPE_PYLON_1 = 6, + TYPE_PYLON_2 = TYPE_PYLON_1 + 1, + TYPE_PYLON_3 = TYPE_PYLON_1 + 2, + TYPE_PYLON_4 = TYPE_PYLON_1 + 3, + TYPE_PYLON_5 = TYPE_PYLON_1 + 4, + + // North + TYPE_KING_GORDOK = 11, + TYPE_MOLDAR = 12, + TYPE_FENGUS = 13, + TYPE_SLIPKIK = 14, + TYPE_KROMCRUSH = 15, + + // East + GO_CRUMBLE_WALL = 177220, + GO_CORRUPT_VINE = 179502, + GO_FELVINE_SHARD = 179559, + GO_CONSERVATORY_DOOR = 176907, // Opened by Ironbark the redeemed + + NPC_ZEVRIM_THORNHOOF = 11490, + NPC_OLD_IRONBARK = 11491, + NPC_IRONBARK_REDEEMED = 14241, + + // West + NPC_TENDRIS_WARPWOOD = 11489, + NPC_PRINCE_TORTHELDRIN = 11486, + NPC_IMMOLTHAR = 11496, + NPC_ARCANE_ABERRATION = 11480, + NPC_MANA_REMNANT = 11483, + NPC_HIGHBORNE_SUMMONER = 11466, + + GO_PRINCES_CHEST = 179545, + GO_PRINCES_CHEST_AURA = 179563, + GO_CRYSTAL_GENERATOR_1 = 177259, + GO_CRYSTAL_GENERATOR_2 = 177257, + GO_CRYSTAL_GENERATOR_3 = 177258, + GO_CRYSTAL_GENERATOR_4 = 179504, + GO_CRYSTAL_GENERATOR_5 = 179505, + GO_FORCEFIELD = 179503, + GO_WARPWOOD_DOOR = 177221, + GO_WEST_LIBRARY_DOOR = 179550, + + // North + NPC_GUARD_MOLDAR = 14326, + NPC_STOMPER_KREEG = 14322, + NPC_GUARD_FENGUS = 14321, + NPC_GUARD_SLIPKIK = 14323, + NPC_CAPTAIN_KROMCRUSH = 14325, + NPC_CHORUSH = 14324, + NPC_KING_GORDOK = 11501, + NPC_MIZZLE_THE_CRAFTY = 14353, + + GO_KNOTS_CACHE = 179501, + GO_KNOTS_BALL_AND_CHAIN = 179511, + GO_GORDOK_TRIBUTE = 179564, + GO_NORTH_LIBRARY_DOOR = 179549, + + SAY_FREE_IMMOLTHAR = -1429000, + SAY_KILL_IMMOLTHAR = -1429001, + SAY_IRONBARK_REDEEM = -1429002, + + FACTION_HOSTILE = 14, + SPELL_KING_OF_GORDOK = 22799, +}; + +class instance_dire_maul : public ScriptedInstance +{ + public: + instance_dire_maul(Map* pMap); + ~instance_dire_maul() {} + + void Initialize() override; + + void OnPlayerEnter(Player* pPlayer) override; + + void OnCreatureCreate(Creature* pCreature) override; + void OnObjectCreate(GameObject* pGo) override; + + void SetData(uint32 uiType, uint32 uiData) override; + uint32 GetData(uint32 uiType) const override; + + void OnCreatureEnterCombat(Creature* pCreature) override; + void OnCreatureDeath(Creature* pCreature) override; + + const char* Save() const override { return m_strInstData.c_str(); } + void Load(const char* chrIn) override; + + bool CheckConditionCriteriaMeet(Player const* pPlayer, uint32 uiInstanceConditionId, WorldObject const* pConditionSource, uint32 conditionSourceType) const override; + + protected: + bool CheckAllGeneratorsDestroyed(); + void ProcessForceFieldOpening(); + void SortPylonGuards(); + void PylonGuardJustDied(Creature* pCreature); + + uint32 m_auiEncounter[MAX_ENCOUNTER]; + std::string m_strInstData; + + // East + bool m_bWallDestroyed; + GuidList m_lFelvineShardGUIDs; + + // West + ObjectGuid m_aCrystalGeneratorGuid[MAX_GENERATORS]; + + GuidList m_luiHighborneSummonerGUIDs; + GuidList m_lGeneratorGuardGUIDs; + std::set m_sSortedGeneratorGuards[MAX_GENERATORS]; + + // North + bool m_bDoNorthBeforeWest; +}; + +#endif diff --git a/src/modules/SD2/scripts/kalimdor/dire_maul/instance_dire_maul.cpp b/src/modules/SD2/scripts/kalimdor/dire_maul/instance_dire_maul.cpp new file mode 100644 index 000000000..5f1867497 --- /dev/null +++ b/src/modules/SD2/scripts/kalimdor/dire_maul/instance_dire_maul.cpp @@ -0,0 +1,510 @@ +/* Copyright (C) 2006 - 2013 ScriptDev2 + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along 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_dire_maul +SD%Complete: 30 +SDComment: Basic Support - Most events and quest-related stuff missing +SDCategory: Dire Maul +EndScriptData */ + +#include "precompiled.h" +#include "dire_maul.h" + +instance_dire_maul::instance_dire_maul(Map* pMap) : ScriptedInstance(pMap), + m_bWallDestroyed(false), + m_bDoNorthBeforeWest(false) +{ + Initialize(); +} + +void instance_dire_maul::Initialize() +{ + memset(&m_auiEncounter, 0, sizeof(m_auiEncounter)); +} + +void instance_dire_maul::OnPlayerEnter(Player* pPlayer) +{ + // figure where to enter to set library doors accordingly + // Enter DM North first + if (pPlayer->IsWithinDist2d(260.0f, -20.0f, 20.0f) && m_auiEncounter[TYPE_WARPWOOD] != DONE) + m_bDoNorthBeforeWest = true; + else + m_bDoNorthBeforeWest = false; + + DoToggleGameObjectFlags(GO_WEST_LIBRARY_DOOR, GO_FLAG_NO_INTERACT, m_bDoNorthBeforeWest); + DoToggleGameObjectFlags(GO_WEST_LIBRARY_DOOR, GO_FLAG_LOCKED, !m_bDoNorthBeforeWest); +} + +void instance_dire_maul::OnCreatureCreate(Creature* pCreature) +{ + switch (pCreature->GetEntry()) + { + // East + case NPC_OLD_IRONBARK: + break; + + // West + case NPC_PRINCE_TORTHELDRIN: + if (m_auiEncounter[TYPE_IMMOLTHAR] == DONE) + pCreature->SetFactionTemporary(FACTION_HOSTILE, TEMPFACTION_RESTORE_RESPAWN | TEMPFACTION_TOGGLE_OOC_NOT_ATTACK); + break; + case NPC_ARCANE_ABERRATION: + case NPC_MANA_REMNANT: + m_lGeneratorGuardGUIDs.push_back(pCreature->GetObjectGuid()); + return; + case NPC_IMMOLTHAR: + break; + case NPC_HIGHBORNE_SUMMONER: + m_luiHighborneSummonerGUIDs.push_back(pCreature->GetObjectGuid()); + return; + + // North + case NPC_CHORUSH: + case NPC_KING_GORDOK: + case NPC_MIZZLE_THE_CRAFTY: + break; + + default: + return; + } + m_mNpcEntryGuidStore[pCreature->GetEntry()] = pCreature->GetObjectGuid(); +} + +void instance_dire_maul::OnObjectCreate(GameObject* pGo) +{ + switch (pGo->GetEntry()) + { + // East + case GO_CONSERVATORY_DOOR: + if (m_auiEncounter[TYPE_IRONBARK] == DONE) + pGo->SetGoState(GO_STATE_ACTIVE); + break; + case GO_CRUMBLE_WALL: + if (m_bWallDestroyed || m_auiEncounter[TYPE_ALZZIN] == DONE) + pGo->SetGoState(GO_STATE_ACTIVE); + break; + case GO_CORRUPT_VINE: + if (m_auiEncounter[TYPE_ALZZIN] == DONE) + pGo->SetGoState(GO_STATE_ACTIVE); + break; + case GO_FELVINE_SHARD: + m_lFelvineShardGUIDs.push_back(pGo->GetObjectGuid()); + break; + + // West + case GO_CRYSTAL_GENERATOR_1: + m_aCrystalGeneratorGuid[0] = pGo->GetObjectGuid(); + if (m_auiEncounter[TYPE_PYLON_1] == DONE) + pGo->SetGoState(GO_STATE_ACTIVE); + return; + case GO_CRYSTAL_GENERATOR_2: + m_aCrystalGeneratorGuid[1] = pGo->GetObjectGuid(); + if (m_auiEncounter[TYPE_PYLON_2] == DONE) + pGo->SetGoState(GO_STATE_ACTIVE); + return; + case GO_CRYSTAL_GENERATOR_3: + m_aCrystalGeneratorGuid[2] = pGo->GetObjectGuid(); + if (m_auiEncounter[TYPE_PYLON_3] == DONE) + pGo->SetGoState(GO_STATE_ACTIVE); + return; + case GO_CRYSTAL_GENERATOR_4: + m_aCrystalGeneratorGuid[3] = pGo->GetObjectGuid(); + if (m_auiEncounter[TYPE_PYLON_4] == DONE) + pGo->SetGoState(GO_STATE_ACTIVE); + return; + case GO_CRYSTAL_GENERATOR_5: + m_aCrystalGeneratorGuid[4] = pGo->GetObjectGuid(); + if (m_auiEncounter[TYPE_PYLON_5] == DONE) + pGo->SetGoState(GO_STATE_ACTIVE); + return; + case GO_FORCEFIELD: + if (CheckAllGeneratorsDestroyed()) + pGo->SetGoState(GO_STATE_ACTIVE); + break; + case GO_PRINCES_CHEST_AURA: + break; + case GO_WARPWOOD_DOOR: + if (m_auiEncounter[TYPE_WARPWOOD] == DONE) + pGo->SetGoState(GO_STATE_ACTIVE); + break; + case GO_WEST_LIBRARY_DOOR: + pGo->SetFlag(GAMEOBJECT_FLAGS, m_bDoNorthBeforeWest ? GO_FLAG_NO_INTERACT : GO_FLAG_LOCKED); + pGo->RemoveFlag(GAMEOBJECT_FLAGS, m_bDoNorthBeforeWest ? GO_FLAG_LOCKED : GO_FLAG_NO_INTERACT); + break; + + // North + case GO_NORTH_LIBRARY_DOOR: + break; + + default: + return; + } + m_mGoEntryGuidStore[pGo->GetEntry()] = pGo->GetObjectGuid(); +} + +void instance_dire_maul::SetData(uint32 uiType, uint32 uiData) +{ + switch (uiType) + { + // East + case TYPE_ZEVRIM: + if (uiData == DONE) + { + // Update Old Ironbark so he can open the conservatory door + if (Creature* pIronbark = GetSingleCreatureFromStorage(NPC_OLD_IRONBARK)) + { + DoScriptText(SAY_IRONBARK_REDEEM, pIronbark); + pIronbark->UpdateEntry(NPC_IRONBARK_REDEEMED); + } + } + m_auiEncounter[uiType] = uiData; + break; + case TYPE_IRONBARK: + m_auiEncounter[uiType] = uiData; + break; + case TYPE_ALZZIN: // This Encounter is expected to be handled within Acid (reason handling at 50% hp) + if (uiData == DONE) + { + if (!m_bWallDestroyed) + { + DoUseDoorOrButton(GO_CRUMBLE_WALL); + m_bWallDestroyed = true; + } + + DoUseDoorOrButton(GO_CORRUPT_VINE); + + if (!m_lFelvineShardGUIDs.empty()) + { + for (GuidList::const_iterator itr = m_lFelvineShardGUIDs.begin(); itr != m_lFelvineShardGUIDs.end(); ++itr) + DoRespawnGameObject(*itr); + } + } + else if (uiData == SPECIAL && !m_bWallDestroyed) + { + DoUseDoorOrButton(GO_CRUMBLE_WALL); + m_bWallDestroyed = true; + } + m_auiEncounter[uiType] = uiData; + break; + + // West + case TYPE_WARPWOOD: + if (uiData == DONE) + DoUseDoorOrButton(GO_WARPWOOD_DOOR); + m_auiEncounter[uiType] = uiData; + break; + case TYPE_IMMOLTHAR: + if (uiData == DONE) + { + if (Creature* pPrince = GetSingleCreatureFromStorage(NPC_PRINCE_TORTHELDRIN)) + { + DoScriptText(SAY_FREE_IMMOLTHAR, pPrince); + pPrince->SetFactionTemporary(FACTION_HOSTILE, TEMPFACTION_RESTORE_RESPAWN | TEMPFACTION_TOGGLE_OOC_NOT_ATTACK); + // Despawn Chest-Aura + if (GameObject* pChestAura = GetSingleGameObjectFromStorage(GO_PRINCES_CHEST_AURA)) + pChestAura->Use(pPrince); + } + } + m_auiEncounter[uiType] = uiData; + break; + case TYPE_PRINCE: + m_auiEncounter[uiType] = uiData; + break; + case TYPE_PYLON_1: + case TYPE_PYLON_2: + case TYPE_PYLON_3: + case TYPE_PYLON_4: + case TYPE_PYLON_5: + m_auiEncounter[uiType] = uiData; + if (uiData == DONE) + { + DoUseDoorOrButton(m_aCrystalGeneratorGuid[uiType - TYPE_PYLON_1]); + if (CheckAllGeneratorsDestroyed()) + ProcessForceFieldOpening(); + } + break; + + // North + case TYPE_KING_GORDOK: + m_auiEncounter[uiType] = uiData; + if (uiData == DONE) + { + // Apply Aura to players in the map + Map::PlayerList const& players = instance->GetPlayers(); + for (Map::PlayerList::const_iterator itr = players.begin(); itr != players.end(); ++itr) + { + if (Player* pPlayer = itr->getSource()) + pPlayer->CastSpell(pPlayer, SPELL_KING_OF_GORDOK, true); + } + } + break; + case TYPE_MOLDAR: + case TYPE_FENGUS: + case TYPE_SLIPKIK: + case TYPE_KROMCRUSH: + m_auiEncounter[uiType] = uiData; + 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] << " " + << m_auiEncounter[12] << " " << m_auiEncounter[13] << " " << m_auiEncounter[14] << " " + << m_auiEncounter[15]; + + m_strInstData = saveStream.str(); + + SaveToDB(); + OUT_SAVE_INST_DATA_COMPLETE; + } +} + +uint32 instance_dire_maul::GetData(uint32 uiType) const +{ + if (uiType < MAX_ENCOUNTER) + return m_auiEncounter[uiType]; + + return 0; +} + +void instance_dire_maul::OnCreatureEnterCombat(Creature* pCreature) +{ + switch (pCreature->GetEntry()) + { + // West + // - Handling of guards of generators + case NPC_ARCANE_ABERRATION: + case NPC_MANA_REMNANT: + SortPylonGuards(); + break; + // - Set InstData for ImmolThar + case NPC_IMMOLTHAR: + SetData(TYPE_IMMOLTHAR, IN_PROGRESS); + break; + } +} + +void instance_dire_maul::OnCreatureDeath(Creature* pCreature) +{ + switch (pCreature->GetEntry()) + { + // East + // - Handling Zevrim and Old Ironbark for the door event + case NPC_ZEVRIM_THORNHOOF: + SetData(TYPE_ZEVRIM, DONE); + break; + case NPC_IRONBARK_REDEEMED: + SetData(TYPE_IRONBARK, DONE); + break; + + // West + // - Handling of guards of generators + case NPC_ARCANE_ABERRATION: + case NPC_MANA_REMNANT: + PylonGuardJustDied(pCreature); + break; + // - InstData settings + case NPC_TENDRIS_WARPWOOD: + SetData(TYPE_WARPWOOD, DONE); + break; + case NPC_IMMOLTHAR: + SetData(TYPE_IMMOLTHAR, DONE); + break; + + // North + // - Handling of Ogre Boss (Assume boss can be handled in Acid) + case NPC_KING_GORDOK: + SetData(TYPE_KING_GORDOK, DONE); + break; + // Handle Ogre guards for Tribute Run chest + case NPC_GUARD_MOLDAR: + SetData(TYPE_MOLDAR, DONE); + break; + case NPC_GUARD_FENGUS: + SetData(TYPE_FENGUS, DONE); + break; + case NPC_GUARD_SLIPKIK: + SetData(TYPE_SLIPKIK, DONE); + break; + case NPC_CAPTAIN_KROMCRUSH: + SetData(TYPE_KROMCRUSH, DONE); + break; + } +} + +void instance_dire_maul::Load(const char* chrIn) +{ + if (!chrIn) + { + 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] >> + m_auiEncounter[12] >> m_auiEncounter[13] >> m_auiEncounter[14] >> + m_auiEncounter[15]; + + if (m_auiEncounter[TYPE_ALZZIN] >= DONE) + m_bWallDestroyed = true; + + for (uint8 i = 0; i < MAX_ENCOUNTER; ++i) + { + if (m_auiEncounter[i] == IN_PROGRESS) + m_auiEncounter[i] = NOT_STARTED; + } + + OUT_LOAD_INST_DATA_COMPLETE; +} + +bool instance_dire_maul::CheckConditionCriteriaMeet(Player const* pPlayer, uint32 uiInstanceConditionId, WorldObject const* pConditionSource, uint32 conditionSourceType) const +{ + switch (uiInstanceConditionId) + { + case INSTANCE_CONDITION_ID_NORMAL_MODE: // No guards alive + case INSTANCE_CONDITION_ID_HARD_MODE: // One guard alive + case INSTANCE_CONDITION_ID_HARD_MODE_2: // Two guards alive + case INSTANCE_CONDITION_ID_HARD_MODE_3: // Three guards alive + case INSTANCE_CONDITION_ID_HARD_MODE_4: // All guards alive + { + uint8 uiTributeRunAliveBosses = (GetData(TYPE_MOLDAR) != DONE ? 1 : 0) + (GetData(TYPE_FENGUS) != DONE ? 1 : 0) + (GetData(TYPE_SLIPKIK) != DONE ? 1 : 0) + + (GetData(TYPE_KROMCRUSH) != DONE ? 1 : 0); + + return uiInstanceConditionId == uiTributeRunAliveBosses; + } + } + + script_error_log("instance_dire_maul::CheckConditionCriteriaMeet called with unsupported Id %u. Called with param plr %s, src %s, condition source type %u", + uiInstanceConditionId, pPlayer ? pPlayer->GetGuidStr().c_str() : "NULL", pConditionSource ? pConditionSource->GetGuidStr().c_str() : "NULL", conditionSourceType); + return false; +} + +bool instance_dire_maul::CheckAllGeneratorsDestroyed() +{ + if (m_auiEncounter[TYPE_PYLON_1] != DONE || m_auiEncounter[TYPE_PYLON_2] != DONE || m_auiEncounter[TYPE_PYLON_3] != DONE || m_auiEncounter[TYPE_PYLON_4] != DONE || m_auiEncounter[TYPE_PYLON_5] != DONE) + return false; + + return true; +} + +void instance_dire_maul::ProcessForceFieldOpening() +{ + // 'Open' the force field + DoUseDoorOrButton(GO_FORCEFIELD); + + // Let the summoners attack Immol'Thar + Creature* pImmolThar = GetSingleCreatureFromStorage(NPC_IMMOLTHAR); + if (!pImmolThar || pImmolThar->IsDead()) + return; + + bool bHasYelled = false; + for (GuidList::const_iterator itr = m_luiHighborneSummonerGUIDs.begin(); itr != m_luiHighborneSummonerGUIDs.end(); ++itr) + { + Creature* pSummoner = instance->GetCreature(*itr); + + if (!bHasYelled && pSummoner) + { + DoScriptText(SAY_KILL_IMMOLTHAR, pSummoner); + bHasYelled = true; + } + + if (!pSummoner || pSummoner->IsDead()) + continue; + + pSummoner->AI()->AttackStart(pImmolThar); + } + m_luiHighborneSummonerGUIDs.clear(); +} + +void instance_dire_maul::SortPylonGuards() +{ + if (!m_lGeneratorGuardGUIDs.empty()) + { + for (uint8 i = 0; i < MAX_GENERATORS; ++i) + { + GameObject* pGenerator = instance->GetGameObject(m_aCrystalGeneratorGuid[i]); + // Skip non-existing or finished generators + if (!pGenerator || GetData(TYPE_PYLON_1 + i) == DONE) + continue; + + // Sort all remaining (alive) NPCs to unfinished generators + for (GuidList::iterator itr = m_lGeneratorGuardGUIDs.begin(); itr != m_lGeneratorGuardGUIDs.end();) + { + Creature* pGuard = instance->GetCreature(*itr); + if (!pGuard || pGuard->IsDead()) // Remove invalid guids and dead guards + { + m_lGeneratorGuardGUIDs.erase(itr++); + continue; + } + + if (pGuard->IsWithinDist2d(pGenerator->GetPositionX(), pGenerator->GetPositionY(), 20.0f)) + { + m_sSortedGeneratorGuards[i].insert(pGuard->GetGUIDLow()); + m_lGeneratorGuardGUIDs.erase(itr++); + } + else + ++itr; + } + } + } +} + +void instance_dire_maul::PylonGuardJustDied(Creature* pCreature) +{ + for (uint8 i = 0; i < MAX_GENERATORS; ++i) + { + // Skip already activated generators + if (GetData(TYPE_PYLON_1 + i) == DONE) + continue; + + // Only process generator where the npc is sorted in + if (m_sSortedGeneratorGuards[i].find(pCreature->GetGUIDLow()) != m_sSortedGeneratorGuards[i].end()) + { + m_sSortedGeneratorGuards[i].erase(pCreature->GetGUIDLow()); + if (m_sSortedGeneratorGuards[i].empty()) + SetData(TYPE_PYLON_1 + i, DONE); + + break; + } + } +} + +InstanceData* GetInstanceData_instance_dire_maul(Map* pMap) +{ + return new instance_dire_maul(pMap); +} + +void AddSC_instance_dire_maul() +{ + Script* pNewScript; + + pNewScript = new Script; + pNewScript->Name = "instance_dire_maul"; + pNewScript->GetInstanceData = &GetInstanceData_instance_dire_maul; + pNewScript->RegisterSelf(); +} diff --git a/src/modules/SD2/scripts/kalimdor/durotar.cpp b/src/modules/SD2/scripts/kalimdor/durotar.cpp new file mode 100644 index 000000000..33c203ad6 --- /dev/null +++ b/src/modules/SD2/scripts/kalimdor/durotar.cpp @@ -0,0 +1,155 @@ +/* Copyright (C) 2006 - 2013 ScriptDev2 + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +/* ScriptData +SDName: Durotar +SD%Complete: 100 +SDComment: Quest support: 5441 +SDCategory: Durotar +EndScriptData */ + +/* ContentData +npc_lazy_peon +EndContentData */ + +#include "precompiled.h" + +/*###### +## npc_lazy_peon +######*/ + +enum +{ + SAY_PEON_AWAKE_1 = -1000795, + SAY_PEON_AWAKE_2 = -1000796, + + SPELL_PEON_SLEEP = 17743, + SPELL_AWAKEN_PEON = 19938, + + NPC_SLEEPING_PEON = 10556, + GO_LUMBERPILE = 175784, +}; + +struct npc_lazy_peonAI : public ScriptedAI +{ + npc_lazy_peonAI(Creature* pCreature) : ScriptedAI(pCreature) + { + Reset(); + m_uiStopSleepingTimer = urand(30000, 120000); // Set on spawn to a potential small timer, to get nice results for initial case + } + + uint32 m_uiResetSleepTimer; // Time, until the npc stops harvesting lumber + uint32 m_uiStopSleepingTimer; // Time, until the npcs (re)starts working on its own + + void Reset() override + { + m_uiResetSleepTimer = 0; + m_uiStopSleepingTimer = urand(90000, 120000); // Sleeping aura has only 2min duration + } + + // Can also be self invoked for random working + void StartLumbering(Unit* pInvoker) + { + m_uiStopSleepingTimer = 0; + if (GameObject* pLumber = GetClosestGameObjectWithEntry(m_creature, GO_LUMBERPILE, 15.0f)) + { + m_creature->RemoveAurasDueToSpell(SPELL_PEON_SLEEP); + + float fX, fY, fZ; + m_creature->SetWalk(false); + pLumber->GetContactPoint(m_creature, fX, fY, fZ, CONTACT_DISTANCE); + + if (pInvoker->GetTypeId() == TYPEID_PLAYER) + { + DoScriptText(SAY_PEON_AWAKE_1, m_creature); + ((Player*)pInvoker)->KilledMonsterCredit(m_creature->GetEntry(), m_creature->GetObjectGuid()); + m_creature->GetMotionMaster()->MovePoint(1, fX, fY, fZ); + } + else + m_creature->GetMotionMaster()->MovePoint(2, fX, fY, fZ); + } + else + script_error_log("No GameObject of entry %u was found in range or something really bad happened.", GO_LUMBERPILE); + } + + void MovementInform(uint32 uiMotionType, uint32 uiPointId) override + { + if (uiMotionType != POINT_MOTION_TYPE || !uiPointId) + return; + + m_creature->HandleEmote(EMOTE_STATE_WORK_CHOPWOOD); + // TODO - random bevahior for self-invoked awakening guesswork + m_uiResetSleepTimer = uiPointId == 1 ? 80000 : urand(30000, 60000); + } + + void UpdateAI(const uint32 uiDiff) override + { + if (m_uiResetSleepTimer) + { + if (m_uiResetSleepTimer <= uiDiff) + { + DoScriptText(SAY_PEON_AWAKE_2, m_creature); + m_creature->HandleEmote(EMOTE_STATE_NONE); + EnterEvadeMode(); + m_uiResetSleepTimer = 0; + } + else + m_uiResetSleepTimer -= uiDiff; + } + + if (m_uiStopSleepingTimer) + { + if (m_uiStopSleepingTimer <= uiDiff) + StartLumbering(m_creature); + else + m_uiStopSleepingTimer -= uiDiff; + } + } +}; + +CreatureAI* GetAI_npc_lazy_peon(Creature* pCreature) +{ + return new npc_lazy_peonAI(pCreature); +} + +bool EffectDummyCreature_lazy_peon_awake(Unit* pCaster, uint32 uiSpellId, SpellEffectIndex uiEffIndex, Creature* pCreatureTarget, ObjectGuid /*originalCasterGuid*/) +{ + // always check spellid and effectindex + if (uiSpellId == SPELL_AWAKEN_PEON && uiEffIndex == EFFECT_INDEX_0) + { + if (!pCreatureTarget->HasAura(SPELL_PEON_SLEEP) || pCaster->GetTypeId() != TYPEID_PLAYER || pCreatureTarget->GetEntry() != NPC_SLEEPING_PEON) + return true; + + if (npc_lazy_peonAI* pPeonAI = dynamic_cast(pCreatureTarget->AI())) + pPeonAI->StartLumbering(pCaster); + + // always return true when we are handling this spell and effect + return true; + } + + return false; +} + +void AddSC_durotar() +{ + Script* pNewScript; + + pNewScript = new Script; + pNewScript->Name = "npc_lazy_peon"; + pNewScript->GetAI = &GetAI_npc_lazy_peon; + pNewScript->pEffectDummyNPC = &EffectDummyCreature_lazy_peon_awake; + pNewScript->RegisterSelf(); +} diff --git a/src/modules/SD2/scripts/kalimdor/dustwallow_marsh.cpp b/src/modules/SD2/scripts/kalimdor/dustwallow_marsh.cpp new file mode 100644 index 000000000..0d1e7ff68 --- /dev/null +++ b/src/modules/SD2/scripts/kalimdor/dustwallow_marsh.cpp @@ -0,0 +1,832 @@ +/* Copyright (C) 2006 - 2013 ScriptDev2 + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along 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: 1173, 1222, 1270, 1273, 1324, 11209, 11180. +SDCategory: Dustwallow Marsh +EndScriptData */ + +/* ContentData +mobs_risen_husk_spirit +npc_restless_apparition +npc_morokk +npc_ogron +npc_private_hendel +npc_stinky_ignatz +at_nats_landing +EndContentData */ + +#include "precompiled.h" +#include "escort_ai.h" + +/*###### +## mobs_risen_husk_spirit +######*/ + +enum +{ + QUEST_WHATS_HAUNTING_WITCH_HILL = 11180, + SPELL_SUMMON_RESTLESS_APPARITION = 42511, + SPELL_CONSUME_FLESH = 37933, // Risen Husk + SPELL_INTANGIBLE_PRESENCE = 43127, // Risen Spirit + NPC_RISEN_HUSK = 23555, + NPC_RISEN_SPIRIT = 23554 +}; + +struct mobs_risen_husk_spiritAI : public ScriptedAI +{ + mobs_risen_husk_spiritAI(Creature* pCreature) : ScriptedAI(pCreature) {Reset();} + + uint32 m_uiConsumeFlesh_Timer; + uint32 m_uiIntangiblePresence_Timer; + + Player* m_pCreditPlayer; + + void Reset() override + { + m_uiConsumeFlesh_Timer = 10000; + m_uiIntangiblePresence_Timer = 5000; + + m_pCreditPlayer = NULL; + } + + void JustSummoned(Creature* pSummoned) override + { + if (m_pCreditPlayer) + m_pCreditPlayer->KilledMonsterCredit(pSummoned->GetEntry(), pSummoned->GetObjectGuid()); + } + + void DamageTaken(Unit* pDoneBy, uint32& uiDamage) override + { + if (uiDamage < m_creature->GetHealth()) + return; + + if (Player* pPlayer = pDoneBy->GetCharmerOrOwnerPlayerOrPlayerItself()) + { + if (pPlayer->GetQuestStatus(QUEST_WHATS_HAUNTING_WITCH_HILL) == QUEST_STATUS_INCOMPLETE) + { + m_pCreditPlayer = pPlayer; + m_creature->CastSpell(pDoneBy, SPELL_SUMMON_RESTLESS_APPARITION, true); + } + } + } + + void UpdateAI(const uint32 uiDiff) override + { + if (!m_creature->SelectHostileTarget() || !m_creature->getVictim()) + return; + + if (m_uiConsumeFlesh_Timer < uiDiff) + { + if (m_creature->GetEntry() == NPC_RISEN_HUSK) + DoCastSpellIfCan(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) + DoCastSpellIfCan(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 +######*/ + +enum +{ + SAY_RAND_1 = -1000543, + SAY_RAND_2 = -1000544, + SAY_RAND_3 = -1000545, + SAY_RAND_4 = -1000546, + SAY_RAND_5 = -1000547, + SAY_RAND_6 = -1000548, + SAY_RAND_7 = -1000549, + SAY_RAND_8 = -1000550 +}; + +struct npc_restless_apparitionAI : public ScriptedAI +{ + npc_restless_apparitionAI(Creature* pCreature) : ScriptedAI(pCreature) {Reset();} + + uint32 m_uiTalk_Timer; + + void Reset() override + { + m_uiTalk_Timer = 1000; + } + + void UpdateAI(const uint32 uiDiff) override + { + if (!m_uiTalk_Timer) + return; + + if (m_uiTalk_Timer <= uiDiff) + { + switch (urand(0, 7)) + { + case 0: DoScriptText(SAY_RAND_1, m_creature); break; + case 1: DoScriptText(SAY_RAND_2, m_creature); break; + case 2: DoScriptText(SAY_RAND_3, m_creature); break; + case 3: DoScriptText(SAY_RAND_4, m_creature); break; + case 4: DoScriptText(SAY_RAND_5, m_creature); break; + case 5: DoScriptText(SAY_RAND_6, m_creature); break; + case 6: DoScriptText(SAY_RAND_7, m_creature); break; + case 7: DoScriptText(SAY_RAND_8, m_creature); break; + } + + m_uiTalk_Timer = 0; + } + else + m_uiTalk_Timer -= uiDiff; + } +}; + +CreatureAI* GetAI_npc_restless_apparition(Creature* pCreature) +{ + return new npc_restless_apparitionAI(pCreature); +} + +/*###### +## npc_morokk +######*/ + +enum +{ + SAY_MOR_CHALLENGE = -1000499, + SAY_MOR_SCARED = -1000500, + + QUEST_CHALLENGE_MOROKK = 1173, + + FACTION_MOR_HOSTILE = 168, + FACTION_MOR_RUNNING = 35 +}; + +struct npc_morokkAI : public npc_escortAI +{ + npc_morokkAI(Creature* pCreature) : npc_escortAI(pCreature) + { + m_bIsSuccess = false; + Reset(); + } + + bool m_bIsSuccess; + + void Reset() override {} + + void WaypointReached(uint32 uiPointId) override + { + switch (uiPointId) + { + case 0: + SetEscortPaused(true); + break; + case 1: + if (m_bIsSuccess) + DoScriptText(SAY_MOR_SCARED, m_creature); + else + { + m_creature->SetDeathState(JUST_DIED); + m_creature->Respawn(); + } + break; + } + } + + void AttackedBy(Unit* pAttacker) override + { + if (m_creature->getVictim()) + return; + + if (m_creature->IsFriendlyTo(pAttacker)) + return; + + AttackStart(pAttacker); + } + + void DamageTaken(Unit* /*pDoneBy*/, uint32& uiDamage) override + { + if (HasEscortState(STATE_ESCORT_ESCORTING)) + { + if (m_creature->GetHealthPercent() < 30.0f) + { + if (Player* pPlayer = GetPlayerForEscort()) + pPlayer->GroupEventHappens(QUEST_CHALLENGE_MOROKK, m_creature); + + m_creature->setFaction(FACTION_MOR_RUNNING); + + m_bIsSuccess = true; + EnterEvadeMode(); + + uiDamage = 0; + } + } + } + + void UpdateEscortAI(const uint32 /*uiDiff*/) override + { + if (!m_creature->SelectHostileTarget() || !m_creature->getVictim()) + { + if (HasEscortState(STATE_ESCORT_PAUSED)) + { + if (Player* pPlayer = GetPlayerForEscort()) + { + m_bIsSuccess = false; + DoScriptText(SAY_MOR_CHALLENGE, m_creature, pPlayer); + m_creature->setFaction(FACTION_MOR_HOSTILE); + AttackStart(pPlayer); + } + + SetEscortPaused(false); + } + + return; + } + + DoMeleeAttackIfReady(); + } +}; + +CreatureAI* GetAI_npc_morokk(Creature* pCreature) +{ + return new npc_morokkAI(pCreature); +} + +bool QuestAccept_npc_morokk(Player* pPlayer, Creature* pCreature, const Quest* pQuest) +{ + if (pQuest->GetQuestId() == QUEST_CHALLENGE_MOROKK) + { + if (npc_morokkAI* pEscortAI = dynamic_cast(pCreature->AI())) + pEscortAI->Start(true, pPlayer, pQuest); + + return true; + } + + return false; +} + +/*###### +## npc_ogron +######*/ + +enum +{ + SAY_OGR_START = -1000452, + SAY_OGR_SPOT = -1000453, + SAY_OGR_RET_WHAT = -1000454, + SAY_OGR_RET_SWEAR = -1000455, + SAY_OGR_REPLY_RET = -1000456, + SAY_OGR_RET_TAKEN = -1000457, + SAY_OGR_TELL_FIRE = -1000458, + SAY_OGR_RET_NOCLOSER = -1000459, + SAY_OGR_RET_NOFIRE = -1000460, + SAY_OGR_RET_HEAR = -1000461, + SAY_OGR_CAL_FOUND = -1000462, + SAY_OGR_CAL_MERCY = -1000463, + SAY_OGR_HALL_GLAD = -1000464, + EMOTE_OGR_RET_ARROW = -1000465, + SAY_OGR_RET_ARROW = -1000466, + SAY_OGR_CAL_CLEANUP = -1000467, + SAY_OGR_NODIE = -1000468, + SAY_OGR_SURVIVE = -1000469, + SAY_OGR_RET_LUCKY = -1000470, + SAY_OGR_THANKS = -1000471, + + QUEST_QUESTIONING = 1273, + + FACTION_GENERIC_FRIENDLY = 35, + FACTION_THER_HOSTILE = 151, + + NPC_REETHE = 4980, + NPC_CALDWELL = 5046, + NPC_HALLAN = 5045, + NPC_SKIRMISHER = 5044, + + SPELL_FAKE_SHOT = 7105, + + PHASE_INTRO = 0, + PHASE_GUESTS = 1, + PHASE_FIGHT = 2, + PHASE_COMPLETE = 3 +}; + +static float m_afSpawn[] = { -3383.501953f, -3203.383301f, 36.149f}; +static float m_afMoveTo[] = { -3371.414795f, -3212.179932f, 34.210f}; + +struct npc_ogronAI : public npc_escortAI +{ + npc_ogronAI(Creature* pCreature) : npc_escortAI(pCreature) + { + lCreatureList.clear(); + m_uiPhase = 0; + m_uiPhaseCounter = 0; + Reset(); + } + + std::list lCreatureList; + + uint32 m_uiPhase; + uint32 m_uiPhaseCounter; + uint32 m_uiGlobalTimer; + + void Reset() override + { + m_uiGlobalTimer = 5000; + + if (HasEscortState(STATE_ESCORT_PAUSED) && m_uiPhase == PHASE_FIGHT) + m_uiPhase = PHASE_COMPLETE; + + if (!HasEscortState(STATE_ESCORT_ESCORTING)) + { + lCreatureList.clear(); + m_uiPhase = 0; + m_uiPhaseCounter = 0; + } + } + + void MoveInLineOfSight(Unit* pWho) override + { + if (HasEscortState(STATE_ESCORT_ESCORTING) && pWho->GetEntry() == NPC_REETHE && lCreatureList.empty()) + lCreatureList.push_back((Creature*)pWho); + + npc_escortAI::MoveInLineOfSight(pWho); + } + + Creature* GetCreature(uint32 uiCreatureEntry) + { + if (!lCreatureList.empty()) + { + for (std::list::iterator itr = lCreatureList.begin(); itr != lCreatureList.end(); ++itr) + { + if ((*itr)->GetEntry() == uiCreatureEntry && (*itr)->IsAlive()) + return (*itr); + } + } + + return NULL; + } + + void WaypointReached(uint32 uiPointId) override + { + switch (uiPointId) + { + case 9: + DoScriptText(SAY_OGR_SPOT, m_creature); + break; + case 10: + if (Creature* pReethe = GetCreature(NPC_REETHE)) + DoScriptText(SAY_OGR_RET_WHAT, pReethe); + break; + case 11: + SetEscortPaused(true); + break; + } + } + + void JustSummoned(Creature* pSummoned) override + { + lCreatureList.push_back(pSummoned); + + pSummoned->setFaction(FACTION_GENERIC_FRIENDLY); + + if (pSummoned->GetEntry() == NPC_CALDWELL) + pSummoned->GetMotionMaster()->MovePoint(0, m_afMoveTo[0], m_afMoveTo[1], m_afMoveTo[2]); + else + { + if (Creature* pCaldwell = GetCreature(NPC_CALDWELL)) + { + // will this conversion work without compile warning/error? + size_t iSize = lCreatureList.size(); + pSummoned->GetMotionMaster()->MoveFollow(pCaldwell, 0.5f, (M_PI / 2) * (int)iSize); + } + } + } + + void DoStartAttackMe() + { + if (!lCreatureList.empty()) + { + for (std::list::iterator itr = lCreatureList.begin(); itr != lCreatureList.end(); ++itr) + { + if ((*itr)->GetEntry() == NPC_REETHE) + continue; + + if ((*itr)->IsAlive()) + { + (*itr)->setFaction(FACTION_THER_HOSTILE); + (*itr)->AI()->AttackStart(m_creature); + } + } + } + } + + void UpdateEscortAI(const uint32 uiDiff) override + { + if (!m_creature->SelectHostileTarget() || !m_creature->getVictim()) + { + if (HasEscortState(STATE_ESCORT_PAUSED)) + { + if (m_uiGlobalTimer < uiDiff) + { + m_uiGlobalTimer = 5000; + + switch (m_uiPhase) + { + case PHASE_INTRO: + { + switch (m_uiPhaseCounter) + { + case 0: + if (Creature* pReethe = GetCreature(NPC_REETHE)) + DoScriptText(SAY_OGR_RET_SWEAR, pReethe); + break; + case 1: + DoScriptText(SAY_OGR_REPLY_RET, m_creature); + break; + case 2: + if (Creature* pReethe = GetCreature(NPC_REETHE)) + DoScriptText(SAY_OGR_RET_TAKEN, pReethe); + break; + case 3: + DoScriptText(SAY_OGR_TELL_FIRE, m_creature); + if (Creature* pReethe = GetCreature(NPC_REETHE)) + DoScriptText(SAY_OGR_RET_NOCLOSER, pReethe); + break; + case 4: + if (Creature* pReethe = GetCreature(NPC_REETHE)) + DoScriptText(SAY_OGR_RET_NOFIRE, pReethe); + break; + case 5: + if (Creature* pReethe = GetCreature(NPC_REETHE)) + DoScriptText(SAY_OGR_RET_HEAR, pReethe); + + m_creature->SummonCreature(NPC_CALDWELL, m_afSpawn[0], m_afSpawn[1], m_afSpawn[2], 0.0f, TEMPSUMMON_TIMED_OOC_OR_DEAD_DESPAWN, 300000); + m_creature->SummonCreature(NPC_HALLAN, m_afSpawn[0], m_afSpawn[1], m_afSpawn[2], 0.0f, TEMPSUMMON_TIMED_OOC_OR_DEAD_DESPAWN, 300000); + m_creature->SummonCreature(NPC_SKIRMISHER, m_afSpawn[0], m_afSpawn[1], m_afSpawn[2], 0.0f, TEMPSUMMON_TIMED_OOC_OR_DEAD_DESPAWN, 300000); + m_creature->SummonCreature(NPC_SKIRMISHER, m_afSpawn[0], m_afSpawn[1], m_afSpawn[2], 0.0f, TEMPSUMMON_TIMED_OOC_OR_DEAD_DESPAWN, 300000); + + m_uiPhase = PHASE_GUESTS; + break; + } + break; + } + case PHASE_GUESTS: + { + switch (m_uiPhaseCounter) + { + case 6: + if (Creature* pCaldwell = GetCreature(NPC_CALDWELL)) + DoScriptText(SAY_OGR_CAL_FOUND, pCaldwell); + break; + case 7: + if (Creature* pCaldwell = GetCreature(NPC_CALDWELL)) + DoScriptText(SAY_OGR_CAL_MERCY, pCaldwell); + break; + case 8: + if (Creature* pHallan = GetCreature(NPC_HALLAN)) + { + DoScriptText(SAY_OGR_HALL_GLAD, pHallan); + + if (Creature* pReethe = GetCreature(NPC_REETHE)) + pHallan->CastSpell(pReethe, SPELL_FAKE_SHOT, false); + } + break; + case 9: + if (Creature* pReethe = GetCreature(NPC_REETHE)) + { + DoScriptText(EMOTE_OGR_RET_ARROW, pReethe); + DoScriptText(SAY_OGR_RET_ARROW, pReethe); + } + break; + case 10: + if (Creature* pCaldwell = GetCreature(NPC_CALDWELL)) + DoScriptText(SAY_OGR_CAL_CLEANUP, pCaldwell); + + DoScriptText(SAY_OGR_NODIE, m_creature); + break; + case 11: + DoStartAttackMe(); + m_uiPhase = PHASE_FIGHT; + break; + } + break; + } + case PHASE_COMPLETE: + { + switch (m_uiPhaseCounter) + { + case 12: + if (Player* pPlayer = GetPlayerForEscort()) + pPlayer->GroupEventHappens(QUEST_QUESTIONING, m_creature); + + DoScriptText(SAY_OGR_SURVIVE, m_creature); + break; + case 13: + if (Creature* pReethe = GetCreature(NPC_REETHE)) + DoScriptText(SAY_OGR_RET_LUCKY, pReethe); + break; + case 14: + DoScriptText(SAY_OGR_THANKS, m_creature); + SetRun(); + SetEscortPaused(false); + break; + } + break; + } + } + + if (m_uiPhase != PHASE_FIGHT) + ++m_uiPhaseCounter; + } + else + m_uiGlobalTimer -= uiDiff; + } + + return; + } + + DoMeleeAttackIfReady(); + } +}; + +bool QuestAccept_npc_ogron(Player* pPlayer, Creature* pCreature, const Quest* pQuest) +{ + if (pQuest->GetQuestId() == QUEST_QUESTIONING) + { + if (npc_ogronAI* pEscortAI = dynamic_cast(pCreature->AI())) + { + pEscortAI->Start(false, pPlayer, pQuest, true); + pCreature->SetFactionTemporary(FACTION_ESCORT_N_FRIEND_PASSIVE, TEMPFACTION_RESTORE_RESPAWN); + DoScriptText(SAY_OGR_START, pCreature, pPlayer); + } + } + + return true; +} + +CreatureAI* GetAI_npc_ogron(Creature* pCreature) +{ + return new npc_ogronAI(pCreature); +} + +/*###### +## npc_private_hendel +######*/ + +enum +{ + SAY_PROGRESS_1_TER = -1000411, + SAY_PROGRESS_2_HEN = -1000412, + 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 npc_private_hendelAI : public ScriptedAI +{ + npc_private_hendelAI(Creature* pCreature) : ScriptedAI(pCreature) { Reset(); } + + void Reset() override {} + + void AttackedBy(Unit* pAttacker) override + { + if (m_creature->getVictim()) + return; + + if (m_creature->IsFriendlyTo(pAttacker)) + return; + + AttackStart(pAttacker); + } + + void DamageTaken(Unit* pDoneBy, uint32& uiDamage) override + { + if (uiDamage > m_creature->GetHealth() || m_creature->GetHealthPercent() < 20.0f) + { + 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->SetFactionTemporary(FACTION_HOSTILE, TEMPFACTION_RESTORE_COMBAT_STOP | TEMPFACTION_RESTORE_RESPAWN); + + return true; +} + +CreatureAI* GetAI_npc_private_hendel(Creature* pCreature) +{ + return new npc_private_hendelAI(pCreature); +} + +/*##### +## npc_stinky_ignatz +## TODO Note: Possible some dynamic behaviour is missing. Faction change is guessed +#####*/ + +enum +{ + QUEST_ID_STINKYS_ESCAPE_ALLIANCE = 1222, + QUEST_ID_STINKYS_ESCAPE_HORDE = 1270, + + SAY_STINKY_BEGIN = -1000958, + SAY_STINKY_FIRST_STOP = -1000959, + SAY_STINKY_2_MONSTERS = -1000960, + SAY_STINKY_GATHERING = -1000961, + SAY_STINKY_END = -1000962, + + GO_BOGBEAN_PLANT = 20939, +}; + +struct npc_stinky_ignatzAI : public npc_escortAI +{ + npc_stinky_ignatzAI(Creature* pCreature) : npc_escortAI(pCreature) + { + Reset(); + } + + ObjectGuid m_bogbeanPlantGuid; + + void Reset() override {} + + void ReceiveAIEvent(AIEventType eventType, Creature* /*pSender*/, Unit* pInvoker, uint32 uiMiscValue) override + { + if (eventType == AI_EVENT_START_ESCORT && pInvoker->GetTypeId() == TYPEID_PLAYER) + { + DoScriptText(SAY_STINKY_BEGIN, m_creature); + Start(false, (Player*)pInvoker, GetQuestTemplateStore(uiMiscValue)); + m_creature->SetFactionTemporary(FACTION_ESCORT_N_NEUTRAL_PASSIVE, TEMPFACTION_RESTORE_RESPAWN); + m_creature->SetStandState(UNIT_STAND_STATE_STAND); + } + } + + void WaypointReached(uint32 uiPointId) override + { + switch (uiPointId) + { + case 11: + DoScriptText(SAY_STINKY_FIRST_STOP, m_creature); + break; + case 17: + // TODO Note: This text would imply some waiting till two mobs are killed, such behaviour is neither confirmed nor implemented. Input welcome! + DoScriptText(SAY_STINKY_2_MONSTERS, m_creature); + + if (GameObject* pBogbeanPlant = GetClosestGameObjectWithEntry(m_creature, GO_BOGBEAN_PLANT, DEFAULT_VISIBILITY_DISTANCE)) + { + m_bogbeanPlantGuid = pBogbeanPlant->GetObjectGuid(); + m_creature->SetFacingToObject(pBogbeanPlant); + } + + break; + case 19: + DoScriptText(SAY_STINKY_GATHERING, m_creature); + break; + case 24: + if (Player* pPlayer = GetPlayerForEscort()) + pPlayer->GroupEventHappens(pPlayer->GetTeam() == ALLIANCE ? QUEST_ID_STINKYS_ESCAPE_ALLIANCE : QUEST_ID_STINKYS_ESCAPE_HORDE, m_creature); + + DoScriptText(SAY_STINKY_END, m_creature); + break; + } + } + + void WaypointStart(uint32 uiPointId) + { + if (uiPointId == 20) + { + if (GameObject* pBogbeanPlant = m_creature->GetMap()->GetGameObject(m_bogbeanPlantGuid)) + pBogbeanPlant->Use(m_creature); + m_bogbeanPlantGuid.Clear(); + m_creature->HandleEmote(EMOTE_ONESHOT_NONE); + } + } +}; + +CreatureAI* GetAI_npc_stinky_ignatz(Creature* pCreature) +{ + return new npc_stinky_ignatzAI(pCreature); +} + +bool QuestAccept_npc_stinky_ignatz(Player* pPlayer, Creature* pCreature, const Quest* pQuest) +{ + if (pQuest->GetQuestId() == QUEST_ID_STINKYS_ESCAPE_ALLIANCE || pQuest->GetQuestId() == QUEST_ID_STINKYS_ESCAPE_HORDE) + pCreature->AI()->SendAIEvent(AI_EVENT_START_ESCORT, pPlayer, pCreature, pQuest->GetQuestId()); + + return true; +} + +/*###### +## at_nats_landing +######*/ +enum +{ + QUEST_NATS_BARGAIN = 11209, + SPELL_FISH_PASTE = 42644, + NPC_LURKING_SHARK = 23928 +}; + +bool AreaTrigger_at_nats_landing(Player* pPlayer, const AreaTriggerEntry* /*pAt*/) +{ + if (pPlayer->GetQuestStatus(QUEST_NATS_BARGAIN) == QUEST_STATUS_INCOMPLETE && pPlayer->HasAura(SPELL_FISH_PASTE)) + { + Creature* pShark = GetClosestCreatureWithEntry(pPlayer, NPC_LURKING_SHARK, 20.0f); + + if (!pShark) + pShark = pPlayer->SummonCreature(NPC_LURKING_SHARK, -4246.243f, -3922.356f, -7.488f, 5.0f, TEMPSUMMON_TIMED_OOC_DESPAWN, 100000); + + pShark->AI()->AttackStart(pPlayer); + return false; + } + return true; +} + +void AddSC_dustwallow_marsh() +{ + Script* pNewScript; + + pNewScript = new Script; + pNewScript->Name = "mobs_risen_husk_spirit"; + pNewScript->GetAI = &GetAI_mobs_risen_husk_spirit; + pNewScript->RegisterSelf(); + + pNewScript = new Script; + pNewScript->Name = "npc_restless_apparition"; + pNewScript->GetAI = &GetAI_npc_restless_apparition; + pNewScript->RegisterSelf(); + + pNewScript = new Script; + pNewScript->Name = "npc_morokk"; + pNewScript->GetAI = &GetAI_npc_morokk; + pNewScript->pQuestAcceptNPC = &QuestAccept_npc_morokk; + pNewScript->RegisterSelf(); + + pNewScript = new Script; + pNewScript->Name = "npc_ogron"; + pNewScript->GetAI = &GetAI_npc_ogron; + pNewScript->pQuestAcceptNPC = &QuestAccept_npc_ogron; + pNewScript->RegisterSelf(); + + pNewScript = new Script; + pNewScript->Name = "npc_private_hendel"; + pNewScript->GetAI = &GetAI_npc_private_hendel; + pNewScript->pQuestAcceptNPC = &QuestAccept_npc_private_hendel; + pNewScript->RegisterSelf(); + + pNewScript = new Script; + pNewScript->Name = "npc_stinky_ignatz"; + pNewScript->GetAI = &GetAI_npc_stinky_ignatz; + pNewScript->pQuestAcceptNPC = &QuestAccept_npc_stinky_ignatz; + pNewScript->RegisterSelf(); + + pNewScript = new Script; + pNewScript->Name = "at_nats_landing"; + pNewScript->pAreaTrigger = &AreaTrigger_at_nats_landing; + pNewScript->RegisterSelf(); +} diff --git a/src/modules/SD2/scripts/kalimdor/felwood.cpp b/src/modules/SD2/scripts/kalimdor/felwood.cpp new file mode 100644 index 000000000..c746104bb --- /dev/null +++ b/src/modules/SD2/scripts/kalimdor/felwood.cpp @@ -0,0 +1,431 @@ +/* Copyright (C) 2006 - 2013 ScriptDev2 + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along 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: 4506, 7603, 7603 (Summon Pollo Grande) +SDCategory: Felwood +EndScriptData */ + +/* ContentData +npc_kitten +npc_niby_the_almighty +npc_kroshius +EndContentData */ + +#include "precompiled.h" +#include "follower_ai.h" +#include "ObjectMgr.h" + +/*#### +# npc_kitten +####*/ + +enum +{ + EMOTE_SAB_JUMP = -1000541, + EMOTE_SAB_FOLLOW = -1000542, + + SPELL_CORRUPT_SABER_VISUAL = 16510, + + QUEST_CORRUPT_SABER = 4506, + NPC_WINNA = 9996, + NPC_CORRUPT_SABER = 10042 +}; + +#define GOSSIP_ITEM_RELEASE "I want to release the corrupted saber to Winna." + +struct npc_kittenAI : public FollowerAI +{ + npc_kittenAI(Creature* pCreature) : FollowerAI(pCreature) + { + if (pCreature->GetOwner() && pCreature->GetOwner()->GetTypeId() == TYPEID_PLAYER) + { + StartFollow((Player*)pCreature->GetOwner()); + SetFollowPaused(true); + DoScriptText(EMOTE_SAB_JUMP, m_creature); + + pCreature->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE); + + // find a decent way to move to center of moonwell + } + + m_uiMoonwellCooldown = 7500; + Reset(); + } + + uint32 m_uiMoonwellCooldown; + + void Reset() override { } + + void MoveInLineOfSight(Unit* pWho) override + { + // should not have npcflag by default, so set when expected + if (!m_creature->getVictim() && !m_creature->HasFlag(UNIT_NPC_FLAGS, UNIT_NPC_FLAG_GOSSIP) && HasFollowState(STATE_FOLLOW_INPROGRESS) && pWho->GetEntry() == NPC_WINNA) + { + if (m_creature->IsWithinDistInMap(pWho, INTERACTION_DISTANCE)) + m_creature->SetFlag(UNIT_NPC_FLAGS, UNIT_NPC_FLAG_GOSSIP); + } + } + + void UpdateFollowerAI(const uint32 uiDiff) override + { + if (!m_creature->SelectHostileTarget() || !m_creature->getVictim()) + { + if (HasFollowState(STATE_FOLLOW_PAUSED)) + { + if (m_uiMoonwellCooldown < uiDiff) + { + m_creature->CastSpell(m_creature, SPELL_CORRUPT_SABER_VISUAL, false); + SetFollowPaused(false); + } + else + m_uiMoonwellCooldown -= uiDiff; + } + + return; + } + + DoMeleeAttackIfReady(); + } +}; + +CreatureAI* GetAI_npc_kitten(Creature* pCreature) +{ + return new npc_kittenAI(pCreature); +} + +bool EffectDummyCreature_npc_kitten(Unit* /*pCaster*/, uint32 uiSpellId, SpellEffectIndex uiEffIndex, Creature* pCreatureTarget, ObjectGuid /*originalCasterGuid*/) +{ + // always check spellid and effectindex + if (uiSpellId == SPELL_CORRUPT_SABER_VISUAL && uiEffIndex == EFFECT_INDEX_0) + { + // Not nice way, however using UpdateEntry will not be correct. + if (const CreatureInfo* pTemp = GetCreatureTemplateStore(NPC_CORRUPT_SABER)) + { + pCreatureTarget->SetEntry(pTemp->Entry); + pCreatureTarget->SetDisplayId(Creature::ChooseDisplayId(pTemp)); + pCreatureTarget->SetName(pTemp->Name); + pCreatureTarget->SetFloatValue(OBJECT_FIELD_SCALE_X, pTemp->scale); + } + + if (Unit* pOwner = pCreatureTarget->GetOwner()) + DoScriptText(EMOTE_SAB_FOLLOW, pCreatureTarget, pOwner); + + // always return true when we are handling this spell and effect + return true; + } + return false; +} + +bool GossipHello_npc_corrupt_saber(Player* pPlayer, Creature* pCreature) +{ + if (pPlayer->GetQuestStatus(QUEST_CORRUPT_SABER) == QUEST_STATUS_INCOMPLETE) + { + if (GetClosestCreatureWithEntry(pCreature, NPC_WINNA, INTERACTION_DISTANCE)) + pPlayer->ADD_GOSSIP_ITEM(GOSSIP_ICON_CHAT, GOSSIP_ITEM_RELEASE, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 1); + } + + pPlayer->SEND_GOSSIP_MENU(pPlayer->GetGossipTextId(pCreature), pCreature->GetObjectGuid()); + return true; +} + +bool GossipSelect_npc_corrupt_saber(Player* pPlayer, Creature* pCreature, uint32 /*uiSender*/, uint32 uiAction) +{ + if (uiAction == GOSSIP_ACTION_INFO_DEF + 1) + { + pPlayer->CLOSE_GOSSIP_MENU(); + + if (npc_kittenAI* pKittenAI = dynamic_cast(pCreature->AI())) + pKittenAI->SetFollowComplete(); + + pPlayer->AreaExploredOrEventHappens(QUEST_CORRUPT_SABER); + } + + return true; +} + +/*###### +## npc_niby_the_almighty (summons el pollo grande) +######*/ + +enum +{ + QUEST_KROSHIUS = 7603, + + NPC_IMPSY = 14470, + + SPELL_SUMMON_POLLO = 23056, + + SAY_NIBY_1 = -1000566, + SAY_NIBY_2 = -1000567, + EMOTE_IMPSY_1 = -1000568, + SAY_IMPSY_1 = -1000569, + SAY_NIBY_3 = -1000570 +}; + +struct npc_niby_the_almightyAI : public ScriptedAI +{ + npc_niby_the_almightyAI(Creature* pCreature) : ScriptedAI(pCreature) { Reset(); } + + uint32 m_uiSummonTimer; + uint8 m_uiSpeech; + + bool m_bEventStarted; + + void Reset() override + { + m_uiSummonTimer = 500; + m_uiSpeech = 0; + + m_bEventStarted = false; + } + + void StartEvent() + { + Reset(); + m_bEventStarted = true; + m_creature->RemoveFlag(UNIT_NPC_FLAGS, UNIT_NPC_FLAG_QUESTGIVER); + } + + void UpdateAI(const uint32 uiDiff) override + { + if (m_bEventStarted) + { + if (m_uiSummonTimer <= uiDiff) + { + switch (m_uiSpeech) + { + case 1: + m_creature->GetMotionMaster()->Clear(); + m_creature->GetMotionMaster()->MovePoint(0, 5407.19f, -753.00f, 350.82f); + m_uiSummonTimer = 6200; + break; + case 2: + m_creature->SetFacingTo(1.2f); + DoScriptText(SAY_NIBY_1, m_creature); + m_uiSummonTimer = 3000; + break; + case 3: + DoScriptText(SAY_NIBY_2, m_creature); + DoCastSpellIfCan(m_creature, SPELL_SUMMON_POLLO); + m_uiSummonTimer = 2000; + break; + case 4: + if (Creature* pImpsy = GetClosestCreatureWithEntry(m_creature, NPC_IMPSY, 20.0)) + { + DoScriptText(EMOTE_IMPSY_1, pImpsy); + DoScriptText(SAY_IMPSY_1, pImpsy); + m_uiSummonTimer = 2500; + } + else + { + // Skip Speech 5 + m_uiSummonTimer = 40000; + ++m_uiSpeech; + } + break; + case 5: + DoScriptText(SAY_NIBY_3, m_creature); + m_uiSummonTimer = 40000; + break; + case 6: + m_creature->GetMotionMaster()->MoveTargetedHome(); + m_creature->SetFlag(UNIT_NPC_FLAGS, UNIT_NPC_FLAG_QUESTGIVER); + m_bEventStarted = false; + break; + } + ++m_uiSpeech; + } + else + m_uiSummonTimer -= uiDiff; + } + } +}; + +CreatureAI* GetAI_npc_niby_the_almighty(Creature* pCreature) +{ + return new npc_niby_the_almightyAI(pCreature); +} + +bool QuestRewarded_npc_niby_the_almighty(Player* /*pPlayer*/, Creature* pCreature, Quest const* pQuest) +{ + if (pQuest->GetQuestId() == QUEST_KROSHIUS) + { + if (npc_niby_the_almightyAI* pNibyAI = dynamic_cast(pCreature->AI())) + { + pNibyAI->StartEvent(); + } + } + return true; +} + +/*###### +## npc_kroshius +######*/ + +enum +{ + NPC_KROSHIUS = 14467, + SPELL_KNOCKBACK = 10101, + SAY_KROSHIUS_REVIVE = -1000589, + EVENT_KROSHIUS_REVIVE = 8328, + FACTION_HOSTILE = 16, +}; + +struct npc_kroshiusAI : public ScriptedAI +{ + npc_kroshiusAI(Creature* pCreature) : ScriptedAI(pCreature) + { + m_uiPhase = 0; + Reset(); + } + + ObjectGuid m_playerGuid; + uint32 m_uiKnockBackTimer; + uint32 m_uiPhaseTimer; + + uint8 m_uiPhase; + + void Reset() override + { + m_uiKnockBackTimer = urand(5000, 8000); + m_playerGuid.Clear(); + + if (!m_uiPhase) + m_creature->SetStandState(UNIT_STAND_STATE_DEAD); + } + + void DoRevive(Player* pSource) + { + if (m_uiPhase) + return; + + m_uiPhase = 1; + m_uiPhaseTimer = 2500; + m_playerGuid = pSource->GetObjectGuid(); + + // TODO: A visual Flame Circle around the mob still missing + } + + void JustDied(Unit* /*pKiller*/) override + { + m_uiPhase = 0; + } + + void UpdateAI(const uint32 uiDiff) override + { + if (!m_uiPhase) + return; + + if (m_uiPhase < 4) + { + if (m_uiPhaseTimer < uiDiff) + { + switch (m_uiPhase) + { + case 1: // Revived + m_creature->SetStandState(UNIT_STAND_STATE_STAND); + m_uiPhaseTimer = 1000; + break; + case 2: + DoScriptText(SAY_KROSHIUS_REVIVE, m_creature); + m_uiPhaseTimer = 3500; + break; + case 3: // Attack + m_creature->SetFactionTemporary(FACTION_HOSTILE, TEMPFACTION_RESTORE_COMBAT_STOP | TEMPFACTION_RESTORE_RESPAWN | TEMPFACTION_TOGGLE_OOC_NOT_ATTACK | TEMPFACTION_TOGGLE_PASSIVE); + if (Player* pPlayer = m_creature->GetMap()->GetPlayer(m_playerGuid)) + { + if (m_creature->IsWithinDistInMap(pPlayer, 30.0f)) + AttackStart(pPlayer); + } + break; + } + ++m_uiPhase; + } + else + m_uiPhaseTimer -= uiDiff; + } + else + { + if (!m_creature->SelectHostileTarget() || !m_creature->getVictim()) + return; + + if (m_uiKnockBackTimer < uiDiff) + { + DoCastSpellIfCan(m_creature->getVictim(), SPELL_KNOCKBACK); + m_uiKnockBackTimer = urand(9000, 12000); + } + else + m_uiKnockBackTimer -= uiDiff; + + DoMeleeAttackIfReady(); + } + } +}; + +CreatureAI* GetAI_npc_kroshius(Creature* pCreature) +{ + return new npc_kroshiusAI(pCreature); +} + +bool ProcessEventId_npc_kroshius(uint32 uiEventId, Object* pSource, Object* /*pTarget*/, bool /*bIsStart*/) +{ + if (uiEventId == EVENT_KROSHIUS_REVIVE) + { + if (pSource->GetTypeId() == TYPEID_PLAYER) + { + if (Creature* pKroshius = GetClosestCreatureWithEntry((Player*)pSource, NPC_KROSHIUS, 20.0f)) + { + if (npc_kroshiusAI* pKroshiusAI = dynamic_cast(pKroshius->AI())) + pKroshiusAI->DoRevive((Player*)pSource); + } + } + + return true; + } + return false; +} + +void AddSC_felwood() +{ + Script* pNewScript; + + pNewScript = new Script; + pNewScript->Name = "npc_kitten"; + pNewScript->GetAI = &GetAI_npc_kitten; + pNewScript->pEffectDummyNPC = &EffectDummyCreature_npc_kitten; + pNewScript->RegisterSelf(); + + pNewScript = new Script; + pNewScript->Name = "npc_corrupt_saber"; + pNewScript->pGossipHello = &GossipHello_npc_corrupt_saber; + pNewScript->pGossipSelect = &GossipSelect_npc_corrupt_saber; + pNewScript->RegisterSelf(); + + pNewScript = new Script; + pNewScript->Name = "npc_niby_the_almighty"; + pNewScript->GetAI = &GetAI_npc_niby_the_almighty; + pNewScript->pQuestRewardedNPC = &QuestRewarded_npc_niby_the_almighty; + pNewScript->RegisterSelf(); + + pNewScript = new Script; + pNewScript->Name = "npc_kroshius"; + pNewScript->GetAI = &GetAI_npc_kroshius; + pNewScript->pProcessEventId = &ProcessEventId_npc_kroshius; + pNewScript->RegisterSelf(); +} diff --git a/src/modules/SD2/scripts/kalimdor/feralas.cpp b/src/modules/SD2/scripts/kalimdor/feralas.cpp new file mode 100644 index 000000000..c31642ea6 --- /dev/null +++ b/src/modules/SD2/scripts/kalimdor/feralas.cpp @@ -0,0 +1,148 @@ +/* Copyright (C) 2006 - 2013 ScriptDev2 + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along 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: 2767. +SDCategory: Feralas +EndScriptData */ + +#include "precompiled.h" +#include "escort_ai.h" + +/*###### +## npc_oox22fe +######*/ + +enum +{ + SAY_OOX_START = -1000287, + SAY_OOX_AGGRO1 = -1000288, + 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 +}; + +struct npc_oox22feAI : public npc_escortAI +{ + npc_oox22feAI(Creature* pCreature) : npc_escortAI(pCreature) { Reset(); } + + void WaypointReached(uint32 i) override + { + switch (i) + { + // First Ambush(3 Yetis) + case 11: + DoScriptText(SAY_OOX_AMBUSH, m_creature); + m_creature->SummonCreature(NPC_YETI, -4841.01f, 1593.91f, 73.42f, 3.98f, TEMPSUMMON_CORPSE_TIMED_DESPAWN, 10000); + m_creature->SummonCreature(NPC_YETI, -4837.61f, 1568.58f, 78.21f, 3.13f, TEMPSUMMON_CORPSE_TIMED_DESPAWN, 10000); + m_creature->SummonCreature(NPC_YETI, -4841.89f, 1569.95f, 76.53f, 0.68f, TEMPSUMMON_CORPSE_TIMED_DESPAWN, 10000); + break; + // Second Ambush(3 Gorillas) + case 21: + DoScriptText(SAY_OOX_AMBUSH, m_creature); + m_creature->SummonCreature(NPC_GORILLA, -4595.81f, 2005.99f, 53.08f, 3.74f, TEMPSUMMON_CORPSE_TIMED_DESPAWN, 10000); + m_creature->SummonCreature(NPC_GORILLA, -4597.53f, 2008.31f, 52.70f, 3.78f, TEMPSUMMON_CORPSE_TIMED_DESPAWN, 10000); + m_creature->SummonCreature(NPC_GORILLA, -4599.37f, 2010.59f, 52.77f, 3.84f, TEMPSUMMON_CORPSE_TIMED_DESPAWN, 10000); + break; + // Third Ambush(4 Gnolls) + case 30: + DoScriptText(SAY_OOX_AMBUSH, m_creature); + m_creature->SummonCreature(NPC_WOODPAW_REAVER, -4425.14f, 2075.87f, 47.77f, 3.77f, TEMPSUMMON_CORPSE_TIMED_DESPAWN, 10000); + m_creature->SummonCreature(NPC_WOODPAW_BRUTE , -4426.68f, 2077.98f, 47.57f, 3.77f, TEMPSUMMON_CORPSE_TIMED_DESPAWN, 10000); + m_creature->SummonCreature(NPC_WOODPAW_MYSTIC, -4428.33f, 2080.24f, 47.43f, 3.87f, TEMPSUMMON_CORPSE_TIMED_DESPAWN, 10000); + m_creature->SummonCreature(NPC_WOODPAW_ALPHA , -4430.04f, 2075.54f, 46.83f, 3.81f, TEMPSUMMON_CORPSE_TIMED_DESPAWN, 10000); + break; + case 37: + DoScriptText(SAY_OOX_END, m_creature); + // Award quest credit + if (Player* pPlayer = GetPlayerForEscort()) + pPlayer->GroupEventHappens(QUEST_RESCUE_OOX22FE, m_creature); + break; + } + } + + void Reset() override + { + if (!HasEscortState(STATE_ESCORT_ESCORTING)) + m_creature->SetStandState(UNIT_STAND_STATE_DEAD); + } + + void Aggro(Unit* /*who*/) override + { + // For an small probability the npc says something when he get aggro + switch (urand(0, 9)) + { + case 0: DoScriptText(SAY_OOX_AGGRO1, m_creature); break; + case 1: DoScriptText(SAY_OOX_AGGRO2, m_creature); break; + } + } + + void JustSummoned(Creature* summoned) override + { + 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) + { + 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->SetFactionTemporary(FACTION_ESCORT_A_PASSIVE, TEMPFACTION_RESTORE_RESPAWN); + + if (pPlayer->GetTeam() == HORDE) + pCreature->SetFactionTemporary(FACTION_ESCORT_H_PASSIVE, TEMPFACTION_RESTORE_RESPAWN); + + if (npc_oox22feAI* pEscortAI = dynamic_cast(pCreature->AI())) + pEscortAI->Start(false, pPlayer, pQuest); + } + return true; +} + +/*###### +## AddSC +######*/ + +void AddSC_feralas() +{ + Script* pNewScript; + + pNewScript = new Script; + pNewScript->Name = "npc_oox22fe"; + pNewScript->GetAI = &GetAI_npc_oox22fe; + pNewScript->pQuestAcceptNPC = &QuestAccept_npc_oox22fe; + pNewScript->RegisterSelf(); +} diff --git a/src/modules/SD2/scripts/kalimdor/firelands/boss_alysrazor.cpp b/src/modules/SD2/scripts/kalimdor/firelands/boss_alysrazor.cpp new file mode 100644 index 000000000..29a243057 --- /dev/null +++ b/src/modules/SD2/scripts/kalimdor/firelands/boss_alysrazor.cpp @@ -0,0 +1,28 @@ +/* Copyright (C) 2006 - 2013 ScriptDev2 + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +/* ScriptData +SDName: boss_alysrazor +SD%Complete: 0 +SDComment: Placeholder +SDCategory: Firelands +EndScriptData */ + +#include "precompiled.h" + +void AddSC_boss_alysrazor() +{ +} diff --git a/src/modules/SD2/scripts/kalimdor/firelands/boss_baleroc.cpp b/src/modules/SD2/scripts/kalimdor/firelands/boss_baleroc.cpp new file mode 100644 index 000000000..568c0ea4b --- /dev/null +++ b/src/modules/SD2/scripts/kalimdor/firelands/boss_baleroc.cpp @@ -0,0 +1,28 @@ +/* Copyright (C) 2006 - 2013 ScriptDev2 + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +/* ScriptData +SDName: boss_baleroc +SD%Complete: 0 +SDComment: Placeholder +SDCategory: Firelands +EndScriptData */ + +#include "precompiled.h" + +void AddSC_boss_baleroc() +{ +} diff --git a/src/modules/SD2/scripts/kalimdor/firelands/boss_bethtilac.cpp b/src/modules/SD2/scripts/kalimdor/firelands/boss_bethtilac.cpp new file mode 100644 index 000000000..cc55f448b --- /dev/null +++ b/src/modules/SD2/scripts/kalimdor/firelands/boss_bethtilac.cpp @@ -0,0 +1,28 @@ +/* Copyright (C) 2006 - 2013 ScriptDev2 + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +/* ScriptData +SDName: boss_bethtilac +SD%Complete: 0 +SDComment: Placeholder +SDCategory: Firelands +EndScriptData */ + +#include "precompiled.h" + +void AddSC_boss_bethtilac() +{ +} diff --git a/src/modules/SD2/scripts/kalimdor/firelands/boss_lord_rhyolith.cpp b/src/modules/SD2/scripts/kalimdor/firelands/boss_lord_rhyolith.cpp new file mode 100644 index 000000000..d78dfa474 --- /dev/null +++ b/src/modules/SD2/scripts/kalimdor/firelands/boss_lord_rhyolith.cpp @@ -0,0 +1,28 @@ +/* Copyright (C) 2006 - 2013 ScriptDev2 + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with 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_rhyolith +SD%Complete: 0 +SDComment: Placeholder +SDCategory: Firelands +EndScriptData */ + +#include "precompiled.h" + +void AddSC_boss_lord_rhyolith() +{ +} diff --git a/src/modules/SD2/scripts/kalimdor/firelands/boss_majordomo_staghelm.cpp b/src/modules/SD2/scripts/kalimdor/firelands/boss_majordomo_staghelm.cpp new file mode 100644 index 000000000..1a68ad9c6 --- /dev/null +++ b/src/modules/SD2/scripts/kalimdor/firelands/boss_majordomo_staghelm.cpp @@ -0,0 +1,28 @@ +/* Copyright (C) 2006 - 2013 ScriptDev2 + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with 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_staghelm +SD%Complete: 0 +SDComment: Placeholder +SDCategory: Firelands +EndScriptData */ + +#include "precompiled.h" + +void AddSC_boss_majordomo_staghelm() +{ +} diff --git a/src/modules/SD2/scripts/kalimdor/firelands/boss_ragnaros_firelands.cpp b/src/modules/SD2/scripts/kalimdor/firelands/boss_ragnaros_firelands.cpp new file mode 100644 index 000000000..23bc7e67f --- /dev/null +++ b/src/modules/SD2/scripts/kalimdor/firelands/boss_ragnaros_firelands.cpp @@ -0,0 +1,28 @@ +/* Copyright (C) 2006 - 2013 ScriptDev2 + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with 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_firelands +SD%Complete: 0 +SDComment: Placeholder +SDCategory: Firelands +EndScriptData */ + +#include "precompiled.h" + +void AddSC_boss_ragnaros_firelands() +{ +} diff --git a/src/modules/SD2/scripts/kalimdor/firelands/boss_shannox.cpp b/src/modules/SD2/scripts/kalimdor/firelands/boss_shannox.cpp new file mode 100644 index 000000000..454f8ca1f --- /dev/null +++ b/src/modules/SD2/scripts/kalimdor/firelands/boss_shannox.cpp @@ -0,0 +1,28 @@ +/* Copyright (C) 2006 - 2013 ScriptDev2 + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +/* ScriptData +SDName: boss_shannox +SD%Complete: 0 +SDComment: Placeholder +SDCategory: Firelands +EndScriptData */ + +#include "precompiled.h" + +void AddSC_boss_shannox() +{ +} diff --git a/src/modules/SD2/scripts/kalimdor/firelands/firelands.h b/src/modules/SD2/scripts/kalimdor/firelands/firelands.h new file mode 100644 index 000000000..6b5f9ef47 --- /dev/null +++ b/src/modules/SD2/scripts/kalimdor/firelands/firelands.h @@ -0,0 +1,3 @@ +/* Copyright (C) 2006 - 2013 ScriptDev2 + * This program is free software licensed under GPL version 2 + * Please see the included DOCS/LICENSE.TXT for more information */ diff --git a/src/modules/SD2/scripts/kalimdor/firelands/instance_firelands.cpp b/src/modules/SD2/scripts/kalimdor/firelands/instance_firelands.cpp new file mode 100644 index 000000000..49d28c48f --- /dev/null +++ b/src/modules/SD2/scripts/kalimdor/firelands/instance_firelands.cpp @@ -0,0 +1,28 @@ +/* Copyright (C) 2006 - 2013 ScriptDev2 + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along 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_firelands +SD%Complete: 0 +SDComment: Placeholder +SDCategory: Firelands +EndScriptData */ + +#include "precompiled.h" + +void AddSC_instance_firelands() +{ +} diff --git a/src/modules/SD2/scripts/kalimdor/lost_city_of_tolvir/boss_general_husam.cpp b/src/modules/SD2/scripts/kalimdor/lost_city_of_tolvir/boss_general_husam.cpp new file mode 100644 index 000000000..1b75724c9 --- /dev/null +++ b/src/modules/SD2/scripts/kalimdor/lost_city_of_tolvir/boss_general_husam.cpp @@ -0,0 +1,28 @@ +/* Copyright (C) 2006 - 2013 ScriptDev2 + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with 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_husam +SD%Complete: 0 +SDComment: Placeholder +SDCategory: Lost City of Tolvir +EndScriptData */ + +#include "precompiled.h" + +void AddSC_boss_general_husam() +{ +} diff --git a/src/modules/SD2/scripts/kalimdor/lost_city_of_tolvir/boss_lockmaw.cpp b/src/modules/SD2/scripts/kalimdor/lost_city_of_tolvir/boss_lockmaw.cpp new file mode 100644 index 000000000..7f7aa6a75 --- /dev/null +++ b/src/modules/SD2/scripts/kalimdor/lost_city_of_tolvir/boss_lockmaw.cpp @@ -0,0 +1,28 @@ +/* Copyright (C) 2006 - 2013 ScriptDev2 + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +/* ScriptData +SDName: boss_lockmaw +SD%Complete: 0 +SDComment: Placeholder +SDCategory: Lost City of Tolvir +EndScriptData */ + +#include "precompiled.h" + +void AddSC_boss_lockmaw() +{ +} diff --git a/src/modules/SD2/scripts/kalimdor/lost_city_of_tolvir/boss_prophet_barim.cpp b/src/modules/SD2/scripts/kalimdor/lost_city_of_tolvir/boss_prophet_barim.cpp new file mode 100644 index 000000000..5a23c6921 --- /dev/null +++ b/src/modules/SD2/scripts/kalimdor/lost_city_of_tolvir/boss_prophet_barim.cpp @@ -0,0 +1,28 @@ +/* Copyright (C) 2006 - 2013 ScriptDev2 + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +/* ScriptData +SDName: boss_prophet_barim +SD%Complete: 0 +SDComment: Placeholder +SDCategory: Lost City of Tolvir +EndScriptData */ + +#include "precompiled.h" + +void AddSC_boss_prophet_barim() +{ +} diff --git a/src/modules/SD2/scripts/kalimdor/lost_city_of_tolvir/boss_siamat.cpp b/src/modules/SD2/scripts/kalimdor/lost_city_of_tolvir/boss_siamat.cpp new file mode 100644 index 000000000..34bd8df0e --- /dev/null +++ b/src/modules/SD2/scripts/kalimdor/lost_city_of_tolvir/boss_siamat.cpp @@ -0,0 +1,28 @@ +/* Copyright (C) 2006 - 2013 ScriptDev2 + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +/* ScriptData +SDName: boss_siamat +SD%Complete: 0 +SDComment: Placeholder +SDCategory: Lost City of Tolvir +EndScriptData */ + +#include "precompiled.h" + +void AddSC_boss_siamat() +{ +} diff --git a/src/modules/SD2/scripts/kalimdor/lost_city_of_tolvir/instance_lost_city_of_tolvir.cpp b/src/modules/SD2/scripts/kalimdor/lost_city_of_tolvir/instance_lost_city_of_tolvir.cpp new file mode 100644 index 000000000..fed7b1391 --- /dev/null +++ b/src/modules/SD2/scripts/kalimdor/lost_city_of_tolvir/instance_lost_city_of_tolvir.cpp @@ -0,0 +1,28 @@ +/* Copyright (C) 2006 - 2013 ScriptDev2 + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along 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_lost_city_of_tolvir +SD%Complete: 0 +SDComment: Placeholder +SDCategory: Lost City of Tolvir +EndScriptData */ + +#include "precompiled.h" + +void AddSC_instance_lost_city_of_tolvir() +{ +} diff --git a/src/modules/SD2/scripts/kalimdor/lost_city_of_tolvir/lost_city_of_tolvir.h b/src/modules/SD2/scripts/kalimdor/lost_city_of_tolvir/lost_city_of_tolvir.h new file mode 100644 index 000000000..6b5f9ef47 --- /dev/null +++ b/src/modules/SD2/scripts/kalimdor/lost_city_of_tolvir/lost_city_of_tolvir.h @@ -0,0 +1,3 @@ +/* Copyright (C) 2006 - 2013 ScriptDev2 + * This program is free software licensed under GPL version 2 + * Please see the included DOCS/LICENSE.TXT for more information */ diff --git a/src/modules/SD2/scripts/kalimdor/maraudon/boss_noxxion.cpp b/src/modules/SD2/scripts/kalimdor/maraudon/boss_noxxion.cpp new file mode 100644 index 000000000..d696cc906 --- /dev/null +++ b/src/modules/SD2/scripts/kalimdor/maraudon/boss_noxxion.cpp @@ -0,0 +1,120 @@ +/* Copyright (C) 2006 - 2013 ScriptDev2 + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with 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" + +enum +{ + SPELL_TOXICVOLLEY = 21687, + SPELL_UPPERCUT = 22916, + SPELL_NOXXION_SPAWNS_AURA = 21708, + SPELL_NOXXION_SPAWNS_SUMMON = 21707, +}; + +struct boss_noxxionAI : public ScriptedAI +{ + boss_noxxionAI(Creature* pCreature) : ScriptedAI(pCreature) {Reset();} + + uint32 m_uiToxicVolleyTimer; + uint32 m_uiUppercutTimer; + uint32 m_uiSummonTimer; + + void Reset() override + { + m_uiToxicVolleyTimer = 7000; + m_uiUppercutTimer = 16000; + m_uiSummonTimer = 19000; + } + + void JustSummoned(Creature* pSummoned) override + { + if (Unit* pTarget = m_creature->SelectAttackingTarget(ATTACKING_TARGET_RANDOM, 0)) + pSummoned->AI()->AttackStart(pTarget); + } + + void UpdateAI(const uint32 diff) override + { + if (!m_creature->SelectHostileTarget() || !m_creature->getVictim()) + return; + + if (m_uiToxicVolleyTimer < diff) + { + if (DoCastSpellIfCan(m_creature, SPELL_TOXICVOLLEY) == CAST_OK) + m_uiToxicVolleyTimer = 9000; + } + else + m_uiToxicVolleyTimer -= diff; + + if (m_uiUppercutTimer < diff) + { + if (DoCastSpellIfCan(m_creature->getVictim(), SPELL_UPPERCUT) == CAST_OK) + m_uiUppercutTimer = 12000; + } + else + m_uiUppercutTimer -= diff; + + if (m_uiSummonTimer < diff) + { + if (DoCastSpellIfCan(m_creature, SPELL_NOXXION_SPAWNS_AURA) == CAST_OK) + m_uiSummonTimer = 40000; + } + else + m_uiSummonTimer -= diff; + + DoMeleeAttackIfReady(); + } +}; + +CreatureAI* GetAI_boss_noxxion(Creature* pCreature) +{ + return new boss_noxxionAI(pCreature); +} + +bool EffectAuraDummy_spell_aura_dummy_noxxion_spawns(const Aura* pAura, bool bApply) +{ + if (pAura->GetId() == SPELL_NOXXION_SPAWNS_AURA && pAura->GetEffIndex() == EFFECT_INDEX_0) + { + if (Creature* pTarget = (Creature*)pAura->GetTarget()) + { + if (bApply) + { + pTarget->CastSpell(pTarget, SPELL_NOXXION_SPAWNS_SUMMON, true); + pTarget->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE); + } + else + pTarget->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE); + } + } + return true; +} + +void AddSC_boss_noxxion() +{ + Script* pNewScript; + + pNewScript = new Script; + pNewScript->Name = "boss_noxxion"; + pNewScript->GetAI = &GetAI_boss_noxxion; + pNewScript->pEffectAuraDummy = &EffectAuraDummy_spell_aura_dummy_noxxion_spawns; + pNewScript->RegisterSelf(); +} diff --git a/src/modules/SD2/scripts/kalimdor/molten_front.cpp b/src/modules/SD2/scripts/kalimdor/molten_front.cpp new file mode 100644 index 000000000..c1ed3277c --- /dev/null +++ b/src/modules/SD2/scripts/kalimdor/molten_front.cpp @@ -0,0 +1,35 @@ +/* Copyright (C) 2006 - 2013 ScriptDev2 + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along 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 Front +SD%Complete: 0 +SDComment: Placeholder +SDCategory: Molten Front +EndScriptData */ + +/* ContentData +EndContentData */ + +#include "precompiled.h" + +/*###### +# +######*/ + +void AddSC_molten_front() +{ +} diff --git a/src/modules/SD2/scripts/kalimdor/moonglade.cpp b/src/modules/SD2/scripts/kalimdor/moonglade.cpp new file mode 100644 index 000000000..ed9ffd05f --- /dev/null +++ b/src/modules/SD2/scripts/kalimdor/moonglade.cpp @@ -0,0 +1,990 @@ +/* Copyright (C) 2006 - 2013 ScriptDev2 + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along 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: 8736, 10965. +SDCategory: Moonglade +EndScriptData */ + +/* ContentData +npc_clintar_dw_spirit +npc_keeper_remulos +boss_eranikus +EndContentData */ + +#include "precompiled.h" +#include "escort_ai.h" +#include "ObjectMgr.h" + +/*###### +# npc_clintar_dw_spirit +####*/ + +enum +{ + SAY_START = -1000280, + SAY_AGGRO_1 = -1000281, + SAY_AGGRO_2 = -1000282, + SAY_RELIC1 = -1000283, + SAY_RELIC2 = -1000284, + SAY_RELIC3 = -1000285, + SAY_END = -1000286, + + QUEST_MERE_DREAM = 10965, + SPELL_EMERALD_DREAM = 39601, + NPC_CLINTAR_DW_SPIRIT = 22916, + NPC_CLINTAR_SPIRIT = 22901, + NPC_ASPECT_OF_RAVEN = 22915, +}; + +struct npc_clintar_dw_spiritAI : public npc_escortAI +{ + npc_clintar_dw_spiritAI(Creature* pCreature) : npc_escortAI(pCreature) { Reset(); } + + void WaypointReached(uint32 i) override + { + Player* pPlayer = GetPlayerForEscort(); + + if (!pPlayer) + return; + + // visual details here probably need refinement + switch (i) + { + case 0: + DoScriptText(SAY_START, m_creature, pPlayer); + break; + case 13: + m_creature->HandleEmote(EMOTE_STATE_USESTANDING_NOSHEATHE); + break; + case 14: + DoScriptText(SAY_RELIC1, m_creature, pPlayer); + break; + case 26: + m_creature->HandleEmote(EMOTE_STATE_USESTANDING_NOSHEATHE); + break; + case 27: + DoScriptText(SAY_RELIC2, m_creature, pPlayer); + break; + case 31: + m_creature->SummonCreature(NPC_ASPECT_OF_RAVEN, 7465.321f, -3088.515f, 429.006f, 5.550f, TEMPSUMMON_TIMED_OOC_DESPAWN, 10000); + break; + case 35: + m_creature->HandleEmote(EMOTE_STATE_USESTANDING_NOSHEATHE); + break; + case 36: + DoScriptText(SAY_RELIC3, m_creature, pPlayer); + break; + case 49: + DoScriptText(SAY_END, m_creature, pPlayer); + pPlayer->TalkedToCreature(m_creature->GetEntry(), m_creature->GetObjectGuid()); + break; + } + } + + void Aggro(Unit* /*who*/) override + { + DoScriptText(urand(0, 1) ? SAY_AGGRO_1 : SAY_AGGRO_2, m_creature); + } + + void Reset() override + { + if (HasEscortState(STATE_ESCORT_ESCORTING)) + return; + + // m_creature are expected to always be spawned, but not visible for player + // spell casted from quest_template.SrcSpell require this to be this way + // we handle the triggered spell to get a "hook" to our guy so he can be escorted on quest accept + + if (CreatureInfo const* pTemp = GetCreatureTemplateStore(m_creature->GetEntry())) + m_creature->SetDisplayId(Creature::ChooseDisplayId(pTemp)); + + m_creature->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE); + m_creature->SetVisibility(VISIBILITY_OFF); + } + + // called only from EffectDummy + void DoStart(Unit* pStarter) + { + // not the best way, maybe check in DummyEffect if this creature are "free" and not in escort. + if (HasEscortState(STATE_ESCORT_ESCORTING)) + return; + + m_creature->SetVisibility(VISIBILITY_ON); + m_creature->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE); + Start(false, pStarter && pStarter->GetTypeId() == TYPEID_PLAYER ? (Player*)pStarter : NULL); + } + + void JustSummoned(Creature* summoned) override + { + summoned->AI()->AttackStart(m_creature); + } +}; + +CreatureAI* GetAI_npc_clintar_dw_spirit(Creature* pCreature) +{ + return new npc_clintar_dw_spiritAI(pCreature); +} + +// we expect this spell to be triggered from spell casted at questAccept +bool EffectDummyCreature_npc_clintar_dw_spirit(Unit* pCaster, uint32 spellId, SpellEffectIndex effIndex, Creature* pCreatureTarget, ObjectGuid /*originalCasterGuid*/) +{ + // always check spellid and effectindex + if (spellId == SPELL_EMERALD_DREAM && effIndex == EFFECT_INDEX_0) + { + if (pCaster->GetTypeId() != TYPEID_PLAYER || pCaster->HasAura(SPELL_EMERALD_DREAM)) + return true; + + if (pCreatureTarget->GetEntry() != NPC_CLINTAR_DW_SPIRIT) + return true; + + if (CreatureInfo const* pTemp = GetCreatureTemplateStore(NPC_CLINTAR_SPIRIT)) + pCreatureTarget->SetDisplayId(Creature::ChooseDisplayId(pTemp)); + else + return true; + + // done here, escort can start + if (npc_clintar_dw_spiritAI* pSpiritAI = dynamic_cast(pCreatureTarget->AI())) + pSpiritAI->DoStart(pCaster); + + // always return true when we are handling this spell and effect + return true; + } + return true; +} + +/*###### +## npc_keeper_remulos +######*/ + +enum +{ + SPELL_CONJURE_RIFT = 25813, // summon Eranikus + SPELL_HEALING_TOUCH = 23381, + SPELL_REGROWTH = 20665, + SPELL_REJUVENATION = 20664, + SPELL_STARFIRE = 21668, + SPELL_ERANIKUS_REDEEMED = 25846, // transform Eranikus + // SPELL_MOONGLADE_TRANQUILITY = unk, // spell which acts as a spotlight over Eranikus after he is redeemed + + NPC_ERANIKUS_TYRANT = 15491, + NPC_NIGHTMARE_PHANTASM = 15629, // shadows summoned during the event - should cast 17228 and 21307 + NPC_REMULOS = 11832, + NPC_TYRANDE_WHISPERWIND = 15633, // appears with the priestess during the event to help the players - should cast healing spells + NPC_ELUNE_PRIESTESS = 15634, + + QUEST_NIGHTMARE_MANIFESTS = 8736, + + // yells -> in cronological order + SAY_REMULOS_INTRO_1 = -1000669, // remulos intro + SAY_REMULOS_INTRO_2 = -1000670, + SAY_REMULOS_INTRO_3 = -1000671, + SAY_REMULOS_INTRO_4 = -1000672, + SAY_REMULOS_INTRO_5 = -1000673, + + EMOTE_SUMMON_ERANIKUS = -1000674, // eranikus spawn - world emote + SAY_ERANIKUS_SPAWN = -1000675, + + SAY_REMULOS_TAUNT_1 = -1000676, // eranikus and remulos chat + EMOTE_ERANIKUS_LAUGH = -1000677, + SAY_ERANIKUS_TAUNT_2 = -1000678, + SAY_REMULOS_TAUNT_3 = -1000679, + SAY_ERANIKUS_TAUNT_4 = -1000680, + + EMOTE_ERANIKUS_ATTACK = -1000681, // start attack + SAY_REMULOS_DEFEND_1 = -1000682, + SAY_REMULOS_DEFEND_2 = -1000683, + SAY_ERANIKUS_SHADOWS = -1000684, + SAY_REMULOS_DEFEND_3 = -1000685, + SAY_ERANIKUS_ATTACK_1 = -1000686, + SAY_ERANIKUS_ATTACK_2 = -1000687, + SAY_ERANIKUS_ATTACK_3 = -1000688, + SAY_ERANIKUS_KILL = -1000706, + + SAY_TYRANDE_APPEAR = -1000689, // Tyrande appears + SAY_TYRANDE_HEAL = -1000690, // yelled by tyrande when healing is needed + SAY_TYRANDE_FORGIVEN_1 = -1000691, + SAY_TYRANDE_FORGIVEN_2 = -1000692, + SAY_TYRANDE_FORGIVEN_3 = -1000693, + SAY_ERANIKUS_DEFEAT_1 = -1000694, + SAY_ERANIKUS_DEFEAT_2 = -1000695, + SAY_ERANIKUS_DEFEAT_3 = -1000696, + EMOTE_ERANIKUS_REDEEM = -1000697, // world emote before WotLK + + EMOTE_TYRANDE_KNEEL = -1000698, + SAY_TYRANDE_REDEEMED = -1000699, + + SAY_REDEEMED_1 = -1000700, // eranikus redeemed + SAY_REDEEMED_2 = -1000701, + SAY_REDEEMED_3 = -1000702, + SAY_REDEEMED_4 = -1000703, + + SAY_REMULOS_OUTRO_1 = -1000704, // remulos outro + SAY_REMULOS_OUTRO_2 = -1000705, + + POINT_ID_ERANIKUS_FLIGHT = 0, + POINT_ID_ERANIKUS_COMBAT = 1, + POINT_ID_ERANIKUS_REDEEMED = 2, + + MAX_SHADOWS = 3, // the max shadows summoned per turn + MAX_SUMMON_TURNS = 10, // There are about 10 summoned shade waves +}; + +static const DialogueEntry aIntroDialogue[] = +{ + {NPC_REMULOS, 0, 14000}, // target player + {SAY_REMULOS_INTRO_4, NPC_REMULOS, 12000}, + {SAY_REMULOS_INTRO_5, NPC_REMULOS, 5000}, + {SPELL_CONJURE_RIFT, 0, 13000}, // conjure rift spell + {SAY_ERANIKUS_SPAWN, NPC_ERANIKUS_TYRANT, 11000}, + {SAY_REMULOS_TAUNT_1, NPC_REMULOS, 5000}, + {EMOTE_ERANIKUS_LAUGH, NPC_ERANIKUS_TYRANT, 3000}, + {SAY_ERANIKUS_TAUNT_2, NPC_ERANIKUS_TYRANT, 10000}, + {SAY_REMULOS_TAUNT_3, NPC_REMULOS, 12000}, + {SAY_ERANIKUS_TAUNT_4, NPC_ERANIKUS_TYRANT, 6000}, + {EMOTE_ERANIKUS_ATTACK, NPC_ERANIKUS_TYRANT, 7000}, + {NPC_ERANIKUS_TYRANT, 0, 0}, // target player - restart the escort and move Eranikus above the village + {SAY_REMULOS_DEFEND_2, NPC_REMULOS, 6000}, // face Eranikus + {SAY_ERANIKUS_SHADOWS, NPC_ERANIKUS_TYRANT, 4000}, + {SAY_REMULOS_DEFEND_3, NPC_REMULOS, 0}, + {0, 0, 0}, +}; + +struct EventLocations +{ + float m_fX, m_fY, m_fZ, m_fO; +}; + +static EventLocations aEranikusLocations[] = +{ + {7881.72f, -2651.23f, 493.29f, 0.40f}, // eranikus spawn loc + {7929.86f, -2574.88f, 505.35f}, // eranikus flight move loc + {7912.98f, -2568.99f, 488.71f}, // eranikus combat move loc + {7906.57f, -2565.63f, 488.39f}, // eranikus redeemed loc +}; + +static EventLocations aTyrandeLocations[] = +{ + // Tyrande should appear along the pathway, but because of the missing pathfinding we'll summon here closer to Eranikus + {7948.89f, -2575.58f, 490.05f, 3.03f}, // tyrande spawn loc + {7888.32f, -2566.25f, 487.02f}, // tyrande heal loc + {7901.83f, -2565.24f, 488.04f}, // tyrande eranikus loc +}; + +static EventLocations aShadowsLocations[] = +{ + // Inside the house shades - first wave only + {7832.78f, -2604.57f, 489.29f}, + {7826.68f, -2538.46f, 489.30f}, + {7811.48f, -2573.20f, 488.49f}, + // Outside shade points - basically only the first set of coords is used for the summoning; there is no solid proof of using the other coords + {7888.32f, -2566.25f, 487.02f}, + {7946.12f, -2577.10f, 489.97f}, + {7963.00f, -2492.03f, 487.84f} +}; + +struct npc_keeper_remulosAI : public npc_escortAI, private DialogueHelper +{ + npc_keeper_remulosAI(Creature* pCreature) : npc_escortAI(pCreature), + DialogueHelper(aIntroDialogue) + { + Reset(); + } + + uint32 m_uiHealTimer; + uint32 m_uiStarfireTimer; + uint32 m_uiShadesummonTimer; + uint32 m_uiOutroTimer; + + ObjectGuid m_eranikusGuid; + + uint8 m_uiOutroPhase; + uint8 m_uiSummonCount; + + bool m_bIsFirstWave; + + void Reset() override + { + if (!HasEscortState(STATE_ESCORT_ESCORTING)) + { + m_uiOutroTimer = 0; + m_uiOutroPhase = 0; + m_uiSummonCount = 0; + + m_eranikusGuid.Clear(); + + m_uiShadesummonTimer = 0; + m_uiHealTimer = 10000; + m_uiStarfireTimer = 25000; + + m_bIsFirstWave = true; + } + } + + void JustSummoned(Creature* pSummoned) override + { + switch (pSummoned->GetEntry()) + { + case NPC_ERANIKUS_TYRANT: + m_eranikusGuid = pSummoned->GetObjectGuid(); + // Make Eranikus unattackable first + // ToDo: uncomment the fly effect when it will be possible to cancel it properly + // pSummoned->SetByteValue(UNIT_FIELD_BYTES_1, 3, UNIT_BYTE1_FLAG_ALWAYS_STAND | UNIT_BYTE1_FLAG_UNK_2); + pSummoned->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE); + pSummoned->SetLevitate(true); + break; + case NPC_NIGHTMARE_PHANTASM: + // ToDo: set faction to DB + pSummoned->setFaction(14); + pSummoned->AI()->AttackStart(m_creature); + break; + } + } + + void SummonedMovementInform(Creature* pSummoned, uint32 uiType, uint32 uiPointId) override + { + if (uiType != POINT_MOTION_TYPE || pSummoned->GetEntry() != NPC_ERANIKUS_TYRANT) + return; + + switch (uiPointId) + { + case POINT_ID_ERANIKUS_FLIGHT: + // Set Eranikus to face Remulos + pSummoned->SetFacingToObject(m_creature); + break; + case POINT_ID_ERANIKUS_COMBAT: + // Start attack + pSummoned->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE); + pSummoned->AI()->AttackStart(m_creature); + DoScriptText(SAY_ERANIKUS_ATTACK_2, pSummoned); + break; + } + } + + void JustDied(Unit* pKiller) override + { + // Make Eranikus evade in order to despawn all the summons + if (Creature* pEranikus = m_creature->GetMap()->GetCreature(m_eranikusGuid)) + pEranikus->AI()->EnterEvadeMode(); + + npc_escortAI::JustDied(pKiller); + } + + void WaypointReached(uint32 uiPointId) override + { + switch (uiPointId) + { + case 0: + if (Player* pPlayer = GetPlayerForEscort()) + DoScriptText(SAY_REMULOS_INTRO_1, m_creature, pPlayer); + break; + case 1: + DoScriptText(SAY_REMULOS_INTRO_2, m_creature); + break; + case 13: + StartNextDialogueText(NPC_REMULOS); + SetEscortPaused(true); + break; + case 17: + StartNextDialogueText(SAY_REMULOS_DEFEND_2); + SetEscortPaused(true); + break; + case 18: + SetEscortPaused(true); + break; + } + } + + Creature* GetSpeakerByEntry(uint32 uiEntry) override + { + switch (uiEntry) + { + case NPC_REMULOS: return m_creature; + case NPC_ERANIKUS_TYRANT: return m_creature->GetMap()->GetCreature(m_eranikusGuid); + + default: + return NULL; + } + } + + void JustDidDialogueStep(int32 iEntry) override + { + switch (iEntry) + { + case NPC_REMULOS: + if (Player* pPlayer = GetPlayerForEscort()) + DoScriptText(SAY_REMULOS_INTRO_3, m_creature, pPlayer); + break; + case SPELL_CONJURE_RIFT: + DoCastSpellIfCan(m_creature, SPELL_CONJURE_RIFT); + break; + case SAY_ERANIKUS_SPAWN: + // This big yellow emote was removed at some point in WotLK + // DoScriptText(EMOTE_SUMMON_ERANIKUS, pEranikus); + break; + case NPC_ERANIKUS_TYRANT: + if (Player* pPlayer = GetPlayerForEscort()) + DoScriptText(SAY_REMULOS_DEFEND_1, m_creature, pPlayer); + if (Creature* pEranikus = m_creature->GetMap()->GetCreature(m_eranikusGuid)) + pEranikus->GetMotionMaster()->MovePoint(POINT_ID_ERANIKUS_FLIGHT, aEranikusLocations[1].m_fX, aEranikusLocations[1].m_fY, aEranikusLocations[1].m_fZ); + SetEscortPaused(false); + break; + case SAY_REMULOS_DEFEND_2: + if (Creature* pEranikus = m_creature->GetMap()->GetCreature(m_eranikusGuid)) + m_creature->SetFacingToObject(pEranikus); + break; + case SAY_REMULOS_DEFEND_3: + SetEscortPaused(true); + m_uiShadesummonTimer = 5000; + break; + } + } + + void DoHandleOutro(Creature* pTarget) + { + if (Player* pPlayer = GetPlayerForEscort()) + pPlayer->GroupEventHappens(QUEST_NIGHTMARE_MANIFESTS, pTarget); + + m_uiOutroTimer = 3000; + } + + void UpdateEscortAI(const uint32 uiDiff) override + { + DialogueUpdate(uiDiff); + + if (m_uiOutroTimer) + { + if (m_uiOutroTimer <= uiDiff) + { + switch (m_uiOutroPhase) + { + case 0: + DoScriptText(SAY_REMULOS_OUTRO_1, m_creature); + m_uiOutroTimer = 3000; + break; + case 1: + // Despawn Remulos after the outro is finished - he will respawn automatically at his home position after a few min + DoScriptText(SAY_REMULOS_OUTRO_2, m_creature); + m_creature->SetRespawnDelay(1 * MINUTE); + m_creature->ForcedDespawn(3000); + m_uiOutroTimer = 0; + break; + } + ++m_uiOutroPhase; + } + else + m_uiOutroTimer -= uiDiff; + } + + // during the battle + if (m_uiShadesummonTimer) + { + if (m_uiShadesummonTimer <= uiDiff) + { + // do this yell only first time + if (m_bIsFirstWave) + { + // summon 3 shades inside the house + for (uint8 i = 0; i < MAX_SHADOWS; ++i) + m_creature->SummonCreature(NPC_NIGHTMARE_PHANTASM, aShadowsLocations[i].m_fX, aShadowsLocations[i].m_fY, aShadowsLocations[i].m_fZ, 0, TEMPSUMMON_DEAD_DESPAWN, 0); + + if (Creature* pEranikus = m_creature->GetMap()->GetCreature(m_eranikusGuid)) + DoScriptText(SAY_ERANIKUS_ATTACK_1, pEranikus); + + ++m_uiSummonCount; + SetEscortPaused(false); + m_bIsFirstWave = false; + } + + // Summon 3 shades per turn until the maximum summon turns are reached + float fX, fY, fZ; + // Randomize the summon point + uint8 uiSummonPoint = roll_chance_i(70) ? uint32(MAX_SHADOWS) : urand(MAX_SHADOWS + 1, MAX_SHADOWS + 2); + + if (m_uiSummonCount < MAX_SUMMON_TURNS) + { + for (uint8 i = 0; i < MAX_SHADOWS; ++i) + { + m_creature->GetRandomPoint(aShadowsLocations[uiSummonPoint].m_fX, aShadowsLocations[uiSummonPoint].m_fY, aShadowsLocations[uiSummonPoint].m_fZ, 10.0f, fX, fY, fZ); + m_creature->SummonCreature(NPC_NIGHTMARE_PHANTASM, fX, fY, fZ, 0.0f, TEMPSUMMON_DEAD_DESPAWN, 0); + } + + ++m_uiSummonCount; + } + + // If all the shades were summoned then set Eranikus in combat + // We don't count the dead shades, because the boss is usually set in combat before all shades are dead + if (m_uiSummonCount == MAX_SUMMON_TURNS) + { + m_uiShadesummonTimer = 0; + + if (Creature* pEranikus = m_creature->GetMap()->GetCreature(m_eranikusGuid)) + { + pEranikus->SetByteFlag(UNIT_FIELD_BYTES_1, 3, 0); + pEranikus->SetLevitate(false); + pEranikus->GetMotionMaster()->MovePoint(POINT_ID_ERANIKUS_COMBAT, aEranikusLocations[2].m_fX, aEranikusLocations[2].m_fY, aEranikusLocations[2].m_fZ); + } + } + else + m_uiShadesummonTimer = urand(20000, 30000); + } + else + m_uiShadesummonTimer -= uiDiff; + } + + // Combat spells + if (!m_creature->SelectHostileTarget() || !m_creature->getVictim()) + return; + + if (m_uiHealTimer < uiDiff) + { + if (Unit* pTarget = DoSelectLowestHpFriendly(DEFAULT_VISIBILITY_DISTANCE)) + { + switch (urand(0, 2)) + { + case 0: DoCastSpellIfCan(pTarget, SPELL_HEALING_TOUCH); break; + case 1: DoCastSpellIfCan(pTarget, SPELL_REJUVENATION); break; + case 2: DoCastSpellIfCan(pTarget, SPELL_REGROWTH); break; + } + } + m_uiHealTimer = 10000; + } + else + m_uiHealTimer -= uiDiff; + + if (m_uiStarfireTimer < uiDiff) + { + if (Unit* pTarget = m_creature->SelectAttackingTarget(ATTACKING_TARGET_RANDOM, 0)) + { + if (DoCastSpellIfCan(pTarget, SPELL_STARFIRE) == CAST_OK) + m_uiStarfireTimer = 20000; + } + } + else + m_uiStarfireTimer -= uiDiff; + + DoMeleeAttackIfReady(); + } +}; + +CreatureAI* GetAI_npc_keeper_remulos(Creature* pCreature) +{ + return new npc_keeper_remulosAI(pCreature); +} + +bool QuestAccept_npc_keeper_remulos(Player* pPlayer, Creature* pCreature, const Quest* pQuest) +{ + if (pQuest->GetQuestId() == QUEST_NIGHTMARE_MANIFESTS) + { + if (npc_keeper_remulosAI* pEscortAI = dynamic_cast(pCreature->AI())) + pEscortAI->Start(true, pPlayer, pQuest); + + return true; + } + + // Return false for other quests in order to handle DB scripts. Example: quest 8447 + return false; +} + +bool EffectDummyCreature_conjure_rift(Unit* pCaster, uint32 uiSpellId, SpellEffectIndex uiEffIndex, Creature* /*pCreatureTarget*/, ObjectGuid /*originalCasterGuid*/) +{ + // always check spellid and effectindex + if (uiSpellId == SPELL_CONJURE_RIFT && uiEffIndex == EFFECT_INDEX_0) + { + pCaster->SummonCreature(NPC_ERANIKUS_TYRANT, aEranikusLocations[0].m_fX, aEranikusLocations[0].m_fY, aEranikusLocations[0].m_fZ, aEranikusLocations[0].m_fO, TEMPSUMMON_DEAD_DESPAWN, 0); + + // always return true when we are handling this spell and effect + return true; + } + + return false; +} + +/*###### +## boss_eranikus +######*/ + +enum +{ + NPC_KEEPER_REMULOS = 11832, + + SPELL_ACID_BREATH = 24839, + SPELL_NOXIOUS_BREATH = 24818, + SPELL_SHADOWBOLT_VOLLEY = 25586, + SPELL_ARCANE_CHANNELING = 23017, // used by Tyrande - not sure if it's the right id + + FACTION_FRIENDLY = 35, + MAX_PRIESTESS = 7, + + POINT_ID_TYRANDE_HEAL = 0, + POINT_ID_TYRANDE_ABSOLUTION = 1, +}; + +struct boss_eranikusAI : public ScriptedAI +{ + boss_eranikusAI(Creature* pCreature) : ScriptedAI(pCreature) { Reset(); } + + uint32 m_uiAcidBreathTimer; + uint32 m_uiNoxiousBreathTimer; + uint32 m_uiShadowboltVolleyTimer; + uint32 m_uiEventTimer; + uint32 m_uiTyrandeMoveTimer; + + uint8 m_uiEventPhase; + uint8 m_uiTyrandeMovePoint; + uint8 m_uiHealthCheck; + + ObjectGuid m_remulosGuid; + ObjectGuid m_tyrandeGuid; + GuidList m_lPriestessList; + + void Reset() override + { + m_uiAcidBreathTimer = 10000; + m_uiNoxiousBreathTimer = 3000; + m_uiShadowboltVolleyTimer = 5000; + m_uiTyrandeMoveTimer = 0; + + m_remulosGuid.Clear(); + m_tyrandeGuid.Clear(); + + m_uiHealthCheck = 85; + m_uiEventPhase = 0; + m_uiEventTimer = 0; + + // For some reason the boss doesn't move in combat + SetCombatMovement(false); + } + + void EnterEvadeMode() override + { + if (m_creature->GetHealthPercent() < 20.0f) + { + m_creature->RemoveAllAurasOnEvade(); + m_creature->DeleteThreatList(); + m_creature->CombatStop(true); + m_creature->LoadCreatureAddon(true); + + m_creature->SetLootRecipient(NULL); + + // Get Remulos guid and make him stop summoning shades + if (Creature* pRemulos = GetClosestCreatureWithEntry(m_creature, NPC_REMULOS, 50.0f)) + { + m_remulosGuid = pRemulos->GetObjectGuid(); + pRemulos->AI()->EnterEvadeMode(); + } + + // Despawn the priestess + DoDespawnSummoned(); + + // redeem eranikus + m_uiEventTimer = 5000; + m_creature->setFaction(FACTION_FRIENDLY); + } + else + { + // There may be a core issue related to the reached home function for summoned creatures so we are cleaning things up here + // if the creature evades while the event is in progress then we despawn all the summoned, including himself + m_creature->ForcedDespawn(); + DoDespawnSummoned(); + + if (Creature* pTyrande = m_creature->GetMap()->GetCreature(m_tyrandeGuid)) + pTyrande->ForcedDespawn(); + } + } + + void KilledUnit(Unit* pVictim) override + { + if (pVictim->GetTypeId() != TYPEID_PLAYER) + return; + + DoScriptText(SAY_ERANIKUS_KILL, m_creature); + } + + void DoSummonHealers() + { + float fX, fY, fZ; + for (uint8 j = 0; j < MAX_PRIESTESS; ++j) + { + m_creature->GetRandomPoint(aTyrandeLocations[0].m_fX, aTyrandeLocations[0].m_fY, aTyrandeLocations[0].m_fZ, 10.0f, fX, fY, fZ); + m_creature->SummonCreature(NPC_ELUNE_PRIESTESS, fX, fY, fZ, 0.0f, TEMPSUMMON_CORPSE_DESPAWN, 0); + } + } + + void JustSummoned(Creature* pSummoned) override + { + switch (pSummoned->GetEntry()) + { + case NPC_TYRANDE_WHISPERWIND: + m_tyrandeGuid = pSummoned->GetObjectGuid(); + pSummoned->SetWalk(false); + pSummoned->GetMotionMaster()->MovePoint(POINT_ID_TYRANDE_HEAL, aTyrandeLocations[1].m_fX, aTyrandeLocations[1].m_fY, aTyrandeLocations[1].m_fZ); + break; + case NPC_ELUNE_PRIESTESS: + m_lPriestessList.push_back(pSummoned->GetObjectGuid()); + float fX, fY, fZ; + pSummoned->SetWalk(false); + m_creature->GetRandomPoint(aTyrandeLocations[1].m_fX, aTyrandeLocations[1].m_fY, aTyrandeLocations[1].m_fZ, 10.0f, fX, fY, fZ); + pSummoned->GetMotionMaster()->MovePoint(POINT_ID_TYRANDE_HEAL, fX, fY, fZ); + break; + } + } + + void DoDespawnSummoned() + { + for (GuidList::const_iterator itr = m_lPriestessList.begin(); itr != m_lPriestessList.end(); ++itr) + { + if (Creature* pTemp = m_creature->GetMap()->GetCreature(*itr)) + pTemp->ForcedDespawn(); + } + } + + void SummonedMovementInform(Creature* pSummoned, uint32 uiType, uint32 uiPointId) override + { + if (uiType != POINT_MOTION_TYPE) + return; + + switch (uiPointId) + { + case POINT_ID_TYRANDE_HEAL: + if (pSummoned->GetEntry() == NPC_TYRANDE_WHISPERWIND) + { + // Unmont, yell and prepare to channel the spell on Eranikus + DoScriptText(SAY_TYRANDE_HEAL, pSummoned); + pSummoned->Unmount(); + m_uiTyrandeMoveTimer = 5000; + } + // Unmount the priestess - unk what is their exact purpose (maybe healer) + else if (pSummoned->GetEntry() == NPC_ELUNE_PRIESTESS) + pSummoned->Unmount(); + break; + case POINT_ID_TYRANDE_ABSOLUTION: + if (pSummoned->GetEntry() == NPC_TYRANDE_WHISPERWIND) + { + pSummoned->CastSpell(pSummoned, SPELL_ARCANE_CHANNELING, false); + DoScriptText(SAY_TYRANDE_FORGIVEN_1, pSummoned); + } + break; + } + } + + void MovementInform(uint32 uiType, uint32 uiPointId) override + { + if (uiType != POINT_MOTION_TYPE || uiPointId != POINT_ID_ERANIKUS_REDEEMED) + return; + + DoScriptText(SAY_REDEEMED_1, m_creature); + m_uiEventTimer = 11000; + } + + void UpdateAI(const uint32 uiDiff) override + { + if (m_uiEventTimer) + { + if (m_uiEventTimer <= uiDiff) + { + switch (m_uiEventPhase) + { + case 0: + // Eranikus is redeemed - make Tyrande kneel and stop casting + if (Creature* pTyrande = m_creature->GetMap()->GetCreature(m_tyrandeGuid)) + { + pTyrande->InterruptNonMeleeSpells(false); + pTyrande->SetStandState(UNIT_STAND_STATE_KNEEL); + DoScriptText(EMOTE_TYRANDE_KNEEL, pTyrande); + } + if (Creature* pRemulos = m_creature->GetMap()->GetCreature(m_remulosGuid)) + pRemulos->SetFacingToObject(m_creature); + // Note: this emote was a world wide yellow emote before WotLK + DoScriptText(EMOTE_ERANIKUS_REDEEM, m_creature); + // DoCastSpellIfCan(m_creature, SPELL_MOONGLADE_TRANQUILITY); // spell id unk for the moment + m_creature->SetStandState(UNIT_STAND_STATE_DEAD); + m_uiEventTimer = 5000; + break; + case 1: + if (Creature* pTyrande = m_creature->GetMap()->GetCreature(m_tyrandeGuid)) + DoScriptText(SAY_TYRANDE_REDEEMED, pTyrande); + m_uiEventTimer = 6000; + break; + case 2: + // Transform Eranikus into elf + DoCastSpellIfCan(m_creature, SPELL_ERANIKUS_REDEEMED); + m_uiEventTimer = 5000; + break; + case 3: + // Move Eranikus in front of Tyrande + m_creature->SetStandState(UNIT_STAND_STATE_STAND); + m_creature->SetWalk(true); + m_creature->GetMotionMaster()->MovePoint(POINT_ID_ERANIKUS_REDEEMED, aEranikusLocations[3].m_fX, aEranikusLocations[3].m_fY, aEranikusLocations[3].m_fZ); + m_uiEventTimer = 0; + break; + case 4: + DoScriptText(SAY_REDEEMED_2, m_creature); + m_uiEventTimer = 11000; + break; + case 5: + DoScriptText(SAY_REDEEMED_3, m_creature); + m_uiEventTimer = 13000; + break; + case 6: + DoScriptText(SAY_REDEEMED_4, m_creature); + m_uiEventTimer = 7000; + break; + case 7: + // Complete Quest and end event + if (Creature* pTyrande = m_creature->GetMap()->GetCreature(m_tyrandeGuid)) + { + pTyrande->SetStandState(UNIT_STAND_STATE_STAND); + pTyrande->ForcedDespawn(9000); + } + if (Creature* pRemulos = m_creature->GetMap()->GetCreature(m_remulosGuid)) + ((npc_keeper_remulosAI*)pRemulos->AI())->DoHandleOutro(m_creature); + m_creature->HandleEmote(EMOTE_ONESHOT_BOW); + m_creature->ForcedDespawn(2000); + break; + } + ++m_uiEventPhase; + } + else + m_uiEventTimer -= uiDiff; + } + + // Return since we have no target + if (!m_creature->SelectHostileTarget() || !m_creature->getVictim()) + return; + + // Move Tyrande after she is summoned + if (m_uiTyrandeMoveTimer) + { + if (m_uiTyrandeMoveTimer <= uiDiff) + { + if (Creature* pTyrande = m_creature->GetMap()->GetCreature(m_tyrandeGuid)) + pTyrande->GetMotionMaster()->MovePoint(POINT_ID_TYRANDE_ABSOLUTION, aTyrandeLocations[2].m_fX, aTyrandeLocations[2].m_fY, aTyrandeLocations[2].m_fZ); + m_uiTyrandeMoveTimer = 0; + } + else + m_uiTyrandeMoveTimer -= uiDiff; + } + + // Not sure if this should be handled by health percent, but this is the only reasonable way + if (m_creature->GetHealthPercent() < m_uiHealthCheck) + { + switch (m_uiHealthCheck) + { + case 85: + DoScriptText(SAY_ERANIKUS_ATTACK_3, m_creature); + // Here Tyrande only yells but she doesn't appear anywhere - we summon here for 1 second just to handle the yell + if (Creature* pTyrande = m_creature->SummonCreature(NPC_TYRANDE_WHISPERWIND, aTyrandeLocations[0].m_fX, aTyrandeLocations[0].m_fY, aTyrandeLocations[0].m_fZ, 0, TEMPSUMMON_TIMED_DESPAWN, 1000)) + DoScriptText(SAY_TYRANDE_APPEAR, pTyrande); + m_uiHealthCheck = 75; + break; + case 75: + // Eranikus yells again + DoScriptText(SAY_ERANIKUS_ATTACK_3, m_creature); + m_uiHealthCheck = 50; + break; + case 50: + // Summon Tyrande - she enters the fight this time + m_creature->SummonCreature(NPC_TYRANDE_WHISPERWIND, aTyrandeLocations[0].m_fX, aTyrandeLocations[0].m_fY, aTyrandeLocations[0].m_fZ, 0, TEMPSUMMON_CORPSE_DESPAWN, 0); + m_uiHealthCheck = 35; + break; + case 35: + // Summon the priestess + DoSummonHealers(); + DoScriptText(SAY_ERANIKUS_DEFEAT_1, m_creature); + m_uiHealthCheck = 31; + break; + case 31: + if (Creature* pTyrande = m_creature->GetMap()->GetCreature(m_tyrandeGuid)) + DoScriptText(SAY_TYRANDE_FORGIVEN_2, pTyrande); + m_uiHealthCheck = 27; + break; + case 27: + if (Creature* pTyrande = m_creature->GetMap()->GetCreature(m_tyrandeGuid)) + DoScriptText(SAY_TYRANDE_FORGIVEN_3, pTyrande); + m_uiHealthCheck = 25; + break; + case 25: + DoScriptText(SAY_ERANIKUS_DEFEAT_2, m_creature); + m_uiHealthCheck = 20; + break; + case 20: + // Eranikus is redeemed - stop the fight + DoScriptText(SAY_ERANIKUS_DEFEAT_3, m_creature); + m_creature->AI()->EnterEvadeMode(); + m_uiHealthCheck = 0; + break; + } + } + + // Combat spells + if (m_uiAcidBreathTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature, SPELL_ACID_BREATH) == CAST_OK) + m_uiAcidBreathTimer = 15000; + } + else + m_uiAcidBreathTimer -= uiDiff; + + if (m_uiNoxiousBreathTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature, SPELL_NOXIOUS_BREATH) == CAST_OK) + m_uiNoxiousBreathTimer = 30000; + } + else + m_uiNoxiousBreathTimer -= uiDiff; + + if (m_uiShadowboltVolleyTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature, SPELL_SHADOWBOLT_VOLLEY) == CAST_OK) + m_uiShadowboltVolleyTimer = 25000; + } + else + m_uiShadowboltVolleyTimer -= uiDiff; + + DoMeleeAttackIfReady(); + } +}; + +CreatureAI* GetAI_boss_eranikus(Creature* pCreature) +{ + return new boss_eranikusAI(pCreature); +} + +void AddSC_moonglade() +{ + Script* pNewScript; + + pNewScript = new Script; + pNewScript->Name = "npc_clintar_dw_spirit"; + pNewScript->GetAI = &GetAI_npc_clintar_dw_spirit; + pNewScript->pEffectDummyNPC = &EffectDummyCreature_npc_clintar_dw_spirit; + pNewScript->RegisterSelf(); + + pNewScript = new Script; + pNewScript->Name = "npc_keeper_remulos"; + pNewScript->GetAI = &GetAI_npc_keeper_remulos; + pNewScript->pQuestAcceptNPC = &QuestAccept_npc_keeper_remulos; + pNewScript->pEffectDummyNPC = &EffectDummyCreature_conjure_rift; + pNewScript->RegisterSelf(); + + pNewScript = new Script; + pNewScript->Name = "boss_eranikus"; + pNewScript->GetAI = &GetAI_boss_eranikus; + pNewScript->RegisterSelf(); +} diff --git a/src/modules/SD2/scripts/kalimdor/mount_hyjal.cpp b/src/modules/SD2/scripts/kalimdor/mount_hyjal.cpp new file mode 100644 index 000000000..b47e18f36 --- /dev/null +++ b/src/modules/SD2/scripts/kalimdor/mount_hyjal.cpp @@ -0,0 +1,35 @@ +/* Copyright (C) 2006 - 2013 ScriptDev2 + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +/* ScriptData +SDName: Mount Hyjal +SD%Complete: 0 +SDComment: Placeholder +SDCategory: Mount Hyjal +EndScriptData */ + +/* ContentData +EndContentData */ + +#include "precompiled.h" + +/*###### +# +######*/ + +void AddSC_mount_hyjal() +{ +} diff --git a/src/modules/SD2/scripts/kalimdor/mulgore.cpp b/src/modules/SD2/scripts/kalimdor/mulgore.cpp new file mode 100644 index 000000000..a4ff4d08e --- /dev/null +++ b/src/modules/SD2/scripts/kalimdor/mulgore.cpp @@ -0,0 +1,178 @@ +/* Copyright (C) 2006 - 2013 ScriptDev2 + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along 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: Quest support: 11129. +SDCategory: Mulgore +EndScriptData */ + +/* ContentData +npc_kyle_the_frenzied +EndContentData */ + +#include "precompiled.h" + +/*###### +# npc_kyle_the_frenzied +######*/ + +enum +{ + EMOTE_SEE_LUNCH = -1000340, + EMOTE_EAT_LUNCH = -1000341, + EMOTE_DANCE = -1000342, + + SPELL_LUNCH = 42222, + NPC_KYLE_FRENZIED = 23616, + NPC_KYLE_FRIENDLY = 23622, + POINT_ID = 1 +}; + +struct npc_kyle_the_frenziedAI : public ScriptedAI +{ + npc_kyle_the_frenziedAI(Creature* pCreature) : ScriptedAI(pCreature) {Reset();} + + bool m_bEvent; + bool m_bIsMovingToLunch; + ObjectGuid m_playerGuid; + uint32 m_uiEventTimer; + uint8 m_uiEventPhase; + + void Reset() override + { + m_bEvent = false; + m_bIsMovingToLunch = false; + m_playerGuid.Clear(); + m_uiEventTimer = 5000; + m_uiEventPhase = 0; + + if (m_creature->GetEntry() == NPC_KYLE_FRIENDLY) + m_creature->UpdateEntry(NPC_KYLE_FRENZIED); + } + + void SpellHit(Unit* pCaster, SpellEntry const* pSpell) override + { + if (!m_creature->getVictim() && !m_bEvent && pSpell->Id == SPELL_LUNCH) + { + if (pCaster->GetTypeId() == TYPEID_PLAYER) + m_playerGuid = pCaster->GetObjectGuid(); + + if (m_creature->GetMotionMaster()->GetCurrentMovementGeneratorType() == WAYPOINT_MOTION_TYPE) + { + m_creature->GetMotionMaster()->MovementExpired(); + m_creature->GetMotionMaster()->MoveIdle(); + m_creature->StopMoving(); + } + + m_bEvent = true; + DoScriptText(EMOTE_SEE_LUNCH, m_creature); + m_creature->HandleEmote(EMOTE_ONESHOT_CREATURE_SPECIAL); + } + } + + void MovementInform(uint32 uiType, uint32 uiPointId) override + { + if (uiType != POINT_MOTION_TYPE || !m_bEvent) + return; + + if (uiPointId == POINT_ID) + m_bIsMovingToLunch = false; + } + + void UpdateAI(const uint32 diff) override + { + if (m_bEvent) + { + if (m_bIsMovingToLunch) + return; + + if (m_uiEventTimer < diff) + { + m_uiEventTimer = 5000; + ++m_uiEventPhase; + + switch (m_uiEventPhase) + { + case 1: + if (Player* pPlayer = m_creature->GetMap()->GetPlayer(m_playerGuid)) + { + GameObject* pGo = pPlayer->GetGameObject(SPELL_LUNCH); + + // Workaround for broken function GetGameObject + if (!pGo) + { + const SpellEntry* pSpell = GetSpellStore()->LookupEntry(SPELL_LUNCH); + + uint32 uiGameobjectEntry = pSpell->GetEffectMiscValue(EFFECT_INDEX_1); + + pGo = GetClosestGameObjectWithEntry(pPlayer, uiGameobjectEntry, 2 * INTERACTION_DISTANCE); + } + + if (pGo) + { + m_bIsMovingToLunch = true; + + float fX, fY, fZ; + pGo->GetContactPoint(m_creature, fX, fY, fZ, CONTACT_DISTANCE); + + m_creature->GetMotionMaster()->MovePoint(POINT_ID, fX, fY, fZ); + } + } + break; + case 2: + DoScriptText(EMOTE_EAT_LUNCH, m_creature); + m_creature->HandleEmote(EMOTE_STATE_USESTANDING); + break; + case 3: + if (Player* pPlayer = m_creature->GetMap()->GetPlayer(m_playerGuid)) + pPlayer->TalkedToCreature(m_creature->GetEntry(), m_creature->GetObjectGuid()); + + m_creature->UpdateEntry(NPC_KYLE_FRIENDLY); + break; + case 4: + m_uiEventTimer = 30000; + DoScriptText(EMOTE_DANCE, m_creature); + m_creature->HandleEmote(EMOTE_STATE_DANCESPECIAL); + break; + case 5: + m_creature->HandleEmote(EMOTE_STATE_NONE); + Reset(); + m_creature->GetMotionMaster()->Clear(); + break; + } + } + else + m_uiEventTimer -= diff; + } + } +}; + +CreatureAI* GetAI_npc_kyle_the_frenzied(Creature* pCreature) +{ + return new npc_kyle_the_frenziedAI(pCreature); +} + +void AddSC_mulgore() +{ + Script* pNewScript; + + pNewScript = new Script; + pNewScript->Name = "npc_kyle_the_frenzied"; + pNewScript->GetAI = &GetAI_npc_kyle_the_frenzied; + pNewScript->RegisterSelf(); +} diff --git a/src/modules/SD2/scripts/kalimdor/onyxias_lair/boss_onyxia.cpp b/src/modules/SD2/scripts/kalimdor/onyxias_lair/boss_onyxia.cpp new file mode 100644 index 000000000..97596371f --- /dev/null +++ b/src/modules/SD2/scripts/kalimdor/onyxias_lair/boss_onyxia.cpp @@ -0,0 +1,638 @@ +/* Copyright (C) 2006 - 2013 ScriptDev2 + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with 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: 85 +SDComment: Phase 3 need additional code. The spawning Whelps need GO-Support. +SDCategory: Onyxia's Lair +EndScriptData */ + +#include "precompiled.h" +#include "onyxias_lair.h" + +enum +{ + SAY_AGGRO = -1249000, + SAY_KILL = -1249001, + SAY_PHASE_2_TRANS = -1249002, + SAY_PHASE_3_TRANS = -1249003, + EMOTE_BREATH = -1249004, + + SPELL_WINGBUFFET = 18500, + SPELL_WINGBUFFET_H = 69293, + SPELL_FLAMEBREATH = 18435, + SPELL_FLAMEBREATH_H = 68970, + SPELL_CLEAVE = 68868, + SPELL_TAILSWEEP = 68867, + SPELL_TAILSWEEP_H = 69286, + SPELL_KNOCK_AWAY = 19633, + SPELL_FIREBALL = 18392, + SPELL_FIREBALL_H = 68926, + + // Not much choise about these. We have to make own defintion on the direction/start-end point + SPELL_BREATH_NORTH_TO_SOUTH = 17086, // 20x in "array" + SPELL_BREATH_SOUTH_TO_NORTH = 18351, // 11x in "array" + + SPELL_BREATH_EAST_TO_WEST = 18576, // 7x in "array" + SPELL_BREATH_WEST_TO_EAST = 18609, // 7x in "array" + + SPELL_BREATH_SE_TO_NW = 18564, // 12x in "array" + SPELL_BREATH_NW_TO_SE = 18584, // 12x in "array" + SPELL_BREATH_SW_TO_NE = 18596, // 12x in "array" + SPELL_BREATH_NE_TO_SW = 18617, // 12x in "array" + + SPELL_VISUAL_BREATH_A = 4880, // Only and all of the above Breath spells (and their triggered spells) have these visuals + SPELL_VISUAL_BREATH_B = 4919, + + SPELL_BREATH_ENTRANCE = 21131, // 8x in "array", different initial cast than the other arrays + + SPELL_BELLOWINGROAR = 18431, + SPELL_HEATED_GROUND = 22191, // Prevent players from hiding in the tunnels when it is time for Onyxia's breath + + SPELL_SUMMONWHELP = 17646, // TODO this spell is only a summon spell, but would need a spell to activate the eggs + SPELL_SUMMON_LAIR_GUARD = 68968, + + MAX_WHELPS_PER_PACK = 40, + + POINT_ID_NORTH = 0, + POINT_ID_SOUTH = 4, + NUM_MOVE_POINT = 8, + POINT_ID_LIFTOFF = 1 + NUM_MOVE_POINT, + POINT_ID_IN_AIR = 2 + NUM_MOVE_POINT, + POINT_ID_INIT_NORTH = 3 + NUM_MOVE_POINT, + POINT_ID_LAND = 4 + NUM_MOVE_POINT, + + PHASE_START = 1, // Health above 65%, normal ground abilities + PHASE_BREATH = 2, // Breath phase (while health above 40%) + PHASE_END = 3, // normal ground abilities + some extra abilities + PHASE_BREATH_POST = 4, // Landing and initial fearing + PHASE_TO_LIFTOFF = 5, // Movement to south-entrance of room and liftoff there + PHASE_BREATH_PRE = 6, // lifting off + initial flying to north side (summons also first pack of whelps) + +}; + +struct OnyxiaMove +{ + uint32 uiSpellId; + float fX, fY, fZ; +}; + +static const OnyxiaMove aMoveData[NUM_MOVE_POINT] = +{ + {SPELL_BREATH_NORTH_TO_SOUTH, 24.16332f, -216.0808f, -58.98009f}, // north (coords verified in wotlk) + {SPELL_BREATH_NE_TO_SW, 10.2191f, -247.912f, -60.896f}, // north-east + {SPELL_BREATH_EAST_TO_WEST, -15.00505f, -244.4841f, -60.40087f}, // east (coords verified in wotlk) + {SPELL_BREATH_SE_TO_NW, -63.5156f, -240.096f, -60.477f}, // south-east + {SPELL_BREATH_SOUTH_TO_NORTH, -66.3589f, -215.928f, -64.23904f}, // south (coords verified in wotlk) + {SPELL_BREATH_SW_TO_NE, -58.2509f, -189.020f, -60.790f}, // south-west + {SPELL_BREATH_WEST_TO_EAST, -16.70134f, -181.4501f, -61.98513f}, // west (coords verified in wotlk) + {SPELL_BREATH_NW_TO_SE, 12.26687f, -181.1084f, -60.23914f}, // north-west (coords verified in wotlk) +}; + +static const float afSpawnLocations[3][3] = +{ + { -30.127f, -254.463f, -89.440f}, // whelps + { -30.817f, -177.106f, -89.258f}, // whelps + { -126.57f, -214.609f, -71.446f} // guardians +}; + +struct boss_onyxiaAI : public ScriptedAI +{ + boss_onyxiaAI(Creature* pCreature) : ScriptedAI(pCreature) + { + m_pInstance = (instance_onyxias_lair*)pCreature->GetInstanceData(); + m_bIsRegularMode = pCreature->GetMap()->IsRegularDifficulty(); + Reset(); + } + + bool m_bIsRegularMode; + instance_onyxias_lair* m_pInstance; + + uint8 m_uiPhase; + + uint32 m_uiFlameBreathTimer; + uint32 m_uiCleaveTimer; + uint32 m_uiTailSweepTimer; + uint32 m_uiWingBuffetTimer; + uint32 m_uiCheckInLairTimer; + + uint32 m_uiMovePoint; + uint32 m_uiMovementTimer; + + uint32 m_uiFireballTimer; + uint32 m_uiSummonWhelpsTimer; + uint32 m_uiBellowingRoarTimer; + uint32 m_uiWhelpTimer; + uint32 m_uiSummonGuardTimer; + + uint8 m_uiSummonCount; + + bool m_bIsSummoningWhelps; + + uint32 m_uiPhaseTimer; + + void Reset() override + { + if (!IsCombatMovement()) + SetCombatMovement(true); + + m_uiPhase = PHASE_START; + + m_uiFlameBreathTimer = urand(10000, 20000); + m_uiTailSweepTimer = urand(15000, 20000); + m_uiCleaveTimer = urand(2000, 5000); + m_uiWingBuffetTimer = urand(10000, 20000); + m_uiCheckInLairTimer = 3000; + + m_uiMovePoint = POINT_ID_NORTH; // First point reached by the flying Onyxia + m_uiMovementTimer = 25000; + + m_uiFireballTimer = 1000; + m_uiSummonWhelpsTimer = 60000; + m_uiBellowingRoarTimer = 30000; + m_uiWhelpTimer = 1000; + m_uiSummonGuardTimer = 15000; + + m_uiSummonCount = 0; + + m_bIsSummoningWhelps = false; + + m_uiPhaseTimer = 0; + } + + void Aggro(Unit* /*pWho*/) override + { + DoScriptText(SAY_AGGRO, m_creature); + + if (m_pInstance) + m_pInstance->SetData(TYPE_ONYXIA, IN_PROGRESS); + } + + void JustReachedHome() override + { + // in case evade in phase 2, see comments for hack where phase 2 is set + m_creature->SetLevitate(false); + m_creature->SetByteFlag(UNIT_FIELD_BYTES_1, 3, 0); + + if (m_pInstance) + m_pInstance->SetData(TYPE_ONYXIA, FAIL); + } + + void JustDied(Unit* /*pKiller*/) override + { + if (m_pInstance) + m_pInstance->SetData(TYPE_ONYXIA, DONE); + } + + void JustSummoned(Creature* pSummoned) override + { + if (!m_pInstance) + return; + + if (Creature* pTrigger = m_pInstance->GetSingleCreatureFromStorage(NPC_ONYXIA_TRIGGER)) + { + // Get some random point near the center + float fX, fY, fZ; + pSummoned->GetRandomPoint(pTrigger->GetPositionX(), pTrigger->GetPositionY(), pTrigger->GetPositionZ(), 20.0f, fX, fY, fZ); + pSummoned->GetMotionMaster()->MovePoint(1, fX, fY, fZ); + } + else + pSummoned->SetInCombatWithZone(); + + if (pSummoned->GetEntry() == NPC_ONYXIA_WHELP) + ++m_uiSummonCount; + } + + void SummonedMovementInform(Creature* pSummoned, uint32 uiMoveType, uint32 uiPointId) override + { + if (uiMoveType != POINT_MOTION_TYPE || uiPointId != 1 || !m_creature->getVictim()) + return; + + pSummoned->SetInCombatWithZone(); + } + + void KilledUnit(Unit* /*pVictim*/) override + { + DoScriptText(SAY_KILL, m_creature); + } + + void SpellHit(Unit* /*pCaster*/, const SpellEntry* pSpell) override + { + if (pSpell->Id == SPELL_BREATH_EAST_TO_WEST || + pSpell->Id == SPELL_BREATH_WEST_TO_EAST || + pSpell->Id == SPELL_BREATH_SE_TO_NW || + pSpell->Id == SPELL_BREATH_NW_TO_SE || + pSpell->Id == SPELL_BREATH_SW_TO_NE || + pSpell->Id == SPELL_BREATH_NE_TO_SW || + pSpell->Id == SPELL_BREATH_SOUTH_TO_NORTH || + pSpell->Id == SPELL_BREATH_NORTH_TO_SOUTH) + { + // This was sent with SendMonsterMove - which resulted in better speed than now + m_creature->GetMotionMaster()->MovePoint(m_uiMovePoint, aMoveData[m_uiMovePoint].fX, aMoveData[m_uiMovePoint].fY, aMoveData[m_uiMovePoint].fZ); + DoCastSpellIfCan(m_creature, SPELL_HEATED_GROUND, CAST_TRIGGERED); + } + } + + void MovementInform(uint32 uiMoveType, uint32 uiPointId) override + { + if (uiMoveType != POINT_MOTION_TYPE || !m_pInstance) + return; + + switch (uiPointId) + { + case POINT_ID_IN_AIR: + // sort of a hack, it is unclear how this really work but the values are valid + m_creature->SetByteValue(UNIT_FIELD_BYTES_1, 3, UNIT_BYTE1_FLAG_ALWAYS_STAND | UNIT_BYTE1_FLAG_UNK_2); + m_creature->SetLevitate(true); + m_uiPhaseTimer = 1000; // Movement to Initial North Position is delayed + return; + case POINT_ID_LAND: + // undo flying + m_creature->SetByteValue(UNIT_FIELD_BYTES_1, 3, 0); + m_creature->SetLevitate(false); + m_uiPhaseTimer = 500; // Start PHASE_END shortly delayed + return; + case POINT_ID_LIFTOFF: + m_uiPhaseTimer = 500; // Start Flying shortly delayed + break; + case POINT_ID_INIT_NORTH: // Start PHASE_BREATH + m_uiPhase = PHASE_BREATH; + m_uiSummonCount = 0; + break; + } + + if (Creature* pTrigger = m_pInstance->GetSingleCreatureFromStorage(NPC_ONYXIA_TRIGGER)) + m_creature->SetFacingToObject(pTrigger); + } + + void AttackStart(Unit* pWho) override + { + if (m_uiPhase == PHASE_START || m_uiPhase == PHASE_END) + ScriptedAI::AttackStart(pWho); + } + + bool DidSummonWhelps(const uint32 uiDiff) + { + if (m_uiSummonCount >= MAX_WHELPS_PER_PACK) + return true; + + if (m_uiWhelpTimer < uiDiff) + { + m_creature->SummonCreature(NPC_ONYXIA_WHELP, afSpawnLocations[0][0], afSpawnLocations[0][1], afSpawnLocations[0][2], 0.0f, TEMPSUMMON_TIMED_OOC_OR_DEAD_DESPAWN, MINUTE * IN_MILLISECONDS); + m_creature->SummonCreature(NPC_ONYXIA_WHELP, afSpawnLocations[1][0], afSpawnLocations[1][1], afSpawnLocations[1][2], 0.0f, TEMPSUMMON_TIMED_OOC_OR_DEAD_DESPAWN, MINUTE * IN_MILLISECONDS); + m_uiWhelpTimer = 500; + } + else + m_uiWhelpTimer -= uiDiff; + return false; + } + + void UpdateAI(const uint32 uiDiff) override + { + if (!m_creature->SelectHostileTarget() || !m_creature->getVictim()) + return; + + switch (m_uiPhase) + { + case PHASE_END: // Here is room for additional summoned whelps and Erruption + if (m_uiBellowingRoarTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature, SPELL_BELLOWINGROAR) == CAST_OK) + m_uiBellowingRoarTimer = 30000; + } + else + m_uiBellowingRoarTimer -= uiDiff; + // no break, phase 3 will use same abilities as in 1 + case PHASE_START: + { + if (m_uiFlameBreathTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature->getVictim(), m_bIsRegularMode ? SPELL_FLAMEBREATH : SPELL_FLAMEBREATH_H) == CAST_OK) + m_uiFlameBreathTimer = urand(10000, 20000); + } + else + m_uiFlameBreathTimer -= uiDiff; + + if (m_uiTailSweepTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature, m_bIsRegularMode ? SPELL_TAILSWEEP : SPELL_TAILSWEEP_H) == CAST_OK) + m_uiTailSweepTimer = urand(15000, 20000); + } + else + m_uiTailSweepTimer -= uiDiff; + + if (m_uiCleaveTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature->getVictim(), SPELL_CLEAVE) == CAST_OK) + m_uiCleaveTimer = urand(2000, 5000); + } + else + m_uiCleaveTimer -= uiDiff; + + if (m_uiWingBuffetTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature, m_bIsRegularMode ? SPELL_WINGBUFFET : SPELL_WINGBUFFET_H) == CAST_OK) + m_uiWingBuffetTimer = urand(15000, 30000); + } + else + m_uiWingBuffetTimer -= uiDiff; + + if (m_uiCheckInLairTimer < uiDiff) + { + if (m_pInstance) + { + Creature* pOnyTrigger = m_pInstance->GetSingleCreatureFromStorage(NPC_ONYXIA_TRIGGER); + if (pOnyTrigger && !m_creature->IsWithinDistInMap(pOnyTrigger, 90.0f, false)) + DoCastSpellIfCan(m_creature, SPELL_BREATH_ENTRANCE); + } + m_uiCheckInLairTimer = 3000; + } + else + m_uiCheckInLairTimer -= uiDiff; + + if (m_uiPhase == PHASE_START && m_creature->GetHealthPercent() < 65.0f) + { + m_uiPhase = PHASE_TO_LIFTOFF; + DoScriptText(SAY_PHASE_2_TRANS, m_creature); + SetCombatMovement(false); + m_creature->GetMotionMaster()->MoveIdle(); + m_creature->SetTargetGuid(ObjectGuid()); + + float fGroundZ = m_creature->GetMap()->GetHeight(m_creature->GetPhaseMask(), aMoveData[POINT_ID_SOUTH].fX, aMoveData[POINT_ID_SOUTH].fY, aMoveData[POINT_ID_SOUTH].fZ); + m_creature->GetMotionMaster()->MovePoint(POINT_ID_LIFTOFF, aMoveData[POINT_ID_SOUTH].fX, aMoveData[POINT_ID_SOUTH].fY, fGroundZ); + return; + } + + DoMeleeAttackIfReady(); + break; + } + case PHASE_BREATH: + { + if (m_creature->GetHealthPercent() < 40.0f) + { + m_uiPhase = PHASE_BREATH_POST; + DoScriptText(SAY_PHASE_3_TRANS, m_creature); + + float fGroundZ = m_creature->GetMap()->GetHeight(m_creature->GetPhaseMask(), m_creature->GetPositionX(), m_creature->GetPositionY(), m_creature->GetPositionZ()); + m_creature->GetMotionMaster()->MoveFlyOrLand(POINT_ID_LAND, m_creature->GetPositionX(), m_creature->GetPositionY(), fGroundZ, false); + return; + } + + if (m_uiMovementTimer < uiDiff) + { + // 3 possible actions + switch (urand(0, 2)) + { + case 0: // breath + DoScriptText(EMOTE_BREATH, m_creature); + DoCastSpellIfCan(m_creature, aMoveData[m_uiMovePoint].uiSpellId, CAST_INTERRUPT_PREVIOUS); + m_uiMovePoint += NUM_MOVE_POINT / 2; + m_uiMovePoint %= NUM_MOVE_POINT; + m_uiMovementTimer = 25000; + return; + case 1: // a point on the left side + { + // C++ is stupid, so add -1 with +7 + m_uiMovePoint += NUM_MOVE_POINT - 1; + m_uiMovePoint %= NUM_MOVE_POINT; + break; + } + case 2: // a point on the right side + ++m_uiMovePoint %= NUM_MOVE_POINT; + break; + } + + m_uiMovementTimer = urand(15000, 25000); + m_creature->GetMotionMaster()->MovePoint(m_uiMovePoint, aMoveData[m_uiMovePoint].fX, aMoveData[m_uiMovePoint].fY, aMoveData[m_uiMovePoint].fZ); + } + else + m_uiMovementTimer -= uiDiff; + + if (m_uiFireballTimer < uiDiff) + { + if (Unit* pTarget = m_creature->SelectAttackingTarget(ATTACKING_TARGET_RANDOM, 0)) + { + if (DoCastSpellIfCan(pTarget, m_bIsRegularMode ? SPELL_FIREBALL : SPELL_FIREBALL_H) == CAST_OK) + m_uiFireballTimer = urand(3000, 5000); + } + } + else + m_uiFireballTimer -= uiDiff; // engulfingflames is supposed to be activated by a fireball but haven't come by + + if (m_bIsSummoningWhelps) + { + if (DidSummonWhelps(uiDiff)) + { + m_bIsSummoningWhelps = false; + m_uiSummonCount = 0; + m_uiSummonWhelpsTimer = 80000; // 90s - 10s for summoning + } + } + else + { + if (m_uiSummonWhelpsTimer < uiDiff) + m_bIsSummoningWhelps = true; + else + m_uiSummonWhelpsTimer -= uiDiff; + } + + if (m_uiSummonGuardTimer < uiDiff) + { + if (!m_creature->IsNonMeleeSpellCasted(false)) + { + m_creature->CastSpell(afSpawnLocations[2][0], afSpawnLocations[2][1], afSpawnLocations[2][2], SPELL_SUMMON_LAIR_GUARD, true); + m_uiSummonGuardTimer = 30000; + } + } + else + m_uiSummonGuardTimer -= uiDiff; + + break; + } + case PHASE_BREATH_PRE: // Summon first rounds of whelps + DidSummonWhelps(uiDiff); + // no break here + default: // Phase-switching phases + if (!m_uiPhaseTimer) + break; + if (m_uiPhaseTimer <= uiDiff) + { + switch (m_uiPhase) + { + case PHASE_TO_LIFTOFF: + m_uiPhase = PHASE_BREATH_PRE; + if (m_pInstance) + m_pInstance->SetData(TYPE_ONYXIA, DATA_LIFTOFF); + m_creature->GetMotionMaster()->MoveFlyOrLand(POINT_ID_IN_AIR, aMoveData[POINT_ID_SOUTH].fX, aMoveData[POINT_ID_SOUTH].fY, aMoveData[POINT_ID_SOUTH].fZ, true); + break; + case PHASE_BREATH_PRE: + m_creature->GetMotionMaster()->MovePoint(POINT_ID_INIT_NORTH, aMoveData[POINT_ID_NORTH].fX, aMoveData[POINT_ID_NORTH].fY, aMoveData[POINT_ID_NORTH].fZ); + break; + case PHASE_BREATH_POST: + m_uiPhase = PHASE_END; + m_creature->SetTargetGuid(m_creature->getVictim()->GetObjectGuid()); + SetCombatMovement(true, true); + DoCastSpellIfCan(m_creature, SPELL_BELLOWINGROAR); + break; + } + m_uiPhaseTimer = 0; + } + else + m_uiPhaseTimer -= uiDiff; + break; + } + } + + void SpellHitTarget(Unit* pTarget, const SpellEntry* pSpell) override + { + // Check if players are hit by Onyxia's Deep Breath + if (pTarget->GetTypeId() != TYPEID_PLAYER || !m_pInstance) + return; + + // All and only the Onyxia Deep Breath Spells have these visuals + if (pSpell->SpellVisual[0] == SPELL_VISUAL_BREATH_A || pSpell->SpellVisual[0] == SPELL_VISUAL_BREATH_B) + m_pInstance->SetData(TYPE_ONYXIA, DATA_PLAYER_TOASTED); + } +}; + +CreatureAI* GetAI_boss_onyxia(Creature* pCreature) +{ + return new boss_onyxiaAI(pCreature); +} + +void AddSC_boss_onyxia() +{ + Script* pNewScript; + + pNewScript = new Script; + pNewScript->Name = "boss_onyxia"; + pNewScript->GetAI = &GetAI_boss_onyxia; + pNewScript->RegisterSelf(); +} + +/* +-- SPELL_BREATH_EAST_TO_WEST +DELETE FROM spell_target_position WHERE id IN (18576, 18578, 18579, 18580, 18581, 18582, 18583); +INSERT INTO spell_target_position VALUES (18576, 249, -37.743851, -243.667923, -88.217651, 1.416); +INSERT INTO spell_target_position VALUES (18578, 249, -35.805332, -232.028900, -87.749153, 1.416); +INSERT INTO spell_target_position VALUES (18579, 249, -34.045738, -224.714661, -85.529465, 1.416); +INSERT INTO spell_target_position VALUES (18580, 249, -32.081570, -214.916962, -88.327438, 1.416); +INSERT INTO spell_target_position VALUES (18581, 249, -36.611721, -202.684677, -85.653786, 1.416); +INSERT INTO spell_target_position VALUES (18582, 249, -37.067261, -195.758652, -87.745834, 1.416); +INSERT INTO spell_target_position VALUES (18583, 249, -37.728523, -188.616806, -88.074898, 1.416); +-- SPELL_BREATH_WEST_TO_EAST +DELETE FROM spell_target_position WHERE id IN (18609, 18611, 18612, 18613, 18614, 18615, 18616); +INSERT INTO spell_target_position VALUES (18609, 249, -37.728523, -188.616806, -88.074898, 4.526); +INSERT INTO spell_target_position VALUES (18611, 249, -37.067261, -195.758652, -87.745834, 4.526); +INSERT INTO spell_target_position VALUES (18612, 249, -36.611721, -202.684677, -85.653786, 4.526); +INSERT INTO spell_target_position VALUES (18613, 249, -32.081570, -214.916962, -88.327438, 4.526); +INSERT INTO spell_target_position VALUES (18614, 249, -34.045738, -224.714661, -85.529465, 4.526); +INSERT INTO spell_target_position VALUES (18615, 249, -35.805332, -232.028900, -87.749153, 4.526); +INSERT INTO spell_target_position VALUES (18616, 249, -37.743851, -243.667923, -88.217651, 4.526); +-- SPELL_BREATH_NW_TO_SE +DELETE FROM spell_target_position WHERE id IN (18584, 18585, 18586, 18587, 18588, 18589, 18590, 18591, 18592, 18593, 18594, 18595); +INSERT INTO spell_target_position VALUES (18584, 249, 6.016711, -181.305771, -85.654648, 3.776); +INSERT INTO spell_target_position VALUES (18585, 249, 3.860220, -183.227249, -86.375381, 3.776); +INSERT INTO spell_target_position VALUES (18586, 249, -2.529650, -188.690491, -87.172859, 3.776); +INSERT INTO spell_target_position VALUES (18587, 249, -8.449303, -193.957962, -87.564957, 3.776); +INSERT INTO spell_target_position VALUES (18588, 249, -14.321238, -199.462219, -87.922478, 3.776); +INSERT INTO spell_target_position VALUES (18589, 249, -15.602085, -216.893936, -88.403183, 3.776); +INSERT INTO spell_target_position VALUES (18590, 249, -23.650263, -221.969086, -89.172699, 3.776); +INSERT INTO spell_target_position VALUES (18591, 249, -29.495876, -213.014359, -88.910423, 3.776); +INSERT INTO spell_target_position VALUES (18592, 249, -35.439922, -217.260284, -87.336311, 3.776); +INSERT INTO spell_target_position VALUES (18593, 249, -41.762104, -221.896545, -86.114113, 3.776); +INSERT INTO spell_target_position VALUES (18594, 249, -51.067528, -228.909988, -85.765556, 3.776); +INSERT INTO spell_target_position VALUES (18595, 249, -56.559654, -241.223923, -85.423607, 3.776); +-- SPELL_BREATH_SE_TO_NW +DELETE FROM spell_target_position WHERE id IN (18564, 18565, 18566, 18567, 18568, 18569, 18570, 18571, 18572, 18573, 18574, 18575); +INSERT INTO spell_target_position VALUES (18564, 249, -56.559654, -241.223923, -85.423607, 0.666); +INSERT INTO spell_target_position VALUES (18565, 249, -51.067528, -228.909988, -85.765556, 0.666); +INSERT INTO spell_target_position VALUES (18566, 249, -41.762104, -221.896545, -86.114113, 0.666); +INSERT INTO spell_target_position VALUES (18567, 249, -35.439922, -217.260284, -87.336311, 0.666); +INSERT INTO spell_target_position VALUES (18568, 249, -29.495876, -213.014359, -88.910423, 0.666); +INSERT INTO spell_target_position VALUES (18569, 249, -23.650263, -221.969086, -89.172699, 0.666); +INSERT INTO spell_target_position VALUES (18570, 249, -15.602085, -216.893936, -88.403183, 0.666); +INSERT INTO spell_target_position VALUES (18571, 249, -14.321238, -199.462219, -87.922478, 0.666); +INSERT INTO spell_target_position VALUES (18572, 249, -8.449303, -193.957962, -87.564957, 0.666); +INSERT INTO spell_target_position VALUES (18573, 249, -2.529650, -188.690491, -87.172859, 0.666); +INSERT INTO spell_target_position VALUES (18574, 249, 3.860220, -183.227249, -86.375381, 0.666); +INSERT INTO spell_target_position VALUES (18575, 249, 6.016711, -181.305771, -85.654648, 0.666); +-- SPELL_BREATH_SW_TO_NE +DELETE FROM spell_target_position WHERE id IN (18596, 18597, 18598, 18599, 18600, 18601, 18602, 18603, 18604, 18605, 18606, 18607); +INSERT INTO spell_target_position VALUES (18596, 249, -58.250900, -189.020004, -85.292267, 5.587); +INSERT INTO spell_target_position VALUES (18597, 249, -52.006271, -193.796570, -85.808769, 5.587); +INSERT INTO spell_target_position VALUES (18598, 249, -46.135464, -198.548553, -85.901764, 5.587); +INSERT INTO spell_target_position VALUES (18599, 249, -40.500187, -203.001053, -85.555107, 5.587); +INSERT INTO spell_target_position VALUES (18600, 249, -30.907579, -211.058197, -88.592125, 5.587); +INSERT INTO spell_target_position VALUES (18601, 249, -20.098139, -218.681427, -88.937088, 5.587); +INSERT INTO spell_target_position VALUES (18602, 249, -12.223192, -224.666168, -87.856300, 5.587); +INSERT INTO spell_target_position VALUES (18603, 249, -6.475297, -229.098724, -87.076401, 5.587); +INSERT INTO spell_target_position VALUES (18604, 249, -2.010256, -232.541992, -86.995140, 5.587); +INSERT INTO spell_target_position VALUES (18605, 249, 2.736300, -236.202347, -86.790367, 5.587); +INSERT INTO spell_target_position VALUES (18606, 249, 7.197779, -239.642868, -86.307297, 5.587); +INSERT INTO spell_target_position VALUES (18607, 249, 12.120926, -243.439407, -85.874260, 5.587); +-- SPELL_BREATH_NE_TO_SW +DELETE FROM spell_target_position WHERE id IN (18617, 18619, 18620, 18621, 18622, 18623, 18624, 18625, 18626, 18627, 18628, 18618); +INSERT INTO spell_target_position VALUES (18617, 249, 12.120926, -243.439407, -85.874260, 2.428); +INSERT INTO spell_target_position VALUES (18619, 249, 7.197779, -239.642868, -86.307297, 2.428); +INSERT INTO spell_target_position VALUES (18620, 249, 2.736300, -236.202347, -86.790367, 2.428); +INSERT INTO spell_target_position VALUES (18621, 249, -2.010256, -232.541992, -86.995140, 2.428); +INSERT INTO spell_target_position VALUES (18622, 249, -6.475297, -229.098724, -87.076401, 2.428); +INSERT INTO spell_target_position VALUES (18623, 249, -12.223192, -224.666168, -87.856300, 2.428); +INSERT INTO spell_target_position VALUES (18624, 249, -20.098139, -218.681427, -88.937088, 2.428); +INSERT INTO spell_target_position VALUES (18625, 249, -30.907579, -211.058197, -88.592125, 2.428); +INSERT INTO spell_target_position VALUES (18626, 249, -40.500187, -203.001053, -85.555107, 2.428); +INSERT INTO spell_target_position VALUES (18627, 249, -46.135464, -198.548553, -85.901764, 2.428); +INSERT INTO spell_target_position VALUES (18628, 249, -52.006271, -193.796570, -85.808769, 2.428); +INSERT INTO spell_target_position VALUES (18618, 249, -58.250900, -189.020004, -85.292267, 2.428); + +-- SPELL_BREATH_SOUTH_TO_NORTH +DELETE FROM spell_target_position WHERE id IN (18351, 18352, 18353, 18354, 18355, 18356, 18357, 18358, 18359, 18360, 18361); +INSERT INTO spell_target_position VALUES (18351, 249, -68.834236, -215.036163, -84.018875, 6.280); +INSERT INTO spell_target_position VALUES (18352, 249, -61.834255, -215.051910, -84.673416, 6.280); +INSERT INTO spell_target_position VALUES (18353, 249, -53.343277, -215.071014, -85.597191, 6.280); +INSERT INTO spell_target_position VALUES (18354, 249, -42.619305, -215.095139, -86.663605, 6.280); +INSERT INTO spell_target_position VALUES (18355, 249, -35.899323, -215.110245, -87.196548, 6.280); +INSERT INTO spell_target_position VALUES (18356, 249, -28.248341, -215.127457, -89.191750, 6.280); +INSERT INTO spell_target_position VALUES (18357, 249, -20.324360, -215.145279, -88.963997, 6.280); +INSERT INTO spell_target_position VALUES (18358, 249, -11.189384, -215.165833, -87.817093, 6.280); +INSERT INTO spell_target_position VALUES (18359, 249, -2.047405, -215.186386, -86.279655, 6.280); +INSERT INTO spell_target_position VALUES (18360, 249, 7.479571, -215.207809, -86.075531, 6.280); +INSERT INTO spell_target_position VALUES (18361, 249, 20.730539, -215.237610, -85.254387, 6.280); +-- SPELL_BREATH_NORTH_TO_SOUTH +DELETE FROM spell_target_position WHERE id IN (17086, 17087, 17088, 17089, 17090, 17091, 17092, 17093, 17094, 17095, 17097, 22267, 22268, 21132, 21133, 21135, 21136, 21137, 21138, 21139); +INSERT INTO spell_target_position VALUES (17086, 249, 20.730539, -215.237610, -85.254387, 3.142); +INSERT INTO spell_target_position VALUES (17087, 249, 7.479571, -215.207809, -86.075531, 3.142); +INSERT INTO spell_target_position VALUES (17088, 249, -2.047405, -215.186386, -86.279655, 3.142); +INSERT INTO spell_target_position VALUES (17089, 249, -11.189384, -215.165833, -87.817093, 3.142); +INSERT INTO spell_target_position VALUES (17090, 249, -20.324360, -215.145279, -88.963997, 3.142); +INSERT INTO spell_target_position VALUES (17091, 249, -28.248341, -215.127457, -89.191750, 3.142); +INSERT INTO spell_target_position VALUES (17092, 249, -35.899323, -215.110245, -87.196548, 3.142); +INSERT INTO spell_target_position VALUES (17093, 249, -42.619305, -215.095139, -86.663605, 3.142); +INSERT INTO spell_target_position VALUES (17094, 249, -53.343277, -215.071014, -85.597191, 3.142); +INSERT INTO spell_target_position VALUES (17095, 249, -61.834255, -215.051910, -84.673416, 3.142); +INSERT INTO spell_target_position VALUES (17097, 249, -68.834236, -215.036163, -84.018875, 3.142); +INSERT INTO spell_target_position VALUES (22267, 249, -75.736046, -214.984970, -83.394188, 3.142); +INSERT INTO spell_target_position VALUES (22268, 249, -84.087578, -214.857834, -82.640053, 3.142); +INSERT INTO spell_target_position VALUES (21132, 249, -90.424416, -214.601974, -82.482697, 3.142); +INSERT INTO spell_target_position VALUES (21133, 249, -96.572411, -214.353745, -82.239967, 3.142); +INSERT INTO spell_target_position VALUES (21135, 249, -102.069931, -214.131775, -80.571190, 3.142); +INSERT INTO spell_target_position VALUES (21136, 249, -107.385597, -213.917145, -77.447037, 3.142); +INSERT INTO spell_target_position VALUES (21137, 249, -114.281258, -213.866486, -73.851128, 3.142); +INSERT INTO spell_target_position VALUES (21138, 249, -123.328560, -213.607910, -71.559921, 3.142); +INSERT INTO spell_target_position VALUES (21139, 249, -130.788300, -213.424026, -70.751007, 3.142); +*/ diff --git a/src/modules/SD2/scripts/kalimdor/onyxias_lair/instance_onyxias_lair.cpp b/src/modules/SD2/scripts/kalimdor/onyxias_lair/instance_onyxias_lair.cpp new file mode 100644 index 000000000..6ab5a58ad --- /dev/null +++ b/src/modules/SD2/scripts/kalimdor/onyxias_lair/instance_onyxias_lair.cpp @@ -0,0 +1,103 @@ +/* Copyright (C) 2006 - 2013 ScriptDev2 + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along 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_Onyxias_Lair +SD%Complete: 50% +SDComment: +SDCategory: Onyxia's Lair +EndScriptData */ + +#include "precompiled.h" +#include "onyxias_lair.h" + +instance_onyxias_lair::instance_onyxias_lair(Map* pMap) : ScriptedInstance(pMap), + m_uiAchievWhelpsCount(0) +{ + Initialize(); +} + +void instance_onyxias_lair::Initialize() +{ + m_uiEncounter = NOT_STARTED; + m_tPhaseTwoStart = time(NULL); +} + +bool instance_onyxias_lair::IsEncounterInProgress() const +{ + return m_uiEncounter == IN_PROGRESS || m_uiEncounter >= DATA_LIFTOFF; +} + +void instance_onyxias_lair::OnCreatureCreate(Creature* pCreature) +{ + switch (pCreature->GetEntry()) + { + case NPC_ONYXIA_TRIGGER: + m_mNpcEntryGuidStore[NPC_ONYXIA_TRIGGER] = pCreature->GetObjectGuid(); + break; + case NPC_ONYXIA_WHELP: + if (m_uiEncounter >= DATA_LIFTOFF && time_t(m_tPhaseTwoStart + TIME_LIMIT_MANY_WHELPS) >= time(NULL)) + ++m_uiAchievWhelpsCount; + break; + } +} + +void instance_onyxias_lair::SetData(uint32 uiType, uint32 uiData) +{ + if (uiType != TYPE_ONYXIA) + return; + + m_uiEncounter = uiData; + if (uiData == IN_PROGRESS) + { + DoStartTimedAchievement(ACHIEVEMENT_CRITERIA_TYPE_KILL_CREATURE, ACHIEV_START_ONYXIA_ID); + m_uiAchievWhelpsCount = 0; + } + if (uiData == DATA_LIFTOFF) + m_tPhaseTwoStart = time(NULL); + + // Currently no reason to save anything +} + +bool instance_onyxias_lair::CheckAchievementCriteriaMeet(uint32 uiCriteriaId, Player const* /*pSource*/, Unit const* /*pTarget*/, uint32 /*uiMiscValue1 = 0*/) const +{ + switch (uiCriteriaId) + { + case ACHIEV_CRIT_MANY_WHELPS_N: + case ACHIEV_CRIT_MANY_WHELPS_H: + return m_uiAchievWhelpsCount >= ACHIEV_CRIT_REQ_MANY_WHELPS; + case ACHIEV_CRIT_NO_BREATH_N: + case ACHIEV_CRIT_NO_BREATH_H: + return m_uiEncounter != DATA_PLAYER_TOASTED; + default: + return false; + } +} + +InstanceData* GetInstanceData_instance_onyxias_lair(Map* pMap) +{ + return new instance_onyxias_lair(pMap); +} + +void AddSC_instance_onyxias_lair() +{ + Script* pNewScript; + + pNewScript = new Script; + pNewScript->Name = "instance_onyxias_lair"; + pNewScript->GetInstanceData = &GetInstanceData_instance_onyxias_lair; + pNewScript->RegisterSelf(); +} diff --git a/src/modules/SD2/scripts/kalimdor/onyxias_lair/onyxias_lair.h b/src/modules/SD2/scripts/kalimdor/onyxias_lair/onyxias_lair.h new file mode 100644 index 000000000..235f0f6b8 --- /dev/null +++ b/src/modules/SD2/scripts/kalimdor/onyxias_lair/onyxias_lair.h @@ -0,0 +1,54 @@ +/* Copyright (C) 2006 - 2013 ScriptDev2 + * This program is free software licensed under GPL version 2 + * Please see the included DOCS/LICENSE.TXT for more information */ + +#ifndef DEF_ONYXIA_H +#define DEF_ONYXIA_H + +enum +{ + TYPE_ONYXIA = 0, + + // Special data fields for Onyxia + DATA_LIFTOFF = 4, + DATA_PLAYER_TOASTED = 5, + + NPC_ONYXIA_WHELP = 11262, + NPC_ONYXIA_TRIGGER = 12758, + + // Achievement Related + TIME_LIMIT_MANY_WHELPS = 10, // 10s timeframe to kill 50 whelps after liftoff + ACHIEV_CRIT_REQ_MANY_WHELPS = 50, + + ACHIEV_CRIT_MANY_WHELPS_N = 12565, // Achievements 4403, 4406 + ACHIEV_CRIT_MANY_WHELPS_H = 12568, + ACHIEV_CRIT_NO_BREATH_N = 12566, // Acheivements 4404, 4407 + ACHIEV_CRIT_NO_BREATH_H = 12569, + + ACHIEV_START_ONYXIA_ID = 6601, +}; + +class instance_onyxias_lair : public ScriptedInstance +{ + public: + instance_onyxias_lair(Map* pMap); + ~instance_onyxias_lair() {} + + void Initialize() override; + + bool IsEncounterInProgress() const override; + + void OnCreatureCreate(Creature* pCreature) override; + + void SetData(uint32 uiType, uint32 uiData) override; + + bool CheckAchievementCriteriaMeet(uint32 uiCriteriaId, Player const* pSource, Unit const* pTarget, uint32 uiMiscValue1 /* = 0*/) const override; + + protected: + uint32 m_uiEncounter; + uint32 m_uiAchievWhelpsCount; + + time_t m_tPhaseTwoStart; +}; + +#endif diff --git a/src/modules/SD2/scripts/kalimdor/orgrimmar.cpp b/src/modules/SD2/scripts/kalimdor/orgrimmar.cpp new file mode 100644 index 000000000..163187ada --- /dev/null +++ b/src/modules/SD2/scripts/kalimdor/orgrimmar.cpp @@ -0,0 +1,198 @@ +/* Copyright (C) 2006 - 2013 ScriptDev2 + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along 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, 6566 +SDCategory: Orgrimmar +EndScriptData */ + +/* ContentData +npc_shenthul +npc_thrall_warchief +EndContentData */ + +#include "precompiled.h" + +/*###### +## npc_shenthul +######*/ + +enum +{ + QUEST_SHATTERED_SALUTE = 2460 +}; + +struct npc_shenthulAI : public ScriptedAI +{ + npc_shenthulAI(Creature* pCreature) : ScriptedAI(pCreature) { Reset(); } + + uint32 m_uiSaluteTimer; + uint32 m_uiResetTimer; + + ObjectGuid m_playerGuid; + + void Reset() override + { + m_uiSaluteTimer = 0; + m_uiResetTimer = 0; + + m_playerGuid.Clear(); + } + + void ReceiveEmote(Player* pPlayer, uint32 uiTextEmote) override + { + if (m_uiResetTimer && uiTextEmote == TEXTEMOTE_SALUTE && pPlayer->GetQuestStatus(QUEST_SHATTERED_SALUTE) == QUEST_STATUS_INCOMPLETE) + { + pPlayer->AreaExploredOrEventHappens(QUEST_SHATTERED_SALUTE); + EnterEvadeMode(); + } + } + + void DoStartQuestEvent(Player* pPlayer) + { + m_playerGuid = pPlayer->GetObjectGuid(); + m_uiSaluteTimer = 6000; + } + + void UpdateAI(const uint32 uiDiff) override + { + if (m_uiResetTimer) + { + if (m_uiResetTimer <= uiDiff) + { + if (Player* pPlayer = m_creature->GetMap()->GetPlayer(m_playerGuid)) + { + if (pPlayer->GetTypeId() == TYPEID_PLAYER && pPlayer->GetQuestStatus(QUEST_SHATTERED_SALUTE) == QUEST_STATUS_INCOMPLETE) + pPlayer->FailQuest(QUEST_SHATTERED_SALUTE); + } + + m_uiResetTimer = 0; + EnterEvadeMode(); + } + else + m_uiResetTimer -= uiDiff; + } + + if (m_uiSaluteTimer) + { + if (m_uiSaluteTimer <= uiDiff) + { + m_creature->HandleEmote(EMOTE_ONESHOT_SALUTE); + m_uiResetTimer = 60000; + m_uiSaluteTimer = 0; + } + else + m_uiSaluteTimer -= uiDiff; + } + + if (!m_creature->SelectHostileTarget() || !m_creature->getVictim()) + return; + + DoMeleeAttackIfReady(); + } +}; + +CreatureAI* GetAI_npc_shenthul(Creature* pCreature) +{ + return new npc_shenthulAI(pCreature); +} + +bool QuestAccept_npc_shenthul(Player* pPlayer, Creature* pCreature, const Quest* pQuest) +{ + if (pQuest->GetQuestId() == QUEST_SHATTERED_SALUTE) + { + if (npc_shenthulAI* pShenAI = dynamic_cast(pCreature->AI())) + pShenAI->DoStartQuestEvent(pPlayer); + } + + return true; +} + +/*###### +## npc_thrall_warchief +######*/ + +enum +{ + QUEST_ID_WHAT_THE_WIND_CARRIES = 6566, +}; + +bool GossipHello_npc_thrall_warchief(Player* pPlayer, Creature* pCreature) +{ + if (pCreature->IsQuestGiver()) + pPlayer->PrepareQuestMenu(pCreature->GetObjectGuid()); + + if (pPlayer->GetQuestStatus(QUEST_ID_WHAT_THE_WIND_CARRIES) == QUEST_STATUS_INCOMPLETE) + pPlayer->ADD_GOSSIP_ITEM(GOSSIP_ICON_CHAT, "Please share your wisdom with me, Warchief.", GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 1); + + pPlayer->SEND_GOSSIP_MENU(pPlayer->GetGossipTextId(pCreature), pCreature->GetObjectGuid()); + return true; +} + +bool GossipSelect_npc_thrall_warchief(Player* pPlayer, Creature* pCreature, uint32 /*uiSender*/, uint32 uiAction) +{ + switch (uiAction) + { + case GOSSIP_ACTION_INFO_DEF+1: + pPlayer->ADD_GOSSIP_ITEM(GOSSIP_ICON_CHAT, "What discoveries?", GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 2); + pPlayer->SEND_GOSSIP_MENU(5733, pCreature->GetObjectGuid()); + break; + case GOSSIP_ACTION_INFO_DEF+2: + pPlayer->ADD_GOSSIP_ITEM(GOSSIP_ICON_CHAT, "Usurper?", GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 3); + pPlayer->SEND_GOSSIP_MENU(5734, pCreature->GetObjectGuid()); + break; + case GOSSIP_ACTION_INFO_DEF+3: + pPlayer->ADD_GOSSIP_ITEM(GOSSIP_ICON_CHAT, "With all due respect, Warchief - why not allow them to be destroyed? Does this not strengthen our position?", GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 4); + pPlayer->SEND_GOSSIP_MENU(5735, pCreature->GetObjectGuid()); + break; + case GOSSIP_ACTION_INFO_DEF+4: + pPlayer->ADD_GOSSIP_ITEM(GOSSIP_ICON_CHAT, "I... I did not think of it that way, Warchief.", GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 5); + pPlayer->SEND_GOSSIP_MENU(5736, pCreature->GetObjectGuid()); + break; + case GOSSIP_ACTION_INFO_DEF+5: + pPlayer->ADD_GOSSIP_ITEM(GOSSIP_ICON_CHAT, "I live only to serve, Warchief! My life is empty and meaningless without your guidance.", GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 6); + pPlayer->SEND_GOSSIP_MENU(5737, pCreature->GetObjectGuid()); + break; + case GOSSIP_ACTION_INFO_DEF+6: + pPlayer->ADD_GOSSIP_ITEM(GOSSIP_ICON_CHAT, "Of course, Warchief!", GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 7); + pPlayer->SEND_GOSSIP_MENU(5738, pCreature->GetObjectGuid()); + break; + case GOSSIP_ACTION_INFO_DEF+7: + pPlayer->CLOSE_GOSSIP_MENU(); + pPlayer->AreaExploredOrEventHappens(QUEST_ID_WHAT_THE_WIND_CARRIES); + break; + } + return true; +} + +void AddSC_orgrimmar() +{ + Script* pNewScript; + + pNewScript = new Script; + pNewScript->Name = "npc_shenthul"; + pNewScript->GetAI = &GetAI_npc_shenthul; + pNewScript->pQuestAcceptNPC = &QuestAccept_npc_shenthul; + pNewScript->RegisterSelf(); + + pNewScript = new Script; + pNewScript->Name = "npc_thrall_warchief"; + pNewScript->pGossipHello = &GossipHello_npc_thrall_warchief; + pNewScript->pGossipSelect = &GossipSelect_npc_thrall_warchief; + pNewScript->RegisterSelf(); +} diff --git a/src/modules/SD2/scripts/kalimdor/razorfen_downs/razorfen_downs.cpp b/src/modules/SD2/scripts/kalimdor/razorfen_downs/razorfen_downs.cpp new file mode 100644 index 000000000..246c99f9c --- /dev/null +++ b/src/modules/SD2/scripts/kalimdor/razorfen_downs/razorfen_downs.cpp @@ -0,0 +1,299 @@ +/* Copyright (C) 2006 - 2013 ScriptDev2 + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along 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: Quest 3525 +SDCategory: Razorfen Downs +EndScriptData */ + +/* ContentData +npc_belnistrasz +EndContentData */ + +#include "precompiled.h" +#include "escort_ai.h" + +/*### +# npc_belnistrasz +####*/ + +enum +{ + QUEST_EXTINGUISHING_THE_IDOL = 3525, + + SAY_BELNISTRASZ_READY = -1129005, + SAY_BELNISTRASZ_START_RIT = -1129006, + SAY_BELNISTRASZ_AGGRO_1 = -1129007, + SAY_BELNISTRASZ_AGGRO_2 = -1129008, + SAY_BELNISTRASZ_3_MIN = -1129009, + SAY_BELNISTRASZ_2_MIN = -1129010, + SAY_BELNISTRASZ_1_MIN = -1129011, + SAY_BELNISTRASZ_FINISH = -1129012, + + NPC_IDOL_ROOM_SPAWNER = 8611, + + NPC_WITHERED_BATTLE_BOAR = 7333, + NPC_WITHERED_QUILGUARD = 7329, + NPC_DEATHS_HEAD_GEOMANCER = 7335, + NPC_PLAGUEMAW_THE_ROTTING = 7356, + + GO_BELNISTRASZ_BRAZIER = 152097, + + SPELL_ARCANE_INTELLECT = 13326, // use this somewhere (he has it as default) + SPELL_FIREBALL = 9053, + SPELL_FROST_NOVA = 11831, + SPELL_IDOL_SHUTDOWN = 12774, + + // summon spells only exist in 1.x + // SPELL_SUMMON_1 = 12694, // NPC_WITHERED_BATTLE_BOAR + // SPELL_SUMMON_2 = 14802, // NPC_DEATHS_HEAD_GEOMANCER + // SPELL_SUMMON_3 = 14801, // NPC_WITHERED_QUILGUARD +}; + +static float m_fSpawnerCoord[3][4] = +{ + {2582.79f, 954.392f, 52.4821f, 3.78736f}, + {2569.42f, 956.380f, 52.2732f, 5.42797f}, + {2570.62f, 942.393f, 53.7433f, 0.71558f} +}; + +struct npc_belnistraszAI : public npc_escortAI +{ + npc_belnistraszAI(Creature* pCreature) : npc_escortAI(pCreature) + { + m_uiRitualPhase = 0; + m_uiRitualTimer = 1000; + m_bAggro = false; + Reset(); + } + + uint8 m_uiRitualPhase; + uint32 m_uiRitualTimer; + bool m_bAggro; + + uint32 m_uiFireballTimer; + uint32 m_uiFrostNovaTimer; + + void Reset() override + { + m_uiFireballTimer = 1000; + m_uiFrostNovaTimer = 6000; + } + + void AttackedBy(Unit* pAttacker) override + { + if (HasEscortState(STATE_ESCORT_PAUSED)) + { + if (!m_bAggro) + { + DoScriptText(urand(0, 1) ? SAY_BELNISTRASZ_AGGRO_1 : SAY_BELNISTRASZ_AGGRO_1, m_creature, pAttacker); + m_bAggro = true; + } + + return; + } + + ScriptedAI::AttackedBy(pAttacker); + } + + void SpawnerSummon(Creature* pSummoner) + { + if (m_uiRitualPhase > 7) + { + pSummoner->SummonCreature(NPC_PLAGUEMAW_THE_ROTTING, pSummoner->GetPositionX(), pSummoner->GetPositionY(), pSummoner->GetPositionZ(), pSummoner->GetOrientation(), TEMPSUMMON_TIMED_OOC_DESPAWN, 60000); + return; + } + + for (int i = 0; i < 4; ++i) + { + uint32 uiEntry = 0; + + // ref TARGET_RANDOM_CIRCUMFERENCE_POINT + float angle = 2.0f * M_PI_F * rand_norm_f(); + float fX, fZ, fY; + pSummoner->GetClosePoint(fX, fZ, fY, 0.0f, 2.0f, angle); + + switch (i) + { + case 0: + case 1: + uiEntry = NPC_WITHERED_BATTLE_BOAR; + break; + case 2: + uiEntry = NPC_WITHERED_QUILGUARD; + break; + case 3: + uiEntry = NPC_DEATHS_HEAD_GEOMANCER; + break; + } + + pSummoner->SummonCreature(uiEntry, fX, fZ, fY, 0.0f, TEMPSUMMON_TIMED_OOC_DESPAWN, 60000); + } + } + + void JustSummoned(Creature* pSummoned) override + { + SpawnerSummon(pSummoned); + } + + void DoSummonSpawner(int32 iType) + { + m_creature->SummonCreature(NPC_IDOL_ROOM_SPAWNER, m_fSpawnerCoord[iType][0], m_fSpawnerCoord[iType][1], m_fSpawnerCoord[iType][2], m_fSpawnerCoord[iType][3], TEMPSUMMON_TIMED_DESPAWN, 10000); + } + + void WaypointReached(uint32 uiPointId) override + { + if (uiPointId == 24) + { + DoScriptText(SAY_BELNISTRASZ_START_RIT, m_creature); + SetEscortPaused(true); + } + } + + void UpdateEscortAI(const uint32 uiDiff) override + { + if (HasEscortState(STATE_ESCORT_PAUSED)) + { + if (m_uiRitualTimer < uiDiff) + { + switch (m_uiRitualPhase) + { + case 0: + DoCastSpellIfCan(m_creature, SPELL_IDOL_SHUTDOWN); + m_uiRitualTimer = 1000; + break; + case 1: + DoSummonSpawner(irand(1, 3)); + m_uiRitualTimer = 39000; + break; + case 2: + DoSummonSpawner(irand(1, 3)); + m_uiRitualTimer = 20000; + break; + case 3: + DoScriptText(SAY_BELNISTRASZ_3_MIN, m_creature, m_creature); + m_uiRitualTimer = 20000; + break; + case 4: + DoSummonSpawner(irand(1, 3)); + m_uiRitualTimer = 40000; + break; + case 5: + DoSummonSpawner(irand(1, 3)); + DoScriptText(SAY_BELNISTRASZ_2_MIN, m_creature, m_creature); + m_uiRitualTimer = 40000; + break; + case 6: + DoSummonSpawner(irand(1, 3)); + m_uiRitualTimer = 20000; + break; + case 7: + DoScriptText(SAY_BELNISTRASZ_1_MIN, m_creature, m_creature); + m_uiRitualTimer = 40000; + break; + case 8: + DoSummonSpawner(irand(1, 3)); + m_uiRitualTimer = 20000; + break; + case 9: + DoScriptText(SAY_BELNISTRASZ_FINISH, m_creature, m_creature); + m_uiRitualTimer = 3000; + break; + case 10: + { + if (Player* pPlayer = GetPlayerForEscort()) + { + pPlayer->GroupEventHappens(QUEST_EXTINGUISHING_THE_IDOL, m_creature); + + if (GameObject* pGo = GetClosestGameObjectWithEntry(m_creature, GO_BELNISTRASZ_BRAZIER, 10.0f)) + { + if (!pGo->isSpawned()) + { + pGo->SetRespawnTime(HOUR * IN_MILLISECONDS); + pGo->Refresh(); + } + } + } + + m_creature->RemoveAurasDueToSpell(SPELL_IDOL_SHUTDOWN); + SetEscortPaused(false); + break; + } + } + + ++m_uiRitualPhase; + } + else + m_uiRitualTimer -= uiDiff; + + return; + } + + if (!m_creature->SelectHostileTarget() || !m_creature->getVictim()) + return; + + if (m_uiFireballTimer < uiDiff) + { + DoCastSpellIfCan(m_creature->getVictim(), SPELL_FIREBALL); + m_uiFireballTimer = urand(2000, 3000); + } + else + m_uiFireballTimer -= uiDiff; + + if (m_uiFrostNovaTimer < uiDiff) + { + DoCastSpellIfCan(m_creature->getVictim(), SPELL_FROST_NOVA); + m_uiFrostNovaTimer = urand(10000, 15000); + } + else + m_uiFrostNovaTimer -= uiDiff; + + DoMeleeAttackIfReady(); + } +}; + +CreatureAI* GetAI_npc_belnistrasz(Creature* pCreature) +{ + return new npc_belnistraszAI(pCreature); +} + +bool QuestAccept_npc_belnistrasz(Player* pPlayer, Creature* pCreature, const Quest* pQuest) +{ + if (pQuest->GetQuestId() == QUEST_EXTINGUISHING_THE_IDOL) + { + if (npc_belnistraszAI* pEscortAI = dynamic_cast(pCreature->AI())) + { + pEscortAI->Start(true, pPlayer, pQuest); + DoScriptText(SAY_BELNISTRASZ_READY, pCreature, pPlayer); + pCreature->SetFactionTemporary(FACTION_ESCORT_N_NEUTRAL_ACTIVE, TEMPFACTION_RESTORE_RESPAWN); + } + } + + return true; +} + +void AddSC_razorfen_downs() +{ + Script* pNewScript; + + pNewScript = new Script; + pNewScript->Name = "npc_belnistrasz"; + pNewScript->GetAI = &GetAI_npc_belnistrasz; + pNewScript->pQuestAcceptNPC = &QuestAccept_npc_belnistrasz; + pNewScript->RegisterSelf(); +} diff --git a/src/modules/SD2/scripts/kalimdor/razorfen_kraul/instance_razorfen_kraul.cpp b/src/modules/SD2/scripts/kalimdor/razorfen_kraul/instance_razorfen_kraul.cpp new file mode 100644 index 000000000..94197ba78 --- /dev/null +++ b/src/modules/SD2/scripts/kalimdor/razorfen_kraul/instance_razorfen_kraul.cpp @@ -0,0 +1,133 @@ +/* Copyright (C) 2006 - 2013 ScriptDev2 + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along 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: 50 +SDComment: +SDCategory: Razorfen Kraul +EndScriptData */ + +#include "precompiled.h" +#include "razorfen_kraul.h" + +instance_razorfen_kraul::instance_razorfen_kraul(Map* pMap) : ScriptedInstance(pMap), + m_uiWardKeepersRemaining(0) +{ + Initialize(); +} + +void instance_razorfen_kraul::Initialize() +{ + memset(&m_auiEncounter, 0, sizeof(m_auiEncounter)); +} + +void instance_razorfen_kraul::OnObjectCreate(GameObject* pGo) +{ + switch (pGo->GetEntry()) + { + case GO_AGATHELOS_WARD: + m_mGoEntryGuidStore[GO_AGATHELOS_WARD] = pGo->GetObjectGuid(); + if (m_auiEncounter[0] == DONE) + pGo->SetGoState(GO_STATE_ACTIVE); + break; + } +} + +void instance_razorfen_kraul::OnCreatureCreate(Creature* pCreature) +{ + switch (pCreature->GetEntry()) + { + case NPC_WARD_KEEPER: + ++m_uiWardKeepersRemaining; + break; + } +} + +void instance_razorfen_kraul::SetData(uint32 uiType, uint32 uiData) +{ + switch (uiType) + { + case TYPE_AGATHELOS: + --m_uiWardKeepersRemaining; + if (!m_uiWardKeepersRemaining) + { + m_auiEncounter[0] = uiData; + DoUseDoorOrButton(GO_AGATHELOS_WARD); + } + break; + } + + if (uiData == DONE) + { + OUT_SAVE_INST_DATA; + + std::ostringstream saveStream; + + saveStream << m_auiEncounter[0]; + m_strInstData = saveStream.str(); + + SaveToDB(); + OUT_SAVE_INST_DATA_COMPLETE; + } +} + +void instance_razorfen_kraul::Load(const char* chrIn) +{ + if (!chrIn) + { + OUT_LOAD_INST_DATA_FAIL; + return; + } + + OUT_LOAD_INST_DATA(chrIn); + + std::istringstream loadStream(chrIn); + loadStream >> m_auiEncounter[0]; + + for (uint8 i = 0; i < MAX_ENCOUNTER; ++i) + { + if (m_auiEncounter[i] == IN_PROGRESS) + m_auiEncounter[i] = NOT_STARTED; + } + + OUT_LOAD_INST_DATA_COMPLETE; +} + +uint32 instance_razorfen_kraul::GetData(uint32 uiType) const +{ + switch (uiType) + { + case TYPE_AGATHELOS: + return m_auiEncounter[0]; + } + return 0; +} + +InstanceData* GetInstanceData_instance_razorfen_kraul(Map* pMap) +{ + return new instance_razorfen_kraul(pMap); +} + +void AddSC_instance_razorfen_kraul() +{ + Script* pNewScript; + + pNewScript = new Script; + pNewScript->Name = "instance_razorfen_kraul"; + pNewScript->GetInstanceData = &GetInstanceData_instance_razorfen_kraul; + pNewScript->RegisterSelf(); +} diff --git a/src/modules/SD2/scripts/kalimdor/razorfen_kraul/razorfen_kraul.cpp b/src/modules/SD2/scripts/kalimdor/razorfen_kraul/razorfen_kraul.cpp new file mode 100644 index 000000000..195a36d13 --- /dev/null +++ b/src/modules/SD2/scripts/kalimdor/razorfen_kraul/razorfen_kraul.cpp @@ -0,0 +1,270 @@ +/* Copyright (C) 2006 - 2013 ScriptDev2 + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along 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, 1221 +SDCategory: Razorfen Kraul +EndScriptData */ + +/* ContentData +quest_willix_the_importer +EndContentData */ + +#include "precompiled.h" +#include "escort_ai.h" +#include "pet_ai.h" + +/*###### +## npc_willix_the_importer +######*/ + +enum +{ + QUEST_WILLIX_THE_IMPORTER = 1144, + + SAY_WILLIX_READY = -1047000, + SAY_WILLIX_1 = -1047001, + SAY_WILLIX_2 = -1047002, + SAY_WILLIX_3 = -1047003, + SAY_WILLIX_4 = -1047004, + SAY_WILLIX_5 = -1047005, + SAY_WILLIX_6 = -1047006, + SAY_WILLIX_7 = -1047007, + SAY_WILLIX_END = -1047008, + + SAY_WILLIX_AGGRO_1 = -1047009, + SAY_WILLIX_AGGRO_2 = -1047010, + SAY_WILLIX_AGGRO_3 = -1047011, + SAY_WILLIX_AGGRO_4 = -1047012, + + NPC_RAGING_AGAMAR = 4514 +}; + +static const float aBoarSpawn[4][3] = +{ + {2151.420f, 1733.18f, 52.10f}, + {2144.463f, 1726.89f, 51.93f}, + {1956.433f, 1597.97f, 81.75f}, + {1958.971f, 1599.01f, 81.44f} +}; + +struct npc_willix_the_importerAI : public npc_escortAI +{ + npc_willix_the_importerAI(Creature* m_creature) : npc_escortAI(m_creature) { Reset(); } + + void Reset() override {} + + // Exact use of these texts remains unknown, it seems that he should only talk when he initiates the attack or he is the first who is attacked by a npc + void Aggro(Unit* pWho) override + { + switch (urand(0, 6)) // Not always said + { + case 0: DoScriptText(SAY_WILLIX_AGGRO_1, m_creature, pWho); break; + case 1: DoScriptText(SAY_WILLIX_AGGRO_2, m_creature, pWho); break; + case 2: DoScriptText(SAY_WILLIX_AGGRO_3, m_creature, pWho); break; + case 3: DoScriptText(SAY_WILLIX_AGGRO_4, m_creature, pWho); break; + } + } + + void JustSummoned(Creature* pSummoned) override + { + pSummoned->AI()->AttackStart(m_creature); + } + + void WaypointReached(uint32 uiPointId) override + { + switch (uiPointId) + { + case 2: + DoScriptText(SAY_WILLIX_1, m_creature); + break; + case 6: + DoScriptText(SAY_WILLIX_2, m_creature); + break; + case 9: + DoScriptText(SAY_WILLIX_3, m_creature); + break; + case 14: + DoScriptText(SAY_WILLIX_4, m_creature); + // Summon 2 boars on the pathway + m_creature->SummonCreature(NPC_RAGING_AGAMAR, aBoarSpawn[0][0], aBoarSpawn[0][1], aBoarSpawn[0][2], 0, TEMPSUMMON_TIMED_OOC_DESPAWN, 25000); + m_creature->SummonCreature(NPC_RAGING_AGAMAR, aBoarSpawn[1][0], aBoarSpawn[1][1], aBoarSpawn[1][2], 0, TEMPSUMMON_TIMED_OOC_DESPAWN, 25000); + break; + case 25: + DoScriptText(SAY_WILLIX_5, m_creature); + break; + case 33: + DoScriptText(SAY_WILLIX_6, m_creature); + break; + case 44: + DoScriptText(SAY_WILLIX_7, m_creature); + // Summon 2 boars at the end + m_creature->SummonCreature(NPC_RAGING_AGAMAR, aBoarSpawn[2][0], aBoarSpawn[2][1], aBoarSpawn[2][2], 0, TEMPSUMMON_TIMED_OOC_DESPAWN, 25000); + m_creature->SummonCreature(NPC_RAGING_AGAMAR, aBoarSpawn[3][0], aBoarSpawn[3][1], aBoarSpawn[3][2], 0, TEMPSUMMON_TIMED_OOC_DESPAWN, 25000); + break; + case 45: + DoScriptText(SAY_WILLIX_END, m_creature); + m_creature->SetFlag(UNIT_NPC_FLAGS, UNIT_NPC_FLAG_QUESTGIVER); + // Complete event + if (Player* pPlayer = GetPlayerForEscort()) + pPlayer->GroupEventHappens(QUEST_WILLIX_THE_IMPORTER, m_creature); + SetEscortPaused(true); + break; + } + } +}; + +CreatureAI* GetAI_npc_willix_the_importer(Creature* pCreature) +{ + return new npc_willix_the_importerAI(pCreature); +} + +bool QuestAccept_npc_willix_the_importer(Player* pPlayer, Creature* pCreature, const Quest* pQuest) +{ + if (pQuest->GetQuestId() == QUEST_WILLIX_THE_IMPORTER) + { + if (npc_willix_the_importerAI* pEscortAI = dynamic_cast(pCreature->AI())) + { + // After 4.0.1 set run = true + pEscortAI->Start(false, pPlayer, pQuest); + DoScriptText(SAY_WILLIX_READY, pCreature, pPlayer); + pCreature->SetFactionTemporary(FACTION_ESCORT_N_NEUTRAL_PASSIVE, TEMPFACTION_RESTORE_RESPAWN); + } + } + + return true; +} + +/*###### +## npc_snufflenose_gopher +######*/ + +enum +{ + SPELL_SNUFFLENOSE_COMMAND = 8283, + NPC_SNUFFLENOSE_GOPHER = 4781, + GO_BLUELEAF_TUBBER = 20920, +}; + +struct npc_snufflenose_gopherAI : public ScriptedPetAI +{ + npc_snufflenose_gopherAI(Creature* pCreature) : ScriptedPetAI(pCreature) { Reset(); } + + bool m_bIsMovementActive; + + ObjectGuid m_targetTubberGuid; + + void Reset() override + { + m_bIsMovementActive = false; + } + + void MovementInform(uint32 uiMoveType, uint32 uiPointId) override + { + if (uiMoveType != POINT_MOTION_TYPE || !uiPointId) + return; + + if (GameObject* pGo = m_creature->GetMap()->GetGameObject(m_targetTubberGuid)) + { + pGo->SetRespawnTime(5 * MINUTE); + pGo->Refresh(); + + pGo->RemoveFlag(GAMEOBJECT_FLAGS, GO_FLAG_INTERACT_COND); + } + + m_bIsMovementActive = false; + } + + // Function to search for new tubber in range + void DoFindNewTubber() + { + std::list lTubbersInRange; + GetGameObjectListWithEntryInGrid(lTubbersInRange, m_creature, GO_BLUELEAF_TUBBER, 40.0f); + + if (lTubbersInRange.empty()) + return; + + lTubbersInRange.sort(ObjectDistanceOrder(m_creature)); + GameObject* pNearestTubber = NULL; + + // Always need to find new ones + for (std::list::const_iterator itr = lTubbersInRange.begin(); itr != lTubbersInRange.end(); ++itr) + { + if (!(*itr)->isSpawned() && (*itr)->HasFlag(GAMEOBJECT_FLAGS, GO_FLAG_INTERACT_COND) && (*itr)->IsWithinLOSInMap(m_creature)) + { + pNearestTubber = *itr; + break; + } + } + + if (!pNearestTubber) + return; + m_targetTubberGuid = pNearestTubber->GetObjectGuid(); + + float fX, fY, fZ; + pNearestTubber->GetContactPoint(m_creature, fX, fY, fZ); + m_creature->GetMotionMaster()->MovePoint(1, fX, fY, fZ); + m_bIsMovementActive = true; + } + + void UpdateAI(const uint32 uiDiff) override + { + if (!m_bIsMovementActive) + ScriptedPetAI::UpdateAI(uiDiff); + } +}; + +CreatureAI* GetAI_npc_snufflenose_gopher(Creature* pCreature) +{ + return new npc_snufflenose_gopherAI(pCreature); +} + +bool EffectDummyCreature_npc_snufflenose_gopher(Unit* pCaster, uint32 uiSpellId, SpellEffectIndex uiEffIndex, Creature* pCreatureTarget, ObjectGuid /*originalCasterGuid*/) +{ + // always check spellid and effectindex + if (uiSpellId == SPELL_SNUFFLENOSE_COMMAND && uiEffIndex == EFFECT_INDEX_0) + { + if (pCreatureTarget->GetEntry() == NPC_SNUFFLENOSE_GOPHER) + { + if (npc_snufflenose_gopherAI* pGopherAI = dynamic_cast(pCreatureTarget->AI())) + pGopherAI->DoFindNewTubber(); + } + + // always return true when we are handling this spell and effect + return true; + } + + return false; +} + +void AddSC_razorfen_kraul() +{ + Script* pNewScript; + + pNewScript = new Script; + pNewScript->Name = "npc_willix_the_importer"; + pNewScript->GetAI = &GetAI_npc_willix_the_importer; + pNewScript->pQuestAcceptNPC = &QuestAccept_npc_willix_the_importer; + pNewScript->RegisterSelf(); + + pNewScript = new Script; + pNewScript->Name = "npc_snufflenose_gopher"; + pNewScript->GetAI = &GetAI_npc_snufflenose_gopher; + pNewScript->pEffectDummyNPC = &EffectDummyCreature_npc_snufflenose_gopher; + pNewScript->RegisterSelf(); +} diff --git a/src/modules/SD2/scripts/kalimdor/razorfen_kraul/razorfen_kraul.h b/src/modules/SD2/scripts/kalimdor/razorfen_kraul/razorfen_kraul.h new file mode 100644 index 000000000..8d0fc160f --- /dev/null +++ b/src/modules/SD2/scripts/kalimdor/razorfen_kraul/razorfen_kraul.h @@ -0,0 +1,42 @@ +/* Copyright (C) 2006 - 2013 ScriptDev2 + * This program is free software licensed under GPL version 2 + * Please see the included DOCS/LICENSE.TXT for more information */ + +#ifndef DEF_RFK_H +#define DEF_RFK_H + +enum +{ + MAX_ENCOUNTER = 1, + + TYPE_AGATHELOS = 1, + + GO_AGATHELOS_WARD = 21099, + + NPC_WARD_KEEPER = 4625 +}; + +class instance_razorfen_kraul : public ScriptedInstance +{ + public: + instance_razorfen_kraul(Map* pMap); + ~instance_razorfen_kraul() {} + + void Initialize() override; + + void OnObjectCreate(GameObject* pGo) override; + void OnCreatureCreate(Creature* pCreature) override; + + void SetData(uint32 uiType, uint32 uiData) override; + uint32 GetData(uint32 uiType) const override; + + const char* Save() const override { return m_strInstData.c_str(); } + void Load(const char* chrIn) override; + + protected: + uint32 m_auiEncounter[MAX_ENCOUNTER]; + std::string m_strInstData; + + uint8 m_uiWardKeepersRemaining; +}; +#endif diff --git a/src/modules/SD2/scripts/kalimdor/ruins_of_ahnqiraj/boss_ayamiss.cpp b/src/modules/SD2/scripts/kalimdor/ruins_of_ahnqiraj/boss_ayamiss.cpp new file mode 100644 index 000000000..0de245fa2 --- /dev/null +++ b/src/modules/SD2/scripts/kalimdor/ruins_of_ahnqiraj/boss_ayamiss.cpp @@ -0,0 +1,328 @@ +/* Copyright (C) 2006 - 2013 ScriptDev2 + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with 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: 80 +SDComment: Timers and summon coords need adjustments +SDCategory: Ruins of Ahn'Qiraj +EndScriptData */ + +#include "precompiled.h" +#include "ruins_of_ahnqiraj.h" + +enum +{ + EMOTE_GENERIC_FRENZY = -1000002, + + SPELL_STINGER_SPRAY = 25749, + SPELL_POISON_STINGER = 25748, // only used in phase1 + // SPELL_SUMMON_SWARMER = 25844, // might be 25708 - spells were removed since 2.0.1 + SPELL_PARALYZE = 25725, + SPELL_LASH = 25852, + SPELL_FRENZY = 8269, + SPELL_TRASH = 3391, + + SPELL_FEED = 25721, // cast by the Larva when reaches the player on the altar + + NPC_LARVA = 15555, + NPC_SWARMER = 15546, + NPC_HORNET = 15934, + + PHASE_AIR = 0, + PHASE_GROUND = 1 +}; + +struct SummonLocation +{ + float m_fX, m_fY, m_fZ; +}; + +// Spawn locations +static const SummonLocation aAyamissSpawnLocs[] = +{ + { -9674.4707f, 1528.4133f, 22.457f}, // larva + { -9701.6005f, 1566.9993f, 24.118f}, // larva + { -9647.352f, 1578.062f, 55.32f}, // anchor point for swarmers + { -9717.18f, 1517.72f, 27.4677f}, // teleport location - need to be hardcoded because the player is teleported after the larva is summoned +}; + +struct boss_ayamissAI : public ScriptedAI +{ + boss_ayamissAI(Creature* pCreature) : ScriptedAI(pCreature) {Reset();} + + uint32 m_uiStingerSprayTimer; + uint32 m_uiPoisonStingerTimer; + uint32 m_uiSummonSwarmerTimer; + uint32 m_uiSwarmerAttackTimer; + uint32 m_uiParalyzeTimer; + uint32 m_uiLashTimer; + uint32 m_uiTrashTimer; + uint8 m_uiPhase; + + bool m_bHasFrenzy; + + ObjectGuid m_paralyzeTarget; + GuidList m_lSwarmersGuidList; + + void Reset() override + { + m_uiStingerSprayTimer = urand(20000, 30000); + m_uiPoisonStingerTimer = 5000; + m_uiSummonSwarmerTimer = 5000; + m_uiSwarmerAttackTimer = 60000; + m_uiParalyzeTimer = 15000; + m_uiLashTimer = urand(5000, 8000); + m_uiTrashTimer = urand(3000, 6000); + + m_bHasFrenzy = false; + + m_uiPhase = PHASE_AIR; + SetCombatMovement(false); + } + + void Aggro(Unit* /*pWho*/) override + { + m_creature->SetLevitate(true); + m_creature->GetMotionMaster()->MovePoint(0, m_creature->GetPositionX(), m_creature->GetPositionY(), m_creature->GetPositionZ() + 15.0f); + } + + void JustSummoned(Creature* pSummoned) override + { + // store the swarmers for a future attack + if (pSummoned->GetEntry() == NPC_SWARMER) + m_lSwarmersGuidList.push_back(pSummoned->GetObjectGuid()); + // move the larva to paralyze target position + else if (pSummoned->GetEntry() == NPC_LARVA) + { + pSummoned->SetWalk(false); + pSummoned->GetMotionMaster()->MovePoint(1, aAyamissSpawnLocs[3].m_fX, aAyamissSpawnLocs[3].m_fY, aAyamissSpawnLocs[3].m_fZ); + } + else if (pSummoned->GetEntry() == NPC_HORNET) + { + if (Unit* pTarget = m_creature->SelectAttackingTarget(ATTACKING_TARGET_RANDOM, 0)) + pSummoned->AI()->AttackStart(pTarget); + } + } + + void SummonedMovementInform(Creature* pSummoned, uint32 /*uiMotionType*/, uint32 uiPointId) override + { + if (uiPointId != 1 || pSummoned->GetEntry() != NPC_LARVA) + return; + + // Cast feed on target + if (Unit* pTarget = m_creature->GetMap()->GetUnit(m_paralyzeTarget)) + pSummoned->CastSpell(pTarget, SPELL_FEED, true, NULL, NULL, m_creature->GetObjectGuid()); + } + + void UpdateAI(const uint32 uiDiff) override + { + if (!m_creature->SelectHostileTarget() || !m_creature->getVictim()) + return; + + if (!m_bHasFrenzy && m_creature->GetHealthPercent() < 20.0f) + { + if (DoCastSpellIfCan(m_creature, SPELL_FRENZY) == CAST_OK) + { + DoScriptText(EMOTE_GENERIC_FRENZY, m_creature); + m_bHasFrenzy = true; + } + } + + // Stinger Spray + if (m_uiStingerSprayTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature, SPELL_STINGER_SPRAY) == CAST_OK) + m_uiStingerSprayTimer = urand(15000, 20000); + } + else + m_uiStingerSprayTimer -= uiDiff; + + // Paralyze + if (m_uiParalyzeTimer < uiDiff) + { + Unit* pTarget = m_creature->SelectAttackingTarget(ATTACKING_TARGET_RANDOM, 1, SPELL_PARALYZE, SELECT_FLAG_PLAYER); + if (!pTarget) + pTarget = m_creature->getVictim(); + + if (DoCastSpellIfCan(pTarget, SPELL_PARALYZE) == CAST_OK) + { + m_paralyzeTarget = pTarget->GetObjectGuid(); + m_uiParalyzeTimer = 15000; + + // Summon a larva + uint8 uiLoc = urand(0, 1); + m_creature->SummonCreature(NPC_LARVA, aAyamissSpawnLocs[uiLoc].m_fX, aAyamissSpawnLocs[uiLoc].m_fY, aAyamissSpawnLocs[uiLoc].m_fZ, 0, TEMPSUMMON_TIMED_OOC_OR_CORPSE_DESPAWN, 30000); + } + } + else + m_uiParalyzeTimer -= uiDiff; + + // Summon Swarmer + if (m_uiSummonSwarmerTimer < uiDiff) + { + // The spell which summons these guys was removed in 2.0.1 -> therefore we need to summon them manually at a random location around the area + // The summon locations is guesswork - the real location is supposed to be handled by world triggers + // There should be about 24 swarmers per min + float fX, fY, fZ; + for (uint8 i = 0; i < 2; ++i) + { + m_creature->GetRandomPoint(aAyamissSpawnLocs[2].m_fX, aAyamissSpawnLocs[2].m_fY, aAyamissSpawnLocs[2].m_fZ, 80.0f, fX, fY, fZ); + m_creature->SummonCreature(NPC_SWARMER, fX, fY, aAyamissSpawnLocs[2].m_fZ, 0.0f, TEMPSUMMON_CORPSE_DESPAWN, 0); + } + m_uiSummonSwarmerTimer = 5000; + } + else + m_uiSummonSwarmerTimer -= uiDiff; + + // All the swarmers attack at a certain period of time + if (m_uiSwarmerAttackTimer < uiDiff) + { + for (GuidList::const_iterator itr = m_lSwarmersGuidList.begin(); itr != m_lSwarmersGuidList.end(); ++itr) + { + if (Creature* pTemp = m_creature->GetMap()->GetCreature(*itr)) + { + if (Unit* pTarget = m_creature->SelectAttackingTarget(ATTACKING_TARGET_RANDOM, 0)) + pTemp->AI()->AttackStart(pTarget); + } + } + m_lSwarmersGuidList.clear(); + m_uiSwarmerAttackTimer = 60000; + } + else + m_uiSwarmerAttackTimer -= uiDiff; + + if (m_uiPhase == PHASE_AIR) + { + // Start ground phase at 70% of HP + if (m_creature->GetHealthPercent() <= 70.0f) + { + m_uiPhase = PHASE_GROUND; + SetCombatMovement(true); + m_creature->SetLevitate(false); + DoResetThreat(); + + if (m_creature->getVictim()) + m_creature->GetMotionMaster()->MoveChase(m_creature->getVictim()); + } + + // Poison Stinger + if (m_uiPoisonStingerTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature->getVictim(), SPELL_POISON_STINGER) == CAST_OK) + m_uiPoisonStingerTimer = urand(2000, 3000); + } + else + m_uiPoisonStingerTimer -= uiDiff; + } + else + { + if (m_uiLashTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature->getVictim(), SPELL_LASH) == CAST_OK) + m_uiLashTimer = urand(8000, 15000); + } + else + m_uiLashTimer -= uiDiff; + + if (m_uiTrashTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature->getVictim(), SPELL_TRASH) == CAST_OK) + m_uiTrashTimer = urand(5000, 7000); + } + else + m_uiTrashTimer -= uiDiff; + + DoMeleeAttackIfReady(); + } + } +}; + +CreatureAI* GetAI_boss_ayamiss(Creature* pCreature) +{ + return new boss_ayamissAI(pCreature); +} + +struct npc_hive_zara_larvaAI : public ScriptedAI +{ + npc_hive_zara_larvaAI(Creature* pCreature) : ScriptedAI(pCreature) + { + m_pInstance = (instance_ruins_of_ahnqiraj*)m_creature->GetInstanceData(); + Reset(); + } + + instance_ruins_of_ahnqiraj* m_pInstance; + + void Reset() override { } + + void AttackStart(Unit* pWho) override + { + // don't attack anything during the Ayamiss encounter + if (m_pInstance) + { + if (m_pInstance->GetData(TYPE_AYAMISS) == IN_PROGRESS) + return; + } + + ScriptedAI::AttackStart(pWho); + } + + void MoveInLineOfSight(Unit* pWho) override + { + // don't attack anything during the Ayamiss encounter + if (m_pInstance) + { + if (m_pInstance->GetData(TYPE_AYAMISS) == IN_PROGRESS) + return; + } + + ScriptedAI::MoveInLineOfSight(pWho); + } + + void UpdateAI(const uint32 /*uiDiff*/) override + { + if (m_pInstance) + { + if (m_pInstance->GetData(TYPE_AYAMISS) == IN_PROGRESS) + return; + } + + if (!m_creature->SelectHostileTarget() || !m_creature->getVictim()) + return; + + DoMeleeAttackIfReady(); + } +}; + +CreatureAI* GetAI_npc_hive_zara_larva(Creature* pCreature) +{ + return new npc_hive_zara_larvaAI(pCreature); +} + +void AddSC_boss_ayamiss() +{ + Script* pNewScript; + + pNewScript = new Script; + pNewScript->Name = "boss_ayamiss"; + pNewScript->GetAI = &GetAI_boss_ayamiss; + pNewScript->RegisterSelf(); + + pNewScript = new Script; + pNewScript->Name = "npc_hive_zara_larva"; + pNewScript->GetAI = &GetAI_npc_hive_zara_larva; + pNewScript->RegisterSelf(); +} diff --git a/src/modules/SD2/scripts/kalimdor/ruins_of_ahnqiraj/boss_buru.cpp b/src/modules/SD2/scripts/kalimdor/ruins_of_ahnqiraj/boss_buru.cpp new file mode 100644 index 000000000..95d7beb47 --- /dev/null +++ b/src/modules/SD2/scripts/kalimdor/ruins_of_ahnqiraj/boss_buru.cpp @@ -0,0 +1,246 @@ +/* Copyright (C) 2006 - 2013 ScriptDev2 + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with 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: 70 +SDComment: Timers; Kill eggs on transform NYI; Egg explode damage and Buru stun are missing +SDCategory: Ruins of Ahn'Qiraj +EndScriptData */ + +#include "precompiled.h" +#include "ruins_of_ahnqiraj.h" + +enum +{ + EMOTE_TARGET = -1509002, + + // boss spells + SPELL_CREEPING_PLAGUE = 20512, + SPELL_DISMEMBER = 96, + SPELL_GATHERING_SPEED = 1834, + SPELL_FULL_SPEED = 1557, + SPELL_THORNS = 25640, + SPELL_BURU_TRANSFORM = 24721, + + // egg spells + SPELL_SUMMON_HATCHLING = 1881, + SPELL_EXPLODE = 19593, + SPELL_BURU_EGG_TRIGGER = 26646, + + // npcs + NPC_BURU_EGG_TRIGGER = 15964, + NPC_BURU_EGG = 15514, + NPC_HATCHLING = 15521, + + PHASE_EGG = 1, + PHASE_TRANSFORM = 2, +}; + +struct boss_buruAI : public ScriptedAI +{ + boss_buruAI(Creature* pCreature) : ScriptedAI(pCreature) { Reset(); } + + uint8 m_uiPhase; + uint32 m_uiDismemberTimer; + uint32 m_uiCreepingPlagueTimer; + uint32 m_uiGatheringSpeedTimer; + uint32 m_uiFullSpeedTimer; + + void Reset() override + { + m_uiDismemberTimer = 5000; + m_uiGatheringSpeedTimer = 9000; + m_uiCreepingPlagueTimer = 0; + m_uiFullSpeedTimer = 60000; + m_uiPhase = PHASE_EGG; + } + + void Aggro(Unit* pWho) override + { + DoScriptText(EMOTE_TARGET, m_creature, pWho); + DoCastSpellIfCan(m_creature, SPELL_THORNS); + m_creature->FixateTarget(pWho); + } + + void KilledUnit(Unit* pVictim) override + { + // Attack a new random target when a player is killed + if (pVictim->GetTypeId() == TYPEID_PLAYER) + DoAttackNewTarget(); + } + + // Wrapper to attack a new target and remove the speed gathering buff + void DoAttackNewTarget() + { + if (m_uiPhase == PHASE_TRANSFORM) + return; + + m_creature->RemoveAurasDueToSpell(SPELL_FULL_SPEED); + m_creature->RemoveAurasDueToSpell(SPELL_GATHERING_SPEED); + + if (Unit* pTarget = m_creature->SelectAttackingTarget(ATTACKING_TARGET_RANDOM, 0, uint32(0), SELECT_FLAG_PLAYER)) + { + m_creature->FixateTarget(pTarget); + DoScriptText(EMOTE_TARGET, m_creature, pTarget); + } + + m_uiFullSpeedTimer = 60000; + m_uiGatheringSpeedTimer = 9000; + } + + void UpdateAI(const uint32 uiDiff) override + { + if (!m_creature->SelectHostileTarget() || !m_creature->getVictim()) + return; + + switch (m_uiPhase) + { + case PHASE_EGG: + + if (m_uiDismemberTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature->getVictim(), SPELL_DISMEMBER) == CAST_OK) + m_uiDismemberTimer = 5000; + } + else + m_uiDismemberTimer -= uiDiff; + + if (m_uiFullSpeedTimer) + { + if (m_uiGatheringSpeedTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature, SPELL_GATHERING_SPEED) == CAST_OK) + m_uiGatheringSpeedTimer = 9000; + } + else + m_uiGatheringSpeedTimer -= uiDiff; + + if (m_uiFullSpeedTimer <= uiDiff) + { + if (DoCastSpellIfCan(m_creature, SPELL_FULL_SPEED) == CAST_OK) + m_uiFullSpeedTimer = 0; + } + else + m_uiFullSpeedTimer -= uiDiff; + } + + if (m_creature->GetHealthPercent() < 20.0f) + { + if (DoCastSpellIfCan(m_creature, SPELL_BURU_TRANSFORM) == CAST_OK) + { + // Not sure of this but the boss should gain full speed in phase II + DoCastSpellIfCan(m_creature, SPELL_FULL_SPEED, CAST_TRIGGERED); + m_creature->RemoveAurasDueToSpell(SPELL_THORNS); + m_creature->FixateTarget(NULL); + m_uiPhase = PHASE_TRANSFORM; + } + } + + break; + case PHASE_TRANSFORM: + + if (m_uiCreepingPlagueTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature, SPELL_CREEPING_PLAGUE) == CAST_OK) + m_uiCreepingPlagueTimer = 6000; + } + else + m_uiCreepingPlagueTimer -= uiDiff; + + break; + } + + DoMeleeAttackIfReady(); + } +}; + +CreatureAI* GetAI_boss_buru(Creature* pCreature) +{ + return new boss_buruAI(pCreature); +} + +struct npc_buru_eggAI : public Scripted_NoMovementAI +{ + npc_buru_eggAI(Creature* pCreature) : Scripted_NoMovementAI(pCreature) + { + m_pInstance = (ScriptedInstance*)pCreature->GetInstanceData(); + Reset(); + } + + ScriptedInstance* m_pInstance; + + void Reset() override + { } + + void JustSummoned(Creature* pSummoned) override + { + // The purpose of this is unk for the moment + if (pSummoned->GetEntry() == NPC_BURU_EGG_TRIGGER) + pSummoned->CastSpell(pSummoned, SPELL_BURU_EGG_TRIGGER, true); + // The Hatchling should attack a random target + else if (pSummoned->GetEntry() == NPC_HATCHLING) + { + if (m_pInstance) + { + if (Creature* pBuru = m_pInstance->GetSingleCreatureFromStorage(NPC_BURU)) + { + if (Unit* pTarget = pBuru->SelectAttackingTarget(ATTACKING_TARGET_RANDOM, 0)) + pSummoned->AI()->AttackStart(pTarget); + } + } + } + } + + void JustDied(Unit* /*pKiller*/) override + { + // Explode and Summon hatchling + DoCastSpellIfCan(m_creature, SPELL_EXPLODE, CAST_TRIGGERED); + DoCastSpellIfCan(m_creature, SPELL_SUMMON_HATCHLING, CAST_TRIGGERED, m_creature->GetObjectGuid()); + + // Reset Buru's target - this might have been done by spell, but currently this is unk to us + if (m_pInstance) + { + if (Creature* pBuru = m_pInstance->GetSingleCreatureFromStorage(NPC_BURU)) + { + if (boss_buruAI* pBuruAI = dynamic_cast(pBuru->AI())) + pBuruAI->DoAttackNewTarget(); + } + } + } + + void UpdateAI(const uint32 /*uiDiff*/) override { } +}; + +CreatureAI* GetAI_npc_buru_egg(Creature* pCreature) +{ + return new npc_buru_eggAI(pCreature); +} + +void AddSC_boss_buru() +{ + Script* pNewScript; + + pNewScript = new Script; + pNewScript->Name = "boss_buru"; + pNewScript->GetAI = &GetAI_boss_buru; + pNewScript->RegisterSelf(); + + pNewScript = new Script; + pNewScript->Name = "npc_buru_egg"; + pNewScript->GetAI = &GetAI_npc_buru_egg; + pNewScript->RegisterSelf(); +} diff --git a/src/modules/SD2/scripts/kalimdor/ruins_of_ahnqiraj/boss_kurinnaxx.cpp b/src/modules/SD2/scripts/kalimdor/ruins_of_ahnqiraj/boss_kurinnaxx.cpp new file mode 100644 index 000000000..c180221bb --- /dev/null +++ b/src/modules/SD2/scripts/kalimdor/ruins_of_ahnqiraj/boss_kurinnaxx.cpp @@ -0,0 +1,153 @@ +/* Copyright (C) 2006 - 2013 ScriptDev2 + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with 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: 90 +SDComment: Summon Player ability NYI +SDCategory: Ruins of Ahn'Qiraj +EndScriptData */ + +#include "precompiled.h" + +enum +{ + SPELL_TRASH = 3391, + SPELL_WIDE_SLASH = 25814, + SPELL_MORTAL_WOUND = 25646, + SPELL_SANDTRAP = 25648, // summons gameobject 180647 + SPELL_ENRAGE = 26527, + SPELL_SUMMON_PLAYER = 26446, + + GO_SAND_TRAP = 180647, +}; + +struct boss_kurinnaxxAI : public ScriptedAI +{ + boss_kurinnaxxAI(Creature* pCreature) : ScriptedAI(pCreature) {Reset();} + + uint32 m_uiMortalWoundTimer; + uint32 m_uiSandTrapTimer; + uint32 m_uiTrashTimer; + uint32 m_uiWideSlashTimer; + uint32 m_uiTrapTriggerTimer; + bool m_bEnraged; + + ObjectGuid m_sandtrapGuid; + + void Reset() override + { + m_bEnraged = false; + + m_uiMortalWoundTimer = urand(8000, 10000); + m_uiSandTrapTimer = urand(5000, 10000); + m_uiTrashTimer = urand(1000, 5000); + m_uiWideSlashTimer = urand(10000, 15000); + m_uiTrapTriggerTimer = 0; + } + + void JustSummoned(GameObject* pGo) override + { + if (pGo->GetEntry() == GO_SAND_TRAP) + { + m_uiTrapTriggerTimer = 3000; + m_sandtrapGuid = pGo->GetObjectGuid(); + } + } + + void UpdateAI(const uint32 uiDiff) override + { + if (!m_creature->SelectHostileTarget() || !m_creature->getVictim()) + return; + + // If we are belowe 30% HP cast enrage + if (!m_bEnraged && m_creature->GetHealthPercent() <= 30.0f) + { + if (DoCastSpellIfCan(m_creature, SPELL_ENRAGE) == CAST_OK) + m_bEnraged = true; + } + + // Mortal Wound + if (m_uiMortalWoundTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature->getVictim(), SPELL_MORTAL_WOUND) == CAST_OK) + m_uiMortalWoundTimer = urand(8000, 10000); + } + else + m_uiMortalWoundTimer -= uiDiff; + + // Sand Trap + if (m_uiSandTrapTimer < uiDiff) + { + Unit* pTarget = m_creature->SelectAttackingTarget(ATTACKING_TARGET_RANDOM, 1); + if (!pTarget) + pTarget = m_creature->getVictim(); + + pTarget->CastSpell(pTarget, SPELL_SANDTRAP, true, NULL, NULL, m_creature->GetObjectGuid()); + m_uiSandTrapTimer = urand(10000, 15000); + } + else + m_uiSandTrapTimer -= uiDiff; + + // Trigger the sand trap in 3 secs after spawn + if (m_uiTrapTriggerTimer) + { + if (m_uiTrapTriggerTimer <= uiDiff) + { + if (GameObject* pTrap = m_creature->GetMap()->GetGameObject(m_sandtrapGuid)) + pTrap->Use(m_creature); + m_uiTrapTriggerTimer = 0; + } + else + m_uiTrapTriggerTimer -= uiDiff; + } + + // Wide Slash + if (m_uiWideSlashTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature->getVictim(), SPELL_WIDE_SLASH) == CAST_OK) + m_uiWideSlashTimer = urand(12000, 15000); + } + else + m_uiWideSlashTimer -= uiDiff; + + // Trash + if (m_uiTrashTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature->getVictim(), SPELL_TRASH) == CAST_OK) + m_uiTrashTimer = urand(12000, 17000); + } + else + m_uiTrashTimer -= uiDiff; + + DoMeleeAttackIfReady(); + } +}; + +CreatureAI* GetAI_boss_kurinnaxx(Creature* pCreature) +{ + return new boss_kurinnaxxAI(pCreature); +} + +void AddSC_boss_kurinnaxx() +{ + Script* pNewScript; + + pNewScript = new Script; + pNewScript->Name = "boss_kurinnaxx"; + pNewScript->GetAI = &GetAI_boss_kurinnaxx; + pNewScript->RegisterSelf(); +} diff --git a/src/modules/SD2/scripts/kalimdor/ruins_of_ahnqiraj/boss_moam.cpp b/src/modules/SD2/scripts/kalimdor/ruins_of_ahnqiraj/boss_moam.cpp new file mode 100644 index 000000000..0fc9353ed --- /dev/null +++ b/src/modules/SD2/scripts/kalimdor/ruins_of_ahnqiraj/boss_moam.cpp @@ -0,0 +1,158 @@ +/* Copyright (C) 2006 - 2013 ScriptDev2 + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with 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: +SDCategory: Ruins of Ahn'Qiraj +EndScriptData */ + +#include "precompiled.h" + +enum +{ + EMOTE_AGGRO = -1509000, + EMOTE_MANA_FULL = -1509001, + EMOTE_ENERGIZING = -1509028, + + SPELL_TRAMPLE = 15550, + SPELL_DRAIN_MANA = 25671, + SPELL_ARCANE_ERUPTION = 25672, + SPELL_SUMMON_MANAFIEND_1 = 25681, + SPELL_SUMMON_MANAFIEND_2 = 25682, + SPELL_SUMMON_MANAFIEND_3 = 25683, + SPELL_ENERGIZE = 25685, + + PHASE_ATTACKING = 0, + PHASE_ENERGIZING = 1 +}; + +struct boss_moamAI : public ScriptedAI +{ + boss_moamAI(Creature* pCreature) : ScriptedAI(pCreature) {Reset();} + + uint8 m_uiPhase; + + uint32 m_uiTrampleTimer; + uint32 m_uiManaDrainTimer; + uint32 m_uiCheckoutManaTimer; + uint32 m_uiSummonManaFiendsTimer; + + void Reset() override + { + m_uiTrampleTimer = 9000; + m_uiManaDrainTimer = 3000; + m_uiSummonManaFiendsTimer = 90000; + m_uiCheckoutManaTimer = 1500; + m_uiPhase = PHASE_ATTACKING; + m_creature->SetPower(POWER_MANA, 0); + m_creature->SetMaxPower(POWER_MANA, 0); + } + + void Aggro(Unit* /*pWho*/) override + { + DoScriptText(EMOTE_AGGRO, m_creature); + m_creature->SetMaxPower(POWER_MANA, m_creature->GetCreatureInfo()->maxmana); + } + + void UpdateAI(const uint32 uiDiff) override + { + if (!m_creature->SelectHostileTarget() || !m_creature->getVictim()) + return; + + switch (m_uiPhase) + { + case PHASE_ATTACKING: + if (m_uiCheckoutManaTimer <= uiDiff) + { + m_uiCheckoutManaTimer = 1500; + if (m_creature->GetPower(POWER_MANA) * 100 / m_creature->GetMaxPower(POWER_MANA) > 75.0f) + { + DoCastSpellIfCan(m_creature, SPELL_ENERGIZE); + DoScriptText(EMOTE_ENERGIZING, m_creature); + m_uiPhase = PHASE_ENERGIZING; + return; + } + } + else + m_uiCheckoutManaTimer -= uiDiff; + + if (m_uiSummonManaFiendsTimer <= uiDiff) + { + DoCastSpellIfCan(m_creature->getVictim(), SPELL_SUMMON_MANAFIEND_1, CAST_TRIGGERED); + DoCastSpellIfCan(m_creature->getVictim(), SPELL_SUMMON_MANAFIEND_2, CAST_TRIGGERED); + DoCastSpellIfCan(m_creature->getVictim(), SPELL_SUMMON_MANAFIEND_3, CAST_TRIGGERED); + m_uiSummonManaFiendsTimer = 90000; + } + else + m_uiSummonManaFiendsTimer -= uiDiff; + + if (m_uiManaDrainTimer <= uiDiff) + { + if (Unit* pTarget = m_creature->SelectAttackingTarget(ATTACKING_TARGET_RANDOM, 0, SPELL_DRAIN_MANA, SELECT_FLAG_POWER_MANA)) + { + if (DoCastSpellIfCan(pTarget, SPELL_DRAIN_MANA) == CAST_OK) + m_uiManaDrainTimer = urand(2000, 6000); + } + } + else + m_uiManaDrainTimer -= uiDiff; + + if (m_uiTrampleTimer <= uiDiff) + { + DoCastSpellIfCan(m_creature->getVictim(), SPELL_TRAMPLE); + m_uiTrampleTimer = 15000; + } + else + m_uiTrampleTimer -= uiDiff; + + DoMeleeAttackIfReady(); + break; + case PHASE_ENERGIZING: + if (m_uiCheckoutManaTimer <= uiDiff) + { + m_uiCheckoutManaTimer = 1500; + if (m_creature->GetPower(POWER_MANA) == m_creature->GetMaxPower(POWER_MANA)) + { + m_creature->RemoveAurasDueToSpell(SPELL_ENERGIZE); + DoCastSpellIfCan(m_creature, SPELL_ARCANE_ERUPTION); + DoScriptText(EMOTE_MANA_FULL, m_creature); + m_uiPhase = PHASE_ATTACKING; + return; + } + } + else + m_uiCheckoutManaTimer -= uiDiff; + break; + } + } +}; + +CreatureAI* GetAI_boss_moam(Creature* pCreature) +{ + return new boss_moamAI(pCreature); +} + +void AddSC_boss_moam() +{ + Script* pNewScript; + + pNewScript = new Script; + pNewScript->Name = "boss_moam"; + pNewScript->GetAI = &GetAI_boss_moam; + pNewScript->RegisterSelf(); +} diff --git a/src/modules/SD2/scripts/kalimdor/ruins_of_ahnqiraj/boss_ossirian.cpp b/src/modules/SD2/scripts/kalimdor/ruins_of_ahnqiraj/boss_ossirian.cpp new file mode 100644 index 000000000..6baffb1bd --- /dev/null +++ b/src/modules/SD2/scripts/kalimdor/ruins_of_ahnqiraj/boss_ossirian.cpp @@ -0,0 +1,282 @@ +/* Copyright (C) 2006 - 2013 ScriptDev2 + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with 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: 80% +SDComment: Weather missing +SDCategory: Ruins of Ahn'Qiraj +EndScriptData */ + +#include "precompiled.h" +#include "ruins_of_ahnqiraj.h" + +enum +{ + SAY_SUPREME_1 = -1509018, + SAY_SUPREME_2 = -1509019, + SAY_SUPREME_3 = -1509020, + SAY_RAND_INTRO_1 = -1509021, + SAY_RAND_INTRO_2 = -1509023, + SAY_RAND_INTRO_3 = -1509024, + SAY_AGGRO = -1509025, + SAY_SLAY = -1509026, + SAY_DEATH = -1509027, + + SPELL_SILENCE = 25195, + SPELL_CYCLONE = 25189, + SPELL_STOMP = 25188, + SPELL_SUPREME = 25176, + SPELL_SUMMON_CRYSTAL = 25192, + SPELL_SAND_STORM = 25160, // tornado spell + SPELL_SUMMON = 20477, // TODO NYI + + MAX_CRYSTAL_POSITIONS = 1, // TODO + + SPELL_WEAKNESS_FIRE = 25177, + SPELL_WEAKNESS_FROST = 25178, + SPELL_WEAKNESS_NATURE = 25180, + SPELL_WEAKNESS_ARCANE = 25181, + SPELL_WEAKNESS_SHADOW = 25183, + + NPC_SAND_VORTEX = 15428, // tornado npc +}; + +static const float aSandVortexSpawnPos[2][4] = +{ + { -9523.482f, 1880.435f, 85.645f, 5.08f}, + { -9321.39f, 1822.968f, 84.266f, 3.16f}, +}; + +static const float aCrystalSpawnPos[3] = { -9355.75f, 1905.43f, 85.55f}; +static const uint32 aWeaknessSpell[] = {SPELL_WEAKNESS_FIRE, SPELL_WEAKNESS_FROST, SPELL_WEAKNESS_NATURE, SPELL_WEAKNESS_ARCANE, SPELL_WEAKNESS_SHADOW}; + +struct boss_ossirianAI : public ScriptedAI +{ + + boss_ossirianAI(Creature* pCreature) : ScriptedAI(pCreature) + { + m_pInstance = (instance_ruins_of_ahnqiraj*)m_creature->GetInstanceData(); + m_bSaidIntro = false; + Reset(); + } + + instance_ruins_of_ahnqiraj* m_pInstance; + + uint32 m_uiSupremeTimer; + uint32 m_uiCycloneTimer; + uint32 m_uiStompTimer; + uint32 m_uiSilenceTimer; + uint8 m_uiCrystalPosition; + + bool m_bSaidIntro; + + void Reset() override + { + m_uiCrystalPosition = 0; + m_uiCycloneTimer = 20000; + m_uiStompTimer = 30000; + m_uiSilenceTimer = 30000; + m_uiSupremeTimer = 45000; + } + + void Aggro(Unit* /*pWho*/) override + { + DoCastSpellIfCan(m_creature, SPELL_SUPREME, CAST_TRIGGERED); + DoScriptText(SAY_AGGRO, m_creature); + DoSpawnNextCrystal(); + + for (uint8 i = 0; i < countof(aSandVortexSpawnPos); ++i) + m_creature->SummonCreature(NPC_SAND_VORTEX, aSandVortexSpawnPos[i][0], aSandVortexSpawnPos[i][1], aSandVortexSpawnPos[i][2], aSandVortexSpawnPos[i][3], TEMPSUMMON_CORPSE_DESPAWN, 0); + } + + void JustDied(Unit* /*pKiller*/) override + { + DoScriptText(SAY_DEATH, m_creature); + } + + void KilledUnit(Unit* /*pVictim*/) override + { + DoScriptText(SAY_SLAY, m_creature); + } + + void DoSpawnNextCrystal() + { + if (!m_pInstance) + return; + + Creature* pOssirianTrigger = NULL; + if (m_uiCrystalPosition == 0) + { + // Respawn static spawned crystal trigger + pOssirianTrigger = m_pInstance->GetSingleCreatureFromStorage(NPC_OSSIRIAN_TRIGGER); + if (pOssirianTrigger && !pOssirianTrigger->IsAlive()) + pOssirianTrigger->Respawn(); + } + else + { + // Summon a new crystal trigger at some position depending on m_uiCrystalPosition + // Note: the summon points seem to be very random; requires additional research + float fX, fY, fZ; + m_creature->GetRandomPoint(aCrystalSpawnPos[0], aCrystalSpawnPos[1], aCrystalSpawnPos[2], 100.0f, fX, fY, fZ); + m_creature->SummonCreature(NPC_OSSIRIAN_TRIGGER, fX, fY, fZ, 0, TEMPSUMMON_CORPSE_DESPAWN, 0); + } + if (!pOssirianTrigger) + return; + + // Respawn GO near crystal trigger + if (GameObject* pCrystal = GetClosestGameObjectWithEntry(pOssirianTrigger, GO_OSSIRIAN_CRYSTAL, 10.0f)) + m_pInstance->DoRespawnGameObject(pCrystal->GetObjectGuid(), 5 * MINUTE); + + // Increase position + ++m_uiCrystalPosition %= MAX_CRYSTAL_POSITIONS; + } + + void JustSummoned(Creature* pSummoned) override + { + if (pSummoned->GetEntry() == NPC_OSSIRIAN_TRIGGER) + pSummoned->CastSpell(pSummoned, SPELL_SUMMON_CRYSTAL, true); + else if (pSummoned->GetEntry() == NPC_SAND_VORTEX) + { + // The movement of this isn't very clear - may require additional research + pSummoned->CastSpell(pSummoned, SPELL_SAND_STORM, true); + pSummoned->GetMotionMaster()->MoveRandomAroundPoint(aCrystalSpawnPos[0], aCrystalSpawnPos[1], aCrystalSpawnPos[2], 100.0f); + } + } + + void SpellHit(Unit* pCaster, const SpellEntry* pSpell) override + { + if (pCaster->GetTypeId() == TYPEID_UNIT && pCaster->GetEntry() == NPC_OSSIRIAN_TRIGGER) + { + // Check for proper spell id + bool bIsWeaknessSpell = false; + for (uint8 i = 0; i < countof(aWeaknessSpell); ++i) + { + if (pSpell->Id == aWeaknessSpell[i]) + { + bIsWeaknessSpell = true; + break; + } + } + if (!bIsWeaknessSpell) + return; + + m_creature->RemoveAurasDueToSpell(SPELL_SUPREME); + m_uiSupremeTimer = 45000; + + ((Creature*)pCaster)->ForcedDespawn(); + DoSpawnNextCrystal(); + } + } + + void MoveInLineOfSight(Unit* pWho) override + { + // TODO: Range guesswork + if (!m_bSaidIntro && pWho->GetTypeId() == TYPEID_PLAYER && m_creature->IsWithinDistInMap(pWho, 75.0f, false)) + { + switch (urand(0, 2)) + { + case 0: DoScriptText(SAY_RAND_INTRO_1, m_creature); break; + case 1: DoScriptText(SAY_RAND_INTRO_2, m_creature); break; + case 2: DoScriptText(SAY_RAND_INTRO_3, m_creature); break; + } + m_bSaidIntro = true; + } + } + + void UpdateAI(const uint32 uiDiff) override + { + if (!m_creature->SelectHostileTarget() || !m_creature->getVictim()) + return; + + // Supreme + if (m_uiSupremeTimer <= uiDiff) + { + if (DoCastSpellIfCan(m_creature, SPELL_SUPREME, CAST_AURA_NOT_PRESENT) == CAST_OK) + { + switch (urand(0, 2)) + { + case 0: DoScriptText(SAY_SUPREME_1, m_creature); break; + case 1: DoScriptText(SAY_SUPREME_2, m_creature); break; + case 2: DoScriptText(SAY_SUPREME_3, m_creature); break; + } + m_uiSupremeTimer = 45000; + } + else + m_uiSupremeTimer = 5000; + } + else + m_uiSupremeTimer -= uiDiff; + + // Stomp + if (m_uiStompTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature, SPELL_STOMP) == CAST_OK) + m_uiStompTimer = 30000; + } + else + m_uiStompTimer -= uiDiff; + + // Cyclone + if (m_uiCycloneTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature->getVictim(), SPELL_CYCLONE) == CAST_OK) + m_uiCycloneTimer = 20000; + } + else + m_uiCycloneTimer -= uiDiff; + + // Silence + if (m_uiSilenceTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature, SPELL_SILENCE) == CAST_OK) + m_uiSilenceTimer = urand(20000, 30000); + } + else + m_uiSilenceTimer -= uiDiff; + + DoMeleeAttackIfReady(); + } +}; + +CreatureAI* GetAI_boss_ossirian(Creature* pCreature) +{ + return new boss_ossirianAI(pCreature); +} + +// This is actually a hack for a server-side spell +bool GOUse_go_ossirian_crystal(Player* /*pPlayer*/, GameObject* pGo) +{ + if (Creature* pOssirianTrigger = GetClosestCreatureWithEntry(pGo, NPC_OSSIRIAN_TRIGGER, 10.0f)) + pOssirianTrigger->CastSpell(pOssirianTrigger, aWeaknessSpell[urand(0, 4)], false); + + return true; +} + +void AddSC_boss_ossirian() +{ + Script* pNewScript; + + pNewScript = new Script; + pNewScript->Name = "boss_ossirian"; + pNewScript->GetAI = &GetAI_boss_ossirian; + pNewScript->RegisterSelf(); + + pNewScript = new Script; + pNewScript->Name = "go_ossirian_crystal"; + pNewScript->pGOUse = &GOUse_go_ossirian_crystal; + pNewScript->RegisterSelf(); +} diff --git a/src/modules/SD2/scripts/kalimdor/ruins_of_ahnqiraj/boss_rajaxx.cpp b/src/modules/SD2/scripts/kalimdor/ruins_of_ahnqiraj/boss_rajaxx.cpp new file mode 100644 index 000000000..37d29792d --- /dev/null +++ b/src/modules/SD2/scripts/kalimdor/ruins_of_ahnqiraj/boss_rajaxx.cpp @@ -0,0 +1,387 @@ +/* Copyright (C) 2006 - 2013 ScriptDev2 + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with 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: 100 +SDComment: General Andorov script +SDCategory: Ruins of Ahn'Qiraj +EndScriptData */ + +#include "precompiled.h" +#include "ruins_of_ahnqiraj.h" + +enum +{ + // Event yells + SAY_ANDOROV_INTRO_1 = -1509004, + SAY_ANDOROV_INTRO_2 = -1509031, + SAY_ANDOROV_INTRO_3 = -1509003, + SAY_ANDOROV_INTRO_4 = -1509029, + SAY_ANDOROV_ATTACK_START = -1509030, + + // Rajaxx kills Andorov + SAY_KILLS_ANDOROV = -1509016, + + // probably related to the opening of AQ event + SAY_UNK1 = -1509011, + SAY_UNK2 = -1509012, + SAY_UNK3 = -1509013, + SAY_UNK4 = -1509014, + + // gossip items + GOSSIP_TEXT_ID_INTRO = 7883, + GOSSIP_TEXT_ID_TRADE = 8305, + + GOSSIP_ITEM_START = -3509000, + GOSSIP_ITEM_TRADE = -3509001, + + // Andorov spells + SPELL_AURA_OF_COMMAND = 25516, + SPELL_BASH = 25515, + SPELL_STRIKE = 22591, + + // Kaldorei spell + SPELL_CLEAVE = 26350, + SPELL_MORTAL_STRIKE = 16856, + + POINT_ID_MOVE_INTRO = 2, + POINT_ID_MOVE_ATTACK = 4, +}; + +static const DialogueEntry aIntroDialogue[] = +{ + {SAY_ANDOROV_INTRO_1, NPC_GENERAL_ANDOROV, 7000}, + {SAY_ANDOROV_INTRO_2, NPC_GENERAL_ANDOROV, 0}, + {SAY_ANDOROV_INTRO_3, NPC_GENERAL_ANDOROV, 4000}, + {SAY_ANDOROV_INTRO_4, NPC_GENERAL_ANDOROV, 6000}, + {SAY_ANDOROV_ATTACK_START, NPC_GENERAL_ANDOROV, 0}, + {0, 0, 0}, +}; + +struct npc_general_andorovAI : public ScriptedAI, private DialogueHelper +{ + npc_general_andorovAI(Creature* pCreature) : ScriptedAI(pCreature), + DialogueHelper(aIntroDialogue) + { + m_pInstance = (instance_ruins_of_ahnqiraj*)pCreature->GetInstanceData(); + InitializeDialogueHelper(m_pInstance); + m_uiMoveTimer = 5000; + m_uiPointId = 0; + Reset(); + } + + instance_ruins_of_ahnqiraj* m_pInstance; + + uint32 m_uiCommandAuraTimer; + uint32 m_uiBashTimer; + uint32 m_uiStrikeTimer; + uint32 m_uiMoveTimer; + + uint8 m_uiPointId; + + void Reset() override + { + m_uiCommandAuraTimer = urand(1000, 3000); + m_uiBashTimer = urand(8000, 11000); + m_uiStrikeTimer = urand(2000, 5000); + } + + void MoveInLineOfSight(Unit* pWho) override + { + // If Rajaxx is in range attack him + if (pWho->GetEntry() == NPC_RAJAXX && m_creature->IsWithinDistInMap(pWho, 50.0f)) + AttackStart(pWho); + + ScriptedAI::MoveInLineOfSight(pWho); + } + + void JustDied(Unit* pKiller) override + { + if (pKiller->GetEntry() != NPC_RAJAXX) + return; + + // Yell when killed by Rajaxx + if (m_pInstance) + { + if (Creature* pRajaxx = m_pInstance->GetSingleCreatureFromStorage(NPC_RAJAXX)) + DoScriptText(SAY_KILLS_ANDOROV, pRajaxx); + } + } + + void JustDidDialogueStep(int32 iEntry) override + { + // Start the event when the dialogue is finished + if (iEntry == SAY_ANDOROV_ATTACK_START) + { + if (m_pInstance) + m_pInstance->SetData(TYPE_RAJAXX, IN_PROGRESS); + } + } + + void MovementInform(uint32 uiType, uint32 uiPointId) override + { + if (uiType != POINT_MOTION_TYPE) + return; + + switch (uiPointId) + { + case 0: + case 1: + case 3: + ++m_uiPointId; + m_creature->GetMotionMaster()->MovePoint(m_uiPointId, aAndorovMoveLocs[m_uiPointId].m_fX, aAndorovMoveLocs[m_uiPointId].m_fY, aAndorovMoveLocs[m_uiPointId].m_fZ); + break; + case POINT_ID_MOVE_INTRO: + m_creature->SetFlag(UNIT_NPC_FLAGS, UNIT_NPC_FLAG_GOSSIP); + m_creature->SetFacingTo(aAndorovMoveLocs[3].m_fO); + ++m_uiPointId; + break; + case POINT_ID_MOVE_ATTACK: + // Start dialogue only the first time it reaches the point + if (m_uiPointId == 4) + { + StartNextDialogueText(SAY_ANDOROV_INTRO_3); + ++m_uiPointId; + } + break; + } + } + + void EnterEvadeMode() override + { + if (!m_pInstance) + return; + + m_creature->RemoveAllAurasOnEvade(); + m_creature->DeleteThreatList(); + m_creature->CombatStop(true); + + if (m_creature->IsAlive()) + { + // reset to combat position + if (m_uiPointId >= 4) + m_creature->GetMotionMaster()->MovePoint(POINT_ID_MOVE_ATTACK, aAndorovMoveLocs[4].m_fX, aAndorovMoveLocs[4].m_fY, aAndorovMoveLocs[4].m_fZ); + // reset to intro position + else + m_creature->GetMotionMaster()->MovePoint(POINT_ID_MOVE_INTRO, aAndorovMoveLocs[2].m_fX, aAndorovMoveLocs[2].m_fY, aAndorovMoveLocs[2].m_fZ); + } + + m_creature->SetLootRecipient(NULL); + + Reset(); + } + + // Wrapper to start initialize Kaldorei followers + void DoInitializeFollowers() + { + if (!m_pInstance) + return; + + GuidList m_lKaldoreiGuids; + m_pInstance->GetKaldoreiGuidList(m_lKaldoreiGuids); + + for (GuidList::const_iterator itr = m_lKaldoreiGuids.begin(); itr != m_lKaldoreiGuids.end(); ++itr) + { + if (Creature* pKaldorei = m_creature->GetMap()->GetCreature(*itr)) + pKaldorei->GetMotionMaster()->MoveFollow(m_creature, pKaldorei->GetDistance(m_creature), pKaldorei->GetAngle(m_creature)); + } + } + + // Wrapper to start the event + void DoMoveToEventLocation() + { + m_creature->GetMotionMaster()->MovePoint(m_uiPointId, aAndorovMoveLocs[m_uiPointId].m_fX, aAndorovMoveLocs[m_uiPointId].m_fY, aAndorovMoveLocs[m_uiPointId].m_fZ); + m_creature->RemoveFlag(UNIT_NPC_FLAGS, UNIT_NPC_FLAG_GOSSIP); + StartNextDialogueText(SAY_ANDOROV_INTRO_1); + } + + void UpdateAI(const uint32 uiDiff) override + { + DialogueUpdate(uiDiff); + + if (m_uiMoveTimer) + { + if (m_uiMoveTimer <= uiDiff) + { + m_creature->SetWalk(false); + m_creature->GetMotionMaster()->MovePoint(m_uiPointId, aAndorovMoveLocs[m_uiPointId].m_fX, aAndorovMoveLocs[m_uiPointId].m_fY, aAndorovMoveLocs[m_uiPointId].m_fZ); + + DoInitializeFollowers(); + m_uiMoveTimer = 0; + } + else + m_uiMoveTimer -= uiDiff; + } + + if (!m_creature->SelectHostileTarget() || !m_creature->getVictim()) + return; + + if (m_uiBashTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature->getVictim(), SPELL_BASH) == CAST_OK) + m_uiBashTimer = urand(12000, 15000); + } + else + m_uiBashTimer -= uiDiff; + + if (m_uiStrikeTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature->getVictim(), SPELL_STRIKE) == CAST_OK) + m_uiStrikeTimer = urand(4000, 6000); + } + else + m_uiStrikeTimer -= uiDiff; + + if (m_uiCommandAuraTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature, SPELL_AURA_OF_COMMAND) == CAST_OK) + m_uiCommandAuraTimer = urand(30000, 45000); + } + else + m_uiCommandAuraTimer -= uiDiff; + + DoMeleeAttackIfReady(); + } +}; + +CreatureAI* GetAI_npc_general_andorov(Creature* pCreature) +{ + return new npc_general_andorovAI(pCreature); +} + +bool GossipHello_npc_general_andorov(Player* pPlayer, Creature* pCreature) +{ + if (instance_ruins_of_ahnqiraj* pInstance = (instance_ruins_of_ahnqiraj*)pCreature->GetInstanceData()) + { + if (pInstance->GetData(TYPE_RAJAXX) == IN_PROGRESS) + return true; + + if (pInstance->GetData(TYPE_RAJAXX) == NOT_STARTED || pInstance->GetData(TYPE_RAJAXX) == FAIL) + pPlayer->ADD_GOSSIP_ITEM_ID(GOSSIP_ICON_CHAT, GOSSIP_ITEM_START, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 1); + + pPlayer->ADD_GOSSIP_ITEM_ID(GOSSIP_ICON_VENDOR, GOSSIP_ITEM_TRADE, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_TRADE); + pPlayer->SEND_GOSSIP_MENU(GOSSIP_TEXT_ID_INTRO, pCreature->GetObjectGuid()); + } + + return true; +} + +bool GossipSelect_npc_general_andorov(Player* pPlayer, Creature* pCreature, uint32 /*uiSender*/, uint32 uiAction) +{ + if (uiAction == GOSSIP_ACTION_INFO_DEF + 1) + { + if (npc_general_andorovAI* pAndorovAI = dynamic_cast(pCreature->AI())) + pAndorovAI->DoMoveToEventLocation(); + + pPlayer->CLOSE_GOSSIP_MENU(); + } + + if (uiAction == GOSSIP_ACTION_TRADE) + pPlayer->SEND_VENDORLIST(pCreature->GetObjectGuid()); + + return true; +} + +struct npc_kaldorei_eliteAI : public ScriptedAI +{ + npc_kaldorei_eliteAI(Creature* pCreature) : ScriptedAI(pCreature) + { + m_pInstance = (ScriptedInstance*)pCreature->GetInstanceData(); + Reset(); + } + + ScriptedInstance* m_pInstance; + + uint32 m_uiCleaveTimer; + uint32 m_uiStrikeTimer; + + void Reset() override + { + m_uiCleaveTimer = urand(2000, 4000); + m_uiStrikeTimer = urand(8000, 11000); + } + + void EnterEvadeMode() override + { + if (!m_pInstance) + return; + + m_creature->RemoveAllAurasOnEvade(); + m_creature->DeleteThreatList(); + m_creature->CombatStop(true); + + // reset only to the last position + if (m_creature->IsAlive()) + { + if (Creature* pAndorov = m_pInstance->GetSingleCreatureFromStorage(NPC_GENERAL_ANDOROV)) + { + if (pAndorov->IsAlive()) + m_creature->GetMotionMaster()->MoveFollow(pAndorov, m_creature->GetDistance(pAndorov), m_creature->GetAngle(pAndorov)); + } + } + + m_creature->SetLootRecipient(NULL); + + Reset(); + } + + void UpdateAI(const uint32 uiDiff) override + { + if (!m_creature->SelectHostileTarget() || !m_creature->getVictim()) + return; + + if (m_uiCleaveTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature->getVictim(), SPELL_CLEAVE) == CAST_OK) + m_uiCleaveTimer = urand(5000, 7000); + } + else + m_uiCleaveTimer -= uiDiff; + + if (m_uiStrikeTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature->getVictim(), SPELL_MORTAL_STRIKE) == CAST_OK) + m_uiStrikeTimer = urand(9000, 13000); + } + else + m_uiStrikeTimer -= uiDiff; + + DoMeleeAttackIfReady(); + } +}; + +CreatureAI* GetAI_npc_kaldorei_elite(Creature* pCreature) +{ + return new npc_kaldorei_eliteAI(pCreature); +} + +void AddSC_boss_rajaxx() +{ + Script* pNewScript; + + pNewScript = new Script; + pNewScript->Name = "npc_general_andorov"; + pNewScript->GetAI = &GetAI_npc_general_andorov; + pNewScript->pGossipHello = &GossipHello_npc_general_andorov; + pNewScript->pGossipSelect = &GossipSelect_npc_general_andorov; + pNewScript->RegisterSelf(); + + pNewScript = new Script; + pNewScript->Name = "npc_kaldorei_elite"; + pNewScript->GetAI = &GetAI_npc_kaldorei_elite; + pNewScript->RegisterSelf(); +} diff --git a/src/modules/SD2/scripts/kalimdor/ruins_of_ahnqiraj/instance_ruins_of_ahnqiraj.cpp b/src/modules/SD2/scripts/kalimdor/ruins_of_ahnqiraj/instance_ruins_of_ahnqiraj.cpp new file mode 100644 index 000000000..c1357a2bf --- /dev/null +++ b/src/modules/SD2/scripts/kalimdor/ruins_of_ahnqiraj/instance_ruins_of_ahnqiraj.cpp @@ -0,0 +1,354 @@ +/* Copyright (C) 2006 - 2013 ScriptDev2 + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along 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: 80 +SDComment: It's not clear if the Rajaxx event should reset if Andorov dies, or party wipes. +SDCategory: Ruins of Ahn'Qiraj +EndScriptData */ + +#include "precompiled.h" +#include "ruins_of_ahnqiraj.h" + +instance_ruins_of_ahnqiraj::instance_ruins_of_ahnqiraj(Map* pMap) : ScriptedInstance(pMap), + m_uiArmyDelayTimer(0), + m_uiCurrentArmyWave(0) +{ + Initialize(); +} + +void instance_ruins_of_ahnqiraj::Initialize() +{ + memset(&m_auiEncounter, 0, sizeof(m_auiEncounter)); +} + +void instance_ruins_of_ahnqiraj::OnPlayerEnter(Player* /*pPlayer*/) +{ + // Spawn andorov if necessary + if (m_auiEncounter[TYPE_KURINNAXX] == DONE) + DoSapwnAndorovIfCan(); +} + +void instance_ruins_of_ahnqiraj::OnCreatureCreate(Creature* pCreature) +{ + switch (pCreature->GetEntry()) + { + case NPC_OSSIRIAN_TRIGGER: + // Only store static spawned + if (pCreature->IsTemporarySummon()) + break; + case NPC_BURU: + case NPC_OSSIRIAN: + case NPC_RAJAXX: + case NPC_GENERAL_ANDOROV: + case NPC_COLONEL_ZERRAN: + case NPC_MAJOR_PAKKON: + case NPC_MAJOR_YEGGETH: + case NPC_CAPTAIN_XURREM: + case NPC_CAPTAIN_DRENN: + case NPC_CAPTAIN_TUUBID: + case NPC_CAPTAIN_QEEZ: + m_mNpcEntryGuidStore[pCreature->GetEntry()] = pCreature->GetObjectGuid(); + break; + case NPC_KALDOREI_ELITE: + m_lKaldoreiGuidList.push_back(pCreature->GetObjectGuid()); + return; + } +} + +void instance_ruins_of_ahnqiraj::OnCreatureEnterCombat(Creature* pCreature) +{ + switch (pCreature->GetEntry()) + { + case NPC_KURINNAXX: SetData(TYPE_KURINNAXX, IN_PROGRESS); break; + case NPC_MOAM: SetData(TYPE_MOAM, IN_PROGRESS); break; + case NPC_BURU: SetData(TYPE_BURU, IN_PROGRESS); break; + case NPC_AYAMISS: SetData(TYPE_AYAMISS, IN_PROGRESS); break; + case NPC_OSSIRIAN: SetData(TYPE_OSSIRIAN, IN_PROGRESS); break; + } +} + +void instance_ruins_of_ahnqiraj::OnCreatureEvade(Creature* pCreature) +{ + switch (pCreature->GetEntry()) + { + case NPC_KURINNAXX: SetData(TYPE_KURINNAXX, FAIL); break; + case NPC_MOAM: SetData(TYPE_MOAM, FAIL); break; + case NPC_BURU: SetData(TYPE_BURU, FAIL); break; + case NPC_AYAMISS: SetData(TYPE_AYAMISS, FAIL); break; + case NPC_OSSIRIAN: SetData(TYPE_OSSIRIAN, FAIL); break; + case NPC_RAJAXX: + // Rajaxx yells on evade + DoScriptText(SAY_DEAGGRO, pCreature); + // no break; + case NPC_COLONEL_ZERRAN: + case NPC_MAJOR_PAKKON: + case NPC_MAJOR_YEGGETH: + case NPC_CAPTAIN_XURREM: + case NPC_CAPTAIN_DRENN: + case NPC_CAPTAIN_TUUBID: + case NPC_CAPTAIN_QEEZ: + SetData(TYPE_RAJAXX, FAIL); + break; + } +} + +void instance_ruins_of_ahnqiraj::OnCreatureDeath(Creature* pCreature) +{ + switch (pCreature->GetEntry()) + { + case NPC_KURINNAXX: SetData(TYPE_KURINNAXX, DONE); break; + case NPC_RAJAXX: SetData(TYPE_RAJAXX, DONE); break; + case NPC_MOAM: SetData(TYPE_MOAM, DONE); break; + case NPC_BURU: SetData(TYPE_BURU, DONE); break; + case NPC_AYAMISS: SetData(TYPE_AYAMISS, DONE); break; + case NPC_OSSIRIAN: SetData(TYPE_OSSIRIAN, DONE); break; + case NPC_COLONEL_ZERRAN: + case NPC_MAJOR_PAKKON: + case NPC_MAJOR_YEGGETH: + case NPC_CAPTAIN_XURREM: + case NPC_CAPTAIN_DRENN: + case NPC_CAPTAIN_TUUBID: + case NPC_CAPTAIN_QEEZ: + case NPC_QIRAJI_WARRIOR: + case NPC_SWARMGUARD_NEEDLER: + { + // If event isn't started by Andorov, return + if (GetData(TYPE_RAJAXX) != IN_PROGRESS) + return; + + // Check if the dead creature belongs to the current wave + if (m_sArmyWavesGuids[m_uiCurrentArmyWave - 1].find(pCreature->GetObjectGuid()) != m_sArmyWavesGuids[m_uiCurrentArmyWave - 1].end()) + { + m_sArmyWavesGuids[m_uiCurrentArmyWave - 1].erase(pCreature->GetObjectGuid()); + + // If all the soldiers from the current wave are dead, then send the next one + if (m_sArmyWavesGuids[m_uiCurrentArmyWave - 1].empty()) + DoSendNextArmyWave(); + } + break; + } + } +} + +void instance_ruins_of_ahnqiraj::SetData(uint32 uiType, uint32 uiData) +{ + switch (uiType) + { + case TYPE_KURINNAXX: + if (uiData == DONE) + { + DoSapwnAndorovIfCan(); + + // Yell after kurinnaxx + DoOrSimulateScriptTextForThisInstance(SAY_OSSIRIAN_INTRO, NPC_OSSIRIAN); + } + m_auiEncounter[uiType] = uiData; + break; + case TYPE_RAJAXX: + m_auiEncounter[uiType] = uiData; + if (uiData == IN_PROGRESS) + DoSortArmyWaves(); + if (uiData == DONE) + { + if (Creature* pAndorov = GetSingleCreatureFromStorage(NPC_GENERAL_ANDOROV)) + { + if (pAndorov->IsAlive()) + pAndorov->SetFlag(UNIT_NPC_FLAGS, UNIT_NPC_FLAG_GOSSIP); + } + } + break; + case TYPE_MOAM: + case TYPE_BURU: + case TYPE_AYAMISS: + case TYPE_OSSIRIAN: + m_auiEncounter[uiType] = uiData; + 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_strInstData = saveStream.str(); + + SaveToDB(); + OUT_SAVE_INST_DATA_COMPLETE; + } +} + +uint32 instance_ruins_of_ahnqiraj::GetData(uint32 uiType) const +{ + if (uiType < MAX_ENCOUNTER) + return m_auiEncounter[uiType]; + + return 0; +} + +void instance_ruins_of_ahnqiraj::DoSapwnAndorovIfCan() +{ + if (GetSingleCreatureFromStorage(NPC_GENERAL_ANDOROV)) + return; + + Player* pPlayer = GetPlayerInMap(); + if (!pPlayer) + return; + + for (uint8 i = 0; i < MAX_HELPERS; ++i) + pPlayer->SummonCreature(aAndorovSpawnLocs[i].m_uiEntry, aAndorovSpawnLocs[i].m_fX, aAndorovSpawnLocs[i].m_fY, aAndorovSpawnLocs[i].m_fZ, aAndorovSpawnLocs[i].m_fO, TEMPSUMMON_DEAD_DESPAWN, 0); +} + +void instance_ruins_of_ahnqiraj::Load(const char* chrIn) +{ + if (!chrIn) + { + 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]; + + for (uint8 i = 0; i < MAX_ENCOUNTER; ++i) + { + if (m_auiEncounter[i] == IN_PROGRESS) + m_auiEncounter[i] = NOT_STARTED; + } + + OUT_LOAD_INST_DATA_COMPLETE; +} + +void instance_ruins_of_ahnqiraj::Update(uint32 uiDiff) +{ + if (GetData(TYPE_RAJAXX) == IN_PROGRESS) + { + if (m_uiArmyDelayTimer) + { + if (m_uiArmyDelayTimer <= uiDiff) + { + DoSendNextArmyWave(); + m_uiArmyDelayTimer = 2 * MINUTE * IN_MILLISECONDS; + } + else + m_uiArmyDelayTimer -= uiDiff; + } + } +} + +void instance_ruins_of_ahnqiraj::DoSortArmyWaves() +{ + std::list lCreatureList; + + // Sort the 7 army waves + // We need to use gridsearcher for this, because coords search is too complicated here + for (uint8 i = 0; i < MAX_ARMY_WAVES; ++i) + { + // Clear all the army waves + m_sArmyWavesGuids[i].clear(); + lCreatureList.clear(); + + if (Creature* pTemp = GetSingleCreatureFromStorage(aArmySortingParameters[i].m_uiEntry)) + { + GetCreatureListWithEntryInGrid(lCreatureList, pTemp, NPC_QIRAJI_WARRIOR, aArmySortingParameters[i].m_fSearchDist); + GetCreatureListWithEntryInGrid(lCreatureList, pTemp, NPC_SWARMGUARD_NEEDLER, aArmySortingParameters[i].m_fSearchDist); + + for (std::list::const_iterator itr = lCreatureList.begin(); itr != lCreatureList.end(); ++itr) + { + if ((*itr)->IsAlive()) + m_sArmyWavesGuids[i].insert((*itr)->GetObjectGuid()); + } + + if (pTemp->IsAlive()) + m_sArmyWavesGuids[i].insert(pTemp->GetObjectGuid()); + } + } + + // send the first wave + m_uiCurrentArmyWave = 0; + DoSendNextArmyWave(); +} + +void instance_ruins_of_ahnqiraj::DoSendNextArmyWave() +{ + // The next army wave is sent into battle after 2 min or after the previous wave is finished + if (GetData(TYPE_RAJAXX) != IN_PROGRESS) + return; + + // The last wave is General Rajaxx itself + if (m_uiCurrentArmyWave == MAX_ARMY_WAVES) + { + if (Creature* pRajaxx = GetSingleCreatureFromStorage(NPC_RAJAXX)) + { + DoScriptText(SAY_INTRO, pRajaxx); + pRajaxx->SetInCombatWithZone(); + } + } + else + { + // Increase the wave id if some are already dead + while (m_sArmyWavesGuids[m_uiCurrentArmyWave].empty()) + ++m_uiCurrentArmyWave; + + float fX, fY, fZ; + for (GuidSet::const_iterator itr = m_sArmyWavesGuids[m_uiCurrentArmyWave].begin(); itr != m_sArmyWavesGuids[m_uiCurrentArmyWave].end(); ++itr) + { + if (Creature* pTemp = instance->GetCreature(*itr)) + { + if (!pTemp->IsAlive()) + continue; + + pTemp->SetWalk(false); + pTemp->GetRandomPoint(aAndorovMoveLocs[4].m_fX, aAndorovMoveLocs[4].m_fY, aAndorovMoveLocs[4].m_fZ, 10.0f, fX, fY, fZ); + pTemp->GetMotionMaster()->MovePoint(0, fX, fY, fZ); + } + } + + // Yell on each wave (except the first 2) + if (aArmySortingParameters[m_uiCurrentArmyWave].m_uiYellEntry) + { + if (Creature* pRajaxx = GetSingleCreatureFromStorage(NPC_RAJAXX)) + DoScriptText(aArmySortingParameters[m_uiCurrentArmyWave].m_uiYellEntry, pRajaxx); + } + } + + // on wowwiki it states that there were 3 min between the waves, but this was reduced in later patches + m_uiArmyDelayTimer = 2 * MINUTE * IN_MILLISECONDS; + ++m_uiCurrentArmyWave; +} + +InstanceData* GetInstanceData_instance_ruins_of_ahnqiraj(Map* pMap) +{ + return new instance_ruins_of_ahnqiraj(pMap); +} + +void AddSC_instance_ruins_of_ahnqiraj() +{ + Script* pNewScript; + + pNewScript = new Script; + pNewScript->Name = "instance_ruins_of_ahnqiraj"; + pNewScript->GetInstanceData = &GetInstanceData_instance_ruins_of_ahnqiraj; + pNewScript->RegisterSelf(); +} diff --git a/src/modules/SD2/scripts/kalimdor/ruins_of_ahnqiraj/ruins_of_ahnqiraj.cpp b/src/modules/SD2/scripts/kalimdor/ruins_of_ahnqiraj/ruins_of_ahnqiraj.cpp new file mode 100644 index 000000000..ae914840f --- /dev/null +++ b/src/modules/SD2/scripts/kalimdor/ruins_of_ahnqiraj/ruins_of_ahnqiraj.cpp @@ -0,0 +1,171 @@ +/* Copyright (C) 2006 - 2013 ScriptDev2 + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +/* ScriptData +SDName: Ruins of Ahn'Qiraj +SD%Complete: 40 +SDComment: +SDCategory: Ruins of Ahn'Qiraj +EndScriptData */ + +/* ContentData +mob_anubisath_guardian +EndContentData */ + +#include "precompiled.h" + +/*###### +## mob_anubisath_guardian +######*/ +enum +{ + SPELL_METEOR = 24340, + SPELL_PLAGUE = 22997, + SPELL_SHADOW_STORM = 26546, + SPELL_THUNDER_CLAP = 26554, + SPELL_REFLECT_ARFR = 13022, + SPELL_REFLECT_FSSH = 19595, + SPELL_ENRAGE = 8599, + SPELL_EXPLODE = 25698, + + SPELL_SUMMON_ANUB_SWARMGUARD = 17430, + SPELL_SUMMON_ANUB_WARRIOR = 17431, + + EMOTE_FRENZY = -1000002, + + NPC_ANUB_WARRIOR = 15537, + NPC_ANUB_SWARM = 15538 +}; + +struct mob_anubisath_guardianAI : public ScriptedAI +{ + mob_anubisath_guardianAI(Creature* pCreature) : ScriptedAI(pCreature) {Reset();} + + uint32 m_uiSpell1; + uint32 m_uiSpell2; + uint32 m_uiSpell3; + uint32 m_uiSpell4; + uint32 m_uiSpell5; + + uint32 m_uiSpell1Timer; + uint32 m_uiSpell2Timer; + uint32 m_uiSpell5Timer; + + uint8 m_uiSummonCount; + + bool m_bIsEnraged; + + void Reset() override + { + m_uiSpell1 = urand(0, 1) ? SPELL_METEOR : SPELL_PLAGUE; + m_uiSpell2 = urand(0, 1) ? SPELL_SHADOW_STORM : SPELL_THUNDER_CLAP; + m_uiSpell3 = urand(0, 1) ? SPELL_REFLECT_ARFR : SPELL_REFLECT_FSSH; + m_uiSpell4 = urand(0, 1) ? SPELL_ENRAGE : SPELL_EXPLODE; + m_uiSpell5 = urand(0, 1) ? SPELL_SUMMON_ANUB_SWARMGUARD : SPELL_SUMMON_ANUB_WARRIOR; + + m_uiSpell1Timer = 10000; + m_uiSpell2Timer = 20000; + m_uiSpell5Timer = 10000; + m_uiSummonCount = 0; + m_bIsEnraged = false; + } + + void Aggro(Unit* /*pWho*/) override + { + // spell reflection + DoCastSpellIfCan(m_creature, m_uiSpell3); + } + + void JustSummoned(Creature* pSummoned) override + { + pSummoned->AI()->AttackStart(m_creature->getVictim()); + ++m_uiSummonCount; + } + + void SummonedCreatureDespawn(Creature* /*pDespawned*/) override + { + --m_uiSummonCount; + } + + void DamageTaken(Unit* /*pDoneBy*/, uint32& /*uiDamage*/) override + { + // when we reach 10% of HP explode or enrage + if (!m_bIsEnraged && m_creature->GetHealthPercent() < 10.0f) + { + if (m_uiSpell4 == SPELL_ENRAGE) + { + DoScriptText(EMOTE_FRENZY, m_creature); + DoCastSpellIfCan(m_creature, m_uiSpell4); + m_bIsEnraged = true; + } + else + DoCastSpellIfCan(m_creature->getVictim(), m_uiSpell4); + } + } + + void UpdateAI(const uint32 uiDiff) override + { + if (!m_creature->SelectHostileTarget() || !m_creature->getVictim()) + return; + + // Meteor or Plague + if (m_uiSpell1Timer < uiDiff) + { + DoCastSpellIfCan(m_creature->getVictim(), m_uiSpell1); + m_uiSpell1Timer = 15000; + } + else + m_uiSpell1Timer -= uiDiff; + + // Shadow Storm or Thunder Clap + if (m_uiSpell2Timer < uiDiff) + { + DoCastSpellIfCan(m_creature->getVictim(), m_uiSpell2); + m_uiSpell2Timer = 15000; + } + else + m_uiSpell2Timer -= uiDiff; + + // summon Anubisath Swarmguard or Anubisath Warrior + if (m_uiSpell5Timer < uiDiff) + { + // change for summon spell + if (m_uiSummonCount < 4) + DoCastSpellIfCan(m_creature->getVictim(), m_uiSpell5); + + m_uiSpell5Timer = 15000; + } + else + m_uiSpell5Timer -= uiDiff; + + DoMeleeAttackIfReady(); + } +}; + +CreatureAI* GetAI_mob_anubisath_guardian(Creature* pCreature) +{ + return new mob_anubisath_guardianAI(pCreature); +} + +void AddSC_ruins_of_ahnqiraj() +{ + Script* pNewScript; + + pNewScript = new Script; + pNewScript->Name = "mob_anubisath_guardian"; + pNewScript->GetAI = &GetAI_mob_anubisath_guardian; + pNewScript->RegisterSelf(); +} diff --git a/src/modules/SD2/scripts/kalimdor/ruins_of_ahnqiraj/ruins_of_ahnqiraj.h b/src/modules/SD2/scripts/kalimdor/ruins_of_ahnqiraj/ruins_of_ahnqiraj.h new file mode 100644 index 000000000..d42fb35c6 --- /dev/null +++ b/src/modules/SD2/scripts/kalimdor/ruins_of_ahnqiraj/ruins_of_ahnqiraj.h @@ -0,0 +1,141 @@ +/* Copyright (C) 2006 - 2013 ScriptDev2 + * This program is free software licensed under GPL version 2 + * Please see the included DOCS/LICENSE.TXT for more information */ + +#ifndef DEF_RUINS_OF_AHNQIRAJ_H +#define DEF_RUINS_OF_AHNQIRAJ_H + +enum +{ + MAX_ENCOUNTER = 6, + MAX_HELPERS = 5, + + TYPE_KURINNAXX = 0, + TYPE_RAJAXX = 1, + TYPE_MOAM = 2, + TYPE_BURU = 3, + TYPE_AYAMISS = 4, + TYPE_OSSIRIAN = 5, + + NPC_KURINNAXX = 15348, + NPC_MOAM = 15340, + NPC_BURU = 15370, + NPC_AYAMISS = 15369, + NPC_OSSIRIAN = 15339, + NPC_GENERAL_ANDOROV = 15471, // The general and the kaldorei are escorted for the rajaxx encounter + NPC_KALDOREI_ELITE = 15473, + NPC_RAJAXX = 15341, // All of the following are used in the rajaxx encounter + NPC_COLONEL_ZERRAN = 15385, + NPC_MAJOR_PAKKON = 15388, + NPC_MAJOR_YEGGETH = 15386, + NPC_CAPTAIN_XURREM = 15390, + NPC_CAPTAIN_DRENN = 15389, + NPC_CAPTAIN_TUUBID = 15392, + NPC_CAPTAIN_QEEZ = 15391, + NPC_QIRAJI_WARRIOR = 15387, + NPC_SWARMGUARD_NEEDLER = 15344, + + MAX_ARMY_WAVES = 7, + + GO_OSSIRIAN_CRYSTAL = 180619, // Used in the ossirian encounter + NPC_OSSIRIAN_TRIGGER = 15590, // Triggers ossirian weakness + + SAY_OSSIRIAN_INTRO = -1509022, // Yelled after Kurinnax dies + + // Rajaxx yells + SAY_WAVE3 = -1509005, + SAY_WAVE4 = -1509006, + SAY_WAVE5 = -1509007, + SAY_WAVE6 = -1509008, + SAY_WAVE7 = -1509009, + SAY_INTRO = -1509010, + SAY_DEAGGRO = -1509015, // on Rajaxx evade + SAY_COMPLETE_QUEST = -1509017, // Yell when realm complete quest 8743 for world event +}; + +struct SpawnLocation +{ + uint32 m_uiEntry; + float m_fX, m_fY, m_fZ, m_fO; +}; + +// Spawn coords for Andorov and his team +static const SpawnLocation aAndorovSpawnLocs[MAX_HELPERS] = +{ + {NPC_GENERAL_ANDOROV, -8660.4f, 1510.29f, 32.449f, 2.2184f}, + {NPC_KALDOREI_ELITE, -8655.84f, 1509.78f, 32.462f, 2.33341f}, + {NPC_KALDOREI_ELITE, -8657.39f, 1506.28f, 32.418f, 2.33346f}, + {NPC_KALDOREI_ELITE, -8660.96f, 1504.9f, 32.1567f, 2.33306f}, + {NPC_KALDOREI_ELITE, -8664.45f, 1506.44f, 32.0944f, 2.33302f} +}; + +// Movement locations for Andorov +static const SpawnLocation aAndorovMoveLocs[] = +{ + {0, -8701.51f, 1561.80f, 32.092f}, + {0, -8718.66f, 1577.69f, 21.612f}, + {0, -8876.97f, 1651.96f, 21.57f, 5.52f}, + {0, -8882.15f, 1602.77f, 21.386f}, + {0, -8940.45f, 1550.69f, 21.616f}, +}; + +struct SortingParameters +{ + uint32 m_uiEntry; + int32 m_uiYellEntry; + float m_fSearchDist; +}; + +static const SortingParameters aArmySortingParameters[MAX_ARMY_WAVES] = +{ + {NPC_CAPTAIN_QEEZ, 0, 20.0f}, + {NPC_CAPTAIN_TUUBID, 0, 22.0f}, + {NPC_CAPTAIN_DRENN, SAY_WAVE3, 22.0f}, + {NPC_CAPTAIN_XURREM, SAY_WAVE4, 22.0f}, + {NPC_MAJOR_YEGGETH, SAY_WAVE5, 20.0f}, + {NPC_MAJOR_PAKKON, SAY_WAVE6, 21.0f}, + {NPC_COLONEL_ZERRAN, SAY_WAVE7, 17.0f}, +}; + +class instance_ruins_of_ahnqiraj : public ScriptedInstance +{ + public: + instance_ruins_of_ahnqiraj(Map* pMap); + ~instance_ruins_of_ahnqiraj() {} + + void Initialize() override; + + // bool IsEncounterInProgress() const override; // not active in AQ20 + + void OnCreatureCreate(Creature* pCreature) override; + void OnPlayerEnter(Player* pPlayer) override; + + void OnCreatureEnterCombat(Creature* pCreature) override; + void OnCreatureEvade(Creature* pCreature); + void OnCreatureDeath(Creature* pCreature) override; + + void SetData(uint32 uiType, uint32 uiData) override; + uint32 GetData(uint32 uiType) const override; + + void GetKaldoreiGuidList(GuidList& lList) { lList = m_lKaldoreiGuidList; } + + void Update(uint32 uiDiff) override; + + const char* Save() const override { return m_strInstData.c_str(); } + void Load(const char* chrIn) override; + + private: + void DoSapwnAndorovIfCan(); + void DoSortArmyWaves(); + void DoSendNextArmyWave(); + + uint32 m_auiEncounter[MAX_ENCOUNTER]; + std::string m_strInstData; + + GuidList m_lKaldoreiGuidList; + GuidSet m_sArmyWavesGuids[MAX_ARMY_WAVES]; + + uint32 m_uiArmyDelayTimer; + uint8 m_uiCurrentArmyWave; +}; +#endif diff --git a/src/modules/SD2/scripts/kalimdor/silithus.cpp b/src/modules/SD2/scripts/kalimdor/silithus.cpp new file mode 100644 index 000000000..def6da138 --- /dev/null +++ b/src/modules/SD2/scripts/kalimdor/silithus.cpp @@ -0,0 +1,684 @@ +/* Copyright (C) 2006 - 2013 ScriptDev2 + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along 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: 8519. +SDCategory: Silithus +EndScriptData */ + +/* ContentData +npc_anachronos_the_ancient +EndContentData */ + +#include "precompiled.h" + +/*### +## npc_anachronos_the_ancient +###*/ + +enum +{ + // Dragons + NPC_MERITHRA_OF_THE_DREAM = 15378, + NPC_CAELESTRASZ = 15379, + NPC_ARYGOS = 15380, + NPC_ANACHRONOS_THE_ANCIENT = 15381, + NPC_ANACHRONOS_QUEST_TRIGGER = 15454, // marks some movement for the dragons + + // Elfs + NPC_FANDRAL_STAGHELM = 15382, + NPC_KALDOREI_INFANTRY = 15423, + + // Qiraji warriors + NPC_QIRAJI_WASP = 15414, + NPC_QIRAJI_DRONE = 15421, + NPC_QIRAJI_TANK = 15422, + NPC_ANUBISATH_CONQUEROR = 15424, + + QUEST_A_PAWN_ON_THE_ETERNAL_BOARD = 8519, + + // Yells -> in chronological order + SAY_ANACHRONOS_INTRO_1 = -1000740, + SAY_FANDRAL_INTRO_2 = -1000741, + SAY_MERITHRA_INTRO_3 = -1000742, + EMOTE_ARYGOS_NOD = -1000743, + SAY_CAELESTRASZ_INTRO_4 = -1000744, + EMOTE_MERITHRA_GLANCE = -1000745, + SAY_MERITHRA_INTRO_5 = -1000746, + + SAY_MERITHRA_ATTACK_1 = -1000747, + SAY_ARYGOS_ATTACK_2 = -1000748, + SAY_ARYGOS_ATTACK_3 = -1000749, + SAY_CAELESTRASZ_ATTACK_4 = -1000750, + SAY_CAELESTRASZ_ATTACK_5 = -1000751, + + SAY_ANACHRONOS_SEAL_1 = -1000752, + SAY_FANDRAL_SEAL_2 = -1000753, + SAY_ANACHRONOS_SEAL_3 = -1000754, + SAY_ANACHRONOS_SEAL_4 = -1000755, + SAY_ANACHRONOS_SEAL_5 = -1000756, + SAY_FANDRAL_SEAL_6 = -1000757, + + EMOTE_FANDRAL_EXHAUSTED = -1000758, + SAY_ANACHRONOS_EPILOGUE_1 = -1000759, + SAY_ANACHRONOS_EPILOGUE_2 = -1000760, + SAY_ANACHRONOS_EPILOGUE_3 = -1000761, + EMOTE_ANACHRONOS_SCEPTER = -1000762, + SAY_FANDRAL_EPILOGUE_4 = -1000763, + SAY_FANDRAL_EPILOGUE_5 = -1000764, + EMOTE_FANDRAL_SHATTER = -1000765, + SAY_ANACHRONOS_EPILOGUE_6 = -1000766, + SAY_FANDRAL_EPILOGUE_7 = -1000767, + EMOTE_ANACHRONOS_DISPPOINTED = -1000768, + EMOTE_ANACHRONOS_PICKUP = -1000769, + SAY_ANACHRONOS_EPILOGUE_8 = -1000770, + + // The transform spell for Anachronos was removed from DBC + DISPLAY_ID_BRONZE_DRAGON = 15500, + + // Spells + SPELL_GREEN_DRAGON_TRANSFORM = 25105, + SPELL_RED_DRAGON_TRANSFORM = 25106, + SPELL_BLUE_DRAGON_TRANSFORM = 25107, + // SPELL_BRONZE_DRAGON_TRANSFORM = 25108, // Spell was removed - exists only before 2.0.1 + + SPELL_MERITHRA_WAKE = 25145, // should trigger 25172 on targets + SPELL_ARYGOS_VENGEANCE = 25149, + SPELL_CAELESTRASZ_MOLTEN_RAIN = 25150, + + SPELL_TIME_STOP = 25158, // Anachronos stops the battle - should trigger 25171 + SPELL_GLYPH_OF_WARDING = 25166, // Sends event 9427 - should activate Go 176148 + SPELL_PRISMATIC_BARRIER = 25159, // Sends event 9425 - should activate Go 176146 + SPELL_CALL_ANCIENTS = 25167, // Sends event 9426 - should activate Go 176147 + SPELL_SHATTER_HAMMER = 25182, // Breakes the scepter - needs DB coords + + POINT_ID_DRAGON_ATTACK = 1, + POINT_ID_EXIT = 2, + POINT_ID_GATE = 3, + POINT_ID_SCEPTER_1 = 4, + POINT_ID_SCEPTER_2 = 5, + POINT_ID_EPILOGUE = 6, + DATA_HANDLE_SCEPTER = 7, // dummy members - used in dialogue helper + DATA_MERITHRA_ATTACK = 8, + DATA_CAELASTRASZ_ATTACK = 9, + + MAX_DRAGONS = 4, + MAX_CONQUERORS = 3, + MAX_QIRAJI = 6, + MAX_KALDOREI = 20, +}; + +/* Known event issues: + * The Kaldorei and Qiraji soldiers don't have the correct flags and factions in DB + * The Ahn'Qiraj gate gameobjects are missing from DB + * The spells used by the dragons upon the Qiraji need core support + * The script events sent by the spells which close the AQ gate needs DB support + * Can't make Fandral equip the Scepter when Anachronos handles it to him + */ + +static const DialogueEntry aEventDialogue[] = +{ + {NPC_ANACHRONOS_THE_ANCIENT, 0, 2000}, // summon the dragons + {SAY_ANACHRONOS_INTRO_1, NPC_ANACHRONOS_THE_ANCIENT, 3000}, + {EMOTE_ONESHOT_SHOUT, NPC_ANACHRONOS_THE_ANCIENT, 3000}, // make Anachronos shout and summon the warriors + {SAY_FANDRAL_INTRO_2, NPC_FANDRAL_STAGHELM, 6000}, + {EMOTE_MERITHRA_GLANCE, NPC_MERITHRA_OF_THE_DREAM, 2000}, + {SAY_MERITHRA_INTRO_3, NPC_MERITHRA_OF_THE_DREAM, 3000}, + {EMOTE_ARYGOS_NOD, NPC_ARYGOS, 4000}, + {SAY_CAELESTRASZ_INTRO_4, NPC_CAELESTRASZ, 9000}, + {SAY_MERITHRA_INTRO_5, NPC_MERITHRA_OF_THE_DREAM, 5000}, + {NPC_ANACHRONOS_QUEST_TRIGGER, 0, 0}, // send Merithra to fight + {DATA_MERITHRA_ATTACK, 0, 5000}, // make Merithra wait + {SAY_MERITHRA_ATTACK_1, NPC_MERITHRA_OF_THE_DREAM, 1000}, + {SPELL_GREEN_DRAGON_TRANSFORM, 0, 6000}, + {SAY_ARYGOS_ATTACK_2, NPC_ARYGOS, 5000}, + {NPC_ARYGOS, 0, 1000}, // send Arygos to fight + {POINT_ID_EXIT, 0, 4000}, // make Merithra exit + {SAY_ARYGOS_ATTACK_3, NPC_ARYGOS, 4000}, + {SPELL_BLUE_DRAGON_TRANSFORM, 0, 5000}, + {SPELL_ARYGOS_VENGEANCE, 0, 7000}, + {POINT_ID_DRAGON_ATTACK, 0, 1000}, // make Arygos exit + {SAY_CAELESTRASZ_ATTACK_4, NPC_CAELESTRASZ, 5000}, + {NPC_CAELESTRASZ, 0, 0}, // send Caelestrasz to fight + {DATA_CAELASTRASZ_ATTACK, 0, 3000}, // make Caelestrasz wait + {SAY_CAELESTRASZ_ATTACK_5, NPC_CAELESTRASZ, 5000}, + {SPELL_RED_DRAGON_TRANSFORM, 0, 4000}, // transform Caelestrasz + {SPELL_CAELESTRASZ_MOLTEN_RAIN, 0, 6000}, // Caelestrasz casts molten rain + {SAY_ANACHRONOS_SEAL_1, NPC_ANACHRONOS_THE_ANCIENT, 5000}, + {SAY_FANDRAL_SEAL_2, NPC_FANDRAL_STAGHELM, 3000}, + {SAY_ANACHRONOS_SEAL_3, NPC_ANACHRONOS_THE_ANCIENT, 1000}, + {POINT_ID_GATE, 0, 1000}, // send Anachronos to the gate + {NPC_FANDRAL_STAGHELM, 0, 0}, // send Fandral to the gate + {SPELL_TIME_STOP, 0, 7000}, // Anachronos casts Time Stop + {SPELL_PRISMATIC_BARRIER, 0, 15000}, + {SPELL_GLYPH_OF_WARDING, 0, 4000}, + {SAY_ANACHRONOS_SEAL_5, NPC_ANACHRONOS_THE_ANCIENT, 3000}, + {SAY_FANDRAL_SEAL_6, NPC_FANDRAL_STAGHELM, 9000}, + {EMOTE_FANDRAL_EXHAUSTED, NPC_FANDRAL_STAGHELM, 1000}, + {SAY_ANACHRONOS_EPILOGUE_1, NPC_ANACHRONOS_THE_ANCIENT, 6000}, + {SAY_ANACHRONOS_EPILOGUE_2, NPC_ANACHRONOS_THE_ANCIENT, 5000}, + {SAY_ANACHRONOS_EPILOGUE_3, NPC_ANACHRONOS_THE_ANCIENT, 15000}, + {DATA_HANDLE_SCEPTER, NPC_ANACHRONOS_THE_ANCIENT, 3000}, // handle the scepter + {SAY_FANDRAL_EPILOGUE_4, NPC_FANDRAL_STAGHELM, 3000}, + {POINT_ID_SCEPTER_2, 0, 4000}, // make Anachronos stand + {SAY_FANDRAL_EPILOGUE_5, NPC_FANDRAL_STAGHELM, 12000}, + {EMOTE_FANDRAL_SHATTER, NPC_FANDRAL_STAGHELM, 3000}, + {SAY_ANACHRONOS_EPILOGUE_6, NPC_ANACHRONOS_THE_ANCIENT, 0}, + {SAY_FANDRAL_EPILOGUE_7, NPC_FANDRAL_STAGHELM, 8000}, + {POINT_ID_EPILOGUE, 0, 4000}, // move Fandral to Anachronos + {EMOTE_ANACHRONOS_DISPPOINTED, NPC_ANACHRONOS_THE_ANCIENT, 1000}, + {POINT_ID_SCEPTER_1, 0, 0}, // make Anachronos pick the pieces + {0, 0, 0}, +}; + +struct EventLocations +{ + float m_fX, m_fY, m_fZ, m_fO; + uint32 m_uiEntry; +}; + +static EventLocations aEternalBoardNPCs[MAX_DRAGONS] = +{ + { -8029.301f, 1534.612f, 2.609f, 3.121f, NPC_FANDRAL_STAGHELM}, + { -8034.227f, 1536.580f, 2.609f, 6.161f, NPC_ARYGOS}, + { -8031.935f, 1532.658f, 2.609f, 1.012f, NPC_CAELESTRASZ}, + { -8034.106f, 1534.224f, 2.609f, 0.290f, NPC_MERITHRA_OF_THE_DREAM}, +}; + +static EventLocations aEternalBoardMovement[] = +{ + { -8159.951f, 1525.241f, 74.994f}, // 0 Flight position for dragons + { -8106.238f, 1525.948f, 2.639f}, // 1 Anachronos gate location + { -8103.861f, 1525.923f, 2.677f}, // 2 Fandral gate location + { -8107.387f, 1523.641f, 2.609f}, // 3 Shattered scepter + { -8100.921f, 1527.740f, 2.871f}, // 4 Fandral epilogue location + { -8115.270f, 1515.926f, 3.305f}, // 5 Anachronos gather broken scepter 1 + { -8116.879f, 1530.615f, 3.762f}, // 6 Anachronos gather broken scepter 2 + { -7997.790f, 1548.664f, 3.738f}, // 7 Fandral exit location + { -8061.933f, 1496.196f, 2.556f}, // 8 Anachronos launch location + { -8008.705f, 1446.063f, 44.104f}, // 9 Anachronos flight location + { -8085.748f, 1521.484f, 2.624f} // 10 Anchor point for the army summoning +}; + +struct npc_anachronos_the_ancientAI : public ScriptedAI, private DialogueHelper +{ + npc_anachronos_the_ancientAI(Creature* pCreature) : ScriptedAI(pCreature), + DialogueHelper(aEventDialogue) + { + Reset(); + } + + uint32 m_uiEventTimer; + + uint8 m_uiEventStage; + + ObjectGuid m_fandralGuid; + ObjectGuid m_merithraGuid; + ObjectGuid m_CaelestraszGuid; + ObjectGuid m_arygosGuid; + ObjectGuid m_playerGuid; + ObjectGuid m_triggerGuid; + + GuidList m_lQirajiWarriorsList; + + void Reset() override + { + // We summon the rest of the dragons on timer + m_uiEventTimer = 100; + m_uiEventStage = 0; + } + + void JustDidDialogueStep(int32 iEntry) override + { + switch (iEntry) + { + case NPC_ANACHRONOS_THE_ANCIENT: + // Call the other dragons + DoSummonDragons(); + break; + case EMOTE_ONESHOT_SHOUT: + // Summon warriors + DoSummonWarriors(); + m_creature->HandleEmote(EMOTE_ONESHOT_SHOUT); + break; + case SAY_FANDRAL_INTRO_2: + if (Creature* pFandral = m_creature->GetMap()->GetCreature(m_fandralGuid)) + pFandral->SetFacingToObject(m_creature); + break; + case EMOTE_MERITHRA_GLANCE: + if (Creature* pMerithra = m_creature->GetMap()->GetCreature(m_merithraGuid)) + { + if (Creature* pFandral = m_creature->GetMap()->GetCreature(m_fandralGuid)) + pFandral->SetFacingToObject(pMerithra); + } + break; + case NPC_ANACHRONOS_QUEST_TRIGGER: + // Move Merithra to attack + if (Creature* pTrigger = GetClosestCreatureWithEntry(m_creature, NPC_ANACHRONOS_QUEST_TRIGGER, 35.0f)) + { + m_triggerGuid = pTrigger->GetObjectGuid(); + if (Creature* pMerithra = m_creature->GetMap()->GetCreature(m_merithraGuid)) + { + pMerithra->SetWalk(false); + pMerithra->GetMotionMaster()->MovePoint(POINT_ID_DRAGON_ATTACK, pTrigger->GetPositionX(), pTrigger->GetPositionY(), pTrigger->GetPositionZ()); + } + } + break; + case SPELL_GREEN_DRAGON_TRANSFORM: + if (Creature* pMerithra = m_creature->GetMap()->GetCreature(m_merithraGuid)) + pMerithra->CastSpell(pMerithra, SPELL_GREEN_DRAGON_TRANSFORM, false); + break; + case SAY_ARYGOS_ATTACK_2: + if (Creature* pMerithra = m_creature->GetMap()->GetCreature(m_merithraGuid)) + pMerithra->CastSpell(pMerithra, SPELL_MERITHRA_WAKE, false); + break; + case NPC_ARYGOS: + // Move Arygos to attack + if (Creature* pTrigger = m_creature->GetMap()->GetCreature(m_triggerGuid)) + { + if (Creature* pArygos = m_creature->GetMap()->GetCreature(m_arygosGuid)) + { + pArygos->SetWalk(false); + pArygos->GetMotionMaster()->MovePoint(POINT_ID_DRAGON_ATTACK, pTrigger->GetPositionX(), pTrigger->GetPositionY(), pTrigger->GetPositionZ()); + } + } + break; + case POINT_ID_EXIT: + // Move Merithra to the exit point + if (Creature* pMerithra = m_creature->GetMap()->GetCreature(m_merithraGuid)) + { + pMerithra->SetByteValue(UNIT_FIELD_BYTES_1, 3, UNIT_BYTE1_FLAG_ALWAYS_STAND | UNIT_BYTE1_FLAG_UNK_2); + pMerithra->SetLevitate(true); + pMerithra->GetMotionMaster()->MovePoint(POINT_ID_EXIT, aEternalBoardMovement[0].m_fX, aEternalBoardMovement[0].m_fY, aEternalBoardMovement[0].m_fZ); + pMerithra->ForcedDespawn(9000); + } + break; + case SPELL_BLUE_DRAGON_TRANSFORM: + if (Creature* pArygos = m_creature->GetMap()->GetCreature(m_arygosGuid)) + pArygos->CastSpell(pArygos, SPELL_BLUE_DRAGON_TRANSFORM, false); + break; + case SPELL_ARYGOS_VENGEANCE: + if (Creature* pArygos = m_creature->GetMap()->GetCreature(m_arygosGuid)) + pArygos->CastSpell(pArygos, SPELL_ARYGOS_VENGEANCE, false); + break; + case POINT_ID_DRAGON_ATTACK: + // Move Arygos to the exit point + if (Creature* pArygos = m_creature->GetMap()->GetCreature(m_arygosGuid)) + { + pArygos->SetByteValue(UNIT_FIELD_BYTES_1, 3, UNIT_BYTE1_FLAG_ALWAYS_STAND | UNIT_BYTE1_FLAG_UNK_2); + pArygos->SetLevitate(true); + pArygos->GetMotionMaster()->MovePoint(POINT_ID_EXIT, aEternalBoardMovement[0].m_fX, aEternalBoardMovement[0].m_fY, aEternalBoardMovement[0].m_fZ); + pArygos->ForcedDespawn(9000); + } + break; + case NPC_CAELESTRASZ: + // Move Caelestrasz to attack + if (Creature* pTrigger = m_creature->GetMap()->GetCreature(m_triggerGuid)) + { + if (Creature* pCaelestrasz = m_creature->GetMap()->GetCreature(m_CaelestraszGuid)) + { + pCaelestrasz->SetWalk(false); + pCaelestrasz->GetMotionMaster()->MovePoint(POINT_ID_DRAGON_ATTACK, pTrigger->GetPositionX(), pTrigger->GetPositionY(), pTrigger->GetPositionZ()); + } + } + break; + case SPELL_RED_DRAGON_TRANSFORM: + if (Creature* pCaelestrasz = m_creature->GetMap()->GetCreature(m_CaelestraszGuid)) + pCaelestrasz->CastSpell(pCaelestrasz, SPELL_RED_DRAGON_TRANSFORM, false); + break; + case SPELL_CAELESTRASZ_MOLTEN_RAIN: + if (Creature* pCaelestrasz = m_creature->GetMap()->GetCreature(m_CaelestraszGuid)) + pCaelestrasz->CastSpell(pCaelestrasz, SPELL_CAELESTRASZ_MOLTEN_RAIN, false); + break; + case SAY_ANACHRONOS_SEAL_1: + // Send Caelestrasz on flight + if (Creature* pCaelestrasz = m_creature->GetMap()->GetCreature(m_CaelestraszGuid)) + { + pCaelestrasz->SetByteValue(UNIT_FIELD_BYTES_1, 3, UNIT_BYTE1_FLAG_ALWAYS_STAND | UNIT_BYTE1_FLAG_UNK_2); + pCaelestrasz->SetLevitate(true); + pCaelestrasz->GetMotionMaster()->MovePoint(POINT_ID_EXIT, aEternalBoardMovement[0].m_fX, aEternalBoardMovement[0].m_fY, aEternalBoardMovement[0].m_fZ); + pCaelestrasz->ForcedDespawn(9000); + } + if (Creature* pFandral = m_creature->GetMap()->GetCreature(m_fandralGuid)) + m_creature->SetFacingToObject(pFandral); + break; + case SAY_FANDRAL_SEAL_2: + if (Creature* pFandral = m_creature->GetMap()->GetCreature(m_fandralGuid)) + pFandral->SetFacingToObject(m_creature); + break; + case POINT_ID_GATE: + // Send Anachronos to the gate + m_creature->SetWalk(false); + m_creature->GetMotionMaster()->MovePoint(POINT_ID_GATE, aEternalBoardMovement[1].m_fX, aEternalBoardMovement[1].m_fY, aEternalBoardMovement[1].m_fZ); + break; + case NPC_FANDRAL_STAGHELM: + // Send Fandral to the gate + if (Creature* pFandral = m_creature->GetMap()->GetCreature(m_fandralGuid)) + { + pFandral->SetWalk(false); + pFandral->GetMotionMaster()->MovePoint(POINT_ID_GATE, aEternalBoardMovement[2].m_fX, aEternalBoardMovement[2].m_fY, aEternalBoardMovement[2].m_fZ); + } + break; + case SPELL_PRISMATIC_BARRIER: + DoCastSpellIfCan(m_creature, SPELL_PRISMATIC_BARRIER); + break; + case SPELL_GLYPH_OF_WARDING: + DoCastSpellIfCan(m_creature, SPELL_GLYPH_OF_WARDING); + break; + case SAY_FANDRAL_SEAL_6: + // Here Anachronos should continue to cast something + if (Creature* pFandral = m_creature->GetMap()->GetCreature(m_fandralGuid)) + pFandral->CastSpell(pFandral, SPELL_CALL_ANCIENTS, false); + break; + case EMOTE_FANDRAL_EXHAUSTED: + if (Creature* pFandral = m_creature->GetMap()->GetCreature(m_fandralGuid)) + { + pFandral->SetStandState(UNIT_STAND_STATE_KNEEL); + m_creature->SetFacingToObject(pFandral); + } + break; + case DATA_HANDLE_SCEPTER: + // Give the scepter to Fandral (it should equip it somehow) + if (Creature* pFandral = m_creature->GetMap()->GetCreature(m_fandralGuid)) + DoScriptText(EMOTE_ANACHRONOS_SCEPTER, m_creature, pFandral); + m_creature->SetStandState(UNIT_STAND_STATE_KNEEL); + break; + case SAY_FANDRAL_EPILOGUE_4: + if (Creature* pFandral = m_creature->GetMap()->GetCreature(m_fandralGuid)) + pFandral->SetStandState(UNIT_STAND_STATE_STAND); + break; + case POINT_ID_SCEPTER_2: + m_creature->SetStandState(UNIT_STAND_STATE_STAND); + break; + case EMOTE_FANDRAL_SHATTER: + // Shatter the scepter + if (Creature* pFandral = m_creature->GetMap()->GetCreature(m_fandralGuid)) + pFandral->CastSpell(pFandral, SPELL_SHATTER_HAMMER, false); + break; + case SAY_ANACHRONOS_EPILOGUE_6: + if (Creature* pFandral = m_creature->GetMap()->GetCreature(m_fandralGuid)) + { + pFandral->SetWalk(true); + pFandral->GetMotionMaster()->MovePoint(POINT_ID_SCEPTER_1, aEternalBoardMovement[3].m_fX, aEternalBoardMovement[3].m_fY, aEternalBoardMovement[3].m_fZ); + } + break; + case POINT_ID_EPILOGUE: + // Make Fandral leave + if (Creature* pFandral = m_creature->GetMap()->GetCreature(m_fandralGuid)) + pFandral->GetMotionMaster()->MovePoint(POINT_ID_EXIT, aEternalBoardMovement[7].m_fX, aEternalBoardMovement[7].m_fY, aEternalBoardMovement[7].m_fZ); + break; + case POINT_ID_SCEPTER_1: + // Anachronos collects the pieces + m_creature->SetWalk(true); + m_creature->GetMotionMaster()->MovePoint(POINT_ID_SCEPTER_1, aEternalBoardMovement[5].m_fX, aEternalBoardMovement[5].m_fY, aEternalBoardMovement[5].m_fZ); + break; + } + } + + Creature* GetSpeakerByEntry(uint32 uiEntry) override + { + switch (uiEntry) + { + case NPC_ANACHRONOS_THE_ANCIENT: return m_creature; + case NPC_ARYGOS: return m_creature->GetMap()->GetCreature(m_arygosGuid); + case NPC_CAELESTRASZ: return m_creature->GetMap()->GetCreature(m_CaelestraszGuid); + case NPC_MERITHRA_OF_THE_DREAM: return m_creature->GetMap()->GetCreature(m_merithraGuid); + case NPC_FANDRAL_STAGHELM: return m_creature->GetMap()->GetCreature(m_fandralGuid); + + default: + return NULL; + } + } + + void DoSummonDragons() + { + for (uint8 i = 0; i < MAX_DRAGONS; ++i) + m_creature->SummonCreature(aEternalBoardNPCs[i].m_uiEntry, aEternalBoardNPCs[i].m_fX, aEternalBoardNPCs[i].m_fY, aEternalBoardNPCs[i].m_fZ, aEternalBoardNPCs[i].m_fO, TEMPSUMMON_CORPSE_DESPAWN, 0); + + // Also summon the 3 anubisath conquerors + float fX, fY, fZ; + for (uint8 i = 0; i < MAX_CONQUERORS; ++i) + { + m_creature->GetRandomPoint(aEternalBoardMovement[10].m_fX, aEternalBoardMovement[10].m_fY, aEternalBoardMovement[10].m_fZ, 20.0f, fX, fY, fZ); + m_creature->SummonCreature(NPC_ANUBISATH_CONQUEROR, fX, fY, fZ, 0, TEMPSUMMON_CORPSE_DESPAWN, 0); + } + } + + void DoSummonWarriors() + { + float fX, fY, fZ; + // Summon kaldorei warriors + for (uint8 i = 0; i < MAX_KALDOREI; ++i) + { + m_creature->GetRandomPoint(aEternalBoardMovement[10].m_fX, aEternalBoardMovement[10].m_fY, aEternalBoardMovement[10].m_fZ, 10.0f, fX, fY, fZ); + m_creature->SummonCreature(NPC_KALDOREI_INFANTRY, fX, fY, fZ, 0.0f, TEMPSUMMON_CORPSE_DESPAWN, 0); + } + + // Summon Qiraji warriors + for (uint8 i = 0; i < MAX_QIRAJI; ++i) + { + m_creature->GetRandomPoint(aEternalBoardMovement[10].m_fX, aEternalBoardMovement[10].m_fY, aEternalBoardMovement[10].m_fZ, 15.0f, fX, fY, fZ); + m_creature->SummonCreature(NPC_QIRAJI_WASP, fX, fY, fZ, 0.0f, TEMPSUMMON_CORPSE_DESPAWN, 0); + + m_creature->GetRandomPoint(aEternalBoardMovement[10].m_fX, aEternalBoardMovement[10].m_fY, aEternalBoardMovement[10].m_fZ, 15.0f, fX, fY, fZ); + m_creature->SummonCreature(NPC_QIRAJI_DRONE, fX, fY, fZ, 0.0f, TEMPSUMMON_CORPSE_DESPAWN, 0); + + m_creature->GetRandomPoint(aEternalBoardMovement[10].m_fX, aEternalBoardMovement[10].m_fY, aEternalBoardMovement[10].m_fZ, 15.0f, fX, fY, fZ); + m_creature->SummonCreature(NPC_QIRAJI_TANK, fX, fY, fZ, 0.0f, TEMPSUMMON_CORPSE_DESPAWN, 0); + } + } + + void DoUnsummonArmy() + { + for (GuidList::const_iterator itr = m_lQirajiWarriorsList.begin(); itr != m_lQirajiWarriorsList.end(); ++itr) + { + if (Creature* pTemp = m_creature->GetMap()->GetCreature(*itr)) + pTemp->ForcedDespawn(); + } + } + + void JustSummoned(Creature* pSummoned) override + { + // Also remove npc flags where needed + switch (pSummoned->GetEntry()) + { + case NPC_FANDRAL_STAGHELM: + m_fandralGuid = pSummoned->GetObjectGuid(); + break; + case NPC_MERITHRA_OF_THE_DREAM: + m_merithraGuid = pSummoned->GetObjectGuid(); + pSummoned->SetUInt32Value(UNIT_NPC_FLAGS, UNIT_NPC_FLAG_NONE); + break; + case NPC_CAELESTRASZ: + m_CaelestraszGuid = pSummoned->GetObjectGuid(); + pSummoned->SetUInt32Value(UNIT_NPC_FLAGS, UNIT_NPC_FLAG_NONE); + break; + case NPC_ARYGOS: + m_arygosGuid = pSummoned->GetObjectGuid(); + pSummoned->SetUInt32Value(UNIT_NPC_FLAGS, UNIT_NPC_FLAG_NONE); + break; + case NPC_ANUBISATH_CONQUEROR: + case NPC_QIRAJI_WASP: + case NPC_QIRAJI_DRONE: + case NPC_QIRAJI_TANK: + case NPC_KALDOREI_INFANTRY: + m_lQirajiWarriorsList.push_back(pSummoned->GetObjectGuid()); + break; + } + } + + void MovementInform(uint32 uiType, uint32 uiPointId) override + { + if (uiType != POINT_MOTION_TYPE) + return; + + switch (uiPointId) + { + case POINT_ID_GATE: + // Cast time stop when he reaches the gate + DoCastSpellIfCan(m_creature, SPELL_TIME_STOP); + StartNextDialogueText(SPELL_TIME_STOP); + break; + case POINT_ID_SCEPTER_1: + // Pickup the pieces + DoScriptText(EMOTE_ANACHRONOS_PICKUP, m_creature); + m_creature->SetStandState(UNIT_STAND_STATE_KNEEL); + m_uiEventTimer = 2000; + break; + case POINT_ID_SCEPTER_2: + // Pickup the pieces + DoScriptText(SAY_ANACHRONOS_EPILOGUE_8, m_creature); + m_creature->SetStandState(UNIT_STAND_STATE_KNEEL); + m_uiEventTimer = 4000; + break; + case POINT_ID_EXIT: + // Spell was removed, manually change the display + // DoCastSpellIfCan(m_creature, SPELL_BRONZE_DRAGON_TRANSFORM); + m_creature->SetDisplayId(DISPLAY_ID_BRONZE_DRAGON); + m_uiEventTimer = 4000; + break; + } + } + + void SummonedMovementInform(Creature* pSummoned, uint32 uiType, uint32 uiPointId) override + { + if (uiType != POINT_MOTION_TYPE) + return; + + if (pSummoned->GetEntry() == NPC_FANDRAL_STAGHELM) + { + switch (uiPointId) + { + case POINT_ID_EPILOGUE: + // Face Anachronos and restart the dialogue + pSummoned->SetFacingToObject(m_creature); + StartNextDialogueText(SAY_FANDRAL_EPILOGUE_7); + DoUnsummonArmy(); + break; + case POINT_ID_SCEPTER_1: + pSummoned->GetMotionMaster()->MovePoint(POINT_ID_EPILOGUE, aEternalBoardMovement[4].m_fX, aEternalBoardMovement[4].m_fY, aEternalBoardMovement[4].m_fZ); + break; + case POINT_ID_EXIT: + pSummoned->ForcedDespawn(); + break; + } + } + else if (uiPointId == POINT_ID_DRAGON_ATTACK) + { + switch (pSummoned->GetEntry()) + { + case NPC_MERITHRA_OF_THE_DREAM: + StartNextDialogueText(DATA_MERITHRA_ATTACK); + break; + case NPC_CAELESTRASZ: + StartNextDialogueText(DATA_CAELASTRASZ_ATTACK); + break; + } + } + } + + void UpdateAI(const uint32 uiDiff) override + { + DialogueUpdate(uiDiff); + + if (m_uiEventTimer) + { + if (m_uiEventTimer <= uiDiff) + { + switch (m_uiEventStage) + { + case 0: + // Start the dialogue + StartNextDialogueText(NPC_ANACHRONOS_THE_ANCIENT); + m_uiEventTimer = 0; + break; + case 1: + // Do the epilogue movement + m_creature->GetMotionMaster()->MovePoint(POINT_ID_SCEPTER_2, aEternalBoardMovement[6].m_fX, aEternalBoardMovement[6].m_fY, aEternalBoardMovement[6].m_fZ); + m_creature->SetStandState(UNIT_STAND_STATE_STAND); + m_uiEventTimer = 0; + break; + case 2: + // Complete quest and despawn gate + if (Player* pPlayer = m_creature->GetMap()->GetPlayer(m_playerGuid)) + pPlayer->GroupEventHappens(QUEST_A_PAWN_ON_THE_ETERNAL_BOARD, m_creature); + m_creature->SetStandState(UNIT_STAND_STATE_STAND); + m_uiEventTimer = 4000; + break; + case 3: + // Move to exit + m_creature->SetWalk(false); + m_creature->GetMotionMaster()->MovePoint(POINT_ID_EXIT, aEternalBoardMovement[8].m_fX, aEternalBoardMovement[8].m_fY, aEternalBoardMovement[8].m_fZ); + m_uiEventTimer = 0; + break; + case 4: + // Take off and fly + m_creature->SetByteValue(UNIT_FIELD_BYTES_1, 3, UNIT_BYTE1_FLAG_ALWAYS_STAND | UNIT_BYTE1_FLAG_UNK_2); + m_creature->SetLevitate(true); + m_creature->GetMotionMaster()->MovePoint(0, aEternalBoardMovement[9].m_fX, aEternalBoardMovement[9].m_fY, aEternalBoardMovement[9].m_fZ); + m_creature->ForcedDespawn(10000); + m_uiEventTimer = 0; + break; + } + ++m_uiEventStage; + } + else + m_uiEventTimer -= uiDiff; + } + } +}; + +CreatureAI* GetAI_npc_anachronos_the_ancient(Creature* pCreature) +{ + return new npc_anachronos_the_ancientAI(pCreature); +} + +bool QuestAcceptGO_crystalline_tear(Player* pPlayer, GameObject* pGo, const Quest* pQuest) +{ + // Summon the controller dragon at GO position (orientation is wrong - hardcoded) + if (pQuest->GetQuestId() == QUEST_A_PAWN_ON_THE_ETERNAL_BOARD) + { + // Check if event is already in progress first + if (GetClosestCreatureWithEntry(pGo, NPC_ANACHRONOS_THE_ANCIENT, 90.0f)) + return true; + + if (Creature* pAnachronos = pPlayer->SummonCreature(NPC_ANACHRONOS_THE_ANCIENT, pGo->GetPositionX(), pGo->GetPositionY(), pGo->GetPositionZ(), 3.75f, TEMPSUMMON_CORPSE_DESPAWN, 0)) + { + // Send the player's guid in order to handle the quest complete + if (npc_anachronos_the_ancientAI* pAnachronosAI = dynamic_cast(pAnachronos->AI())) + pAnachronosAI->m_playerGuid = pPlayer->GetObjectGuid(); + } + } + + return true; +} + +void AddSC_silithus() +{ + Script* pNewScript; + + pNewScript = new Script; + pNewScript->Name = "npc_anachronos_the_ancient"; + pNewScript->GetAI = &GetAI_npc_anachronos_the_ancient; + pNewScript->RegisterSelf(); + + pNewScript = new Script; + pNewScript->Name = "go_crystalline_tear"; + pNewScript->pQuestAcceptGO = &QuestAcceptGO_crystalline_tear; + pNewScript->RegisterSelf(); +} diff --git a/src/modules/SD2/scripts/kalimdor/stonetalon_mountains.cpp b/src/modules/SD2/scripts/kalimdor/stonetalon_mountains.cpp new file mode 100644 index 000000000..14dc35a4c --- /dev/null +++ b/src/modules/SD2/scripts/kalimdor/stonetalon_mountains.cpp @@ -0,0 +1,113 @@ +/* Copyright (C) 2006 - 2013 ScriptDev2 + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along 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: 100 +SDComment: Quest support: 6523. +SDCategory: Stonetalon Mountains +EndScriptData */ + +#include "precompiled.h" +#include "escort_ai.h" + +/*###### +## npc_kaya +######*/ + +enum +{ + NPC_GRIMTOTEM_RUFFIAN = 11910, + NPC_GRIMTOTEM_BRUTE = 11912, + NPC_GRIMTOTEM_SORCERER = 11913, + + SAY_START = -1000357, + SAY_AMBUSH = -1000358, + SAY_END = -1000359, + + QUEST_PROTECT_KAYA = 6523 +}; + +struct npc_kayaAI : public npc_escortAI +{ + npc_kayaAI(Creature* pCreature) : npc_escortAI(pCreature) { Reset(); } + + void Reset() override { } + + void JustSummoned(Creature* pSummoned) override + { + pSummoned->AI()->AttackStart(m_creature); + } + + void WaypointReached(uint32 uiPointId) override + { + switch (uiPointId) + { + // Ambush + case 16: + // note about event here: + // apparently NPC say _after_ the ambush is over, and is most likely a bug at you-know-where. + // we simplify this, and make say when the ambush actually start. + DoScriptText(SAY_AMBUSH, m_creature); + m_creature->SummonCreature(NPC_GRIMTOTEM_RUFFIAN, -50.75f, -500.77f, -46.13f, 0.4f, TEMPSUMMON_CORPSE_TIMED_DESPAWN, 30000); + m_creature->SummonCreature(NPC_GRIMTOTEM_BRUTE, -40.05f, -510.89f, -46.05f, 1.7f, TEMPSUMMON_CORPSE_TIMED_DESPAWN, 30000); + m_creature->SummonCreature(NPC_GRIMTOTEM_SORCERER, -32.21f, -499.20f, -45.35f, 2.8f, TEMPSUMMON_CORPSE_TIMED_DESPAWN, 30000); + break; + // Award quest credit + case 18: + DoScriptText(SAY_END, m_creature); + + if (Player* pPlayer = GetPlayerForEscort()) + pPlayer->GroupEventHappens(QUEST_PROTECT_KAYA, m_creature); + break; + } + } +}; + +CreatureAI* GetAI_npc_kaya(Creature* pCreature) +{ + return new npc_kayaAI(pCreature); +} + +bool QuestAccept_npc_kaya(Player* pPlayer, Creature* pCreature, Quest const* pQuest) +{ + // Casting Spell and Starting the Escort quest is buggy, so this is a hack. Use the spell when it is possible. + + if (pQuest->GetQuestId() == QUEST_PROTECT_KAYA) + { + pCreature->SetFactionTemporary(FACTION_ESCORT_H_PASSIVE, TEMPFACTION_RESTORE_RESPAWN); + DoScriptText(SAY_START, pCreature); + + if (npc_kayaAI* pEscortAI = dynamic_cast(pCreature->AI())) + pEscortAI->Start(false, pPlayer, pQuest); + } + return true; +} + +/*###### +## AddSC +######*/ + +void AddSC_stonetalon_mountains() +{ + Script* pNewScript; + + pNewScript = new Script; + pNewScript->Name = "npc_kaya"; + pNewScript->GetAI = &GetAI_npc_kaya; + pNewScript->pQuestAcceptNPC = &QuestAccept_npc_kaya; + pNewScript->RegisterSelf(); +} diff --git a/src/modules/SD2/scripts/kalimdor/tanaris.cpp b/src/modules/SD2/scripts/kalimdor/tanaris.cpp new file mode 100644 index 000000000..e17d16a79 --- /dev/null +++ b/src/modules/SD2/scripts/kalimdor/tanaris.cpp @@ -0,0 +1,572 @@ +/* Copyright (C) 2006 - 2013 ScriptDev2 + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along 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. +SDCategory: Tanaris +EndScriptData */ + +/* ContentData +mob_aquementas +npc_custodian_of_time +npc_oox17tn +npc_stone_watcher_of_norgannon +npc_tooga +EndContentData */ + +#include "precompiled.h" +#include "escort_ai.h" +#include "follower_ai.h" + +/*###### +## mob_aquementas +######*/ + +enum +{ + AGGRO_YELL_AQUE = -1000168, + + SPELL_AQUA_JET = 13586, + SPELL_FROST_SHOCK = 15089, + + ITEM_SILVER_TOTEM = 11522, + ITEM_BOOK_AQUOR = 11169, + ITEM_SILVERY_CLAWS = 11172, + ITEM_IRONTREE_HEART = 11173, + + FACTION_FRIENDLY = 35, + FACTION_ELEMENTAL = 91, +}; + +struct mob_aquementasAI : public ScriptedAI +{ + mob_aquementasAI(Creature* pCreature) : ScriptedAI(pCreature) { Reset(); } + + uint32 m_uiSwitchFactionTimer; + uint32 m_uiFrostShockTimer; + uint32 m_uiAquaJetTimer; + + void Reset() override + { + m_uiSwitchFactionTimer = 10000; + m_uiAquaJetTimer = 5000; + m_uiFrostShockTimer = 1000; + + m_creature->setFaction(FACTION_FRIENDLY); // TODO: Either do this way, or might require a DB change + } + + void SendItem(Player* pReceiver) + { + if (pReceiver->HasItemCount(ITEM_BOOK_AQUOR, 1) && + pReceiver->HasItemCount(ITEM_SILVERY_CLAWS, 11) && + pReceiver->HasItemCount(ITEM_IRONTREE_HEART, 1) && + !pReceiver->HasItemCount(ITEM_SILVER_TOTEM, 1)) + { + if (Item* pItem = pReceiver->StoreNewItemInInventorySlot(ITEM_SILVER_TOTEM, 1)) + pReceiver->SendNewItem(pItem, 1, true, false); + } + } + + void Aggro(Unit* pWho) override + { + DoScriptText(AGGRO_YELL_AQUE, m_creature, pWho); + + Player* pInvokedPlayer = pWho->GetCharmerOrOwnerPlayerOrPlayerItself(); + if (pInvokedPlayer) + SendItem(pInvokedPlayer); + } + + void UpdateAI(const uint32 uiDiff) override + { + if (m_uiSwitchFactionTimer) + { + if (m_uiSwitchFactionTimer <= uiDiff) + { + m_creature->setFaction(FACTION_ELEMENTAL); + m_uiSwitchFactionTimer = 0; + } + else + m_uiSwitchFactionTimer -= uiDiff; + } + + if (!m_creature->SelectHostileTarget() || !m_creature->getVictim()) + return; + + if (m_uiFrostShockTimer < uiDiff) + { + DoCastSpellIfCan(m_creature->getVictim(), SPELL_FROST_SHOCK); + m_uiFrostShockTimer = 15000; + } + else + m_uiFrostShockTimer -= uiDiff; + + if (m_uiAquaJetTimer < uiDiff) + { + DoCastSpellIfCan(m_creature, SPELL_AQUA_JET); + m_uiAquaJetTimer = 15000; + } + else + m_uiAquaJetTimer -= uiDiff; + + DoMeleeAttackIfReady(); + } +}; + +CreatureAI* GetAI_mob_aquementas(Creature* pCreature) +{ + return new mob_aquementasAI(pCreature); +} + +/*###### +## npc_custodian_of_time +######*/ + +enum +{ + WHISPER_CUSTODIAN_1 = -1000217, + WHISPER_CUSTODIAN_2 = -1000218, + WHISPER_CUSTODIAN_3 = -1000219, + WHISPER_CUSTODIAN_4 = -1000220, + WHISPER_CUSTODIAN_5 = -1000221, + WHISPER_CUSTODIAN_6 = -1000222, + WHISPER_CUSTODIAN_7 = -1000223, + WHISPER_CUSTODIAN_8 = -1000224, + WHISPER_CUSTODIAN_9 = -1000225, + WHISPER_CUSTODIAN_10 = -1000226, + WHISPER_CUSTODIAN_11 = -1000227, + WHISPER_CUSTODIAN_12 = -1000228, + WHISPER_CUSTODIAN_13 = -1000229, + WHISPER_CUSTODIAN_14 = -1000230, + + SPELL_CUSTODIAN_OF_TIME = 34877, + SPELL_QID_10277 = 34883, + + QUEST_ID_CAVERNS_OF_TIME = 10277, +}; + +struct npc_custodian_of_timeAI : public npc_escortAI +{ + npc_custodian_of_timeAI(Creature* pCreature) : npc_escortAI(pCreature) { Reset(); } + + void WaypointReached(uint32 uiPointId) override + { + Player* pPlayer = GetPlayerForEscort(); + + if (!pPlayer) + return; + + switch (uiPointId) + { + case 0: DoScriptText(WHISPER_CUSTODIAN_1, m_creature, pPlayer); break; + case 1: DoScriptText(WHISPER_CUSTODIAN_2, m_creature, pPlayer); break; + case 2: DoScriptText(WHISPER_CUSTODIAN_3, m_creature, pPlayer); break; + case 3: DoScriptText(WHISPER_CUSTODIAN_4, m_creature, pPlayer); break; + case 5: DoScriptText(WHISPER_CUSTODIAN_5, m_creature, pPlayer); break; + case 6: DoScriptText(WHISPER_CUSTODIAN_6, m_creature, pPlayer); break; + case 7: DoScriptText(WHISPER_CUSTODIAN_7, m_creature, pPlayer); break; + case 8: DoScriptText(WHISPER_CUSTODIAN_8, m_creature, pPlayer); break; + case 9: DoScriptText(WHISPER_CUSTODIAN_9, m_creature, pPlayer); break; + case 10: DoScriptText(WHISPER_CUSTODIAN_4, m_creature, pPlayer); break; + case 13: DoScriptText(WHISPER_CUSTODIAN_10, m_creature, pPlayer); break; + case 14: DoScriptText(WHISPER_CUSTODIAN_4, m_creature, pPlayer); break; + case 16: DoScriptText(WHISPER_CUSTODIAN_11, m_creature, pPlayer); break; + case 17: DoScriptText(WHISPER_CUSTODIAN_12, m_creature, pPlayer); break; + case 18: DoScriptText(WHISPER_CUSTODIAN_4, m_creature, pPlayer); break; + case 22: DoScriptText(WHISPER_CUSTODIAN_13, m_creature, pPlayer); break; + case 23: DoScriptText(WHISPER_CUSTODIAN_4, m_creature, pPlayer); break; + case 24: + DoScriptText(WHISPER_CUSTODIAN_14, m_creature, pPlayer); + DoCastSpellIfCan(pPlayer, SPELL_QID_10277); + break; + } + } + + void MoveInLineOfSight(Unit* pWho) override + { + if (HasEscortState(STATE_ESCORT_ESCORTING)) + return; + + if (pWho->GetTypeId() == TYPEID_PLAYER) + { + if (pWho->HasAura(SPELL_CUSTODIAN_OF_TIME) && ((Player*)pWho)->GetQuestStatus(QUEST_ID_CAVERNS_OF_TIME) == QUEST_STATUS_INCOMPLETE) + { + float fRadius = 10.0f; + + if (m_creature->IsWithinDistInMap(pWho, fRadius)) + Start(false, (Player*)pWho); + } + } + } + + void Reset() override { } +}; + +CreatureAI* GetAI_npc_custodian_of_time(Creature* pCreature) +{ + return new npc_custodian_of_timeAI(pCreature); +} + +/*###### +## npc_oox17tn +######*/ + +enum +{ + SAY_OOX_START = -1000287, + SAY_OOX_AGGRO1 = -1000288, + SAY_OOX_AGGRO2 = -1000289, + SAY_OOX_AMBUSH = -1000290, + SAY_OOX17_AMBUSH_REPLY = -1000291, + SAY_OOX_END = -1000292, + + QUEST_RESCUE_OOX_17TN = 648, + + NPC_SCORPION = 7803, + NPC_SCOFFLAW = 7805, + NPC_SHADOW_MAGE = 5617 +}; + +struct npc_oox17tnAI : public npc_escortAI +{ + npc_oox17tnAI(Creature* pCreature) : npc_escortAI(pCreature) { Reset(); } + + void WaypointReached(uint32 i) override + { + Player* pPlayer = GetPlayerForEscort(); + + if (!pPlayer) + return; + + switch (i) + { + // 1. Ambush: 3 scorpions + case 22: + DoScriptText(SAY_OOX_AMBUSH, m_creature); + m_creature->SummonCreature(NPC_SCORPION, -8340.70f, -4448.17f, 9.17f, 3.10f, TEMPSUMMON_CORPSE_TIMED_DESPAWN, 30000); + m_creature->SummonCreature(NPC_SCORPION, -8343.18f, -4444.35f, 9.44f, 2.35f, TEMPSUMMON_CORPSE_TIMED_DESPAWN, 30000); + m_creature->SummonCreature(NPC_SCORPION, -8348.70f, -4457.80f, 9.58f, 2.02f, TEMPSUMMON_CORPSE_TIMED_DESPAWN, 30000); + break; + // 2. Ambush: 2 Rogues & 1 Shadow Mage + case 28: + DoScriptText(SAY_OOX_AMBUSH, m_creature); + + m_creature->SummonCreature(NPC_SCOFFLAW, -7488.02f, -4786.56f, 10.67f, 3.74f, TEMPSUMMON_CORPSE_TIMED_DESPAWN, 10000); + m_creature->SummonCreature(NPC_SHADOW_MAGE, -7486.41f, -4791.55f, 10.54f, 3.26f, TEMPSUMMON_CORPSE_TIMED_DESPAWN, 30000); + + if (Creature* pCreature = m_creature->SummonCreature(NPC_SCOFFLAW, -7488.47f, -4800.77f, 9.77f, 2.50f, TEMPSUMMON_CORPSE_TIMED_DESPAWN, 30000)) + DoScriptText(SAY_OOX17_AMBUSH_REPLY, pCreature); + + break; + case 34: + DoScriptText(SAY_OOX_END, m_creature); + // Award quest credit + pPlayer->GroupEventHappens(QUEST_RESCUE_OOX_17TN, m_creature); + break; + } + } + + void Reset() override { } + + void Aggro(Unit* /*who*/) override + { + // For an small probability he say something when it aggros + switch (urand(0, 9)) + { + case 0: DoScriptText(SAY_OOX_AGGRO1, m_creature); break; + case 1: DoScriptText(SAY_OOX_AGGRO2, m_creature); break; + } + } + + void JustSummoned(Creature* summoned) override + { + summoned->AI()->AttackStart(m_creature); + } +}; + +CreatureAI* GetAI_npc_oox17tn(Creature* pCreature) +{ + return new npc_oox17tnAI(pCreature); +} + +bool QuestAccept_npc_oox17tn(Player* pPlayer, Creature* pCreature, const Quest* pQuest) +{ + if (pQuest->GetQuestId() == QUEST_RESCUE_OOX_17TN) + { + DoScriptText(SAY_OOX_START, pCreature); + + pCreature->SetStandState(UNIT_STAND_STATE_STAND); + + if (pPlayer->GetTeam() == ALLIANCE) + pCreature->SetFactionTemporary(FACTION_ESCORT_A_PASSIVE, TEMPFACTION_RESTORE_RESPAWN); + + if (pPlayer->GetTeam() == HORDE) + pCreature->SetFactionTemporary(FACTION_ESCORT_H_PASSIVE, TEMPFACTION_RESTORE_RESPAWN); + + if (npc_oox17tnAI* pEscortAI = dynamic_cast(pCreature->AI())) + pEscortAI->Start(false, pPlayer, pQuest); + } + 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->GetObjectGuid()); + + 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->GetObjectGuid()); + + return true; +} + +bool GossipSelect_npc_stone_watcher_of_norgannon(Player* pPlayer, Creature* pCreature, uint32 /*uiSender*/, uint32 uiAction) +{ + switch (uiAction) + { + case GOSSIP_ACTION_INFO_DEF: + pPlayer->ADD_GOSSIP_ITEM(GOSSIP_ICON_CHAT, GOSSIP_ITEM_NORGANNON_2, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 1); + pPlayer->SEND_GOSSIP_MENU(1675, pCreature->GetObjectGuid()); + break; + case GOSSIP_ACTION_INFO_DEF+1: + pPlayer->ADD_GOSSIP_ITEM(GOSSIP_ICON_CHAT, GOSSIP_ITEM_NORGANNON_3, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 2); + pPlayer->SEND_GOSSIP_MENU(1676, pCreature->GetObjectGuid()); + break; + case GOSSIP_ACTION_INFO_DEF+2: + pPlayer->ADD_GOSSIP_ITEM(GOSSIP_ICON_CHAT, GOSSIP_ITEM_NORGANNON_4, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 3); + pPlayer->SEND_GOSSIP_MENU(1677, pCreature->GetObjectGuid()); + break; + case GOSSIP_ACTION_INFO_DEF+3: + pPlayer->ADD_GOSSIP_ITEM(GOSSIP_ICON_CHAT, GOSSIP_ITEM_NORGANNON_5, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 4); + pPlayer->SEND_GOSSIP_MENU(1678, pCreature->GetObjectGuid()); + break; + case GOSSIP_ACTION_INFO_DEF+4: + pPlayer->ADD_GOSSIP_ITEM(GOSSIP_ICON_CHAT, GOSSIP_ITEM_NORGANNON_6, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 5); + pPlayer->SEND_GOSSIP_MENU(1679, pCreature->GetObjectGuid()); + break; + case GOSSIP_ACTION_INFO_DEF+5: + pPlayer->CLOSE_GOSSIP_MENU(); + pPlayer->AreaExploredOrEventHappens(2954); + break; + } + return true; +} + +/*#### +# npc_tooga +####*/ + +enum +{ + SAY_TOOG_THIRST = -1000391, + SAY_TOOG_WORRIED = -1000392, + SAY_TOOG_POST_1 = -1000393, + SAY_TORT_POST_2 = -1000394, + SAY_TOOG_POST_3 = -1000395, + 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 +}; + +const float m_afToWaterLoc[] = { -7032.664551f, -4906.199219f, -1.606446f}; + +struct npc_toogaAI : public FollowerAI +{ + npc_toogaAI(Creature* pCreature) : FollowerAI(pCreature) { Reset(); } + + uint32 m_uiCheckSpeechTimer; + uint32 m_uiPostEventTimer; + uint32 m_uiPhasePostEvent; + + Unit* pTorta; + + void Reset() override + { + m_uiCheckSpeechTimer = 2500; + m_uiPostEventTimer = 1000; + m_uiPhasePostEvent = 0; + + pTorta = NULL; + } + + void MoveInLineOfSight(Unit* pWho) override + { + 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)) + { + if (Player* pPlayer = GetLeaderForFollower()) + { + if (pPlayer->GetQuestStatus(QUEST_TOOGA) == QUEST_STATUS_INCOMPLETE) + pPlayer->GroupEventHappens(QUEST_TOOGA, m_creature); + } + + pTorta = pWho; + SetFollowComplete(true); + } + } + } + + void MovementInform(uint32 uiMotionType, uint32 uiPointId) override + { + FollowerAI::MovementInform(uiMotionType, uiPointId); + + if (uiMotionType != POINT_MOTION_TYPE) + return; + + if (uiPointId == POINT_ID_TO_WATER) + SetFollowComplete(); + } + + void UpdateFollowerAI(const uint32 uiDiff) override + { + if (!m_creature->SelectHostileTarget() || !m_creature->getVictim()) + { + // we are doing the post-event, or... + if (HasFollowState(STATE_FOLLOW_POSTEVENT)) + { + if (m_uiPostEventTimer < uiDiff) + { + m_uiPostEventTimer = 5000; + + if (!pTorta || !pTorta->IsAlive()) + { + // something happened, so just complete + SetFollowComplete(); + return; + } + + switch (m_uiPhasePostEvent) + { + case 1: + DoScriptText(SAY_TOOG_POST_1, m_creature); + break; + case 2: + DoScriptText(SAY_TORT_POST_2, pTorta); + break; + case 3: + DoScriptText(SAY_TOOG_POST_3, m_creature); + break; + case 4: + DoScriptText(SAY_TORT_POST_4, pTorta); + break; + case 5: + DoScriptText(SAY_TOOG_POST_5, m_creature); + break; + case 6: + DoScriptText(SAY_TORT_POST_6, pTorta); + m_creature->GetMotionMaster()->MovePoint(POINT_ID_TO_WATER, m_afToWaterLoc[0], m_afToWaterLoc[1], m_afToWaterLoc[2]); + break; + } + + ++m_uiPhasePostEvent; + } + else + m_uiPostEventTimer -= uiDiff; + } + //...we are doing regular speech check + else if (HasFollowState(STATE_FOLLOW_INPROGRESS)) + { + if (m_uiCheckSpeechTimer < uiDiff) + { + m_uiCheckSpeechTimer = 5000; + + switch (urand(0, 50)) + { + case 10: DoScriptText(SAY_TOOG_THIRST, m_creature); break; + case 25: DoScriptText(SAY_TOOG_WORRIED, m_creature); break; + } + } + 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) + { + if (npc_toogaAI* pToogaAI = dynamic_cast(pCreature->AI())) + pToogaAI->StartFollow(pPlayer, FACTION_ESCORT_N_FRIEND_PASSIVE, pQuest); + } + + return true; +} + +void AddSC_tanaris() +{ + Script* pNewScript; + + pNewScript = new Script; + pNewScript->Name = "mob_aquementas"; + pNewScript->GetAI = &GetAI_mob_aquementas; + pNewScript->RegisterSelf(); + + pNewScript = new Script; + pNewScript->Name = "npc_custodian_of_time"; + pNewScript->GetAI = &GetAI_npc_custodian_of_time; + pNewScript->RegisterSelf(); + + pNewScript = new Script; + pNewScript->Name = "npc_oox17tn"; + pNewScript->GetAI = &GetAI_npc_oox17tn; + pNewScript->pQuestAcceptNPC = &QuestAccept_npc_oox17tn; + pNewScript->RegisterSelf(); + + pNewScript = new Script; + pNewScript->Name = "npc_stone_watcher_of_norgannon"; + pNewScript->pGossipHello = &GossipHello_npc_stone_watcher_of_norgannon; + pNewScript->pGossipSelect = &GossipSelect_npc_stone_watcher_of_norgannon; + pNewScript->RegisterSelf(); + + pNewScript = new Script; + pNewScript->Name = "npc_tooga"; + pNewScript->GetAI = &GetAI_npc_tooga; + pNewScript->pQuestAcceptNPC = &QuestAccept_npc_tooga; + pNewScript->RegisterSelf(); +} diff --git a/src/modules/SD2/scripts/kalimdor/teldrassil.cpp b/src/modules/SD2/scripts/kalimdor/teldrassil.cpp new file mode 100644 index 000000000..1800b68da --- /dev/null +++ b/src/modules/SD2/scripts/kalimdor/teldrassil.cpp @@ -0,0 +1,113 @@ +/* Copyright (C) 2006 - 2013 ScriptDev2 + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along 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 +{ + SAY_AT_HOME = -1000323, + EMOTE_AT_HOME = -1000324, + QUEST_MIST = 938, + NPC_ARYNIA = 3519, + FACTION_DARNASSUS = 79 +}; + +struct npc_mistAI : public FollowerAI +{ + npc_mistAI(Creature* pCreature) : FollowerAI(pCreature) { Reset(); } + + void Reset() override { } + + void MoveInLineOfSight(Unit* pWho) override + { + FollowerAI::MoveInLineOfSight(pWho); + + if (!m_creature->getVictim() && !HasFollowState(STATE_FOLLOW_COMPLETE) && pWho->GetEntry() == NPC_ARYNIA) + { + if (m_creature->IsWithinDistInMap(pWho, 10.0f)) + { + DoScriptText(SAY_AT_HOME, pWho); + DoComplete(); + } + } + } + + 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) override + { + if (!m_creature->SelectHostileTarget() || !m_creature->getVictim()) + return; + + DoMeleeAttackIfReady(); + }*/ +}; + +CreatureAI* GetAI_npc_mist(Creature* pCreature) +{ + return new npc_mistAI(pCreature); +} + +bool QuestAccept_npc_mist(Player* pPlayer, Creature* pCreature, const Quest* pQuest) +{ + if (pQuest->GetQuestId() == QUEST_MIST) + { + if (npc_mistAI* pMistAI = dynamic_cast(pCreature->AI())) + pMistAI->StartFollow(pPlayer, FACTION_DARNASSUS, pQuest); + } + + return true; +} + +void AddSC_teldrassil() +{ + Script* pNewScript; + + pNewScript = new Script; + pNewScript->Name = "npc_mist"; + pNewScript->GetAI = &GetAI_npc_mist; + pNewScript->pQuestAcceptNPC = &QuestAccept_npc_mist; + pNewScript->RegisterSelf(); +} diff --git a/src/modules/SD2/scripts/kalimdor/temple_of_ahnqiraj/boss_bug_trio.cpp b/src/modules/SD2/scripts/kalimdor/temple_of_ahnqiraj/boss_bug_trio.cpp new file mode 100644 index 000000000..536e78369 --- /dev/null +++ b/src/modules/SD2/scripts/kalimdor/temple_of_ahnqiraj/boss_bug_trio.cpp @@ -0,0 +1,305 @@ +/* Copyright (C) 2006 - 2013 ScriptDev2 + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +/* ScriptData +SDName: bug_trio +SD%Complete: 75 +SDComment: Summon Player spell NYI; Poison Cloud damage spell NYI; Timers need adjustments +SDCategory: Temple of Ahn'Qiraj +EndScriptData */ + +#include "precompiled.h" +#include "temple_of_ahnqiraj.h" + +enum +{ + // kri + SPELL_CLEAVE = 26350, + SPELL_TOXIC_VOLLEY = 25812, + SPELL_SUMMON_CLOUD = 26590, // summons 15933 + + // vem + SPELL_CHARGE = 26561, + SPELL_VENGEANCE = 25790, + SPELL_KNOCKBACK = 26027, + + // yauj + SPELL_HEAL = 25807, + SPELL_FEAR = 26580, + + NPC_YAUJ_BROOD = 15621 +}; + +struct boss_kriAI : public ScriptedAI +{ + boss_kriAI(Creature* pCreature) : ScriptedAI(pCreature) + { + m_pInstance = (ScriptedInstance*)pCreature->GetInstanceData(); + Reset(); + } + + ScriptedInstance* m_pInstance; + + uint32 m_uiCleaveTimer; + uint32 m_uiToxicVolleyTimer; + + void Reset() override + { + m_uiCleaveTimer = urand(4000, 8000); + m_uiToxicVolleyTimer = urand(6000, 12000); + } + + void JustDied(Unit* /*pKiller*/) override + { + // poison cloud on death + DoCastSpellIfCan(m_creature, SPELL_SUMMON_CLOUD, CAST_TRIGGERED); + + if (!m_pInstance) + return; + + // If the other 2 bugs are still alive, make unlootable + if (m_pInstance->GetData(TYPE_BUG_TRIO) != DONE) + { + m_creature->RemoveFlag(UNIT_DYNAMIC_FLAGS, UNIT_DYNFLAG_LOOTABLE); + m_pInstance->SetData(TYPE_BUG_TRIO, SPECIAL); + } + } + + void JustReachedHome() override + { + if (m_pInstance) + m_pInstance->SetData(TYPE_BUG_TRIO, FAIL); + } + + void UpdateAI(const uint32 uiDiff) override + { + // Return since we have no target + if (!m_creature->SelectHostileTarget() || !m_creature->getVictim()) + return; + + // Cleave_Timer + if (m_uiCleaveTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature->getVictim(), SPELL_CLEAVE) == CAST_OK) + m_uiCleaveTimer = urand(5000, 12000); + } + else + m_uiCleaveTimer -= uiDiff; + + // ToxicVolley_Timer + if (m_uiToxicVolleyTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature, SPELL_TOXIC_VOLLEY) == CAST_OK) + m_uiToxicVolleyTimer = urand(10000, 15000); + } + else + m_uiToxicVolleyTimer -= uiDiff; + + DoMeleeAttackIfReady(); + } +}; + +struct boss_vemAI : public ScriptedAI +{ + boss_vemAI(Creature* pCreature) : ScriptedAI(pCreature) + { + m_pInstance = (ScriptedInstance*)pCreature->GetInstanceData(); + Reset(); + } + + ScriptedInstance* m_pInstance; + + uint32 m_uiChargeTimer; + uint32 m_uiKnockBackTimer; + + void Reset() override + { + m_uiChargeTimer = urand(15000, 27000); + m_uiKnockBackTimer = urand(8000, 20000); + } + + void JustDied(Unit* /*pKiller*/) override + { + // Enrage the other bugs + DoCastSpellIfCan(m_creature, SPELL_VENGEANCE, CAST_TRIGGERED); + + if (!m_pInstance) + return; + + // If the other 2 bugs are still alive, make unlootable + if (m_pInstance->GetData(TYPE_BUG_TRIO) != DONE) + { + m_creature->RemoveFlag(UNIT_DYNAMIC_FLAGS, UNIT_DYNFLAG_LOOTABLE); + m_pInstance->SetData(TYPE_BUG_TRIO, SPECIAL); + } + } + + void JustReachedHome() override + { + if (m_pInstance) + m_pInstance->SetData(TYPE_BUG_TRIO, FAIL); + } + + void UpdateAI(const uint32 uiDiff) override + { + // Return since we have no target + if (!m_creature->SelectHostileTarget() || !m_creature->getVictim()) + return; + + // Charge_Timer + if (m_uiChargeTimer < uiDiff) + { + if (Unit* pTarget = m_creature->SelectAttackingTarget(ATTACKING_TARGET_RANDOM, 0)) + { + if (DoCastSpellIfCan(pTarget, SPELL_CHARGE) == CAST_OK) + m_uiChargeTimer = urand(8000, 16000); + } + } + else + m_uiChargeTimer -= uiDiff; + + // KnockBack_Timer + if (m_uiKnockBackTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature, SPELL_KNOCKBACK) == CAST_OK) + { + if (m_creature->GetThreatManager().getThreat(m_creature->getVictim())) + m_creature->GetThreatManager().modifyThreatPercent(m_creature->getVictim(), -80); + + m_uiKnockBackTimer = urand(15000, 25000); + } + } + else + m_uiKnockBackTimer -= uiDiff; + + DoMeleeAttackIfReady(); + } +}; + +struct boss_yaujAI : public ScriptedAI +{ + boss_yaujAI(Creature* pCreature) : ScriptedAI(pCreature) + { + m_pInstance = (ScriptedInstance*)pCreature->GetInstanceData(); + Reset(); + } + + ScriptedInstance* m_pInstance; + + uint32 m_uiHealTimer; + uint32 m_uiFearTimer; + + void Reset() override + { + m_uiHealTimer = urand(25000, 40000); + m_uiFearTimer = urand(12000, 24000); + } + + void JustDied(Unit* /*Killer*/) override + { + // Spawn 10 yauj brood on death + float fX, fY, fZ; + for (int i = 0; i < 10; ++i) + { + m_creature->GetRandomPoint(m_creature->GetPositionX(), m_creature->GetPositionY(), m_creature->GetPositionZ(), 10.0f, fX, fY, fZ); + m_creature->SummonCreature(NPC_YAUJ_BROOD, fX, fY, fZ, 0.0f, TEMPSUMMON_TIMED_OOC_DESPAWN, 30000); + } + + if (!m_pInstance) + return; + + // If the other 2 bugs are still alive, make unlootable + if (m_pInstance->GetData(TYPE_BUG_TRIO) != DONE) + { + m_creature->RemoveFlag(UNIT_DYNAMIC_FLAGS, UNIT_DYNFLAG_LOOTABLE); + m_pInstance->SetData(TYPE_BUG_TRIO, SPECIAL); + } + } + + void JustReachedHome() override + { + if (m_pInstance) + m_pInstance->SetData(TYPE_BUG_TRIO, FAIL); + } + + void UpdateAI(const uint32 uiDiff) override + { + // Return since we have no target + if (!m_creature->SelectHostileTarget() || !m_creature->getVictim()) + return; + + // Fear_Timer + if (m_uiFearTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature, SPELL_FEAR) == CAST_OK) + { + DoResetThreat(); + m_uiFearTimer = 20000; + } + } + else + m_uiFearTimer -= uiDiff; + + // Heal + if (m_uiHealTimer < uiDiff) + { + if (Unit* pTarget = DoSelectLowestHpFriendly(100.0f)) + { + if (DoCastSpellIfCan(pTarget, SPELL_HEAL) == CAST_OK) + m_uiHealTimer = urand(15000, 30000); + } + } + else + m_uiHealTimer -= uiDiff; + + 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* pNewScript; + + pNewScript = new Script; + pNewScript->Name = "boss_kri"; + pNewScript->GetAI = &GetAI_boss_kri; + pNewScript->RegisterSelf(); + + pNewScript = new Script; + pNewScript->Name = "boss_vem"; + pNewScript->GetAI = &GetAI_boss_vem; + pNewScript->RegisterSelf(); + + pNewScript = new Script; + pNewScript->Name = "boss_yauj"; + pNewScript->GetAI = &GetAI_boss_yauj; + pNewScript->RegisterSelf(); +} diff --git a/src/modules/SD2/scripts/kalimdor/temple_of_ahnqiraj/boss_cthun.cpp b/src/modules/SD2/scripts/kalimdor/temple_of_ahnqiraj/boss_cthun.cpp new file mode 100644 index 000000000..0d301f370 --- /dev/null +++ b/src/modules/SD2/scripts/kalimdor/temple_of_ahnqiraj/boss_cthun.cpp @@ -0,0 +1,828 @@ +/* Copyright (C) 2006 - 2013 ScriptDev2 + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with 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: Transform spell has some minor core issues. Eject from stomach event contains workarounds because of the missing spells. Digestive Acid should be handled in core. +SDCategory: Temple of Ahn'Qiraj +EndScriptData */ + +#include "precompiled.h" +#include "temple_of_ahnqiraj.h" + +enum +{ + EMOTE_WEAKENED = -1531011, + + // ***** Phase 1 ******** + SPELL_EYE_BEAM = 26134, + // SPELL_DARK_GLARE = 26029, + SPELL_ROTATE_TRIGGER = 26137, // phase switch spell - triggers 26009 or 26136. These trigger the Dark Glare spell - 26029 + SPELL_ROTATE_360_LEFT = 26009, + SPELL_ROTATE_360_RIGHT = 26136, + + // ***** Phase 2 ****** + // SPELL_CARAPACE_CTHUN = 26156, // Was removed from client dbcs + SPELL_TRANSFORM = 26232, + SPELL_CTHUN_VULNERABLE = 26235, + SPELL_MOUTH_TENTACLE = 26332, // prepare target to teleport to stomach + // SPELL_DIGESTIVE_ACID_TELEPORT = 26220, // removed from DBC. stomach teleport spell + SPELL_EXIT_STOMACH_KNOCKBACK = 25383, // spell id is wrong + // SPELL_EXIT_STOMACH_JUMP = 26224, // removed from DBC. should make the player jump to the ceiling + // SPELL_EXIT_STOMACH_EFFECT = 26230, // removed from DBC. used to complete the eject effect from the stomach + // SPELL_PORT_OUT_STOMACH_EFFECT = 26648, // removed from DBC. used to kill players inside the stomach on evade + SPELL_DIGESTIVE_ACID = 26476, // damage spell - should be handled by the map + // SPELL_EXIT_STOMACH = 26221, // summons 15800 + + // ***** Summoned spells ***** + // Giant Claw tentacles + SPELL_GIANT_GROUND_RUPTURE = 26478, + // SPELL_MASSIVE_GROUND_RUPTURE = 26100, // spell not confirmed + SPELL_GROUND_TREMOR = 6524, + SPELL_HAMSTRING = 26211, + SPELL_THRASH = 3391, + + // Npcs + // Phase 1 npcs + NPC_CLAW_TENTACLE = 15725, // summoned by missing spell 26140 + NPC_EYE_TENTACLE = 15726, // summoned by spells 26144 - 26151; script effect of 26152 - triggered every 45 secs + NPC_TENTACLE_PORTAL = 15904, // summoned by missing spell 26396 + + // Phase 2 npcs + NPC_GIANT_CLAW_TENTACLE = 15728, // summoned by missing spell 26216 every 60 secs - also teleported by spell 26191 + NPC_GIANT_EYE_TENTACLE = 15334, // summoned by missing spell 26768 every 60 secs + NPC_FLESH_TENTACLE = 15802, // summoned by missing spell 26237 every 10 secs + NPC_GIANT_TENTACLE_PORTAL = 15910, // summoned by missing spell 26477 + NPC_EXIT_TRIGGER = 15800, + + DISPLAY_ID_CTHUN_BODY = 15786, // Helper display id; This is needed in order to have the proper transform animation. ToDo: remove this when auras are fixed in core. + + AREATRIGGER_STOMACH_1 = 4033, + AREATRIGGER_STOMACH_2 = 4034, + + MAX_FLESH_TENTACLES = 2, + MAX_EYE_TENTACLES = 8, +}; + +static const float afCthunLocations[4][4] = +{ + { -8571.0f, 1990.0f, -98.0f, 1.22f}, // flesh tentacles locations + { -8525.0f, 1994.0f, -98.0f, 2.12f}, + { -8562.0f, 2037.0f, -70.0f, 5.05f}, // stomach teleport location + { -8545.6f, 1987.7f, -32.9f, 0.0f}, // stomach eject location +}; + +enum CThunPhase +{ + PHASE_EYE_NORMAL = 0, + PHASE_EYE_DARK_GLARE = 1, + PHASE_TRANSITION = 2, + PHASE_CTHUN = 3, + PHASE_CTHUN_WEAKENED = 4, +}; + +/*###### +## boss_eye_of_cthun +######*/ + +struct boss_eye_of_cthunAI : public Scripted_NoMovementAI +{ + boss_eye_of_cthunAI(Creature* pCreature) : Scripted_NoMovementAI(pCreature) + { + m_pInstance = (ScriptedInstance*)pCreature->GetInstanceData(); + Reset(); + } + + ScriptedInstance* m_pInstance; + + CThunPhase m_Phase; + + uint32 m_uiClawTentacleTimer; + uint32 m_uiEyeTentacleTimer; + uint32 m_uiBeamTimer; + uint32 m_uiDarkGlareTimer; + uint32 m_uiDarkGlareEndTimer; + + GuidList m_lEyeTentaclesList; + + void Reset() override + { + m_Phase = PHASE_EYE_NORMAL; + + m_uiDarkGlareTimer = 45000; + m_uiDarkGlareEndTimer = 40000; + m_uiClawTentacleTimer = urand(10000, 15000); + m_uiBeamTimer = 0; + m_uiEyeTentacleTimer = 45000; + } + + void Aggro(Unit* /*pWho*/) override + { + if (m_pInstance) + m_pInstance->SetData(TYPE_CTHUN, IN_PROGRESS); + } + + void JustDied(Unit* pKiller) override + { + // Allow the body to begin the transition + if (m_pInstance) + { + if (Creature* pCthun = m_pInstance->GetSingleCreatureFromStorage(NPC_CTHUN)) + pCthun->AI()->AttackStart(pKiller); + else + script_error_log("C'thun could not be found. Please check your database!"); + } + } + + void JustReachedHome() override + { + // Despawn Eye tentacles on evade + DoDespawnEyeTentacles(); + + if (m_pInstance) + m_pInstance->SetData(TYPE_CTHUN, FAIL); + } + + void JustSummoned(Creature* pSummoned) override + { + switch (pSummoned->GetEntry()) + { + case NPC_EYE_TENTACLE: + m_lEyeTentaclesList.push_back(pSummoned->GetObjectGuid()); + // no break; + case NPC_CLAW_TENTACLE: + if (Unit* pTarget = m_creature->SelectAttackingTarget(ATTACKING_TARGET_RANDOM, 0)) + pSummoned->AI()->AttackStart(pTarget); + + pSummoned->SummonCreature(NPC_TENTACLE_PORTAL, pSummoned->GetPositionX(), pSummoned->GetPositionY(), pSummoned->GetPositionZ(), 0, TEMPSUMMON_CORPSE_DESPAWN, 0); + break; + } + } + + void SummonedCreatureJustDied(Creature* pSummoned) override + { + // Despawn the tentacle portal - this applies to all the summoned tentacles + if (Creature* pPortal = GetClosestCreatureWithEntry(pSummoned, NPC_TENTACLE_PORTAL, 5.0f)) + pPortal->ForcedDespawn(); + } + + void SummonedCreatureDespawn(Creature* pSummoned) override + { + // Used only after evade + if (SelectHostileTarget()) + return; + + // Despawn the tentacle portal - this applies to all the summoned tentacles for evade case (which is handled by creature linking) + if (Creature* pPortal = GetClosestCreatureWithEntry(pSummoned, NPC_TENTACLE_PORTAL, 5.0f)) + pPortal->ForcedDespawn(); + } + + // Wrapper to kill the eye tentacles before summoning new ones + void DoDespawnEyeTentacles() + { + for (GuidList::const_iterator itr = m_lEyeTentaclesList.begin(); itr != m_lEyeTentaclesList.end(); ++itr) + { + if (Creature* pTemp = m_creature->GetMap()->GetCreature(*itr)) + pTemp->DealDamage(pTemp, pTemp->GetHealth(), NULL, DIRECT_DAMAGE, SPELL_SCHOOL_MASK_NONE, NULL, false); + } + + m_lEyeTentaclesList.clear(); + } + + // Custom threat management + bool SelectHostileTarget() + { + Unit* pTarget = NULL; + Unit* pOldTarget = m_creature->getVictim(); + + if (!m_creature->GetThreatManager().isThreatListEmpty()) + pTarget = m_creature->GetThreatManager().getHostileTarget(); + + if (pTarget) + { + if (pOldTarget != pTarget && m_Phase == PHASE_EYE_NORMAL) + AttackStart(pTarget); + + // Set victim to old target (if not while Dark Glare) + if (pOldTarget && pOldTarget->IsAlive() && m_Phase == PHASE_EYE_NORMAL) + { + m_creature->SetTargetGuid(pOldTarget->GetObjectGuid()); + m_creature->SetInFront(pOldTarget); + } + + return true; + } + + // Will call EnterEvadeMode if fit + return m_creature->SelectHostileTarget(); + } + + void UpdateAI(const uint32 uiDiff) override + { + if (!SelectHostileTarget()) + return; + + switch (m_Phase) + { + case PHASE_EYE_NORMAL: + + if (m_uiBeamTimer < uiDiff) + { + if (Unit* pTarget = m_creature->SelectAttackingTarget(ATTACKING_TARGET_RANDOM, 0)) + { + if (DoCastSpellIfCan(pTarget, SPELL_EYE_BEAM) == CAST_OK) + { + m_creature->SetTargetGuid(pTarget->GetObjectGuid()); + m_uiBeamTimer = 3000; + } + } + } + else + m_uiBeamTimer -= uiDiff; + + if (m_uiDarkGlareTimer < uiDiff) + { + // Cast the rotation spell + if (DoCastSpellIfCan(m_creature, SPELL_ROTATE_TRIGGER) == CAST_OK) + { + // Remove the target focus but allow the boss to face the current victim + m_creature->SetTargetGuid(ObjectGuid()); + m_creature->SetFacingToObject(m_creature->getVictim()); + + // Switch to Dark Glare phase + m_uiDarkGlareTimer = 45000; + m_Phase = PHASE_EYE_DARK_GLARE; + } + } + else + m_uiDarkGlareTimer -= uiDiff; + + break; + case PHASE_EYE_DARK_GLARE: + + if (m_uiDarkGlareEndTimer < uiDiff) + { + // Remove rotation auras + m_creature->RemoveAurasDueToSpell(SPELL_ROTATE_360_LEFT); + m_creature->RemoveAurasDueToSpell(SPELL_ROTATE_360_RIGHT); + + // Switch to Eye Beam + m_uiDarkGlareEndTimer = 40000; + m_uiBeamTimer = 1000; + m_Phase = PHASE_EYE_NORMAL; + } + else + m_uiDarkGlareEndTimer -= uiDiff; + + break; + } + + if (m_uiClawTentacleTimer < uiDiff) + { + if (Unit* pTarget = m_creature->SelectAttackingTarget(ATTACKING_TARGET_RANDOM, 0)) + { + // Spawn claw tentacle on the random target on both phases + m_creature->SummonCreature(NPC_CLAW_TENTACLE, pTarget->GetPositionX(), pTarget->GetPositionY(), pTarget->GetPositionZ(), 0, TEMPSUMMON_DEAD_DESPAWN, 0); + m_uiClawTentacleTimer = urand(7000, 13000); + } + } + else + m_uiClawTentacleTimer -= uiDiff; + + if (m_uiEyeTentacleTimer <= uiDiff) + { + // Despawn the eye tentacles if necessary + DoDespawnEyeTentacles(); + + // Spawn 8 Eye Tentacles + float fX, fY, fZ; + for (uint8 i = 0; i < MAX_EYE_TENTACLES; ++i) + { + m_creature->GetNearPoint(m_creature, fX, fY, fZ, 0, 30.0f, M_PI_F / 4 * i); + m_creature->SummonCreature(NPC_EYE_TENTACLE, fX, fY, fZ, 0, TEMPSUMMON_DEAD_DESPAWN, 0); + } + + m_uiEyeTentacleTimer = 45000; + } + else + m_uiEyeTentacleTimer -= uiDiff; + } +}; + +/*###### +## boss_cthun +######*/ + +struct boss_cthunAI : public Scripted_NoMovementAI +{ + boss_cthunAI(Creature* pCreature) : Scripted_NoMovementAI(pCreature) + { + m_pInstance = (ScriptedInstance*)pCreature->GetInstanceData(); + // Set active in order to be used during the instance progress + m_creature->SetActiveObjectState(true); + Reset(); + } + + ScriptedInstance* m_pInstance; + + CThunPhase m_Phase; + + // Global variables + uint32 m_uiPhaseTimer; + uint8 m_uiFleshTentaclesKilled; + uint32 m_uiEyeTentacleTimer; + uint32 m_uiGiantClawTentacleTimer; + uint32 m_uiGiantEyeTentacleTimer; + uint32 m_uiDigestiveAcidTimer; + + // Body Phase + uint32 m_uiMouthTentacleTimer; + uint32 m_uiStomachEnterTimer; + + GuidList m_lEyeTentaclesList; + GuidList m_lPlayersInStomachList; + + ObjectGuid m_stomachEnterTargetGuid; + + void Reset() override + { + // Phase information + m_Phase = PHASE_TRANSITION; + + m_uiPhaseTimer = 5000; + m_uiFleshTentaclesKilled = 0; + m_uiEyeTentacleTimer = 35000; + m_uiGiantClawTentacleTimer = 20000; + m_uiGiantEyeTentacleTimer = 50000; + m_uiDigestiveAcidTimer = 4000; + + // Body Phase + m_uiMouthTentacleTimer = 15000; + m_uiStomachEnterTimer = 0; + + // Clear players in stomach + m_lPlayersInStomachList.clear(); + + // Reset flags + m_creature->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE); + } + + void DamageTaken(Unit* /*pDealer*/, uint32& uiDamage) override + { + // Ignore damage reduction when vulnerable + if (m_Phase == PHASE_CTHUN_WEAKENED) + return; + + // Not weakened so reduce damage by 99% - workaround for missing spell 26156 + if (uiDamage / 99 > 0) + uiDamage /= 99; + else + uiDamage = 1; + + // Prevent death in non-weakened state + if (uiDamage >= m_creature->GetHealth()) + uiDamage = 0; + } + + void EnterEvadeMode() override + { + // Kill any player from the stomach on evade - this is becuase C'thun cannot be soloed. + for (GuidList::const_iterator itr = m_lPlayersInStomachList.begin(); itr != m_lPlayersInStomachList.end(); ++itr) + { + // Workaround for missing spell 26648 + if (Player* pPlayer = m_creature->GetMap()->GetPlayer(*itr)) + m_creature->DealDamage(pPlayer, pPlayer->GetHealth(), NULL, DIRECT_DAMAGE, SPELL_SCHOOL_MASK_NONE, NULL, false); + } + + Scripted_NoMovementAI::EnterEvadeMode(); + } + + void JustReachedHome() override + { + if (m_pInstance) + m_pInstance->SetData(TYPE_CTHUN, FAIL); + } + + void JustDied(Unit* /*pKiller*/) override + { + m_creature->SetActiveObjectState(false); + + if (m_pInstance) + m_pInstance->SetData(TYPE_CTHUN, DONE); + } + + void JustSummoned(Creature* pSummoned) override + { + switch (pSummoned->GetEntry()) + { + case NPC_EYE_TENTACLE: + if (Unit* pTarget = m_creature->SelectAttackingTarget(ATTACKING_TARGET_RANDOM, 0)) + pSummoned->AI()->AttackStart(pTarget); + + m_lEyeTentaclesList.push_back(pSummoned->GetObjectGuid()); + pSummoned->SummonCreature(NPC_TENTACLE_PORTAL, pSummoned->GetPositionX(), pSummoned->GetPositionY(), pSummoned->GetPositionZ(), 0, TEMPSUMMON_CORPSE_DESPAWN, 0); + break; + case NPC_GIANT_EYE_TENTACLE: + case NPC_GIANT_CLAW_TENTACLE: + if (Unit* pTarget = m_creature->SelectAttackingTarget(ATTACKING_TARGET_RANDOM, 0)) + pSummoned->AI()->AttackStart(pTarget); + + pSummoned->SummonCreature(NPC_GIANT_TENTACLE_PORTAL, pSummoned->GetPositionX(), pSummoned->GetPositionY(), pSummoned->GetPositionZ(), 0, TEMPSUMMON_CORPSE_DESPAWN, 0); + break; + } + } + + void SummonedCreatureJustDied(Creature* pSummoned) override + { + switch (pSummoned->GetEntry()) + { + // Handle portal despawn on tentacle kill + case NPC_EYE_TENTACLE: + if (Creature* pPortal = GetClosestCreatureWithEntry(pSummoned, NPC_TENTACLE_PORTAL, 5.0f)) + pPortal->ForcedDespawn(); + break; + case NPC_GIANT_EYE_TENTACLE: + case NPC_GIANT_CLAW_TENTACLE: + if (Creature* pPortal = GetClosestCreatureWithEntry(pSummoned, NPC_GIANT_TENTACLE_PORTAL, 5.0f)) + pPortal->ForcedDespawn(); + break; + // Handle the stomach tentacles kill + case NPC_FLESH_TENTACLE: + ++m_uiFleshTentaclesKilled; + if (m_uiFleshTentaclesKilled == MAX_FLESH_TENTACLES) + { + if (DoCastSpellIfCan(m_creature, SPELL_CTHUN_VULNERABLE, CAST_INTERRUPT_PREVIOUS) == CAST_OK) + { + DoScriptText(EMOTE_WEAKENED, m_creature); + m_uiPhaseTimer = 45000; + m_Phase = PHASE_CTHUN_WEAKENED; + } + } + break; + } + } + + // Wrapper to handle the Flesh Tentacles spawn + void DoSpawnFleshTentacles() + { + m_uiFleshTentaclesKilled = 0; + + // Spawn 2 flesh tentacles + for (uint8 i = 0; i < MAX_FLESH_TENTACLES; ++i) + m_creature->SummonCreature(NPC_FLESH_TENTACLE, afCthunLocations[i][0], afCthunLocations[i][1], afCthunLocations[i][2], afCthunLocations[i][3], TEMPSUMMON_DEAD_DESPAWN, 0); + } + + // Wrapper to kill the eye tentacles before summoning new ones + void DoDespawnEyeTentacles() + { + for (GuidList::const_iterator itr = m_lEyeTentaclesList.begin(); itr != m_lEyeTentaclesList.end(); ++itr) + { + if (Creature* pTemp = m_creature->GetMap()->GetCreature(*itr)) + pTemp->DealDamage(pTemp, pTemp->GetHealth(), NULL, DIRECT_DAMAGE, SPELL_SCHOOL_MASK_NONE, NULL, false); + } + + m_lEyeTentaclesList.clear(); + } + + // Wrapper to remove a stored player from the stomach + void DoRemovePlayerFromStomach(Player* pPlayer) + { + if (pPlayer) + m_lPlayersInStomachList.remove(pPlayer->GetObjectGuid()); + } + + // Custom threat management + bool SelectHostileTarget() + { + Unit* pTarget = NULL; + Unit* pOldTarget = m_creature->getVictim(); + + if (!m_creature->GetThreatManager().isThreatListEmpty()) + pTarget = m_creature->GetThreatManager().getHostileTarget(); + + if (pTarget) + { + if (pOldTarget != pTarget) + AttackStart(pTarget); + + // Set victim to old target + if (pOldTarget && pOldTarget->IsAlive()) + { + m_creature->SetTargetGuid(pOldTarget->GetObjectGuid()); + m_creature->SetInFront(pOldTarget); + } + + return true; + } + + // Will call EnterEvadeMode if fit + return m_creature->SelectHostileTarget(); + } + + void UpdateAI(const uint32 uiDiff) override + { + if (!SelectHostileTarget()) + return; + + switch (m_Phase) + { + case PHASE_TRANSITION: + + if (m_uiPhaseTimer < uiDiff) + { + // Note: we need to set the display id before casting the transform spell, in order to get the proper animation + m_creature->SetDisplayId(DISPLAY_ID_CTHUN_BODY); + + // Transform and start C'thun phase + if (DoCastSpellIfCan(m_creature, SPELL_TRANSFORM) == CAST_OK) + { + m_creature->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE); + DoSpawnFleshTentacles(); + + m_Phase = PHASE_CTHUN; + m_uiPhaseTimer = 0; + } + } + else + m_uiPhaseTimer -= uiDiff; + + break; + case PHASE_CTHUN: + + if (m_uiMouthTentacleTimer < uiDiff) + { + if (Unit* pTarget = m_creature->SelectAttackingTarget(ATTACKING_TARGET_RANDOM, 0, uint32(0), SELECT_FLAG_IN_LOS)) + { + // Cast the spell using the target as source + pTarget->InterruptNonMeleeSpells(false); + pTarget->CastSpell(pTarget, SPELL_MOUTH_TENTACLE, true, NULL, NULL, m_creature->GetObjectGuid()); + m_stomachEnterTargetGuid = pTarget->GetObjectGuid(); + + m_uiStomachEnterTimer = 3800; + m_uiMouthTentacleTimer = urand(13000, 15000); + } + } + else + m_uiMouthTentacleTimer -= uiDiff; + + // Teleport the target to the stomach after a few seconds + if (m_uiStomachEnterTimer) + { + if (m_uiStomachEnterTimer <= uiDiff) + { + // Check for valid player + if (Player* pPlayer = m_creature->GetMap()->GetPlayer(m_stomachEnterTargetGuid)) + { + DoTeleportPlayer(pPlayer, afCthunLocations[2][0], afCthunLocations[2][1], afCthunLocations[2][2], afCthunLocations[2][3]); + m_lPlayersInStomachList.push_back(pPlayer->GetObjectGuid()); + } + + m_stomachEnterTargetGuid.Clear(); + m_uiStomachEnterTimer = 0; + } + else + m_uiStomachEnterTimer -= uiDiff; + } + + break; + case PHASE_CTHUN_WEAKENED: + + // Handle Flesh Tentacles respawn when the vulnerability spell expires + if (m_uiPhaseTimer < uiDiff) + { + DoSpawnFleshTentacles(); + + m_uiPhaseTimer = 0; + m_Phase = PHASE_CTHUN; + } + else + m_uiPhaseTimer -= uiDiff; + + break; + } + + if (m_uiGiantClawTentacleTimer < uiDiff) + { + // Summon 1 Giant Claw Tentacle every 60 seconds + if (Unit* pTarget = m_creature->SelectAttackingTarget(ATTACKING_TARGET_RANDOM, 0, uint32(0), SELECT_FLAG_IN_LOS)) + m_creature->SummonCreature(NPC_GIANT_CLAW_TENTACLE, pTarget->GetPositionX(), pTarget->GetPositionY(), pTarget->GetPositionZ(), 0, TEMPSUMMON_DEAD_DESPAWN, 0); + + m_uiGiantClawTentacleTimer = 60000; + } + else + m_uiGiantClawTentacleTimer -= uiDiff; + + if (m_uiGiantEyeTentacleTimer < uiDiff) + { + // Summon 1 Giant Eye Tentacle every 60 seconds + if (Unit* pTarget = m_creature->SelectAttackingTarget(ATTACKING_TARGET_RANDOM, 0, uint32(0), SELECT_FLAG_IN_LOS)) + m_creature->SummonCreature(NPC_GIANT_EYE_TENTACLE, pTarget->GetPositionX(), pTarget->GetPositionY(), pTarget->GetPositionZ(), 0, TEMPSUMMON_DEAD_DESPAWN, 0); + + m_uiGiantEyeTentacleTimer = 60000; + } + else + m_uiGiantEyeTentacleTimer -= uiDiff; + + if (m_uiEyeTentacleTimer < uiDiff) + { + DoDespawnEyeTentacles(); + + // Spawn 8 Eye Tentacles every 30 seconds + float fX, fY, fZ; + for (uint8 i = 0; i < MAX_EYE_TENTACLES; ++i) + { + m_creature->GetNearPoint(m_creature, fX, fY, fZ, 0, 30.0f, M_PI_F / 4 * i); + m_creature->SummonCreature(NPC_EYE_TENTACLE, fX, fY, fZ, 0, TEMPSUMMON_DEAD_DESPAWN, 0); + } + + m_uiEyeTentacleTimer = 30000; + } + else + m_uiEyeTentacleTimer -= uiDiff; + + // Note: this should be handled by the maps + if (m_uiDigestiveAcidTimer < uiDiff) + { + // Iterate the Stomach players list and apply the Digesti acid debuff on them every 4 sec + for (GuidList::const_iterator itr = m_lPlayersInStomachList.begin(); itr != m_lPlayersInStomachList.end(); ++itr) + { + if (Player* pPlayer = m_creature->GetMap()->GetPlayer(*itr)) + pPlayer->CastSpell(pPlayer, SPELL_DIGESTIVE_ACID, true, NULL, NULL, m_creature->GetObjectGuid()); + } + m_uiDigestiveAcidTimer = 4000; + } + else + m_uiDigestiveAcidTimer -= uiDiff; + } +}; + +/*###### +## npc_giant_claw_tentacle +######*/ + +struct npc_giant_claw_tentacleAI : public Scripted_NoMovementAI +{ + npc_giant_claw_tentacleAI(Creature* pCreature) : Scripted_NoMovementAI(pCreature) + { + m_pInstance = (ScriptedInstance*)pCreature->GetInstanceData(); + Reset(); + } + + ScriptedInstance* m_pInstance; + + uint32 m_uiThrashTimer; + uint32 m_uiHamstringTimer; + uint32 m_uiDistCheckTimer; + + void Reset() override + { + m_uiHamstringTimer = 2000; + m_uiThrashTimer = 5000; + m_uiDistCheckTimer = 5000; + + DoCastSpellIfCan(m_creature, SPELL_GIANT_GROUND_RUPTURE); + } + + void UpdateAI(const uint32 uiDiff) override + { + // Check if we have a target + if (!m_creature->SelectHostileTarget() || !m_creature->getVictim()) + return; + + if (m_uiDistCheckTimer < uiDiff) + { + // If there is nobody in range, spawn a new tentacle at a new target location + if (!m_creature->SelectAttackingTarget(ATTACKING_TARGET_TOPAGGRO, 0, uint32(0), SELECT_FLAG_IN_MELEE_RANGE) && m_pInstance) + { + if (Creature* pCthun = m_pInstance->GetSingleCreatureFromStorage(NPC_CTHUN)) + { + if (Unit* pTarget = m_creature->SelectAttackingTarget(ATTACKING_TARGET_RANDOM, 0, uint32(0), SELECT_FLAG_NOT_IN_MELEE_RANGE)) + { + pCthun->SummonCreature(NPC_GIANT_CLAW_TENTACLE, pTarget->GetPositionX(), pTarget->GetPositionY(), pTarget->GetPositionZ(), 0, TEMPSUMMON_DEAD_DESPAWN, 0); + + // Self kill when a new tentacle is spawned + m_creature->DealDamage(m_creature, m_creature->GetHealth(), NULL, DIRECT_DAMAGE, SPELL_SCHOOL_MASK_NONE, NULL, false); + return; + } + } + } + else + m_uiDistCheckTimer = 5000; + } + else + m_uiDistCheckTimer -= uiDiff; + + if (m_uiThrashTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature->getVictim(), SPELL_THRASH) == CAST_OK) + m_uiThrashTimer = 10000; + } + else + m_uiThrashTimer -= uiDiff; + + if (m_uiHamstringTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature->getVictim(), SPELL_HAMSTRING) == CAST_OK) + m_uiHamstringTimer = 10000; + } + else + m_uiHamstringTimer -= uiDiff; + + DoMeleeAttackIfReady(); + } +}; + +/*###### +## at_stomach_cthun +######*/ + +bool AreaTrigger_at_stomach_cthun(Player* pPlayer, AreaTriggerEntry const* pAt) +{ + if (pAt->id == AREATRIGGER_STOMACH_1) + { + if (pPlayer->isGameMaster() || !pPlayer->IsAlive()) + return false; + + // Summon the exit trigger which should push the player outside the stomach - not used because missing eject spells + // if (!GetClosestCreatureWithEntry(pPlayer, NPC_EXIT_TRIGGER, 10.0f)) + // pPlayer->CastSpell(pPlayer, SPELL_EXIT_STOMACH, true); + + // Note: because of the missing spell id 26224, we will use basic jump movement + pPlayer->GetMotionMaster()->MoveJump(afCthunLocations[3][0], afCthunLocations[3][1], afCthunLocations[3][2], pPlayer->GetSpeed(MOVE_RUN) * 5, 0); + } + else if (pAt->id == AREATRIGGER_STOMACH_2) + { + if (ScriptedInstance* pInstance = (ScriptedInstance*)pPlayer->GetInstanceData()) + { + if (Creature* pCthun = pInstance->GetSingleCreatureFromStorage(NPC_CTHUN)) + { + // Remove player from the Stomach + if (boss_cthunAI* pBossAI = dynamic_cast(pCthun->AI())) + pBossAI->DoRemovePlayerFromStomach(pPlayer); + + // Teleport back to C'thun and remove the Digestive Acid + pPlayer->RemoveAurasDueToSpell(SPELL_DIGESTIVE_ACID); + pPlayer->NearTeleportTo(pCthun->GetPositionX(), pCthun->GetPositionY(), pCthun->GetPositionZ() + 15.0f, frand(0, 2 * M_PI_F)); + + // Note: the real knockback spell id should be 26230 + pPlayer->CastSpell(pPlayer, SPELL_EXIT_STOMACH_KNOCKBACK, true, NULL, NULL, pCthun->GetObjectGuid()); + } + } + } + + return false; +} + +CreatureAI* GetAI_boss_eye_of_cthun(Creature* pCreature) +{ + return new boss_eye_of_cthunAI(pCreature); +} + +CreatureAI* GetAI_boss_cthun(Creature* pCreature) +{ + return new boss_cthunAI(pCreature); +} + +CreatureAI* GetAI_npc_giant_claw_tentacle(Creature* pCreature) +{ + return new npc_giant_claw_tentacleAI(pCreature); +} + +void AddSC_boss_cthun() +{ + Script* pNewScript; + + pNewScript = new Script; + pNewScript->Name = "boss_eye_of_cthun"; + pNewScript->GetAI = &GetAI_boss_eye_of_cthun; + pNewScript->RegisterSelf(); + + pNewScript = new Script; + pNewScript->Name = "boss_cthun"; + pNewScript->GetAI = &GetAI_boss_cthun; + pNewScript->RegisterSelf(); + + pNewScript = new Script; + pNewScript->Name = "mob_giant_claw_tentacle"; + pNewScript->GetAI = &GetAI_npc_giant_claw_tentacle; + pNewScript->RegisterSelf(); + + pNewScript = new Script; + pNewScript->Name = "at_stomach_cthun"; + pNewScript->pAreaTrigger = &AreaTrigger_at_stomach_cthun; + pNewScript->RegisterSelf(); +} diff --git a/src/modules/SD2/scripts/kalimdor/temple_of_ahnqiraj/boss_fankriss.cpp b/src/modules/SD2/scripts/kalimdor/temple_of_ahnqiraj/boss_fankriss.cpp new file mode 100644 index 000000000..1a4692158 --- /dev/null +++ b/src/modules/SD2/scripts/kalimdor/temple_of_ahnqiraj/boss_fankriss.cpp @@ -0,0 +1,183 @@ +/* Copyright (C) 2006 - 2013 ScriptDev2 + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with 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" +#include "temple_of_ahnqiraj.h" + +enum +{ + SOUND_SENTENCE_YOU = 8588, + SOUND_SERVE_TO = 8589, + SOUND_LAWS = 8590, + SOUND_TRESPASS = 8591, + SOUND_WILL_BE = 8592, + + SPELL_MORTAL_WOUND = 25646, + SPELL_ENTANGLE_1 = 720, + SPELL_ENTANGLE_2 = 731, + SPELL_ENTANGLE_3 = 1121, + SPELL_SUMMON_WORM_1 = 518, + SPELL_SUMMON_WORM_2 = 25831, + SPELL_SUMMON_WORM_3 = 25832, + + NPC_SPAWN_FANKRISS = 15630, + NPC_VEKNISS_HATCHLING = 15962, +}; + +static const uint32 aSummonWormSpells[3] = {SPELL_SUMMON_WORM_1, SPELL_SUMMON_WORM_2, SPELL_SUMMON_WORM_3}; +static const uint32 aEntangleSpells[3] = {SPELL_ENTANGLE_1, SPELL_ENTANGLE_2, SPELL_ENTANGLE_3}; + +struct boss_fankrissAI : public ScriptedAI +{ + boss_fankrissAI(Creature* pCreature) : ScriptedAI(pCreature) + { + m_pInstance = (ScriptedInstance*)pCreature->GetInstanceData(); + Reset(); + } + + ScriptedInstance* m_pInstance; + + uint32 m_uiMortalWoundTimer; + uint32 m_uiSummonWormTimer; + uint32 m_uiEntangleTimer; + uint32 m_uiEntangleSummonTimer; + + ObjectGuid m_EntangleTargetGuid; + + void Reset() override + { + m_uiMortalWoundTimer = urand(10000, 15000); + m_uiSummonWormTimer = urand(30000, 50000); + m_uiEntangleTimer = urand(25000, 40000); + m_uiEntangleSummonTimer = 0; + } + + void Aggro(Unit* /*pWho*/) override + { + if (m_pInstance) + m_pInstance->SetData(TYPE_FANKRISS, IN_PROGRESS); + } + + void JustReachedHome() override + { + if (m_pInstance) + m_pInstance->SetData(TYPE_FANKRISS, FAIL); + } + + void JustDied(Unit* /*pKiller*/) override + { + if (m_pInstance) + m_pInstance->SetData(TYPE_FANKRISS, DONE); + } + + void JustSummoned(Creature* pSummoned) override + { + if (pSummoned->GetEntry() == NPC_SPAWN_FANKRISS) + { + if (Unit* pTarget = m_creature->SelectAttackingTarget(ATTACKING_TARGET_RANDOM, 0)) + pSummoned->AI()->AttackStart(pTarget); + } + else if (pSummoned->GetEntry() == NPC_VEKNISS_HATCHLING) + { + if (Player* pTarget = m_creature->GetMap()->GetPlayer(m_EntangleTargetGuid)) + pSummoned->AI()->AttackStart(pTarget); + } + } + + void UpdateAI(const uint32 uiDiff) override + { + if (!m_creature->SelectHostileTarget() || !m_creature->getVictim()) + return; + + if (m_uiMortalWoundTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature->getVictim(), SPELL_MORTAL_WOUND) == CAST_OK) + m_uiMortalWoundTimer = urand(7000, 14000); + } + else + m_uiMortalWoundTimer -= uiDiff; + + if (m_uiSummonWormTimer < uiDiff) + { + uint8 uiSpawnIndex = urand(0, 2); + if (DoCastSpellIfCan(m_creature, aSummonWormSpells[uiSpawnIndex]) == CAST_OK) + m_uiSummonWormTimer = urand(15000, 40000); + } + else + m_uiSummonWormTimer -= uiDiff; + + // Teleporting Random Target to one of the three tunnels and spawn 4 hatchlings near the gamer. + if (m_uiEntangleTimer < uiDiff) + { + if (Unit* pTarget = m_creature->SelectAttackingTarget(ATTACKING_TARGET_RANDOM, 0, uint32(0), SELECT_FLAG_PLAYER)) + { + uint8 uiEntangleIndex = urand(0, 2); + if (DoCastSpellIfCan(pTarget, aEntangleSpells[uiEntangleIndex]) == CAST_OK) + { + m_EntangleTargetGuid = pTarget->GetObjectGuid(); + m_uiEntangleSummonTimer = 1000; + m_uiEntangleTimer = urand(40000, 70000); + } + } + } + else + m_uiEntangleTimer -= uiDiff; + + // Summon 4 Hatchlings around the target + if (m_uiEntangleSummonTimer) + { + if (m_uiEntangleSummonTimer <= uiDiff) + { + if (Player* pTarget = m_creature->GetMap()->GetPlayer(m_EntangleTargetGuid)) + { + float fX, fY, fZ; + for (uint8 i = 0; i < 4; ++i) + { + m_creature->GetRandomPoint(pTarget->GetPositionX(), pTarget->GetPositionY(), pTarget->GetPositionZ(), 3.0f, fX, fY, fZ); + m_creature->SummonCreature(NPC_VEKNISS_HATCHLING, fX, fY, fZ, 0.0f, TEMPSUMMON_TIMED_OOC_DESPAWN, 10000); + } + m_uiEntangleSummonTimer = 0; + } + } + else + m_uiEntangleSummonTimer -= uiDiff; + } + + DoMeleeAttackIfReady(); + } +}; + +CreatureAI* GetAI_boss_fankriss(Creature* pCreature) +{ + return new boss_fankrissAI(pCreature); +} + +void AddSC_boss_fankriss() +{ + Script* pNewScript; + + pNewScript = new Script; + pNewScript->Name = "boss_fankriss"; + pNewScript->GetAI = &GetAI_boss_fankriss; + pNewScript->RegisterSelf(); +} diff --git a/src/modules/SD2/scripts/kalimdor/temple_of_ahnqiraj/boss_huhuran.cpp b/src/modules/SD2/scripts/kalimdor/temple_of_ahnqiraj/boss_huhuran.cpp new file mode 100644 index 000000000..f62872f7f --- /dev/null +++ b/src/modules/SD2/scripts/kalimdor/temple_of_ahnqiraj/boss_huhuran.cpp @@ -0,0 +1,161 @@ +/* Copyright (C) 2006 - 2013 ScriptDev2 + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with 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: 90 +SDComment: Timed enrage NYI; Timers +SDCategory: Temple of Ahn'Qiraj +EndScriptData */ + +#include "precompiled.h" +#include "temple_of_ahnqiraj.h" + +enum +{ + EMOTE_GENERIC_FRENZY_KILL = -1000001, + EMOTE_GENERIC_BERSERK = -1000004, + + SPELL_ENRAGE = 26051, // triggers 26052 + SPELL_BERSERK = 26068, // triggers 26052 + SPELL_NOXIOUS_POISON = 26053, + SPELL_WYVERN_STING = 26180, + SPELL_ACID_SPIT = 26050 +}; + +struct boss_huhuranAI : public ScriptedAI +{ + boss_huhuranAI(Creature* pCreature) : ScriptedAI(pCreature) + { + m_pInstance = (ScriptedInstance*)pCreature->GetInstanceData(); + Reset(); + } + + ScriptedInstance* m_pInstance; + + uint32 m_uiFrenzyTimer; + uint32 m_uiWyvernTimer; + uint32 m_uiSpitTimer; + uint32 m_uiNoxiousPoisonTimer; + + bool m_bIsBerserk; + + void Reset() override + { + m_uiFrenzyTimer = urand(25000, 35000); + m_uiWyvernTimer = urand(18000, 28000); + m_uiSpitTimer = 8000; + m_uiNoxiousPoisonTimer = urand(10000, 20000); + m_bIsBerserk = false; + } + + void Aggro(Unit* /*pWho*/) override + { + if (m_pInstance) + m_pInstance->SetData(TYPE_HUHURAN, IN_PROGRESS); + } + + void JustReachedHome() override + { + if (m_pInstance) + m_pInstance->SetData(TYPE_HUHURAN, FAIL); + } + + void JustDied(Unit* /*pKiller*/) override + { + if (m_pInstance) + m_pInstance->SetData(TYPE_HUHURAN, DONE); + } + + void UpdateAI(const uint32 uiDiff) override + { + // Return since we have no target + if (!m_creature->SelectHostileTarget() || !m_creature->getVictim()) + return; + + // Frenzy_Timer + if (!m_bIsBerserk) + { + if (m_uiFrenzyTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature, SPELL_ENRAGE) == CAST_OK) + { + DoScriptText(EMOTE_GENERIC_FRENZY_KILL, m_creature); + m_uiFrenzyTimer = urand(25000, 35000); + } + } + else + m_uiFrenzyTimer -= uiDiff; + } + + // Wyvern Timer + if (m_uiWyvernTimer < uiDiff) + { + if (Unit* pTarget = m_creature->SelectAttackingTarget(ATTACKING_TARGET_RANDOM, 0)) + { + if (DoCastSpellIfCan(pTarget, SPELL_WYVERN_STING) == CAST_OK) + m_uiWyvernTimer = urand(15000, 32000); + } + } + else + m_uiWyvernTimer -= uiDiff; + + // Spit Timer + if (m_uiSpitTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature->getVictim(), SPELL_ACID_SPIT) == CAST_OK) + m_uiSpitTimer = urand(5000, 10000); + } + else + m_uiSpitTimer -= uiDiff; + + // NoxiousPoison_Timer + if (m_uiNoxiousPoisonTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature->getVictim(), SPELL_NOXIOUS_POISON) == CAST_OK) + m_uiNoxiousPoisonTimer = urand(12000, 24000); + } + else + m_uiNoxiousPoisonTimer -= uiDiff; + + // Berserk + if (!m_bIsBerserk && m_creature->GetHealthPercent() < 30.0f) + { + if (DoCastSpellIfCan(m_creature, SPELL_BERSERK) == CAST_OK) + { + DoScriptText(EMOTE_GENERIC_BERSERK, m_creature); + m_bIsBerserk = true; + } + } + + DoMeleeAttackIfReady(); + } +}; + +CreatureAI* GetAI_boss_huhuran(Creature* pCreature) +{ + return new boss_huhuranAI(pCreature); +} + +void AddSC_boss_huhuran() +{ + Script* pNewScript; + + pNewScript = new Script; + pNewScript->Name = "boss_huhuran"; + pNewScript->GetAI = &GetAI_boss_huhuran; + pNewScript->RegisterSelf(); +} diff --git a/src/modules/SD2/scripts/kalimdor/temple_of_ahnqiraj/boss_ouro.cpp b/src/modules/SD2/scripts/kalimdor/temple_of_ahnqiraj/boss_ouro.cpp new file mode 100644 index 000000000..c70567f6b --- /dev/null +++ b/src/modules/SD2/scripts/kalimdor/temple_of_ahnqiraj/boss_ouro.cpp @@ -0,0 +1,325 @@ +/* Copyright (C) 2006 - 2013 ScriptDev2 + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with 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: 90 +SDComment: Some minor adjustments may be required +SDCategory: Temple of Ahn'Qiraj +EndScriptData */ + +#include "precompiled.h" +#include "temple_of_ahnqiraj.h" + +enum +{ + // ground spells + SPELL_SWEEP = 26103, + SPELL_SANDBLAST = 26102, + SPELL_BOULDER = 26616, + SPELL_BERSERK = 26615, + + // emerge spells + SPELL_BIRTH = 26262, // The Birth Animation + SPELL_GROUND_RUPTURE = 26100, // spell not confirmed + SPELL_SUMMON_BASE = 26133, // summons gameobject 180795 + + // submerge spells + SPELL_SUBMERGE_VISUAL = 26063, + SPELL_SUMMON_OURO_MOUND = 26058, // summons 5 dirt mounds + SPELL_SUMMON_TRIGGER = 26284, + + SPELL_SUMMON_OURO = 26642, + SPELL_QUAKE = 26093, + + // other spells - not used + // SPELL_SUMMON_SCARABS = 26060, // triggered after 30 secs - cast by the Dirt Mounds + // SPELL_DIRTMOUND_PASSIVE = 26092, // casts 26093 every 1 sec - removed from DBC + // SPELL_SET_OURO_HEALTH = 26075, // removed from DBC + // SPELL_SAVE_OURO_HEALTH = 26076, // removed from DBC + // SPELL_TELEPORT_TRIGGER = 26285, // removed from DBC + // SPELL_SUBMERGE_TRIGGER = 26104, // removed from DBC + // SPELL_SUMMON_OURO_MOUND = 26617, // removed from DBC + // SPELL_SCARABS_PERIODIC = 26619, // cast by the Dirt Mounds in order to spawn the scarabs - removed from DBC + + // summoned npcs + NPC_OURO = 15517, + // NPC_OURO_SCARAB = 15718, // summoned by Dirt Mounds + NPC_OURO_TRIGGER = 15717, + NPC_DIRT_MOUND = 15712, // summoned also by missing spell 26617 +}; + +struct boss_ouroAI : public Scripted_NoMovementAI +{ + boss_ouroAI(Creature* pCreature) : Scripted_NoMovementAI(pCreature) + { + m_pInstance = (ScriptedInstance*)pCreature->GetInstanceData(); + Reset(); + } + + ScriptedInstance* m_pInstance; + + uint32 m_uiSweepTimer; + uint32 m_uiSandBlastTimer; + uint32 m_uiSubmergeTimer; + uint32 m_uiSummonBaseTimer; + + uint32 m_uiSummonMoundTimer; + + bool m_bEnraged; + bool m_bSubmerged; + + ObjectGuid m_ouroTriggerGuid; + + void Reset() override + { + m_uiSweepTimer = urand(35000, 40000); + m_uiSandBlastTimer = urand(30000, 45000); + m_uiSubmergeTimer = 90000; + m_uiSummonBaseTimer = 2000; + + m_uiSummonMoundTimer = 10000; + + m_bEnraged = false; + m_bSubmerged = false; + } + + void Aggro(Unit* /*pWho*/) override + { + if (m_pInstance) + m_pInstance->SetData(TYPE_OURO, IN_PROGRESS); + } + + void JustReachedHome() override + { + if (m_pInstance) + m_pInstance->SetData(TYPE_OURO, FAIL); + + m_creature->ForcedDespawn(); + } + + void JustDied(Unit* /*pKiller*/) override + { + if (m_pInstance) + m_pInstance->SetData(TYPE_OURO, DONE); + } + + void JustSummoned(Creature* pSummoned) override + { + switch (pSummoned->GetEntry()) + { + case NPC_OURO_TRIGGER: + m_ouroTriggerGuid = pSummoned->GetObjectGuid(); + // no break; + case NPC_DIRT_MOUND: + pSummoned->GetMotionMaster()->MoveRandomAroundPoint(pSummoned->GetPositionX(), pSummoned->GetPositionY(), pSummoned->GetPositionZ(), 40.0f); + break; + } + } + + void UpdateAI(const uint32 uiDiff) override + { + // Return since we have no pTarget + if (!m_creature->SelectHostileTarget() || !m_creature->getVictim()) + return; + + if (!m_bSubmerged) + { + // Summon sandworm base + if (m_uiSummonBaseTimer) + { + if (m_uiSummonBaseTimer <= uiDiff) + { + // Note: server side spells should be cast directly + m_creature->CastSpell(m_creature, SPELL_SUMMON_BASE, true); + m_uiSummonBaseTimer = 0; + } + else + m_uiSummonBaseTimer -= uiDiff; + } + + // Sweep + if (m_uiSweepTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature, SPELL_SWEEP) == CAST_OK) + m_uiSweepTimer = 20000; + } + else + m_uiSweepTimer -= uiDiff; + + // Sand Blast + if (m_uiSandBlastTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature, SPELL_SANDBLAST) == CAST_OK) + m_uiSandBlastTimer = 22000; + } + else + m_uiSandBlastTimer -= uiDiff; + + if (!m_bEnraged) + { + // Enrage at 20% HP + if (m_creature->GetHealthPercent() < 20.0f) + { + if (DoCastSpellIfCan(m_creature, SPELL_BERSERK) == CAST_OK) + { + m_bEnraged = true; + return; + } + } + + // Submerge + if (m_uiSubmergeTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature, SPELL_SUBMERGE_VISUAL) == CAST_OK) + { + DoCastSpellIfCan(m_creature, SPELL_SUMMON_OURO_MOUND, CAST_TRIGGERED); + DoCastSpellIfCan(m_creature, SPELL_SUMMON_TRIGGER, CAST_TRIGGERED); + + m_creature->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE); + + m_bSubmerged = true; + m_uiSubmergeTimer = 30000; + } + } + else + m_uiSubmergeTimer -= uiDiff; + } + else + { + // Summon 1 mound every 10 secs when enraged + if (m_uiSummonMoundTimer < uiDiff) + { + DoSpawnCreature(NPC_DIRT_MOUND, 0, 0, 0, 0, TEMPSUMMON_CORPSE_DESPAWN, 0); + m_uiSummonMoundTimer = 10000; + } + else + m_uiSummonMoundTimer -= uiDiff; + } + + // If we are within range melee the target + if (m_creature->CanReachWithMeleeAttack(m_creature->getVictim())) + DoMeleeAttackIfReady(); + // Spam Boulder spell when enraged and not tanked + else if (m_bEnraged) + { + if (!m_creature->IsNonMeleeSpellCasted(false)) + { + if (Unit* pTarget = m_creature->SelectAttackingTarget(ATTACKING_TARGET_RANDOM, 0)) + DoCastSpellIfCan(pTarget, SPELL_BOULDER); + } + } + } + else + { + // Resume combat + if (m_uiSubmergeTimer < uiDiff) + { + // Teleport to the trigger in order to get a new location + if (Creature* pTrigger = m_creature->GetMap()->GetCreature(m_ouroTriggerGuid)) + m_creature->NearTeleportTo(pTrigger->GetPositionX(), pTrigger->GetPositionY(), pTrigger->GetPositionZ(), 0); + + if (DoCastSpellIfCan(m_creature, SPELL_BIRTH) == CAST_OK) + { + m_creature->RemoveAurasDueToSpell(SPELL_SUBMERGE_VISUAL); + m_creature->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE); + + m_bSubmerged = false; + m_uiSummonBaseTimer = 2000; + m_uiSubmergeTimer = 90000; + } + } + else + m_uiSubmergeTimer -= uiDiff; + } + } +}; + +CreatureAI* GetAI_boss_ouro(Creature* pCreature) +{ + return new boss_ouroAI(pCreature); +} + +struct npc_ouro_spawnerAI : public Scripted_NoMovementAI +{ + npc_ouro_spawnerAI(Creature* pCreature) : Scripted_NoMovementAI(pCreature) {Reset();} + + uint32 m_uiQuakeTimer; + bool m_bHasSummoned; + + void Reset() override + { + m_uiQuakeTimer = 1000; + m_bHasSummoned = false; + } + + void MoveInLineOfSight(Unit* pWho) override + { + // Spawn Ouro on LoS check + if (!m_bHasSummoned && pWho->GetTypeId() == TYPEID_PLAYER && !((Player*)pWho)->isGameMaster() && m_creature->IsWithinDistInMap(pWho, 50.0f)) + { + if (DoCastSpellIfCan(m_creature, SPELL_SUMMON_OURO) == CAST_OK) + m_bHasSummoned = true; + } + + ScriptedAI::MoveInLineOfSight(pWho); + } + + void JustSummoned(Creature* pSummoned) override + { + // Despanw when Ouro is spawned + if (pSummoned->GetEntry() == NPC_OURO) + { + pSummoned->CastSpell(pSummoned, SPELL_BIRTH, false); + pSummoned->SetInCombatWithZone(); + m_creature->ForcedDespawn(); + } + } + + void UpdateAI(const uint32 uiDiff) override + { + if (m_bHasSummoned) + { + if (m_uiQuakeTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature, SPELL_QUAKE) == CAST_OK) + m_uiQuakeTimer = 1000; + } + else + m_uiQuakeTimer -= uiDiff; + } + } +}; + +CreatureAI* GetAI_npc_ouro_spawner(Creature* pCreature) +{ + return new npc_ouro_spawnerAI(pCreature); +} + +void AddSC_boss_ouro() +{ + Script* pNewScript; + + pNewScript = new Script; + pNewScript->Name = "boss_ouro"; + pNewScript->GetAI = &GetAI_boss_ouro; + pNewScript->RegisterSelf(); + + pNewScript = new Script; + pNewScript->Name = "npc_ouro_spawner"; + pNewScript->GetAI = &GetAI_npc_ouro_spawner; + pNewScript->RegisterSelf(); +} diff --git a/src/modules/SD2/scripts/kalimdor/temple_of_ahnqiraj/boss_sartura.cpp b/src/modules/SD2/scripts/kalimdor/temple_of_ahnqiraj/boss_sartura.cpp new file mode 100644 index 000000000..aeb514be4 --- /dev/null +++ b/src/modules/SD2/scripts/kalimdor/temple_of_ahnqiraj/boss_sartura.cpp @@ -0,0 +1,321 @@ +/* Copyright (C) 2006 - 2013 ScriptDev2 + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with 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" +#include "temple_of_ahnqiraj.h" + +enum +{ + SAY_AGGRO = -1531008, + SAY_SLAY = -1531009, + SAY_DEATH = -1531010, + + SPELL_WHIRLWIND = 26083, + SPELL_ENRAGE = 28747, // Not sure if right ID. + SPELL_ENRAGEHARD = 28798, + + // Guard Spell + SPELL_WHIRLWIND_ADD = 26038, + SPELL_KNOCKBACK = 26027, +}; + +struct boss_sarturaAI : public ScriptedAI +{ + boss_sarturaAI(Creature* pCreature) : ScriptedAI(pCreature) + { + m_pInstance = (ScriptedInstance*)pCreature->GetInstanceData(); + Reset(); + } + + ScriptedInstance* m_pInstance; + + uint32 m_uiWhirlWindTimer; + uint32 m_uiWhirlWindRandomTimer; + uint32 m_uiWhirlWindEndTimer; + uint32 m_uiAggroResetTimer; + uint32 m_uiAggroResetEndTimer; + uint32 m_uiEnrageHardTimer; + + bool m_bIsEnraged; + + void Reset() override + { + m_uiWhirlWindTimer = 30000; + m_uiWhirlWindRandomTimer = urand(3000, 7000); + m_uiWhirlWindEndTimer = 0; + m_uiAggroResetTimer = urand(45000, 55000); + m_uiAggroResetEndTimer = 0; + m_uiEnrageHardTimer = 10 * 60000; + + m_bIsEnraged = false; + } + + void Aggro(Unit* /*pWho*/) override + { + DoScriptText(SAY_AGGRO, m_creature); + + if (m_pInstance) + m_pInstance->SetData(TYPE_SARTURA, IN_PROGRESS); + } + + void KilledUnit(Unit* /*pVictim*/) override + { + DoScriptText(SAY_SLAY, m_creature); + } + + void JustDied(Unit* /*pKiller*/) override + { + DoScriptText(SAY_DEATH, m_creature); + + if (m_pInstance) + m_pInstance->SetData(TYPE_SARTURA, DONE); + } + + void JustReachedHome() override + { + if (m_pInstance) + m_pInstance->SetData(TYPE_SARTURA, FAIL); + } + + void UpdateAI(const uint32 uiDiff) override + { + if (!m_creature->SelectHostileTarget() || !m_creature->getVictim()) + return; + + if (m_uiWhirlWindEndTimer) // Is in Whirlwind + { + // While whirlwind, switch to random targets often + if (m_uiWhirlWindRandomTimer < uiDiff) + { + if (Unit* pTarget = m_creature->SelectAttackingTarget(ATTACKING_TARGET_RANDOM, 0)) + m_creature->FixateTarget(pTarget); + + m_uiWhirlWindRandomTimer = urand(3000, 7000); + } + else + m_uiWhirlWindRandomTimer -= uiDiff; + + // End Whirlwind Phase + if (m_uiWhirlWindEndTimer <= uiDiff) + { + m_creature->FixateTarget(NULL); + m_uiWhirlWindEndTimer = 0; + m_uiWhirlWindTimer = urand(25000, 40000); + } + else + m_uiWhirlWindEndTimer -= uiDiff; + } + else // if (!m_uiWhirlWindEndTimer) // Is not in whirlwind + { + // Enter Whirlwind Phase + if (m_uiWhirlWindTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature, SPELL_WHIRLWIND) == CAST_OK) + { + m_uiWhirlWindEndTimer = 15000; + m_uiWhirlWindRandomTimer = 500; + } + } + else + m_uiWhirlWindTimer -= uiDiff; + + // Aquire a new target sometimes + if (!m_uiAggroResetEndTimer) // No random target picket + { + if (m_uiAggroResetTimer < uiDiff) + { + if (Unit* pTarget = m_creature->SelectAttackingTarget(ATTACKING_TARGET_RANDOM, 1)) + m_creature->FixateTarget(pTarget); + + m_uiAggroResetEndTimer = 5000; + } + else + m_uiAggroResetTimer -= uiDiff; + } + else // Reset from recent random target + { + // Remove remaining taunts + if (m_uiAggroResetEndTimer <= uiDiff) + { + m_creature->FixateTarget(NULL); + m_uiAggroResetEndTimer = 0; + m_uiAggroResetTimer = urand(35000, 45000); + } + else + m_uiAggroResetEndTimer -= uiDiff; + } + } + + // If she is 20% enrage + if (!m_bIsEnraged && m_creature->GetHealthPercent() <= 20.0f) + { + if (DoCastSpellIfCan(m_creature, SPELL_ENRAGE, m_uiWhirlWindEndTimer ? CAST_TRIGGERED : 0) == CAST_OK) + m_bIsEnraged = true; + } + + // After 10 minutes hard enrage + if (m_uiEnrageHardTimer) + { + if (m_uiEnrageHardTimer <= uiDiff) + { + if (DoCastSpellIfCan(m_creature, SPELL_ENRAGEHARD, m_uiWhirlWindEndTimer ? CAST_TRIGGERED : 0) == CAST_OK) + m_uiEnrageHardTimer = 0; + } + else + m_uiEnrageHardTimer -= uiDiff; + } + + // No melee damage while in whirlwind + if (!m_uiWhirlWindEndTimer) + DoMeleeAttackIfReady(); + } +}; + +struct mob_sartura_royal_guardAI : public ScriptedAI +{ + mob_sartura_royal_guardAI(Creature* pCreature) : ScriptedAI(pCreature) { Reset(); } + + uint32 m_uiWhirlWindTimer; + uint32 m_uiWhirlWindRandomTimer; + uint32 m_uiWhirlWindEndTimer; + uint32 m_uiAggroResetTimer; + uint32 m_uiAggroResetEndTimer; + uint32 m_uiKnockBackTimer; + + void Reset() override + { + m_uiWhirlWindTimer = 30000; + m_uiWhirlWindRandomTimer = urand(3000, 7000); + m_uiWhirlWindEndTimer = 0; + m_uiAggroResetTimer = urand(45000, 55000); + m_uiAggroResetEndTimer = 0; + m_uiKnockBackTimer = 10000; + } + + void UpdateAI(const uint32 uiDiff) override + { + if (!m_creature->SelectHostileTarget() || !m_creature->getVictim()) + return; + + if (m_uiWhirlWindEndTimer) // Is in Whirlwind + { + // While whirlwind, switch to random targets often + if (m_uiWhirlWindRandomTimer < uiDiff) + { + if (Unit* pTarget = m_creature->SelectAttackingTarget(ATTACKING_TARGET_RANDOM, 0)) + m_creature->FixateTarget(pTarget); + + m_uiWhirlWindRandomTimer = urand(3000, 7000); + } + else + m_uiWhirlWindRandomTimer -= uiDiff; + + // End Whirlwind Phase + if (m_uiWhirlWindEndTimer <= uiDiff) + { + m_creature->FixateTarget(NULL); + m_uiWhirlWindEndTimer = 0; + m_uiWhirlWindTimer = urand(25000, 40000); + } + else + m_uiWhirlWindEndTimer -= uiDiff; + } + else // if (!m_uiWhirlWindEndTimer) // Is not in Whirlwind + { + // Enter Whirlwind Phase + if (m_uiWhirlWindTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature, SPELL_WHIRLWIND_ADD) == CAST_OK) + { + m_uiWhirlWindEndTimer = 8000; + m_uiWhirlWindRandomTimer = 500; + } + } + else + m_uiWhirlWindTimer -= uiDiff; + + // Aquire a new target sometimes + if (!m_uiAggroResetEndTimer) // No random target picket + { + if (m_uiAggroResetTimer < uiDiff) + { + if (Unit* pTarget = m_creature->SelectAttackingTarget(ATTACKING_TARGET_RANDOM, 1)) + m_creature->FixateTarget(pTarget); + + m_uiAggroResetEndTimer = 5000; + } + else + m_uiAggroResetTimer -= uiDiff; + } + else // Reset from recent random target + { + if (m_uiAggroResetEndTimer <= uiDiff) + { + m_creature->FixateTarget(NULL); + m_uiAggroResetEndTimer = 0; + m_uiAggroResetTimer = urand(30000, 40000); + } + else + m_uiAggroResetEndTimer -= uiDiff; + } + + // Knockback nearby enemies + if (m_uiKnockBackTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature, SPELL_KNOCKBACK) == CAST_OK) + m_uiKnockBackTimer = urand(10000, 20000); + } + else + m_uiKnockBackTimer -= uiDiff; + } + + // No melee damage while in whirlwind + if (!m_uiWhirlWindEndTimer) + 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* pNewScript; + + pNewScript = new Script; + pNewScript->Name = "boss_sartura"; + pNewScript->GetAI = &GetAI_boss_sartura; + pNewScript->RegisterSelf(); + + pNewScript = new Script; + pNewScript->Name = "mob_sartura_royal_guard"; + pNewScript->GetAI = &GetAI_mob_sartura_royal_guard; + pNewScript->RegisterSelf(); +} diff --git a/src/modules/SD2/scripts/kalimdor/temple_of_ahnqiraj/boss_skeram.cpp b/src/modules/SD2/scripts/kalimdor/temple_of_ahnqiraj/boss_skeram.cpp new file mode 100644 index 000000000..8d70cda24 --- /dev/null +++ b/src/modules/SD2/scripts/kalimdor/temple_of_ahnqiraj/boss_skeram.cpp @@ -0,0 +1,276 @@ +/* Copyright (C) 2006 - 2013 ScriptDev2 + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with 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: 90 +SDComment: Timers +SDCategory: Temple of Ahn'Qiraj +EndScriptData */ + +#include "precompiled.h" +#include "temple_of_ahnqiraj.h" + +enum +{ + SAY_AGGRO1 = -1531000, + SAY_AGGRO2 = -1531001, + SAY_AGGRO3 = -1531002, + SAY_SLAY1 = -1531003, + SAY_SLAY2 = -1531004, + SAY_SLAY3 = -1531005, + SAY_SPLIT = -1531006, + SAY_DEATH = -1531007, + + SPELL_ARCANE_EXPLOSION = 26192, + SPELL_EARTH_SHOCK = 26194, + SPELL_TRUE_FULFILLMENT = 785, + SPELL_TELEPORT_1 = 4801, + SPELL_TELEPORT_2 = 8195, + SPELL_TELEPORT_3 = 20449, + SPELL_INITIALIZE_IMAGE = 3730, + SPELL_SUMMON_IMAGES = 747, +}; + +struct boss_skeramAI : public ScriptedAI +{ + boss_skeramAI(Creature* pCreature) : ScriptedAI(pCreature) + { + m_pInstance = (ScriptedInstance*)pCreature->GetInstanceData(); + Reset(); + + // Check this for images, because the Initialize spell hits the target only after aggro + if (m_creature->IsTemporarySummon()) + m_bIsImage = true; + else + m_bIsImage = false; + } + + ScriptedInstance* m_pInstance; + + uint32 m_uiArcaneExplosionTimer; + uint32 m_uiFullFillmentTimer; + uint32 m_uiBlinkTimer; + + float m_fHpCheck; + + bool m_bIsImage; + + void Reset() override + { + m_uiArcaneExplosionTimer = urand(6000, 12000); + m_uiFullFillmentTimer = 15000; + m_uiBlinkTimer = urand(30000, 45000); + + m_fHpCheck = 75.0f; + + if (m_creature->GetVisibility() != VISIBILITY_ON) + m_creature->SetVisibility(VISIBILITY_ON); + } + + void KilledUnit(Unit* /*pVictim*/) override + { + switch (urand(0, 2)) + { + case 0: DoScriptText(SAY_SLAY1, m_creature); break; + case 1: DoScriptText(SAY_SLAY2, m_creature); break; + case 2: DoScriptText(SAY_SLAY3, m_creature); break; + } + } + + void JustDied(Unit* /*pKiller*/) override + { + if (!m_bIsImage) + { + DoScriptText(SAY_DEATH, m_creature); + + if (m_pInstance) + m_pInstance->SetData(TYPE_SKERAM, DONE); + } + // Else despawn to avoid looting + else + m_creature->ForcedDespawn(); + } + + void Aggro(Unit* /*pWho*/) override + { + if (m_bIsImage) + return; + + switch (urand(0, 2)) + { + case 0: DoScriptText(SAY_AGGRO1, m_creature); break; + case 1: DoScriptText(SAY_AGGRO2, m_creature); break; + case 2: DoScriptText(SAY_AGGRO3, m_creature); break; + } + + if (m_pInstance) + m_pInstance->SetData(TYPE_SKERAM, IN_PROGRESS); + } + + void JustReachedHome() override + { + if (m_pInstance) + m_pInstance->SetData(TYPE_SKERAM, FAIL); + + if (m_bIsImage) + m_creature->ForcedDespawn(); + } + + void JustSummoned(Creature* pSummoned) override + { + if (Unit* pTarget = m_creature->SelectAttackingTarget(ATTACKING_TARGET_RANDOM, 0)) + pSummoned->AI()->AttackStart(pTarget); + + DoCastSpellIfCan(pSummoned, SPELL_INITIALIZE_IMAGE, CAST_TRIGGERED); + } + + // Wrapper to handle the image version initialize + void DoInitializeImage() + { + if (!m_pInstance) + return; + + // Initialize the health of the clone + if (Creature* pProphet = m_pInstance->GetSingleCreatureFromStorage(NPC_SKERAM)) + { + float fHealthPct = pProphet->GetHealthPercent(); + float fMaxHealthPct = 0; + + // The max health depends on the split phase. It's percent * original boss health + if (fHealthPct < 25.0f) + fMaxHealthPct = 0.50f; + else if (fHealthPct < 50.0f) + fMaxHealthPct = 0.20f; + else + fMaxHealthPct = 0.10f; + + // Set the same health percent as the original boss + m_creature->SetMaxHealth(m_creature->GetMaxHealth()*fMaxHealthPct); + m_creature->SetHealthPercent(fHealthPct); + } + } + + void UpdateAI(const uint32 uiDiff) override + { + // Return since we have no target + if (!m_creature->SelectHostileTarget() || !m_creature->getVictim()) + return; + + // ArcaneExplosion_Timer + if (m_uiArcaneExplosionTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature, SPELL_ARCANE_EXPLOSION) == CAST_OK) + m_uiArcaneExplosionTimer = urand(8000, 18000); + } + else + m_uiArcaneExplosionTimer -= uiDiff; + + // True Fullfilment + if (m_uiFullFillmentTimer < uiDiff) + { + if (Unit* pTarget = m_creature->SelectAttackingTarget(ATTACKING_TARGET_RANDOM, 1)) + { + if (DoCastSpellIfCan(pTarget, SPELL_TRUE_FULFILLMENT) == CAST_OK) + m_uiFullFillmentTimer = urand(20000, 30000); + } + } + else + m_uiFullFillmentTimer -= uiDiff; + + // Blink_Timer + if (m_uiBlinkTimer < uiDiff) + { + switch (urand(0, 2)) + { + case 0: DoCastSpellIfCan(m_creature, SPELL_TELEPORT_1); break; + case 1: DoCastSpellIfCan(m_creature, SPELL_TELEPORT_2); break; + case 2: DoCastSpellIfCan(m_creature, SPELL_TELEPORT_3); break; + } + + DoResetThreat(); + if (m_creature->GetVisibility() != VISIBILITY_ON) + m_creature->SetVisibility(VISIBILITY_ON); + + m_uiBlinkTimer = urand(10000, 30000); + } + else + m_uiBlinkTimer -= uiDiff; + + // Summon images at 75%, 50% and 25% + if (!m_bIsImage && m_creature->GetHealthPercent() < m_fHpCheck) + { + if (DoCastSpellIfCan(m_creature, SPELL_SUMMON_IMAGES) == CAST_OK) + { + DoScriptText(SAY_SPLIT, m_creature); + m_fHpCheck -= 25.0f; + // Teleport shortly after the images are summoned and set invisible to clear the selection (Workaround alert!!!) + m_creature->SetVisibility(VISIBILITY_OFF); + m_uiBlinkTimer = 2000; + } + } + + // If we are within range melee the target + if (m_creature->CanReachWithMeleeAttack(m_creature->getVictim())) + DoMeleeAttackIfReady(); + else + { + if (!m_creature->IsNonMeleeSpellCasted(false)) + { + if (Unit* pTarget = m_creature->SelectAttackingTarget(ATTACKING_TARGET_RANDOM, 0)) + DoCastSpellIfCan(pTarget, SPELL_EARTH_SHOCK); + } + } + } +}; + +CreatureAI* GetAI_boss_skeram(Creature* pCreature) +{ + return new boss_skeramAI(pCreature); +} + +bool EffectDummyCreature_prophet_skeram(Unit* /*pCaster*/, uint32 uiSpellId, SpellEffectIndex uiEffIndex, Creature* pCreatureTarget, ObjectGuid /*originalCasterGuid*/) +{ + // always check spellid and effectindex + if (uiSpellId == SPELL_INITIALIZE_IMAGE && uiEffIndex == EFFECT_INDEX_0) + { + // check for target == caster first + if (instance_temple_of_ahnqiraj* pInstance = (instance_temple_of_ahnqiraj*)pCreatureTarget->GetInstanceData()) + { + if (Creature* pProphet = pInstance->GetSingleCreatureFromStorage(NPC_SKERAM)) + { + if (pProphet == pCreatureTarget) + return false; + } + } + + if (boss_skeramAI* pSkeramAI = dynamic_cast(pCreatureTarget->AI())) + pSkeramAI->DoInitializeImage(); + } + + return false; +} + +void AddSC_boss_skeram() +{ + Script* pNewScript; + + pNewScript = new Script; + pNewScript->Name = "boss_skeram"; + pNewScript->GetAI = &GetAI_boss_skeram; + pNewScript->pEffectDummyNPC = &EffectDummyCreature_prophet_skeram; + pNewScript->RegisterSelf(); +} diff --git a/src/modules/SD2/scripts/kalimdor/temple_of_ahnqiraj/boss_twinemperors.cpp b/src/modules/SD2/scripts/kalimdor/temple_of_ahnqiraj/boss_twinemperors.cpp new file mode 100644 index 000000000..2dbe9504b --- /dev/null +++ b/src/modules/SD2/scripts/kalimdor/temple_of_ahnqiraj/boss_twinemperors.cpp @@ -0,0 +1,440 @@ +/* Copyright (C) 2006 - 2013 ScriptDev2 + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with 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: Timers +SDCategory: Temple of Ahn'Qiraj +EndScriptData */ + +#include "precompiled.h" +#include "temple_of_ahnqiraj.h" + +enum +{ + // yells + SAY_VEKLOR_AGGRO_1 = -1531019, + SAY_VEKLOR_AGGRO_2 = -1531020, + SAY_VEKLOR_AGGRO_3 = -1531021, + SAY_VEKLOR_AGGRO_4 = -1531022, + SAY_VEKLOR_SLAY = -1531023, + SAY_VEKLOR_DEATH = -1531024, + SAY_VEKLOR_SPECIAL = -1531025, + + SAY_VEKNILASH_AGGRO_1 = -1531026, + SAY_VEKNILASH_AGGRO_2 = -1531027, + SAY_VEKNILASH_AGGRO_3 = -1531028, + SAY_VEKNILASH_AGGRO_4 = -1531029, + SAY_VEKNILASH_SLAY = -1531030, + SAY_VEKNILASH_DEATH = -1531031, + SAY_VEKNILASH_SPECIAL = -1531032, + + // common spells + SPELL_TWIN_TELEPORT = 799, + SPELL_TWIN_TELEPORT_STUN = 800, + SPELL_HEAL_BROTHER = 7393, + SPELL_TWIN_TELEPORT_VISUAL = 26638, + + // veklor spells + SPELL_ARCANE_BURST = 568, + SPELL_EXPLODE_BUG = 804, // targets 15316 or 15317 + SPELL_SHADOW_BOLT = 26006, + SPELL_BLIZZARD = 26607, + SPELL_FRENZY = 27897, + + // veknilash spells + SPELL_MUTATE_BUG = 802, // targets 15316 or 15317 + SPELL_UPPERCUT = 26007, + SPELL_UNBALANCING_STRIKE = 26613, + SPELL_BERSERK = 27680, +}; + +struct boss_twin_emperorsAI : public ScriptedAI +{ + boss_twin_emperorsAI(Creature* pCreature) : ScriptedAI(pCreature) + { + m_pInstance = (instance_temple_of_ahnqiraj*)pCreature->GetInstanceData(); + Reset(); + } + + instance_temple_of_ahnqiraj* m_pInstance; + + uint32 m_uiBerserkTimer; + uint32 m_uiBugAbilityTimer; + uint32 m_uiTeleportTimer; + + void Reset() override + { + m_uiTeleportTimer = 35000; + m_uiBugAbilityTimer = urand(7000, 14000); + m_uiBerserkTimer = 15 * MINUTE * IN_MILLISECONDS; + } + + // Workaround for the shared health pool + void DamageTaken(Unit* /*pDoneBy*/, uint32& uiDamage) override + { + if (!m_pInstance) + return; + + if (Creature* pTwin = m_pInstance->GetSingleCreatureFromStorage(m_creature->GetEntry() == NPC_VEKLOR ? NPC_VEKLOR : NPC_VEKNILASH)) + { + float fDamPercent = ((float)uiDamage) / ((float)m_creature->GetMaxHealth()); + uint32 uiTwinDamage = (uint32)(fDamPercent * ((float)pTwin->GetMaxHealth())); + uint32 uiTwinHealth = pTwin->GetHealth() - uiTwinDamage; + pTwin->SetHealth(uiTwinHealth > 0 ? uiTwinHealth : 0); + + if (!uiTwinHealth) + { + pTwin->SetDeathState(JUST_DIED); + pTwin->RemoveFlag(UNIT_DYNAMIC_FLAGS, UNIT_DYNFLAG_LOOTABLE); + } + } + } + + // Workaround for the shared health pool + void HealedBy(Unit* pHealer, uint32& uiHealedAmount) override + { + if (!m_pInstance) + return; + + if (Creature* pTwin = m_pInstance->GetSingleCreatureFromStorage(m_creature->GetEntry() == NPC_VEKLOR ? NPC_VEKLOR : NPC_VEKNILASH)) + { + float fHealPercent = ((float)uiHealedAmount) / ((float)m_creature->GetMaxHealth()); + uint32 uiTwinHeal = (uint32)(fHealPercent * ((float)pTwin->GetMaxHealth())); + uint32 uiTwinHealth = pTwin->GetHealth() + uiTwinHeal; + pTwin->SetHealth(uiTwinHealth < pTwin->GetMaxHealth() ? uiTwinHealth : pTwin->GetMaxHealth()); + } + } + + void Aggro(Unit* /*pWho*/) override + { + if (m_pInstance) + m_pInstance->SetData(TYPE_TWINS, IN_PROGRESS); + } + + void JustDied(Unit* /*pKiller*/) override + { + if (m_pInstance) + m_pInstance->SetData(TYPE_TWINS, DONE); + } + + void JustReachedHome() override + { + if (m_pInstance) + m_pInstance->SetData(TYPE_TWINS, FAIL); + } + + void SpellHit(Unit* /*pCaster*/, const SpellEntry* pSpell) override + { + if (pSpell->Id == SPELL_TWIN_TELEPORT) + { + DoTeleportAbility(); + DoResetThreat(); + DoCastSpellIfCan(m_creature, SPELL_TWIN_TELEPORT_STUN, CAST_TRIGGERED); + DoCastSpellIfCan(m_creature, SPELL_TWIN_TELEPORT_VISUAL, CAST_TRIGGERED); + } + } + + // Return true, if succeeded + virtual void DoTeleportAbility() {} + virtual bool DoHandleBugAbility() = 0; + virtual bool DoHandleBerserk() = 0; + + // Return true to handle shared timers and MeleeAttack + virtual bool UpdateEmperorAI(const uint32 /*uiDiff*/) { return true; } + + void UpdateAI(const uint32 uiDiff) override + { + // Return since we have no target + if (!m_creature->SelectHostileTarget() || !m_creature->getVictim()) + return; + + // Call emperor specific virtual function + if (!UpdateEmperorAI(uiDiff)) + return; + + if (m_uiTeleportTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature, SPELL_TWIN_TELEPORT) == CAST_OK) + m_uiTeleportTimer = 35000; + } + else + m_uiTeleportTimer -= uiDiff; + + if (m_uiBugAbilityTimer < uiDiff) + { + if (DoHandleBugAbility()) + m_uiBugAbilityTimer = urand(10000, 17000); + } + else + m_uiBugAbilityTimer -= uiDiff; + + if (m_uiBerserkTimer) + { + if (m_uiBerserkTimer <= uiDiff) + { + if (DoHandleBerserk()) + m_uiBerserkTimer = 0; + } + else + m_uiBerserkTimer -= uiDiff; + } + } +}; + +struct boss_veknilashAI : public boss_twin_emperorsAI +{ + boss_veknilashAI(Creature* pCreature) : boss_twin_emperorsAI(pCreature) { Reset(); } + + uint32 m_uiUppercutTimer; + uint32 m_uiUnbalancingStrikeTimer; + + void Reset() override + { + boss_twin_emperorsAI::Reset(); + + m_uiUppercutTimer = urand(14000, 29000); + m_uiUnbalancingStrikeTimer = urand(8000, 18000); + } + + void MoveInLineOfSight(Unit* pWho) override + { + if (m_pInstance && m_pInstance->GetData(TYPE_TWINS) == IN_PROGRESS && pWho->GetEntry() == NPC_VEKLOR && pWho->IsWithinDistInMap(m_creature, 60.0f)) + DoCastSpellIfCan(pWho, SPELL_HEAL_BROTHER); + + ScriptedAI::MoveInLineOfSight(pWho); + } + + void Aggro(Unit* pWho) override + { + boss_twin_emperorsAI::Aggro(pWho); + + switch (urand(0, 3)) + { + case 0: DoScriptText(SAY_VEKNILASH_AGGRO_1, m_creature); break; + case 1: DoScriptText(SAY_VEKNILASH_AGGRO_2, m_creature); break; + case 2: DoScriptText(SAY_VEKNILASH_AGGRO_3, m_creature); break; + case 3: DoScriptText(SAY_VEKNILASH_AGGRO_4, m_creature); break; + } + } + + void KilledUnit(Unit* /*pVictim*/) override + { + DoScriptText(SAY_VEKNILASH_SLAY, m_creature); + } + + void JustDied(Unit* pKiller) override + { + boss_twin_emperorsAI::JustDied(pKiller); + + DoScriptText(SAY_VEKNILASH_DEATH, m_creature); + } + + bool DoHandleBugAbility() + { + if (DoCastSpellIfCan(m_creature, SPELL_MUTATE_BUG) == CAST_OK) + return true; + + return false; + } + + bool DoHandleBerserk() + { + if (DoCastSpellIfCan(m_creature, SPELL_BERSERK) == CAST_OK) + return true; + + return false; + } + + // Only Vek'nilash handles the teleport for both of them + void DoTeleportAbility() + { + if (!m_pInstance) + return; + + if (Creature* pVeklor = m_pInstance->GetSingleCreatureFromStorage(NPC_VEKLOR)) + { + float fTargetX, fTargetY, fTargetZ, fTargetOrient; + pVeklor->GetPosition(fTargetX, fTargetY, fTargetZ); + fTargetOrient = pVeklor->GetOrientation(); + + pVeklor->NearTeleportTo(m_creature->GetPositionX(), m_creature->GetPositionY(), m_creature->GetPositionZ(), m_creature->GetOrientation(), true); + m_creature->NearTeleportTo(fTargetX, fTargetY, fTargetZ, fTargetOrient, true); + } + } + + bool UpdateEmperorAI(const uint32 uiDiff) + { + if (m_uiUnbalancingStrikeTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature->getVictim(), SPELL_UNBALANCING_STRIKE) == CAST_OK) + m_uiUnbalancingStrikeTimer = urand(8000, 20000); + } + else + m_uiUnbalancingStrikeTimer -= uiDiff; + + if (m_uiUppercutTimer < uiDiff) + { + if (Unit* pTarget = m_creature->SelectAttackingTarget(ATTACKING_TARGET_RANDOM, 0, SPELL_UPPERCUT, SELECT_FLAG_IN_MELEE_RANGE)) + { + if (DoCastSpellIfCan(pTarget, SPELL_UPPERCUT) == CAST_OK) + m_uiUppercutTimer = urand(15000, 30000); + } + } + else + m_uiUppercutTimer -= uiDiff; + + DoMeleeAttackIfReady(); + + return true; + } +}; + +struct boss_veklorAI : public boss_twin_emperorsAI +{ + boss_veklorAI(Creature* pCreature) : boss_twin_emperorsAI(pCreature) { Reset(); } + + uint32 m_uiShadowBoltTimer; + uint32 m_uiBlizzardTimer; + uint32 m_uiArcaneBurstTimer; + + void Reset() override + { + boss_twin_emperorsAI::Reset(); + + m_uiShadowBoltTimer = 1000; + m_uiBlizzardTimer = urand(15000, 20000); + m_uiArcaneBurstTimer = 1000; + } + + void MoveInLineOfSight(Unit* pWho) override + { + if (m_pInstance && m_pInstance->GetData(TYPE_TWINS) == IN_PROGRESS && pWho->GetEntry() == NPC_VEKNILASH && pWho->IsWithinDistInMap(m_creature, 60.0f)) + DoCastSpellIfCan(pWho, SPELL_HEAL_BROTHER); + + ScriptedAI::MoveInLineOfSight(pWho); + } + + void Aggro(Unit* pWho) override + { + boss_twin_emperorsAI::Aggro(pWho); + + switch (urand(0, 3)) + { + case 0: DoScriptText(SAY_VEKLOR_AGGRO_1, m_creature); break; + case 1: DoScriptText(SAY_VEKLOR_AGGRO_2, m_creature); break; + case 2: DoScriptText(SAY_VEKLOR_AGGRO_3, m_creature); break; + case 3: DoScriptText(SAY_VEKLOR_AGGRO_4, m_creature); break; + } + } + + void KilledUnit(Unit* /*pVictim*/) override + { + DoScriptText(SAY_VEKLOR_SLAY, m_creature); + } + + void JustDied(Unit* pKiller) override + { + boss_twin_emperorsAI::JustDied(pKiller); + + DoScriptText(SAY_VEKLOR_DEATH, m_creature); + } + + void AttackStart(Unit* pWho) override + { + if (m_creature->Attack(pWho, false)) + { + m_creature->AddThreat(pWho); + m_creature->SetInCombatWith(pWho); + pWho->SetInCombatWith(m_creature); + m_creature->GetMotionMaster()->MoveChase(pWho, 20.0f); + } + } + + bool DoHandleBugAbility() + { + if (DoCastSpellIfCan(m_creature, SPELL_EXPLODE_BUG) == CAST_OK) + return true; + + return false; + } + + bool DoHandleBerserk() + { + if (DoCastSpellIfCan(m_creature, SPELL_FRENZY) == CAST_OK) + return true; + + return false; + } + + bool UpdateEmperorAI(const uint32 uiDiff) + { + if (m_uiShadowBoltTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature->getVictim(), SPELL_SHADOW_BOLT) == CAST_OK) + m_uiShadowBoltTimer = 2000; + } + else + m_uiShadowBoltTimer -= uiDiff; + + if (m_uiBlizzardTimer < uiDiff) + { + if (Unit* pTarget = m_creature->SelectAttackingTarget(ATTACKING_TARGET_RANDOM, 0)) + { + if (DoCastSpellIfCan(pTarget, SPELL_BLIZZARD) == CAST_OK) + m_uiBlizzardTimer = urand(15000, 30000); + } + } + else + m_uiBlizzardTimer -= uiDiff; + + if (m_uiArcaneBurstTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature, SPELL_ARCANE_BURST) == CAST_OK) + m_uiArcaneBurstTimer = 5000; + } + else + m_uiArcaneBurstTimer -= uiDiff; + + return true; + } +}; + +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* pNewScript; + + pNewScript = new Script; + pNewScript->Name = "boss_veknilash"; + pNewScript->GetAI = &GetAI_boss_veknilash; + pNewScript->RegisterSelf(); + + pNewScript = new Script; + pNewScript->Name = "boss_veklor"; + pNewScript->GetAI = &GetAI_boss_veklor; + pNewScript->RegisterSelf(); +} diff --git a/src/modules/SD2/scripts/kalimdor/temple_of_ahnqiraj/boss_viscidus.cpp b/src/modules/SD2/scripts/kalimdor/temple_of_ahnqiraj/boss_viscidus.cpp new file mode 100644 index 000000000..5b4b990db --- /dev/null +++ b/src/modules/SD2/scripts/kalimdor/temple_of_ahnqiraj/boss_viscidus.cpp @@ -0,0 +1,133 @@ +/* Copyright (C) 2006 - 2013 ScriptDev2 + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with 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: 40 +SDComment: Only basic spells - freeze and explode events require more research +SDCategory: Temple of Ahn'Qiraj +EndScriptData */ + +#include "precompiled.h" +#include "temple_of_ahnqiraj.h" + +enum +{ + // Timer spells + SPELL_POISON_SHOCK = 25993, + SPELL_POISONBOLT_VOLLEY = 25991, + SPELL_TOXIN = 26575, // Triggers toxin cloud - 25989 + + // Debuffs gained by the boss on frost damage + SPELL_VISCIDUS_SLOWED = 26034, + SPELL_VISCIDUS_SLOWED_MORE = 26036, + SPELL_VISCIDUS_FREEZE = 25937, + + // When frost damage exceeds a certain limit, then boss explodes + SPELL_REJOIN_VISCIDUS = 25896, + SPELL_VISCIDUS_EXPLODE = 25938, + SPELL_VISCIDUS_SUICIDE = 26003, // cast when boss explodes and is below 5% Hp - should trigger 26002 + + // SPELL_MEMBRANE_VISCIDUS = 25994, // damage reduction spell - removed from DBC + // SPELL_VISCIDUS_WEAKNESS = 25926, // aura which procs at damage - should trigger the slow spells - removed from DBC + // SPELL_VISCIDUS_SHRINKS = 25893, // removed from DBC + // SPELL_VISCIDUS_SHRINKS_2 = 27934, // removed from DBC + // SPELL_VISCIDUS_GROWS = 25897, // removed from DBC + // SPELL_SUMMON_GLOBS = 25885, // summons npc 15667 using spells from 25865 to 25884; All spells have target coords - removed from DBC + // SPELL_VISCIDUS_TELEPORT = 25904, // removed from DBC + // SPELL_SUMMONT_TRIGGER = 26564, // summons 15992 - removed from DBC + + NPC_GLOB_OF_VISCIDUS = 15667 +}; + +struct boss_viscidusAI : public ScriptedAI +{ + boss_viscidusAI(Creature* pCreature) : ScriptedAI(pCreature) + { + m_pInstance = (ScriptedInstance*)pCreature->GetInstanceData(); + Reset(); + } + + ScriptedInstance* m_pInstance; + + uint32 m_uiPoisonShockTimer; + uint32 m_uiPoisonBoltVolleyTimer; + + void Reset() override + { + m_uiPoisonShockTimer = urand(7000, 12000); + m_uiPoisonBoltVolleyTimer = urand(10000, 15000); + } + + void Aggro(Unit* /*pWho*/) override + { + DoCastSpellIfCan(m_creature, SPELL_TOXIN); + + if (m_pInstance) + m_pInstance->SetData(TYPE_VISCIDUS, IN_PROGRESS); + } + + void JustReachedHome() override + { + if (m_pInstance) + m_pInstance->SetData(TYPE_VISCIDUS, FAIL); + } + + void JustDied(Unit* /*pKiller*/) override + { + if (m_pInstance) + m_pInstance->SetData(TYPE_VISCIDUS, DONE); + } + + void UpdateAI(const uint32 uiDiff) override + { + if (!m_creature->SelectHostileTarget() || !m_creature->getVictim()) + return; + + if (m_uiPoisonShockTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature, SPELL_POISON_SHOCK) == CAST_OK) + m_uiPoisonShockTimer = urand(7000, 12000); + } + else + m_uiPoisonShockTimer -= uiDiff; + + if (m_uiPoisonBoltVolleyTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature, SPELL_POISONBOLT_VOLLEY) == CAST_OK) + m_uiPoisonBoltVolleyTimer = urand(10000, 15000); + } + else + m_uiPoisonBoltVolleyTimer -= uiDiff; + + DoMeleeAttackIfReady(); + } +}; + +CreatureAI* GetAI_boss_viscidus(Creature* pCreature) +{ + return new boss_viscidusAI(pCreature); +} + +void AddSC_boss_viscidus() +{ + Script* pNewScript; + + pNewScript = new Script; + pNewScript->Name = "boss_viscidus"; + pNewScript->GetAI = &GetAI_boss_viscidus; + pNewScript->RegisterSelf(); +} diff --git a/src/modules/SD2/scripts/kalimdor/temple_of_ahnqiraj/instance_temple_of_ahnqiraj.cpp b/src/modules/SD2/scripts/kalimdor/temple_of_ahnqiraj/instance_temple_of_ahnqiraj.cpp new file mode 100644 index 000000000..d824e40db --- /dev/null +++ b/src/modules/SD2/scripts/kalimdor/temple_of_ahnqiraj/instance_temple_of_ahnqiraj.cpp @@ -0,0 +1,293 @@ +/* Copyright (C) 2006 - 2013 ScriptDev2 + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along 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: C'thun whisperings spells NYI. +SDCategory: Temple of Ahn'Qiraj +EndScriptData */ + +#include "precompiled.h" +#include "temple_of_ahnqiraj.h" + +static const DialogueEntry aIntroDialogue[] = +{ + {EMOTE_EYE_INTRO, NPC_MASTERS_EYE, 7000}, + {SAY_EMPERORS_INTRO_1, NPC_VEKLOR, 6000}, + {SAY_EMPERORS_INTRO_2, NPC_VEKNILASH, 8000}, + {SAY_EMPERORS_INTRO_3, NPC_VEKLOR, 3000}, + {SAY_EMPERORS_INTRO_4, NPC_VEKNILASH, 3000}, + {SAY_EMPERORS_INTRO_5, NPC_VEKLOR, 3000}, + {SAY_EMPERORS_INTRO_6, NPC_VEKNILASH, 0}, + {0, 0, 0} +}; + +instance_temple_of_ahnqiraj::instance_temple_of_ahnqiraj(Map* pMap) : ScriptedInstance(pMap), + m_uiBugTrioDeathCount(0), + m_uiCthunWhisperTimer(90000), + m_bIsEmperorsIntroDone(false), + m_dialogueHelper(aIntroDialogue) +{ + Initialize(); +}; + +void instance_temple_of_ahnqiraj::Initialize() +{ + memset(&m_auiEncounter, 0, sizeof(m_auiEncounter)); + + m_dialogueHelper.InitializeDialogueHelper(this); +} + +bool instance_temple_of_ahnqiraj::IsEncounterInProgress() const +{ + for (uint8 i = 0; i < MAX_ENCOUNTER; ++i) + { + if (m_auiEncounter[i] == IN_PROGRESS) + return true; + } + + return false; +} + +void instance_temple_of_ahnqiraj::DoHandleTempleAreaTrigger(uint32 uiTriggerId) +{ + if (uiTriggerId == AREATRIGGER_TWIN_EMPERORS && !m_bIsEmperorsIntroDone) + { + m_dialogueHelper.StartNextDialogueText(EMOTE_EYE_INTRO); + // Note: there may be more related to this; The emperors should kneel before the Eye and they stand up after it despawns + if (Creature* pEye = GetSingleCreatureFromStorage(NPC_MASTERS_EYE)) + pEye->ForcedDespawn(1000); + m_bIsEmperorsIntroDone = true; + } +} + +void instance_temple_of_ahnqiraj::OnCreatureCreate(Creature* pCreature) +{ + switch (pCreature->GetEntry()) + { + case NPC_SKERAM: + // Don't store the summoned images guid + if (GetData(TYPE_SKERAM) == IN_PROGRESS) + break; + case NPC_VEKLOR: + case NPC_VEKNILASH: + case NPC_MASTERS_EYE: + case NPC_OURO_SPAWNER: + case NPC_CTHUN: + m_mNpcEntryGuidStore[pCreature->GetEntry()] = pCreature->GetObjectGuid(); + break; + } +} + +void instance_temple_of_ahnqiraj::OnObjectCreate(GameObject* pGo) +{ + switch (pGo->GetEntry()) + { + case GO_SKERAM_GATE: + if (m_auiEncounter[TYPE_SKERAM] == DONE) + pGo->SetGoState(GO_STATE_ACTIVE); + break; + case GO_TWINS_ENTER_DOOR: + break; + case GO_TWINS_EXIT_DOOR: + if (m_auiEncounter[TYPE_TWINS] == DONE) + pGo->SetGoState(GO_STATE_ACTIVE); + break; + case GO_SANDWORM_BASE: + break; + + default: + return; + } + + m_mGoEntryGuidStore[pGo->GetEntry()] = pGo->GetObjectGuid(); +} + +void instance_temple_of_ahnqiraj::SetData(uint32 uiType, uint32 uiData) +{ + switch (uiType) + { + case TYPE_SKERAM: + m_auiEncounter[uiType] = uiData; + if (uiData == DONE) + DoUseDoorOrButton(GO_SKERAM_GATE); + break; + case TYPE_BUG_TRIO: + if (uiData == SPECIAL) + { + ++m_uiBugTrioDeathCount; + if (m_uiBugTrioDeathCount == 2) + SetData(TYPE_BUG_TRIO, DONE); + + // don't store any special data + break; + } + if (uiData == FAIL) + m_uiBugTrioDeathCount = 0; + m_auiEncounter[uiType] = uiData; + break; + case TYPE_SARTURA: + case TYPE_FANKRISS: + case TYPE_VISCIDUS: + case TYPE_HUHURAN: + m_auiEncounter[uiType] = uiData; + break; + case TYPE_TWINS: + // Either of the twins can set data, so return to avoid double changing + if (m_auiEncounter[uiType] == uiData) + return; + + m_auiEncounter[uiType] = uiData; + DoUseDoorOrButton(GO_TWINS_ENTER_DOOR); + if (uiData == DONE) + DoUseDoorOrButton(GO_TWINS_EXIT_DOOR); + break; + case TYPE_OURO: + switch (uiData) + { + case FAIL: + // Respawn the Ouro spawner on fail + if (Creature* pSpawner = GetSingleCreatureFromStorage(NPC_OURO_SPAWNER)) + pSpawner->Respawn(); + // no break; + case DONE: + // Despawn the sandworm base on Done or Fail + if (GameObject* pBase = GetSingleGameObjectFromStorage(GO_SANDWORM_BASE)) + pBase->SetLootState(GO_JUST_DEACTIVATED); + break; + } + m_auiEncounter[uiType] = uiData; + break; + case TYPE_CTHUN: + m_auiEncounter[uiType] = uiData; + 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_strInstData = saveStream.str(); + + SaveToDB(); + OUT_SAVE_INST_DATA_COMPLETE; + } +} + +void instance_temple_of_ahnqiraj::Load(const char* chrIn) +{ + if (!chrIn) + { + 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]; + + for (uint8 i = 0; i < MAX_ENCOUNTER; ++i) + { + if (m_auiEncounter[i] == IN_PROGRESS) + m_auiEncounter[i] = NOT_STARTED; + } + + OUT_LOAD_INST_DATA_COMPLETE; +} + +uint32 instance_temple_of_ahnqiraj::GetData(uint32 uiType) const +{ + if (uiType < MAX_ENCOUNTER) + return m_auiEncounter[uiType]; + + return 0; +} + +void instance_temple_of_ahnqiraj::Update(uint32 uiDiff) +{ + m_dialogueHelper.DialogueUpdate(uiDiff); + + if (GetData(TYPE_CTHUN) == IN_PROGRESS || GetData(TYPE_CTHUN) == DONE) + return; + + if (m_uiCthunWhisperTimer < uiDiff) + { + if (Player* pPlayer = GetPlayerInMap()) + { + if (Creature* pCthun = GetSingleCreatureFromStorage(NPC_CTHUN)) + { + // ToDo: also cast the C'thun Whispering charm spell - requires additional research + switch (urand(0, 7)) + { + case 0: DoScriptText(SAY_CTHUN_WHISPER_1, pCthun, pPlayer); break; + case 1: DoScriptText(SAY_CTHUN_WHISPER_2, pCthun, pPlayer); break; + case 2: DoScriptText(SAY_CTHUN_WHISPER_3, pCthun, pPlayer); break; + case 3: DoScriptText(SAY_CTHUN_WHISPER_4, pCthun, pPlayer); break; + case 4: DoScriptText(SAY_CTHUN_WHISPER_5, pCthun, pPlayer); break; + case 5: DoScriptText(SAY_CTHUN_WHISPER_6, pCthun, pPlayer); break; + case 6: DoScriptText(SAY_CTHUN_WHISPER_7, pCthun, pPlayer); break; + case 7: DoScriptText(SAY_CTHUN_WHISPER_8, pCthun, pPlayer); break; + } + } + } + m_uiCthunWhisperTimer = urand(1.5 * MINUTE * IN_MILLISECONDS, 5 * MINUTE * IN_MILLISECONDS); + } + else + m_uiCthunWhisperTimer -= uiDiff; +} + +InstanceData* GetInstanceData_instance_temple_of_ahnqiraj(Map* pMap) +{ + return new instance_temple_of_ahnqiraj(pMap); +} + +bool AreaTrigger_at_temple_ahnqiraj(Player* pPlayer, AreaTriggerEntry const* pAt) +{ + if (pAt->id == AREATRIGGER_TWIN_EMPERORS) + { + if (pPlayer->isGameMaster() || !pPlayer->IsAlive()) + return false; + + if (instance_temple_of_ahnqiraj* pInstance = (instance_temple_of_ahnqiraj*)pPlayer->GetInstanceData()) + pInstance->DoHandleTempleAreaTrigger(pAt->id); + } + + return false; +} + +void AddSC_instance_temple_of_ahnqiraj() +{ + Script* pNewScript; + + pNewScript = new Script; + pNewScript->Name = "instance_temple_of_ahnqiraj"; + pNewScript->GetInstanceData = &GetInstanceData_instance_temple_of_ahnqiraj; + pNewScript->RegisterSelf(); + + pNewScript = new Script; + pNewScript->Name = "at_temple_ahnqiraj"; + pNewScript->pAreaTrigger = &AreaTrigger_at_temple_ahnqiraj; + pNewScript->RegisterSelf(); +} diff --git a/src/modules/SD2/scripts/kalimdor/temple_of_ahnqiraj/mob_anubisath_sentinel.cpp b/src/modules/SD2/scripts/kalimdor/temple_of_ahnqiraj/mob_anubisath_sentinel.cpp new file mode 100644 index 000000000..c105e248c --- /dev/null +++ b/src/modules/SD2/scripts/kalimdor/temple_of_ahnqiraj/mob_anubisath_sentinel.cpp @@ -0,0 +1,204 @@ +/* Copyright (C) 2006 - 2013 ScriptDev2 + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along 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: 75 +SDComment: Abilities selection needs further improvements. 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" + +enum +{ + EMOTE_GENERIC_FRENZY = -1000002, + + SPELL_PERIODIC_MANA_BURN = 812, + SPELL_MENDING = 2147, + SPELL_PERIODIC_SHADOW_STORM = 2148, + SPELL_PERIODIC_THUNDERCLAP = 2834, + SPELL_MORTAL_STRIKE = 9347, + SPELL_FIRE_ARCANE_REFLECT = 13022, + SPELL_SHADOW_FROST_REFLECT = 19595, + SPELL_PERIODIC_KNOCK_AWAY = 21737, + SPELL_THORNS = 25777, + + SPELL_ENRAGE = 8599, + + MAX_BUDDY = 4 +}; + +struct npc_anubisath_sentinelAI : public ScriptedAI +{ + npc_anubisath_sentinelAI(Creature* pCreature) : ScriptedAI(pCreature) + { + m_lAssistList.clear(); + Reset(); + } + + uint32 m_uiMyAbility; + bool m_bEnraged; + + GuidList m_lAssistList; + + void Reset() override + { + m_uiMyAbility = 0; + m_bEnraged = false; + } + + void GetAIInformation(ChatHandler& reader) override + { + if (m_lAssistList.empty()) + reader.PSendSysMessage("Anubisath Sentinel - group not assigned, will be assigned OnAggro"); + if (m_lAssistList.size() == MAX_BUDDY) + reader.PSendSysMessage("Anubisath Sentinel - proper group found"); + else + reader.PSendSysMessage("Anubisath Sentinel - not correct number of mobs for group found. Number found %u, should be %u", m_lAssistList.size(), MAX_BUDDY); + } + + void JustReachedHome() override + { + for (GuidList::const_iterator itr = m_lAssistList.begin(); itr != m_lAssistList.end(); ++itr) + { + if (*itr == m_creature->GetObjectGuid()) + continue; + + if (Creature* pBuddy = m_creature->GetMap()->GetCreature(*itr)) + { + if (pBuddy->IsDead()) + pBuddy->Respawn(); + } + } + } + + void Aggro(Unit* pWho) override + { + SetAbility(); + InitSentinelsNear(pWho); + } + + void JustDied(Unit* /*pKiller*/) override + { + DoTransferAbility(); + } + + // this way will make it quite possible that sentinels get the same buff as others, need to fix that, it should be one unique each + void SetAbility() + { + switch (urand(0, 8)) + { + case 0: m_uiMyAbility = SPELL_MENDING; break; + case 1: m_uiMyAbility = SPELL_PERIODIC_KNOCK_AWAY; break; + case 2: m_uiMyAbility = SPELL_PERIODIC_MANA_BURN; break; + case 3: m_uiMyAbility = SPELL_FIRE_ARCANE_REFLECT; break; + case 4: m_uiMyAbility = SPELL_SHADOW_FROST_REFLECT; break; + case 5: m_uiMyAbility = SPELL_THORNS; break; + case 6: m_uiMyAbility = SPELL_PERIODIC_THUNDERCLAP; break; + case 7: m_uiMyAbility = SPELL_MORTAL_STRIKE; break; + case 8: m_uiMyAbility = SPELL_PERIODIC_SHADOW_STORM; break; + } + + DoCastSpellIfCan(m_creature, m_uiMyAbility, CAST_TRIGGERED); + } + + void DoTransferAbility() + { + for (GuidList::const_iterator itr = m_lAssistList.begin(); itr != m_lAssistList.end(); ++itr) + { + if (Creature* pBuddy = m_creature->GetMap()->GetCreature(*itr)) + { + if (*itr == m_creature->GetObjectGuid()) + continue; + + if (!pBuddy->IsAlive()) + continue; + + pBuddy->SetHealth(pBuddy->GetMaxHealth()); + DoCastSpellIfCan(pBuddy, m_uiMyAbility, CAST_TRIGGERED); + } + } + } + + void InitSentinelsNear(Unit* pTarget) + { + if (!m_lAssistList.empty()) + { + for (GuidList::const_iterator itr = m_lAssistList.begin(); itr != m_lAssistList.end(); ++itr) + { + if (*itr == m_creature->GetObjectGuid()) + continue; + + if (Creature* pBuddy = m_creature->GetMap()->GetCreature(*itr)) + { + if (pBuddy->IsAlive()) + pBuddy->AI()->AttackStart(pTarget); + } + } + + return; + } + + std::list lAssistList; + GetCreatureListWithEntryInGrid(lAssistList, m_creature, m_creature->GetEntry(), 80.0f); + + for (std::list::iterator iter = lAssistList.begin(); iter != lAssistList.end(); ++iter) + { + m_lAssistList.push_back((*iter)->GetObjectGuid()); + + if ((*iter)->GetObjectGuid() == m_creature->GetObjectGuid()) + continue; + + (*iter)->AI()->AttackStart(pTarget); + } + + if (m_lAssistList.size() != MAX_BUDDY) + script_error_log("npc_anubisath_sentinel for %s found too few/too many buddies, expected %u.", m_creature->GetGuidStr().c_str(), MAX_BUDDY); + } + + void UpdateAI(const uint32 /*uiDiff*/) override + { + if (!m_creature->SelectHostileTarget() || !m_creature->getVictim()) + return; + + if (!m_bEnraged && m_creature->GetHealthPercent() < 30.0f) + { + if (DoCastSpellIfCan(m_creature, SPELL_ENRAGE) == CAST_OK) + { + DoScriptText(EMOTE_GENERIC_FRENZY, m_creature); + m_bEnraged = true; + } + } + + DoMeleeAttackIfReady(); + } +}; + +CreatureAI* GetAI_npc_anubisath_sentinel(Creature* pCreature) +{ + return new npc_anubisath_sentinelAI(pCreature); +} + +void AddSC_mob_anubisath_sentinel() +{ + Script* pNewScript; + + pNewScript = new Script; + pNewScript->Name = "mob_anubisath_sentinel"; + pNewScript->GetAI = &GetAI_npc_anubisath_sentinel; + pNewScript->RegisterSelf(); +} diff --git a/src/modules/SD2/scripts/kalimdor/temple_of_ahnqiraj/temple_of_ahnqiraj.h b/src/modules/SD2/scripts/kalimdor/temple_of_ahnqiraj/temple_of_ahnqiraj.h new file mode 100644 index 000000000..6e0d2994a --- /dev/null +++ b/src/modules/SD2/scripts/kalimdor/temple_of_ahnqiraj/temple_of_ahnqiraj.h @@ -0,0 +1,102 @@ +/* Copyright (C) 2006 - 2013 ScriptDev2 + * This program is free software licensed under GPL version 2 + * Please see the included DOCS/LICENSE.TXT for more information */ + +#ifndef DEF_TEMPLE_OF_AHNQIRAJ_H +#define DEF_TEMPLE_OF_AHNQIRAJ_H + +enum +{ + MAX_ENCOUNTER = 9, + + TYPE_SKERAM = 0, + TYPE_BUG_TRIO = 1, + TYPE_SARTURA = 2, + TYPE_FANKRISS = 3, + TYPE_VISCIDUS = 4, + TYPE_HUHURAN = 5, + TYPE_TWINS = 6, + TYPE_OURO = 7, + TYPE_CTHUN = 8, + + NPC_SKERAM = 15263, + // NPC_KRI = 15511, + // NPC_VEM = 15544, + // NPC_YAUJ = 15543, + NPC_VEKLOR = 15276, + NPC_VEKNILASH = 15275, + NPC_MASTERS_EYE = 15963, + NPC_OURO_SPAWNER = 15957, + // NPC_EYE_OF_CTHUN = 15589, + NPC_CTHUN = 15727, + + GO_SKERAM_GATE = 180636, + GO_TWINS_ENTER_DOOR = 180634, + GO_TWINS_EXIT_DOOR = 180635, + GO_SANDWORM_BASE = 180795, + + EMOTE_EYE_INTRO = -1531012, + SAY_EMPERORS_INTRO_1 = -1531013, + SAY_EMPERORS_INTRO_2 = -1531014, + SAY_EMPERORS_INTRO_3 = -1531015, + SAY_EMPERORS_INTRO_4 = -1531016, + SAY_EMPERORS_INTRO_5 = -1531017, + SAY_EMPERORS_INTRO_6 = -1531018, + + // Whispered on players around the map + SAY_CTHUN_WHISPER_1 = -1531033, + SAY_CTHUN_WHISPER_2 = -1531034, + SAY_CTHUN_WHISPER_3 = -1531035, + SAY_CTHUN_WHISPER_4 = -1531036, + SAY_CTHUN_WHISPER_5 = -1531037, + SAY_CTHUN_WHISPER_6 = -1531038, + SAY_CTHUN_WHISPER_7 = -1531039, + SAY_CTHUN_WHISPER_8 = -1531040, + + AREATRIGGER_TWIN_EMPERORS = 4047, + + SPELL_SUMMON_PLAYER = 20477, + + // Cast periodically on players around the instance + SPELL_WHISPERINGS_CTHUN_1 = 26195, + SPELL_WHISPERINGS_CTHUN_2 = 26197, + SPELL_WHISPERINGS_CTHUN_3 = 26198, + SPELL_WHISPERINGS_CTHUN_4 = 26258, + SPELL_WHISPERINGS_CTHUN_5 = 26259, +}; + +class instance_temple_of_ahnqiraj : public ScriptedInstance +{ + public: + instance_temple_of_ahnqiraj(Map* pMap); + + void Initialize() override; + + bool IsEncounterInProgress() const override; + + void OnCreatureCreate(Creature* pCreature) override; + void OnObjectCreate(GameObject* pGo) override; + + void SetData(uint32 uiType, uint32 uiData) override; + uint32 GetData(uint32 uiType) const override; + + void DoHandleTempleAreaTrigger(uint32 uiTriggerId); + + const char* Save() const override { return m_strInstData.c_str(); } + void Load(const char* chrIn) override; + + void Update(uint32 uiDiff) override; + + private: + uint32 m_auiEncounter[MAX_ENCOUNTER]; + std::string m_strInstData; + + uint8 m_uiBugTrioDeathCount; + uint32 m_uiCthunWhisperTimer; + + bool m_bIsEmperorsIntroDone; + + DialogueHelper m_dialogueHelper; +}; + +#endif diff --git a/src/modules/SD2/scripts/kalimdor/the_barrens.cpp b/src/modules/SD2/scripts/kalimdor/the_barrens.cpp new file mode 100644 index 000000000..671b08129 --- /dev/null +++ b/src/modules/SD2/scripts/kalimdor/the_barrens.cpp @@ -0,0 +1,660 @@ +/* Copyright (C) 2006 - 2013 ScriptDev2 + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along 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. +SDCategory: Barrens +EndScriptData */ + +/* ContentData +npc_beaten_corpse +npc_gilthares +npc_taskmaster_fizzule +npc_twiggy_flathead +at_twiggy_flathead +npc_wizzlecrank_shredder +EndContentData */ + +#include "precompiled.h" +#include "escort_ai.h" + +/*###### +## npc_beaten_corpse +######*/ + +enum +{ + 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, "Examine corpse in detail...", GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 1); + + pPlayer->SEND_GOSSIP_MENU(3557, pCreature->GetObjectGuid()); + return true; +} + +bool GossipSelect_npc_beaten_corpse(Player* pPlayer, Creature* pCreature, uint32 /*uiSender*/, uint32 uiAction) +{ + if (uiAction == GOSSIP_ACTION_INFO_DEF + 1) + { + pPlayer->SEND_GOSSIP_MENU(3558, pCreature->GetObjectGuid()); + pPlayer->TalkedToCreature(pCreature->GetEntry(), pCreature->GetObjectGuid()); + } + return true; +} + +/*###### +# npc_gilthares +######*/ + +enum +{ + SAY_GIL_START = -1000370, + SAY_GIL_AT_LAST = -1000371, + SAY_GIL_PROCEED = -1000372, + SAY_GIL_FREEBOOTERS = -1000373, + SAY_GIL_AGGRO_1 = -1000374, + SAY_GIL_AGGRO_2 = -1000375, + SAY_GIL_AGGRO_3 = -1000376, + SAY_GIL_AGGRO_4 = -1000377, + SAY_GIL_ALMOST = -1000378, + SAY_GIL_SWEET = -1000379, + SAY_GIL_FREED = -1000380, + + QUEST_FREE_FROM_HOLD = 898, + AREA_MERCHANT_COAST = 391 +}; + +struct npc_giltharesAI : public npc_escortAI +{ + npc_giltharesAI(Creature* pCreature) : npc_escortAI(pCreature) { Reset(); } + + void Reset() override { } + + void WaypointReached(uint32 uiPointId) override + { + Player* pPlayer = GetPlayerForEscort(); + + if (!pPlayer) + return; + + switch (uiPointId) + { + case 16: + DoScriptText(SAY_GIL_AT_LAST, m_creature, pPlayer); + break; + case 17: + DoScriptText(SAY_GIL_PROCEED, m_creature, pPlayer); + break; + case 18: + DoScriptText(SAY_GIL_FREEBOOTERS, m_creature, pPlayer); + break; + case 37: + DoScriptText(SAY_GIL_ALMOST, m_creature, pPlayer); + break; + case 47: + DoScriptText(SAY_GIL_SWEET, m_creature, pPlayer); + break; + case 53: + DoScriptText(SAY_GIL_FREED, m_creature, pPlayer); + pPlayer->GroupEventHappens(QUEST_FREE_FROM_HOLD, m_creature); + break; + } + } + + void Aggro(Unit* pWho) override + { + // not always use + if (urand(0, 3)) + return; + + // only aggro text if not player and only in this area + if (pWho->GetTypeId() != TYPEID_PLAYER && m_creature->GetAreaId() == AREA_MERCHANT_COAST) + { + // appears to be pretty much random (possible only if escorter not in combat with pWho yet?) + switch (urand(0, 3)) + { + case 0: DoScriptText(SAY_GIL_AGGRO_1, m_creature, pWho); break; + case 1: DoScriptText(SAY_GIL_AGGRO_2, m_creature, pWho); break; + case 2: DoScriptText(SAY_GIL_AGGRO_3, m_creature, pWho); break; + case 3: DoScriptText(SAY_GIL_AGGRO_4, m_creature, pWho); break; + } + } + } +}; + +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->SetFactionTemporary(FACTION_ESCORT_H_NEUTRAL_ACTIVE, TEMPFACTION_RESTORE_RESPAWN); + pCreature->SetStandState(UNIT_STAND_STATE_STAND); + + DoScriptText(SAY_GIL_START, pCreature, pPlayer); + + if (npc_giltharesAI* pEscortAI = dynamic_cast(pCreature->AI())) + pEscortAI->Start(false, pPlayer, pQuest); + } + return true; +} + +/*###### +## npc_taskmaster_fizzule +######*/ + +enum +{ + FACTION_FRIENDLY_F = 35, + SPELL_FLARE = 10113, + SPELL_FOLLY = 10137, +}; + +struct npc_taskmaster_fizzuleAI : public ScriptedAI +{ + npc_taskmaster_fizzuleAI(Creature* pCreature) : ScriptedAI(pCreature) { Reset(); } + + uint32 m_uiResetTimer; + uint8 m_uiFlareCount; + + void Reset() override + { + m_uiResetTimer = 0; + m_uiFlareCount = 0; + } + + void EnterEvadeMode() override + { + if (m_uiResetTimer) + { + m_creature->RemoveAllAurasOnEvade(); + m_creature->DeleteThreatList(); + m_creature->CombatStop(true); + m_creature->LoadCreatureAddon(true); + + m_creature->SetLootRecipient(NULL); + + m_creature->SetFactionTemporary(FACTION_FRIENDLY_F, TEMPFACTION_RESTORE_REACH_HOME); + m_creature->HandleEmote(EMOTE_ONESHOT_SALUTE); + } + else + ScriptedAI::EnterEvadeMode(); + } + + void SpellHit(Unit* pCaster, const SpellEntry* pSpell) override + { + if (pCaster->GetTypeId() == TYPEID_PLAYER && (pSpell->Id == SPELL_FLARE || pSpell->Id == SPELL_FOLLY)) + { + ++m_uiFlareCount; + + if (m_uiFlareCount >= 2 && m_creature->getFaction() != FACTION_FRIENDLY_F) + m_uiResetTimer = 120000; + } + } + + void UpdateAI(const uint32 uiDiff) override + { + if (m_uiResetTimer) + { + if (m_uiResetTimer <= uiDiff) + { + m_uiResetTimer = 0; + EnterEvadeMode(); + } + else + m_uiResetTimer -= uiDiff; + } + + if (!m_creature->SelectHostileTarget() || !m_creature->getVictim()) + return; + + DoMeleeAttackIfReady(); + } + + void ReceiveEmote(Player* /*pPlayer*/, uint32 uiTextEmote) override + { + if (uiTextEmote == TEXTEMOTE_SALUTE) + { + if (m_uiFlareCount >= 2 && m_creature->getFaction() != FACTION_FRIENDLY_F) + EnterEvadeMode(); + } + } +}; + +CreatureAI* GetAI_npc_taskmaster_fizzule(Creature* pCreature) +{ + return new npc_taskmaster_fizzuleAI(pCreature); +} + +/*##### +## npc_twiggy_flathead +#####*/ + +enum +{ + SAY_BIG_WILL_READY = -1000123, + SAY_TWIGGY_BEGIN = -1000124, + SAY_TWIGGY_FRAY = -1000125, + SAY_TWIGGY_DOWN = -1000126, + SAY_TWIGGY_OVER = -1000127, + + NPC_TWIGGY = 6248, + NPC_BIG_WILL = 6238, + NPC_AFFRAY_CHALLENGER = 6240, + + QUEST_AFFRAY = 1719, + + FACTION_FRIENDLY = 35, + FACTION_HOSTILE_WILL = 32, + FACTION_HOSTILE_CHALLENGER = 14, + + MAX_CHALLENGERS = 6, +}; + +static const float aAffrayChallengerLoc[8][4] = +{ + { -1683.0f, -4326.0f, 2.79f, 0.00f}, + { -1682.0f, -4329.0f, 2.79f, 0.00f}, + { -1683.0f, -4330.0f, 2.79f, 0.00f}, + { -1680.0f, -4334.0f, 2.79f, 1.49f}, + { -1674.0f, -4326.0f, 2.79f, 3.49f}, + { -1677.0f, -4334.0f, 2.79f, 1.66f}, + { -1713.79f, -4342.09f, 6.05f, 6.15f}, // Big Will spawn loc + { -1682.31f, -4329.68f, 2.78f, 0.0f}, // Big Will move loc +}; + +struct npc_twiggy_flatheadAI : public ScriptedAI +{ + npc_twiggy_flatheadAI(Creature* pCreature) : ScriptedAI(pCreature) { Reset(); } + + bool m_bIsEventInProgress; + + uint32 m_uiEventTimer; + uint32 m_uiChallengerCount; + uint8 m_uiStep; + + ObjectGuid m_playerGuid; + ObjectGuid m_bigWillGuid; + GuidVector m_vAffrayChallengerGuidsVector; + + void Reset() override + { + m_bIsEventInProgress = false; + + m_uiEventTimer = 1000; + m_uiChallengerCount = 0; + m_uiStep = 0; + + m_playerGuid.Clear(); + m_bigWillGuid.Clear(); + m_vAffrayChallengerGuidsVector.clear(); + } + + bool CanStartEvent(Player* pPlayer) + { + if (!m_bIsEventInProgress) + { + DoScriptText(SAY_TWIGGY_BEGIN, m_creature, pPlayer); + m_playerGuid = pPlayer->GetObjectGuid(); + m_bIsEventInProgress = true; + + return true; + } + + debug_log("SD2: npc_twiggy_flathead event already in progress, need to wait."); + return false; + } + + void SetChallengers() + { + m_vAffrayChallengerGuidsVector.reserve(MAX_CHALLENGERS); + + for (uint8 i = 0; i < MAX_CHALLENGERS; ++i) + m_creature->SummonCreature(NPC_AFFRAY_CHALLENGER, aAffrayChallengerLoc[i][0], aAffrayChallengerLoc[i][1], aAffrayChallengerLoc[i][2], aAffrayChallengerLoc[i][3], TEMPSUMMON_TIMED_OOC_OR_DEAD_DESPAWN, 600000); + } + + void SetChallengerReady(Creature* pChallenger) + { + pChallenger->setFaction(FACTION_HOSTILE_CHALLENGER); + pChallenger->HandleEmote(EMOTE_ONESHOT_ROAR); + ++m_uiChallengerCount; + + if (Player* pPlayer = m_creature->GetMap()->GetPlayer(m_playerGuid)) + pChallenger->AI()->AttackStart(pPlayer); + } + + void JustSummoned(Creature* pSummoned) override + { + if (pSummoned->GetEntry() == NPC_BIG_WILL) + { + m_bigWillGuid = pSummoned->GetObjectGuid(); + pSummoned->setFaction(FACTION_FRIENDLY); + pSummoned->SetWalk(false); + pSummoned->GetMotionMaster()->MovePoint(1, aAffrayChallengerLoc[7][0], aAffrayChallengerLoc[7][1], aAffrayChallengerLoc[7][2]); + } + else + { + pSummoned->setFaction(FACTION_FRIENDLY); + pSummoned->HandleEmote(EMOTE_ONESHOT_ROAR); + m_vAffrayChallengerGuidsVector.push_back(pSummoned->GetObjectGuid()); + } + } + + void SummonedMovementInform(Creature* pSummoned, uint32 uiMoveType, uint32 uiPointId) override + { + if (uiMoveType != POINT_MOTION_TYPE || !uiPointId || pSummoned->GetEntry() != NPC_BIG_WILL) + return; + + pSummoned->setFaction(FACTION_HOSTILE_WILL); + + if (Player* pPlayer = m_creature->GetMap()->GetPlayer(m_playerGuid)) + { + DoScriptText(SAY_BIG_WILL_READY, pSummoned, pPlayer); + pSummoned->SetFacingToObject(pPlayer); + } + } + + void SummonedCreatureJustDied(Creature* pSummoned) override + { + if (pSummoned->GetEntry() == NPC_BIG_WILL) + { + DoScriptText(SAY_TWIGGY_OVER, m_creature); + EnterEvadeMode(); + } + else + { + DoScriptText(SAY_TWIGGY_DOWN, m_creature); + m_uiEventTimer = 15000; + } + } + + void UpdateAI(const uint32 uiDiff) override + { + if (!m_bIsEventInProgress) + return; + + if (m_uiEventTimer < uiDiff) + { + Player* pPlayer = m_creature->GetMap()->GetPlayer(m_playerGuid); + + if (!pPlayer || !pPlayer->IsAlive()) + EnterEvadeMode(); + + switch (m_uiStep) + { + case 0: + SetChallengers(); + m_uiEventTimer = 5000; + ++m_uiStep; + break; + case 1: + DoScriptText(SAY_TWIGGY_FRAY, m_creature); + if (Creature* pChallenger = m_creature->GetMap()->GetCreature(m_vAffrayChallengerGuidsVector[m_uiChallengerCount])) + SetChallengerReady(pChallenger); + else + EnterEvadeMode(); + + if (m_uiChallengerCount == MAX_CHALLENGERS) + { + ++m_uiStep; + m_uiEventTimer = 5000; + } + else + m_uiEventTimer = 25000; + break; + case 2: + m_creature->SummonCreature(NPC_BIG_WILL, aAffrayChallengerLoc[6][0], aAffrayChallengerLoc[6][1], aAffrayChallengerLoc[6][2], aAffrayChallengerLoc[6][3], TEMPSUMMON_TIMED_OOC_OR_DEAD_DESPAWN, 300000); + m_uiEventTimer = 15000; + ++m_uiStep; + break; + default: + m_uiEventTimer = 5000; + break; + } + } + else + m_uiEventTimer -= uiDiff; + } +}; + +CreatureAI* GetAI_npc_twiggy_flathead(Creature* pCreature) +{ + return new npc_twiggy_flatheadAI(pCreature); +} + +bool AreaTrigger_at_twiggy_flathead(Player* pPlayer, AreaTriggerEntry const* /*pAt*/) +{ + if (pPlayer->IsAlive() && !pPlayer->isGameMaster() && pPlayer->GetQuestStatus(QUEST_AFFRAY) == QUEST_STATUS_INCOMPLETE) + { + Creature* pCreature = GetClosestCreatureWithEntry(pPlayer, NPC_TWIGGY, 30.0f); + if (!pCreature) + return true; + + if (npc_twiggy_flatheadAI* pTwiggyAI = dynamic_cast(pCreature->AI())) + { + if (pTwiggyAI->CanStartEvent(pPlayer)) + return false; // ok to let mangos process further + } + + return true; + } + return true; +} + +/*##### +## npc_wizzlecranks_shredder +#####*/ + +enum +{ + SAY_START = -1000298, + SAY_STARTUP1 = -1000299, + SAY_STARTUP2 = -1000300, + SAY_MERCENARY = -1000301, + SAY_PROGRESS_1 = -1000302, + SAY_PROGRESS_2 = -1000303, + SAY_PROGRESS_3 = -1000304, + SAY_END = -1000305, + + QUEST_ESCAPE = 863, + FACTION_RATCHET = 637, + NPC_PILOT_WIZZ = 3451, + NPC_MERCENARY = 3282 +}; + +struct npc_wizzlecranks_shredderAI : public npc_escortAI +{ + npc_wizzlecranks_shredderAI(Creature* pCreature) : npc_escortAI(pCreature) + { + m_bIsPostEvent = false; + m_uiPostEventTimer = 1000; + m_uiPostEventCount = 0; + Reset(); + } + + bool m_bIsPostEvent; + uint32 m_uiPostEventTimer; + uint32 m_uiPostEventCount; + + void Reset() override + { + 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) override + { + switch (uiPointId) + { + case 0: + if (Player* pPlayer = GetPlayerForEscort()) + DoScriptText(SAY_STARTUP1, m_creature, pPlayer); + break; + case 9: + SetRun(false); + break; + case 17: + if (Creature* pTemp = m_creature->SummonCreature(NPC_MERCENARY, 1128.489f, -3037.611f, 92.701f, 1.472f, TEMPSUMMON_TIMED_OOC_DESPAWN, 120000)) + { + DoScriptText(SAY_MERCENARY, pTemp); + m_creature->SummonCreature(NPC_MERCENARY, 1160.172f, -2980.168f, 97.313f, 3.690f, TEMPSUMMON_TIMED_OOC_DESPAWN, 120000); + } + break; + case 24: + m_bIsPostEvent = true; + break; + } + } + + void WaypointStart(uint32 uiPointId) override + { + switch (uiPointId) + { + case 9: + if (Player* pPlayer = GetPlayerForEscort()) + DoScriptText(SAY_STARTUP2, m_creature, pPlayer); + break; + case 18: + if (Player* pPlayer = GetPlayerForEscort()) + DoScriptText(SAY_PROGRESS_1, m_creature, pPlayer); + SetRun(); + break; + } + } + + void JustSummoned(Creature* pSummoned) override + { + 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) override + { + if (!m_creature->SelectHostileTarget() || !m_creature->getVictim()) + { + if (m_bIsPostEvent) + { + if (m_uiPostEventTimer < uiDiff) + { + switch (m_uiPostEventCount) + { + case 0: + DoScriptText(SAY_PROGRESS_2, m_creature); + break; + case 1: + DoScriptText(SAY_PROGRESS_3, m_creature); + break; + case 2: + DoScriptText(SAY_END, m_creature); + break; + case 3: + if (Player* pPlayer = GetPlayerForEscort()) + { + pPlayer->GroupEventHappens(QUEST_ESCAPE, m_creature); + m_creature->SummonCreature(NPC_PILOT_WIZZ, 0.0f, 0.0f, 0.0f, 0.0f, TEMPSUMMON_TIMED_DESPAWN, 180000); + } + break; + } + + ++m_uiPostEventCount; + m_uiPostEventTimer = 5000; + } + else + m_uiPostEventTimer -= uiDiff; + } + + return; + } + + DoMeleeAttackIfReady(); + } +}; + +bool QuestAccept_npc_wizzlecranks_shredder(Player* pPlayer, Creature* pCreature, const Quest* pQuest) +{ + if (pQuest->GetQuestId() == QUEST_ESCAPE) + { + DoScriptText(SAY_START, pCreature); + pCreature->SetFactionTemporary(FACTION_RATCHET, TEMPFACTION_RESTORE_RESPAWN); + + if (npc_wizzlecranks_shredderAI* pEscortAI = dynamic_cast(pCreature->AI())) + pEscortAI->Start(true, pPlayer, pQuest); + } + return true; +} + +CreatureAI* GetAI_npc_wizzlecranks_shredder(Creature* pCreature) +{ + return new npc_wizzlecranks_shredderAI(pCreature); +} + +void AddSC_the_barrens() +{ + Script* pNewScript; + + pNewScript = new Script; + pNewScript->Name = "npc_beaten_corpse"; + pNewScript->pGossipHello = &GossipHello_npc_beaten_corpse; + pNewScript->pGossipSelect = &GossipSelect_npc_beaten_corpse; + pNewScript->RegisterSelf(); + + pNewScript = new Script; + pNewScript->Name = "npc_gilthares"; + pNewScript->GetAI = &GetAI_npc_gilthares; + pNewScript->pQuestAcceptNPC = &QuestAccept_npc_gilthares; + pNewScript->RegisterSelf(); + + pNewScript = new Script; + pNewScript->Name = "npc_taskmaster_fizzule"; + pNewScript->GetAI = &GetAI_npc_taskmaster_fizzule; + pNewScript->RegisterSelf(); + + pNewScript = new Script; + pNewScript->Name = "npc_twiggy_flathead"; + pNewScript->GetAI = &GetAI_npc_twiggy_flathead; + pNewScript->RegisterSelf(); + + pNewScript = new Script; + pNewScript->Name = "at_twiggy_flathead"; + pNewScript->pAreaTrigger = &AreaTrigger_at_twiggy_flathead; + pNewScript->RegisterSelf(); + + pNewScript = new Script; + pNewScript->Name = "npc_wizzlecranks_shredder"; + pNewScript->GetAI = &GetAI_npc_wizzlecranks_shredder; + pNewScript->pQuestAcceptNPC = &QuestAccept_npc_wizzlecranks_shredder; + pNewScript->RegisterSelf(); +} diff --git a/src/modules/SD2/scripts/kalimdor/thousand_needles.cpp b/src/modules/SD2/scripts/kalimdor/thousand_needles.cpp new file mode 100644 index 000000000..d5f83f984 --- /dev/null +++ b/src/modules/SD2/scripts/kalimdor/thousand_needles.cpp @@ -0,0 +1,393 @@ +/* Copyright (C) 2006 - 2013 ScriptDev2 + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along 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: 90 +SDComment: Quest support: 1950, 4770, 4904, 4966 +SDCategory: Thousand Needles +EndScriptData +*/ + +/* ContentData +npc_kanati +npc_lakota_windsong +npc_paoka_swiftmountain +npc_plucky_johnson +EndContentData */ + +#include "precompiled.h" +#include "escort_ai.h" + +/*###### +# npc_kanati +######*/ + +enum +{ + SAY_KAN_START = -1000410, + + QUEST_PROTECT_KANATI = 4966, + NPC_GALAK_ASS = 10720 +}; + +const float m_afGalakLoc[] = { -4867.387695f, -1357.353760f, -48.226f}; + +struct npc_kanatiAI : public npc_escortAI +{ + npc_kanatiAI(Creature* pCreature) : npc_escortAI(pCreature) { Reset(); } + + void Reset() override { } + + void WaypointReached(uint32 uiPointId) override + { + switch (uiPointId) + { + case 0: + DoScriptText(SAY_KAN_START, m_creature); + DoSpawnGalak(); + break; + case 1: + if (Player* pPlayer = GetPlayerForEscort()) + pPlayer->GroupEventHappens(QUEST_PROTECT_KANATI, m_creature); + break; + } + } + + void DoSpawnGalak() + { + 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_OOC_DESPAWN, 25000); + } + + void JustSummoned(Creature* pSummoned) override + { + 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) + { + if (npc_kanatiAI* pEscortAI = dynamic_cast(pCreature->AI())) + pEscortAI->Start(false, pPlayer, pQuest, true); + } + return true; +} + +/*###### +# npc_lakota_windsong +######*/ + +enum +{ + SAY_LAKO_START = -1000365, + SAY_LAKO_LOOK_OUT = -1000366, + SAY_LAKO_HERE_COME = -1000367, + SAY_LAKO_MORE = -1000368, + SAY_LAKO_END = -1000369, + + QUEST_FREE_AT_LAST = 4904, + NPC_GRIM_BANDIT = 10758, + + ID_AMBUSH_1 = 0, + ID_AMBUSH_2 = 2, + ID_AMBUSH_3 = 4 +}; + +float m_afBanditLoc[6][6] = +{ + { -4905.479492f, -2062.732666f, 84.352f}, + { -4915.201172f, -2073.528320f, 84.733f}, + { -4878.883301f, -1986.947876f, 91.966f}, + { -4877.503906f, -1966.113403f, 91.859f}, + { -4767.985352f, -1873.169189f, 90.192f}, + { -4788.861328f, -1888.007813f, 89.888f} +}; + +struct npc_lakota_windsongAI : public npc_escortAI +{ + npc_lakota_windsongAI(Creature* pCreature) : npc_escortAI(pCreature) { Reset(); } + + void Reset() override { } + + void WaypointReached(uint32 uiPointId) override + { + switch (uiPointId) + { + case 8: + DoScriptText(SAY_LAKO_LOOK_OUT, m_creature); + DoSpawnBandits(ID_AMBUSH_1); + break; + case 14: + DoScriptText(SAY_LAKO_HERE_COME, m_creature); + DoSpawnBandits(ID_AMBUSH_2); + break; + case 21: + DoScriptText(SAY_LAKO_MORE, m_creature); + DoSpawnBandits(ID_AMBUSH_3); + break; + case 45: + if (Player* pPlayer = GetPlayerForEscort()) + pPlayer->GroupEventHappens(QUEST_FREE_AT_LAST, m_creature); + break; + } + } + + void DoSpawnBandits(int uiAmbushId) + { + 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_OOC_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->SetFactionTemporary(FACTION_ESCORT_H_NEUTRAL_ACTIVE, TEMPFACTION_RESTORE_RESPAWN); + + if (npc_lakota_windsongAI* pEscortAI = dynamic_cast(pCreature->AI())) + pEscortAI->Start(false, pPlayer, pQuest); + } + return true; +} + +/*###### +# npc_paoka_swiftmountain +######*/ + +enum +{ + SAY_START = -1000362, + SAY_WYVERN = -1000363, + SAY_COMPLETE = -1000364, + + QUEST_HOMEWARD = 4770, + NPC_WYVERN = 4107 +}; + +float m_afWyvernLoc[3][3] = +{ + { -4990.606f, -906.057f, -5.343f}, + { -4970.241f, -927.378f, -4.951f}, + { -4985.364f, -952.528f, -5.199f} +}; + +struct npc_paoka_swiftmountainAI : public npc_escortAI +{ + npc_paoka_swiftmountainAI(Creature* pCreature) : npc_escortAI(pCreature) { Reset(); } + + void Reset() override { } + + void WaypointReached(uint32 uiPointId) override + { + switch (uiPointId) + { + case 15: + DoScriptText(SAY_WYVERN, m_creature); + DoSpawnWyvern(); + break; + case 26: + DoScriptText(SAY_COMPLETE, m_creature); + break; + case 27: + if (Player* pPlayer = GetPlayerForEscort()) + pPlayer->GroupEventHappens(QUEST_HOMEWARD, m_creature); + break; + } + } + + void DoSpawnWyvern() + { + 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_OOC_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->SetFactionTemporary(FACTION_ESCORT_H_NEUTRAL_ACTIVE, TEMPFACTION_RESTORE_RESPAWN); + + if (npc_paoka_swiftmountainAI* pEscortAI = dynamic_cast(pCreature->AI())) + pEscortAI->Start(false, pPlayer, pQuest); + } + return true; +} + +/*###### +# "Plucky" Johnson +######*/ + +enum +{ + FACTION_FRIENDLY = 35, + QUEST_SCOOP = 1950, + SPELL_PLUCKY_HUMAN = 9192, + SPELL_PLUCKY_CHICKEN = 9220 +}; + +#define GOSSIP_ITEM_QUEST "Please tell me the Phrase.." + +struct npc_plucky_johnsonAI : public ScriptedAI +{ + npc_plucky_johnsonAI(Creature* pCreature) : ScriptedAI(pCreature) + { + Reset(); + } + + uint32 m_uiResetTimer; + + void Reset() override + { + m_uiResetTimer = 120000; + + 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) override + { + if (pPlayer->GetQuestStatus(QUEST_SCOOP) == QUEST_STATUS_INCOMPLETE) + { + if (uiTextEmote == TEXTEMOTE_BECKON) + { + m_creature->SetFactionTemporary(FACTION_FRIENDLY, TEMPFACTION_RESTORE_RESPAWN | TEMPFACTION_RESTORE_COMBAT_STOP); + m_creature->SetFlag(UNIT_NPC_FLAGS, UNIT_NPC_FLAG_GOSSIP); + m_creature->CastSpell(m_creature, SPELL_PLUCKY_HUMAN, false); + } + } + + if (uiTextEmote == TEXTEMOTE_CHICKEN) + { + if (m_creature->HasFlag(UNIT_NPC_FLAGS, UNIT_NPC_FLAG_GOSSIP)) + return; + else + { + m_creature->SetFactionTemporary(FACTION_FRIENDLY, TEMPFACTION_RESTORE_RESPAWN | TEMPFACTION_RESTORE_COMBAT_STOP); + m_creature->SetFlag(UNIT_NPC_FLAGS, UNIT_NPC_FLAG_GOSSIP); + m_creature->CastSpell(m_creature, SPELL_PLUCKY_HUMAN, false); + m_creature->HandleEmote(EMOTE_ONESHOT_WAVE); + } + } + } + + void UpdateAI(const uint32 uiDiff) override + { + if (m_creature->HasFlag(UNIT_NPC_FLAGS, UNIT_NPC_FLAG_GOSSIP)) + { + if (m_uiResetTimer < uiDiff) + { + if (!m_creature->getVictim()) + EnterEvadeMode(); + else + m_creature->RemoveFlag(UNIT_NPC_FLAGS, UNIT_NPC_FLAG_GOSSIP); + + return; + } + else + m_uiResetTimer -= uiDiff; + } + + if (!m_creature->SelectHostileTarget() || !m_creature->getVictim()) + return; + + DoMeleeAttackIfReady(); + } +}; + +CreatureAI* GetAI_npc_plucky_johnson(Creature* pCreature) +{ + return new npc_plucky_johnsonAI(pCreature); +} + +bool GossipHello_npc_plucky_johnson(Player* pPlayer, Creature* pCreature) +{ + if (pPlayer->GetQuestStatus(QUEST_SCOOP) == QUEST_STATUS_INCOMPLETE) + pPlayer->ADD_GOSSIP_ITEM(GOSSIP_ICON_CHAT, GOSSIP_ITEM_QUEST, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF); + + pPlayer->SEND_GOSSIP_MENU(720, pCreature->GetObjectGuid()); + return true; +} + +bool GossipSelect_npc_plucky_johnson(Player* pPlayer, Creature* pCreature, uint32 /*uiSender*/, uint32 uiAction) +{ + if (uiAction == GOSSIP_ACTION_INFO_DEF) + { + pPlayer->SEND_GOSSIP_MENU(738, pCreature->GetObjectGuid()); + pPlayer->AreaExploredOrEventHappens(QUEST_SCOOP); + } + + return true; +} + +void AddSC_thousand_needles() +{ + Script* pNewScript; + + pNewScript = new Script; + pNewScript->Name = "npc_kanati"; + pNewScript->GetAI = &GetAI_npc_kanati; + pNewScript->pQuestAcceptNPC = &QuestAccept_npc_kanati; + pNewScript->RegisterSelf(); + + pNewScript = new Script; + pNewScript->Name = "npc_lakota_windsong"; + pNewScript->GetAI = &GetAI_npc_lakota_windsong; + pNewScript->pQuestAcceptNPC = &QuestAccept_npc_lakota_windsong; + pNewScript->RegisterSelf(); + + pNewScript = new Script; + pNewScript->Name = "npc_paoka_swiftmountain"; + pNewScript->GetAI = &GetAI_npc_paoka_swiftmountain; + pNewScript->pQuestAcceptNPC = &QuestAccept_npc_paoka_swiftmountain; + pNewScript->RegisterSelf(); + + pNewScript = new Script; + pNewScript->Name = "npc_plucky_johnson"; + pNewScript->GetAI = &GetAI_npc_plucky_johnson; + pNewScript->pGossipHello = &GossipHello_npc_plucky_johnson; + pNewScript->pGossipSelect = &GossipSelect_npc_plucky_johnson; + pNewScript->RegisterSelf(); +} diff --git a/src/modules/SD2/scripts/kalimdor/throne_of_the_four_winds/boss_alakir.cpp b/src/modules/SD2/scripts/kalimdor/throne_of_the_four_winds/boss_alakir.cpp new file mode 100644 index 000000000..e5de99d97 --- /dev/null +++ b/src/modules/SD2/scripts/kalimdor/throne_of_the_four_winds/boss_alakir.cpp @@ -0,0 +1,28 @@ +/* Copyright (C) 2006 - 2013 ScriptDev2 + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +/* ScriptData +SDName: boss_alakir +SD%Complete: 0 +SDComment: Placeholder +SDCategory: Throne of the Four Winds +EndScriptData */ + +#include "precompiled.h" + +void AddSC_boss_alakir() +{ +} diff --git a/src/modules/SD2/scripts/kalimdor/throne_of_the_four_winds/conclave_of_the_wind.cpp b/src/modules/SD2/scripts/kalimdor/throne_of_the_four_winds/conclave_of_the_wind.cpp new file mode 100644 index 000000000..6f8862f93 --- /dev/null +++ b/src/modules/SD2/scripts/kalimdor/throne_of_the_four_winds/conclave_of_the_wind.cpp @@ -0,0 +1,28 @@ +/* Copyright (C) 2006 - 2013 ScriptDev2 + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +/* ScriptData +SDName: conclave_of_the_wind +SD%Complete: 0 +SDComment: Placeholder +SDCategory: Throne of the Four Winds +EndScriptData */ + +#include "precompiled.h" + +void AddSC_conclave_of_the_wind() +{ +} diff --git a/src/modules/SD2/scripts/kalimdor/throne_of_the_four_winds/instance_throne_of_the_four_winds.cpp b/src/modules/SD2/scripts/kalimdor/throne_of_the_four_winds/instance_throne_of_the_four_winds.cpp new file mode 100644 index 000000000..7a9fb68e7 --- /dev/null +++ b/src/modules/SD2/scripts/kalimdor/throne_of_the_four_winds/instance_throne_of_the_four_winds.cpp @@ -0,0 +1,28 @@ +/* Copyright (C) 2006 - 2013 ScriptDev2 + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along 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_throne_of_the_four_winds +SD%Complete: 0 +SDComment: Placeholder +SDCategory: Throne of the Four Winds +EndScriptData */ + +#include "precompiled.h" + +void AddSC_instance_throne_of_the_four_winds() +{ +} diff --git a/src/modules/SD2/scripts/kalimdor/throne_of_the_four_winds/throne_of_the_four_winds.h b/src/modules/SD2/scripts/kalimdor/throne_of_the_four_winds/throne_of_the_four_winds.h new file mode 100644 index 000000000..6b5f9ef47 --- /dev/null +++ b/src/modules/SD2/scripts/kalimdor/throne_of_the_four_winds/throne_of_the_four_winds.h @@ -0,0 +1,3 @@ +/* Copyright (C) 2006 - 2013 ScriptDev2 + * This program is free software licensed under GPL version 2 + * Please see the included DOCS/LICENSE.TXT for more information */ diff --git a/src/modules/SD2/scripts/kalimdor/thunder_bluff.cpp b/src/modules/SD2/scripts/kalimdor/thunder_bluff.cpp new file mode 100644 index 000000000..36e8e8f7f --- /dev/null +++ b/src/modules/SD2/scripts/kalimdor/thunder_bluff.cpp @@ -0,0 +1,32 @@ +/* Copyright (C) 2006 - 2013 ScriptDev2 + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along 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: 0 +SDComment: +SDCategory: Thunder Bluff +EndScriptData */ + +#include "precompiled.h" + +/*##### +# +######*/ + +void AddSC_thunder_bluff() +{ +} diff --git a/src/modules/SD2/scripts/kalimdor/uldum.cpp b/src/modules/SD2/scripts/kalimdor/uldum.cpp new file mode 100644 index 000000000..2da318b72 --- /dev/null +++ b/src/modules/SD2/scripts/kalimdor/uldum.cpp @@ -0,0 +1,35 @@ +/* Copyright (C) 2006 - 2013 ScriptDev2 + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +/* ScriptData +SDName: Uldum +SD%Complete: 0 +SDComment: Placeholder +SDCategory: Uldum +EndScriptData */ + +/* ContentData +EndContentData */ + +#include "precompiled.h" + +/*###### +# +######*/ + +void AddSC_uldum() +{ +} diff --git a/src/modules/SD2/scripts/kalimdor/ungoro_crater.cpp b/src/modules/SD2/scripts/kalimdor/ungoro_crater.cpp new file mode 100644 index 000000000..83680055f --- /dev/null +++ b/src/modules/SD2/scripts/kalimdor/ungoro_crater.cpp @@ -0,0 +1,345 @@ +/* Copyright (C) 2006 - 2013 ScriptDev2 + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along 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: Quest support: 4245, 4491 +SDCategory: Un'Goro Crater +EndScriptData */ + +/* ContentData +npc_ringo +EndContentData */ + +#include "precompiled.h" +#include "escort_ai.h" +#include "follower_ai.h" + +/*###### +## npc_ame01 +######*/ + +enum +{ + SAY_AME_START = -1000446, + SAY_AME_PROGRESS = -1000447, + SAY_AME_END = -1000448, + SAY_AME_AGGRO1 = -1000449, + SAY_AME_AGGRO2 = -1000450, + SAY_AME_AGGRO3 = -1000451, + + QUEST_CHASING_AME = 4245 +}; + +struct npc_ame01AI : public npc_escortAI +{ + npc_ame01AI(Creature* pCreature) : npc_escortAI(pCreature) { Reset(); } + + void Reset() override {} + + void WaypointReached(uint32 uiPointId) override + { + switch (uiPointId) + { + case 0: + DoScriptText(SAY_AME_START, m_creature); + break; + case 19: + DoScriptText(SAY_AME_PROGRESS, m_creature); + break; + case 37: + DoScriptText(SAY_AME_END, m_creature); + if (Player* pPlayer = GetPlayerForEscort()) + pPlayer->GroupEventHappens(QUEST_CHASING_AME, m_creature); + break; + } + } + + void Aggro(Unit* pWho) override + { + if (pWho->GetTypeId() == TYPEID_PLAYER) + return; + + if (Player* pPlayer = GetPlayerForEscort()) + { + if (pPlayer->getVictim() && pPlayer->getVictim() == pWho) + return; + + switch (urand(0, 2)) + { + case 0: DoScriptText(SAY_AME_AGGRO1, m_creature); break; + case 1: DoScriptText(SAY_AME_AGGRO2, m_creature); break; + case 2: DoScriptText(SAY_AME_AGGRO3, m_creature); break; + } + } + } +}; + +bool QuestAccept_npc_ame01(Player* pPlayer, Creature* pCreature, const Quest* pQuest) +{ + if (pQuest->GetQuestId() == QUEST_CHASING_AME) + { + if (npc_ame01AI* pAmeAI = dynamic_cast(pCreature->AI())) + { + pCreature->SetStandState(UNIT_STAND_STATE_STAND); + + if (pPlayer->GetTeam() == ALLIANCE) + pCreature->SetFactionTemporary(FACTION_ESCORT_A_PASSIVE, TEMPFACTION_RESTORE_RESPAWN); + else if (pPlayer->GetTeam() == HORDE) + pCreature->SetFactionTemporary(FACTION_ESCORT_H_PASSIVE, TEMPFACTION_RESTORE_RESPAWN); + + pAmeAI->Start(false, pPlayer, pQuest); + } + } + return true; +} + +CreatureAI* GetAI_npc_ame01(Creature* pCreature) +{ + return new npc_ame01AI(pCreature); +} + +/*#### +# npc_ringo +####*/ + +enum +{ + 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, + EMOTE_RIN_END_4 = -1000429, + EMOTE_RIN_END_5 = -1000430, + SAY_RIN_END_6 = -1000431, + SAY_SPR_END_7 = -1000432, + EMOTE_RIN_END_8 = -1000433, + + SPELL_REVIVE_RINGO = 15591, + QUEST_A_LITTLE_HELP = 4491, + NPC_SPRAGGLE = 9997 +}; + +struct npc_ringoAI : public FollowerAI +{ + npc_ringoAI(Creature* pCreature) : FollowerAI(pCreature) { Reset(); } + + uint32 m_uiFaintTimer; + uint32 m_uiEndEventProgress; + uint32 m_uiEndEventTimer; + + Unit* pSpraggle; + + void Reset() override + { + m_uiFaintTimer = urand(30000, 60000); + m_uiEndEventProgress = 0; + m_uiEndEventTimer = 1000; + pSpraggle = NULL; + } + + void MoveInLineOfSight(Unit* pWho) override + { + FollowerAI::MoveInLineOfSight(pWho); + + if (!m_creature->getVictim() && !HasFollowState(STATE_FOLLOW_COMPLETE) && pWho->GetEntry() == NPC_SPRAGGLE) + { + if (m_creature->IsWithinDistInMap(pWho, INTERACTION_DISTANCE)) + { + if (Player* pPlayer = GetLeaderForFollower()) + { + 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) override + { + if (HasFollowState(STATE_FOLLOW_INPROGRESS | STATE_FOLLOW_PAUSED) && pSpell->Id == SPELL_REVIVE_RINGO) + ClearFaint(); + } + + void SetFaint() + { + if (!HasFollowState(STATE_FOLLOW_POSTEVENT)) + { + SetFollowPaused(true); + + switch (urand(0, 3)) + { + case 0: DoScriptText(SAY_FAINT_1, m_creature); break; + case 1: DoScriptText(SAY_FAINT_2, m_creature); break; + case 2: DoScriptText(SAY_FAINT_3, m_creature); break; + case 3: DoScriptText(SAY_FAINT_4, m_creature); break; + } + } + + // 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; + + switch (urand(0, 3)) + { + case 0: DoScriptText(SAY_WAKE_1, m_creature); break; + case 1: DoScriptText(SAY_WAKE_2, m_creature); break; + case 2: DoScriptText(SAY_WAKE_3, m_creature); break; + case 3: DoScriptText(SAY_WAKE_4, m_creature); break; + } + + SetFollowPaused(false); + } + + void UpdateFollowerAI(const uint32 uiDiff) override + { + if (!m_creature->SelectHostileTarget() || !m_creature->getVictim()) + { + if (HasFollowState(STATE_FOLLOW_POSTEVENT)) + { + if (m_uiEndEventTimer < uiDiff) + { + if (!pSpraggle || !pSpraggle->IsAlive()) + { + SetFollowComplete(); + return; + } + + switch (m_uiEndEventProgress) + { + case 1: + DoScriptText(SAY_RIN_END_1, m_creature); + m_uiEndEventTimer = 3000; + break; + case 2: + DoScriptText(SAY_SPR_END_2, pSpraggle); + m_uiEndEventTimer = 5000; + break; + case 3: + DoScriptText(SAY_RIN_END_3, m_creature); + m_uiEndEventTimer = 1000; + break; + case 4: + DoScriptText(EMOTE_RIN_END_4, m_creature); + SetFaint(); + m_uiEndEventTimer = 9000; + break; + case 5: + DoScriptText(EMOTE_RIN_END_5, m_creature); + ClearFaint(); + m_uiEndEventTimer = 1000; + break; + case 6: + DoScriptText(SAY_RIN_END_6, m_creature); + m_uiEndEventTimer = 3000; + break; + case 7: + DoScriptText(SAY_SPR_END_7, pSpraggle); + m_uiEndEventTimer = 10000; + break; + case 8: + DoScriptText(EMOTE_RIN_END_8, m_creature); + m_uiEndEventTimer = 5000; + break; + case 9: + SetFollowComplete(); + break; + } + + ++m_uiEndEventProgress; + } + else + m_uiEndEventTimer -= uiDiff; + } + else if (HasFollowState(STATE_FOLLOW_INPROGRESS)) + { + if (!HasFollowState(STATE_FOLLOW_PAUSED)) + { + if (m_uiFaintTimer < uiDiff) + { + SetFaint(); + m_uiFaintTimer = urand(60000, 120000); + } + else + 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) + { + if (npc_ringoAI* pRingoAI = dynamic_cast(pCreature->AI())) + { + pCreature->SetStandState(UNIT_STAND_STATE_STAND); + pRingoAI->StartFollow(pPlayer, FACTION_ESCORT_N_FRIEND_PASSIVE, pQuest); + } + } + + return true; +} + +void AddSC_ungoro_crater() +{ + Script* pNewScript; + + pNewScript = new Script; + pNewScript->Name = "npc_ame01"; + pNewScript->GetAI = &GetAI_npc_ame01; + pNewScript->pQuestAcceptNPC = &QuestAccept_npc_ame01; + pNewScript->RegisterSelf(); + + pNewScript = new Script; + pNewScript->Name = "npc_ringo"; + pNewScript->GetAI = &GetAI_npc_ringo; + pNewScript->pQuestAcceptNPC = &QuestAccept_npc_ringo; + pNewScript->RegisterSelf(); +} diff --git a/src/modules/SD2/scripts/kalimdor/vortex_pinnacle/boss_altairus.cpp b/src/modules/SD2/scripts/kalimdor/vortex_pinnacle/boss_altairus.cpp new file mode 100644 index 000000000..56dcf9e44 --- /dev/null +++ b/src/modules/SD2/scripts/kalimdor/vortex_pinnacle/boss_altairus.cpp @@ -0,0 +1,28 @@ +/* Copyright (C) 2006 - 2013 ScriptDev2 + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +/* ScriptData +SDName: boss_altairus +SD%Complete: 0 +SDComment: Placeholder +SDCategory: Vortex Pinnacle +EndScriptData */ + +#include "precompiled.h" + +void AddSC_boss_altairus() +{ +} diff --git a/src/modules/SD2/scripts/kalimdor/vortex_pinnacle/boss_asaad.cpp b/src/modules/SD2/scripts/kalimdor/vortex_pinnacle/boss_asaad.cpp new file mode 100644 index 000000000..7d1e3c3fc --- /dev/null +++ b/src/modules/SD2/scripts/kalimdor/vortex_pinnacle/boss_asaad.cpp @@ -0,0 +1,28 @@ +/* Copyright (C) 2006 - 2013 ScriptDev2 + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +/* ScriptData +SDName: boss_asaad +SD%Complete: 0 +SDComment: Placeholder +SDCategory: Vortex Pinnacle +EndScriptData */ + +#include "precompiled.h" + +void AddSC_boss_asaad() +{ +} diff --git a/src/modules/SD2/scripts/kalimdor/vortex_pinnacle/boss_grand_vizier_ertan.cpp b/src/modules/SD2/scripts/kalimdor/vortex_pinnacle/boss_grand_vizier_ertan.cpp new file mode 100644 index 000000000..e1854c173 --- /dev/null +++ b/src/modules/SD2/scripts/kalimdor/vortex_pinnacle/boss_grand_vizier_ertan.cpp @@ -0,0 +1,28 @@ +/* Copyright (C) 2006 - 2013 ScriptDev2 + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with 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_vizier_etan +SD%Complete: 0 +SDComment: Placeholder +SDCategory: Vortex Pinnacle +EndScriptData */ + +#include "precompiled.h" + +void AddSC_boss_grand_vizier_etan() +{ +} diff --git a/src/modules/SD2/scripts/kalimdor/vortex_pinnacle/instance_vortex_pinnacle.cpp b/src/modules/SD2/scripts/kalimdor/vortex_pinnacle/instance_vortex_pinnacle.cpp new file mode 100644 index 000000000..ff6448177 --- /dev/null +++ b/src/modules/SD2/scripts/kalimdor/vortex_pinnacle/instance_vortex_pinnacle.cpp @@ -0,0 +1,28 @@ +/* Copyright (C) 2006 - 2013 ScriptDev2 + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along 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_vortex_pinnacle +SD%Complete: 0 +SDComment: Placeholder +SDCategory: Vortex Pinnacle +EndScriptData */ + +#include "precompiled.h" + +void AddSC_instance_vortex_pinnacle() +{ +} diff --git a/src/modules/SD2/scripts/kalimdor/vortex_pinnacle/vortex_pinnacle.h b/src/modules/SD2/scripts/kalimdor/vortex_pinnacle/vortex_pinnacle.h new file mode 100644 index 000000000..6b5f9ef47 --- /dev/null +++ b/src/modules/SD2/scripts/kalimdor/vortex_pinnacle/vortex_pinnacle.h @@ -0,0 +1,3 @@ +/* Copyright (C) 2006 - 2013 ScriptDev2 + * This program is free software licensed under GPL version 2 + * Please see the included DOCS/LICENSE.TXT for more information */ diff --git a/src/modules/SD2/scripts/kalimdor/wailing_caverns/instance_wailing_caverns.cpp b/src/modules/SD2/scripts/kalimdor/wailing_caverns/instance_wailing_caverns.cpp new file mode 100644 index 000000000..15d019efd --- /dev/null +++ b/src/modules/SD2/scripts/kalimdor/wailing_caverns/instance_wailing_caverns.cpp @@ -0,0 +1,158 @@ +/* Copyright (C) 2006 - 2013 ScriptDev2 + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along 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: 90 +SDComment: +SDCategory: Wailing Caverns +EndScriptData */ + +#include "precompiled.h" +#include "wailing_caverns.h" + +instance_wailing_caverns::instance_wailing_caverns(Map* pMap) : ScriptedInstance(pMap) +{ + Initialize(); +} + +void instance_wailing_caverns::Initialize() +{ + memset(&m_auiEncounter, 0, sizeof(m_auiEncounter)); +} + +void instance_wailing_caverns::OnPlayerEnter(Player* pPlayer) +{ + // Respawn the Mysterious chest if one of the players who enter the instance has the quest in his log + if (pPlayer->GetQuestStatus(QUEST_FORTUNE_AWAITS) == QUEST_STATUS_COMPLETE && + !pPlayer->GetQuestRewardStatus(QUEST_FORTUNE_AWAITS)) + DoRespawnGameObject(GO_MYSTERIOUS_CHEST, HOUR); +} + +void instance_wailing_caverns::OnCreatureCreate(Creature* pCreature) +{ + switch (pCreature->GetEntry()) + { + case NPC_NARALEX: + case NPC_DISCIPLE: + m_mNpcEntryGuidStore[pCreature->GetEntry()] = pCreature->GetObjectGuid(); + break; + } +} + +void instance_wailing_caverns::OnObjectCreate(GameObject* pGo) +{ + if (pGo->GetEntry() == GO_MYSTERIOUS_CHEST) + m_mGoEntryGuidStore[GO_MYSTERIOUS_CHEST] = pGo->GetObjectGuid(); +} + +void instance_wailing_caverns::SetData(uint32 uiType, uint32 uiData) +{ + switch (uiType) + { + case TYPE_ANACONDRA: + m_auiEncounter[0] = uiData; + break; + case TYPE_COBRAHN: + m_auiEncounter[1] = uiData; + break; + case TYPE_PYTHAS: + m_auiEncounter[2] = uiData; + break; + case TYPE_SERPENTIS: + m_auiEncounter[3] = uiData; + break; + case TYPE_DISCIPLE: + m_auiEncounter[4] = uiData; + break; + case TYPE_MUTANUS: + m_auiEncounter[5] = uiData; + break; + } + + // Set to special in order to start the escort event; only if all four bosses are done + if (m_auiEncounter[0] == DONE && m_auiEncounter[1] == DONE && m_auiEncounter[2] == DONE && m_auiEncounter[3] == DONE && (m_auiEncounter[4] == NOT_STARTED || m_auiEncounter[4] == FAIL)) + { + // Yell intro text; only the first time + if (m_auiEncounter[4] == NOT_STARTED) + { + if (Creature* pDisciple = GetSingleCreatureFromStorage(NPC_DISCIPLE)) + DoScriptText(SAY_INTRO, pDisciple); + } + + m_auiEncounter[4] = SPECIAL; + } + + 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_strInstData = saveStream.str(); + SaveToDB(); + OUT_SAVE_INST_DATA_COMPLETE; + } +} + +void instance_wailing_caverns::Load(const char* chrIn) +{ + if (!chrIn) + { + 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]; + + for (uint8 i = 0; i < MAX_ENCOUNTER; ++i) + { + if (m_auiEncounter[i] == IN_PROGRESS) + m_auiEncounter[i] = NOT_STARTED; + } + + OUT_LOAD_INST_DATA_COMPLETE; +} + +uint32 instance_wailing_caverns::GetData(uint32 uiType) const +{ + if (uiType < MAX_ENCOUNTER) + return m_auiEncounter[uiType]; + + return 0; +} + +InstanceData* GetInstanceData_instance_wailing_caverns(Map* pMap) +{ + return new instance_wailing_caverns(pMap); +} + +void AddSC_instance_wailing_caverns() +{ + Script* pNewScript; + + pNewScript = new Script; + pNewScript->Name = "instance_wailing_caverns"; + pNewScript->GetInstanceData = &GetInstanceData_instance_wailing_caverns; + pNewScript->RegisterSelf(); +} diff --git a/src/modules/SD2/scripts/kalimdor/wailing_caverns/wailing_caverns.cpp b/src/modules/SD2/scripts/kalimdor/wailing_caverns/wailing_caverns.cpp new file mode 100644 index 000000000..e5f334e22 --- /dev/null +++ b/src/modules/SD2/scripts/kalimdor/wailing_caverns/wailing_caverns.cpp @@ -0,0 +1,498 @@ +/* Copyright (C) 2006 - 2013 ScriptDev2 + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along 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: 90 +SDComment: Missing vipers emerge effect, Naralex doesn't fly at exit(Core issue) +SDCategory: Wailing Caverns +EndScriptData */ + +#include "precompiled.h" +#include "wailing_caverns.h" +#include "escort_ai.h" + +enum +{ + SAY_PREPARE = -1043001, + SAY_FIRST_CORNER = -1043002, + SAY_CONTINUE = -1043003, + SAY_CIRCLE_BANISH = -1043004, + SAY_PURIFIED = -1043005, + SAY_NARALEX_CHAMBER = -1043006, + SAY_BEGIN_RITUAL = -1043007, + SAY_MUTANUS = -1043012, + SAY_NARALEX_AWAKE = -1043013, + SAY_AWAKE = -1043014, + SAY_NARALEX_THANKYOU = -1043015, + SAY_FAREWELL = -1043016, + SAY_AGGRO_1 = -1043017, // Random between the first 2 + SAY_AGGRO_2 = -1043018, + SAY_AGGRO_3 = -1043019, // During the awakening ritual + + EMOTE_RITUAL_BEGIN = -1043008, + EMOTE_NARALEX_AWAKE = -1043009, + EMOTE_BREAK_THROUGH = -1043010, + EMOTE_VISION = -1043011, + + GOSSIP_ITEM_BEGIN = -3043000, + TEXT_ID_DISCIPLE = 698, + + SPELL_MARK = 5232, // Buff before the fight. To be removed after 4.0.3 + SPELL_SLEEP = 1090, + SPELL_POTION = 8141, + SPELL_CLEANSING = 6270, + SPELL_AWAKENING = 6271, + SPELL_SHAPESHIFT = 8153, + + NPC_DEVIATE_RAPTOR = 3636, // 2 of them at the first stop + NPC_DEVIATE_VIPER = 5755, // 3 of them at the circle + NPC_DEVIATE_MOCCASIN = 5762, // 6 of them at Naralex chamber + NPC_NIGHTMARE_ECTOPLASM = 5763, // 10 of them at Naralex chamber + NPC_MUTANUS = 3654 +}; + +// Distance, Angle or Offset +static const float aSummonPositions[5][2] = +{ + {50.0f, -0.507f}, // First Raptors + {53.0f, -0.603f}, + { 7.0f, 1.73f}, // Vipers + {45.0f, 5.16f}, // Chamber + {47.0f, 0.5901f} // Mutanus +}; + +struct npc_disciple_of_naralexAI : public npc_escortAI +{ + npc_disciple_of_naralexAI(Creature* pCreature) : npc_escortAI(pCreature) + { + m_pInstance = (instance_wailing_caverns*)pCreature->GetInstanceData(); + Reset(); + } + + instance_wailing_caverns* m_pInstance; + + uint32 m_uiEventTimer; + uint32 m_uiSleepTimer; + uint32 m_uiPotionTimer; + uint32 m_uiCleansingTimer; + uint8 m_uiSummonedAlive; + bool m_bIsFirstHit; + + uint32 m_uiPoint; + uint8 m_uiSubeventPhase; + + void Reset() override + { + m_uiSleepTimer = 5000; + m_uiPotionTimer = 5000; + m_uiCleansingTimer = 0; + m_bIsFirstHit = false; // Used to trigger the combat yell; reset at every evade + + // Don't reset mob counter during the event + if (!HasEscortState(STATE_ESCORT_ESCORTING)) + { + m_uiEventTimer = 0; + + m_uiPoint = 0; + m_uiSubeventPhase = 0; + + m_uiSummonedAlive = 0; + } + } + + void JustRespawned() override + { + npc_escortAI::JustRespawned(); + + // Reset event if can + if (m_pInstance && m_pInstance->GetData(TYPE_DISCIPLE) != DONE) + m_pInstance->SetData(TYPE_DISCIPLE, FAIL); + } + + void AttackedBy(Unit* pAttacker) override + { + if (!m_bIsFirstHit) + { + if (pAttacker->GetEntry() == NPC_MUTANUS) + DoScriptText(SAY_MUTANUS, m_creature, pAttacker); + // Check if already in ritual + else if (m_uiPoint >= 30) + DoScriptText(SAY_AGGRO_3, m_creature, pAttacker); + else + // Aggro 1 should be in 90% of the cases + DoScriptText(roll_chance_i(90) ? SAY_AGGRO_1 : SAY_AGGRO_2, m_creature, pAttacker); + + m_bIsFirstHit = true; + } + } + + // Overwrite the evade function, to change the combat stop function (keep casting some spells) + void EnterEvadeMode() override + { + // Do not stop casting at these points + if (m_uiPoint == 15 || m_uiPoint == 32) + { + m_creature->SetLootRecipient(NULL); + m_creature->DeleteThreatList(); + m_creature->CombatStop(false); + Reset(); + + // Remove running + m_creature->SetWalk(true); + } + else + npc_escortAI::EnterEvadeMode(); + } + + void JustStartedEscort() override + { + DoScriptText(SAY_PREPARE, m_creature); + + if (m_pInstance) + m_pInstance->SetData(TYPE_DISCIPLE, IN_PROGRESS); + } + + void WaypointReached(uint32 uiPointId) override + { + switch (uiPointId) + { + case 7: + DoScriptText(SAY_FIRST_CORNER, m_creature); + m_uiSubeventPhase = 0; + m_uiEventTimer = 2000; + m_uiPoint = uiPointId; + SetEscortPaused(true); + break; + case 15: + m_uiSubeventPhase = 0; + m_uiEventTimer = 2000; + m_uiPoint = uiPointId; + SetEscortPaused(true); + break; + case 26: + DoScriptText(SAY_NARALEX_CHAMBER, m_creature); + break; + case 32: + if (Creature* pNaralex = m_pInstance->GetSingleCreatureFromStorage(NPC_NARALEX)) + m_creature->SetFacingToObject(pNaralex); + + m_creature->SetStandState(UNIT_STAND_STATE_KNEEL); + m_uiEventTimer = 2000; + m_uiSubeventPhase = 0; + m_uiPoint = uiPointId; + SetEscortPaused(true); + break; + } + } + + void JustSummoned(Creature* pSummoned) override + { + // Attack the disciple + pSummoned->AI()->AttackStart(m_creature); + + ++m_uiSummonedAlive; + } + + void SummonedCreatureJustDied(Creature* /*pSummoned*/) override + { + if (m_uiSummonedAlive == 0) + return; // Actually if this happens, something went wrong before + + --m_uiSummonedAlive; + + // Continue Event if all are dead and we are in a stopped subevent + if (m_uiSummonedAlive == 0 && m_uiEventTimer == 0) + m_uiEventTimer = 1000; + } + + // Summon mobs at calculated points + void DoSpawnMob(uint32 uiEntry, float fDistance, float fAngle) + { + float fX, fY, fZ; + m_creature->GetNearPoint(m_creature, fX, fY, fZ, 0, fDistance, fAngle); + + m_creature->SummonCreature(uiEntry, fX, fY, fZ, 0, TEMPSUMMON_TIMED_OOC_DESPAWN, 20000); + } + + void UpdateEscortAI(const uint32 uiDiff) override + { + if (m_uiEventTimer) + { + if (m_uiEventTimer <= uiDiff) + { + switch (m_uiPoint) + { + // Corner stop -> raptors + case 7: + switch (m_uiSubeventPhase) + { + case 0: + // Summon raptors at first stop + DoSpawnMob(NPC_DEVIATE_RAPTOR, aSummonPositions[0][0], aSummonPositions[0][1]); + DoSpawnMob(NPC_DEVIATE_RAPTOR, aSummonPositions[1][0], aSummonPositions[1][1]); + m_uiEventTimer = 0; + ++m_uiSubeventPhase; + break; + case 1: + // After the summoned mobs are killed continue + DoScriptText(SAY_CONTINUE, m_creature); + SetEscortPaused(false); + m_uiEventTimer = 0; + break; + } + break; + // Circle stop -> vipers + case 15: + switch (m_uiSubeventPhase) + { + case 0: + DoScriptText(SAY_CIRCLE_BANISH, m_creature); + m_uiEventTimer = 2000; + ++m_uiSubeventPhase; + break; + case 1: + DoCastSpellIfCan(m_creature, SPELL_CLEANSING); + m_uiEventTimer = 20000; + ++m_uiSubeventPhase; + break; + case 2: + // Summon vipers at the first circle + for (uint8 i = 0; i < 3; ++i) + DoSpawnMob(NPC_DEVIATE_VIPER, aSummonPositions[2][0], aSummonPositions[2][1] + 2 * M_PI_F / 3 * i); + m_uiEventTimer = 0; + ++m_uiSubeventPhase; + break; + case 3: + // Wait for casting to be complete - TODO, this might have to be done better + ++m_uiSubeventPhase; + m_uiEventTimer = 10000; + break; + case 4: + DoScriptText(SAY_PURIFIED, m_creature); + m_uiEventTimer = 0; + ++m_uiPoint; // Increment this in order to avoid the special case evade + SetEscortPaused(false); + break; + } + break; + // Chamber stop -> ritual and final boss + case 32: + switch (m_uiSubeventPhase) + { + case 0: + DoScriptText(SAY_BEGIN_RITUAL, m_creature); + m_creature->SetStandState(UNIT_STAND_STATE_STAND); + m_uiEventTimer = 2000; + ++m_uiSubeventPhase; + break; + case 1: + DoCastSpellIfCan(m_creature, SPELL_AWAKENING); + DoScriptText(EMOTE_RITUAL_BEGIN, m_creature); + m_uiEventTimer = 4000; + ++m_uiSubeventPhase; + break; + case 2: + if (Creature* pNaralex = m_pInstance->GetSingleCreatureFromStorage(NPC_NARALEX)) + DoScriptText(EMOTE_NARALEX_AWAKE, pNaralex); + m_uiEventTimer = 5000; + ++m_uiSubeventPhase; + break; + case 3: + // First set of mobs + for (uint8 i = 0; i < 3; ++i) + DoSpawnMob(NPC_DEVIATE_MOCCASIN, aSummonPositions[3][0], aSummonPositions[3][1] + M_PI_F / 3 * i); + m_uiEventTimer = 20000; + ++m_uiSubeventPhase; + break; + case 4: + // Second set of mobs + for (uint8 i = 0; i < 7; ++i) + DoSpawnMob(NPC_NIGHTMARE_ECTOPLASM, aSummonPositions[3][0], aSummonPositions[3][1] + M_PI_F / 7 * i); + m_uiEventTimer = 0; + ++m_uiSubeventPhase; + break; + case 5: + // Advance only when all mobs are dead + if (Creature* pNaralex = m_pInstance->GetSingleCreatureFromStorage(NPC_NARALEX)) + DoScriptText(EMOTE_BREAK_THROUGH, pNaralex); + ++m_uiSubeventPhase; + m_uiEventTimer = 10000; + break; + case 6: + // Mutanus + if (Creature* pNaralex = m_pInstance->GetSingleCreatureFromStorage(NPC_NARALEX)) + DoScriptText(EMOTE_VISION, pNaralex); + DoSpawnMob(NPC_MUTANUS, aSummonPositions[4][0], aSummonPositions[4][1]); + m_uiEventTimer = 0; + ++m_uiSubeventPhase; + break; + case 7: + // Awaken Naralex after mutanus is defeated + if (Creature* pNaralex = m_pInstance->GetSingleCreatureFromStorage(NPC_NARALEX)) + { + pNaralex->SetStandState(UNIT_STAND_STATE_SIT); + DoScriptText(SAY_NARALEX_AWAKE, pNaralex); + } + m_creature->InterruptNonMeleeSpells(false, SPELL_AWAKENING); + m_creature->RemoveAurasDueToSpell(SPELL_AWAKENING); + m_pInstance->SetData(TYPE_DISCIPLE, DONE); + ++m_uiSubeventPhase; + m_uiEventTimer = 2000; + break; + case 8: + DoScriptText(SAY_AWAKE, m_creature); + m_uiEventTimer = 5000; + ++m_uiSubeventPhase; + break; + case 9: + if (Creature* pNaralex = m_pInstance->GetSingleCreatureFromStorage(NPC_NARALEX)) + { + DoScriptText(SAY_NARALEX_THANKYOU, pNaralex); + pNaralex->SetStandState(UNIT_STAND_STATE_STAND); + } + m_uiEventTimer = 10000; + ++m_uiSubeventPhase; + break; + case 10: + // Shapeshift into a bird + if (Creature* pNaralex = m_pInstance->GetSingleCreatureFromStorage(NPC_NARALEX)) + { + DoScriptText(SAY_FAREWELL, pNaralex); + pNaralex->CastSpell(pNaralex, SPELL_SHAPESHIFT, false); + } + DoCastSpellIfCan(m_creature, SPELL_SHAPESHIFT); + m_uiEventTimer = 10000; + ++m_uiSubeventPhase; + break; + case 11: + SetEscortPaused(false); + m_creature->SetLevitate(true); + SetRun(); + // Send them flying somewhere outside of the room + if (Creature* pNaralex = m_pInstance->GetSingleCreatureFromStorage(NPC_NARALEX)) + { + // ToDo: Make Naralex fly + // sort of a hack, compare to boss_onyxia + pNaralex->SetByteValue(UNIT_FIELD_BYTES_1, 3, UNIT_BYTE1_FLAG_ALWAYS_STAND | UNIT_BYTE1_FLAG_UNK_2); + + // Set to flying + pNaralex->SetLevitate(true); + pNaralex->SetWalk(false); + + // Set following + pNaralex->GetMotionMaster()->MoveFollow(m_creature, 5.0f, 0); + // Despawn after some time + pNaralex->ForcedDespawn(30000); + } + m_uiEventTimer = 0; + break; + } + break; + } + } + else + m_uiEventTimer -= uiDiff; + } + + if (m_uiPotionTimer < uiDiff) + { + // if health lower than 80%, cast heal + if (m_creature->GetHealthPercent() < 80.0f) + { + if (DoCastSpellIfCan(m_creature, SPELL_POTION) == CAST_OK) + m_uiPotionTimer = 45000; + } + else + m_uiPotionTimer = 5000; + } + else + m_uiPotionTimer -= uiDiff; + + if (!m_creature->SelectHostileTarget() || !m_creature->getVictim()) + return; + + if (m_uiSleepTimer < uiDiff) + { + if (Unit* pTarget = m_creature->SelectAttackingTarget(ATTACKING_TARGET_RANDOM, 1)) + { + if (DoCastSpellIfCan(pTarget, SPELL_SLEEP) == CAST_OK) + m_uiSleepTimer = 30000; + } + } + else + m_uiSleepTimer -= uiDiff; + + DoMeleeAttackIfReady(); + } +}; + +bool GossipHello_npc_disciple_of_naralex(Player* pPlayer, Creature* pCreature) +{ + ScriptedInstance* m_pInstance = (ScriptedInstance*)pCreature->GetInstanceData(); + + if (pCreature->IsQuestGiver()) + pPlayer->PrepareQuestMenu(pCreature->GetObjectGuid()); + + // Buff the players + pCreature->CastSpell(pPlayer, SPELL_MARK, false); + + if (m_pInstance && m_pInstance->GetData(TYPE_DISCIPLE) == SPECIAL) + { + pPlayer->ADD_GOSSIP_ITEM_ID(GOSSIP_ICON_CHAT, GOSSIP_ITEM_BEGIN, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 1); + pPlayer->SEND_GOSSIP_MENU(TEXT_ID_DISCIPLE, pCreature->GetObjectGuid()); + } + else + pPlayer->SEND_GOSSIP_MENU(pPlayer->GetGossipTextId(pCreature), pCreature->GetObjectGuid()); + + return true; +} + +bool GossipSelect_npc_disciple_of_naralex(Player* pPlayer, Creature* pCreature, uint32 /*uiSender*/, uint32 uiAction) +{ + ScriptedInstance* m_pInstance = (ScriptedInstance*)pCreature->GetInstanceData(); + + if (!m_pInstance) + return false; + + if (uiAction == GOSSIP_ACTION_INFO_DEF + 1) + { + if (npc_disciple_of_naralexAI* pEscortAI = dynamic_cast(pCreature->AI())) + { + pEscortAI->Start(false, pPlayer); // Note: after 4.0.3 set him run = true + pCreature->SetFactionTemporary(FACTION_ESCORT_N_ACTIVE, TEMPFACTION_RESTORE_RESPAWN); + } + pPlayer->CLOSE_GOSSIP_MENU(); + } + return true; +} + +CreatureAI* GetAI_npc_disciple_of_naralex(Creature* pCreature) +{ + return new npc_disciple_of_naralexAI(pCreature); +} + +void AddSC_wailing_caverns() +{ + Script* pNewScript; + + pNewScript = new Script; + pNewScript->Name = "npc_disciple_of_naralex"; + pNewScript->GetAI = &GetAI_npc_disciple_of_naralex; + pNewScript->pGossipHello = &GossipHello_npc_disciple_of_naralex; + pNewScript->pGossipSelect = &GossipSelect_npc_disciple_of_naralex; + pNewScript->RegisterSelf(); +} diff --git a/src/modules/SD2/scripts/kalimdor/wailing_caverns/wailing_caverns.h b/src/modules/SD2/scripts/kalimdor/wailing_caverns/wailing_caverns.h new file mode 100644 index 000000000..f9e24df96 --- /dev/null +++ b/src/modules/SD2/scripts/kalimdor/wailing_caverns/wailing_caverns.h @@ -0,0 +1,51 @@ +/* Copyright (C) 2006 - 2013 ScriptDev2 + * 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 +{ + MAX_ENCOUNTER = 6, + + TYPE_ANACONDRA = 0, + TYPE_COBRAHN = 1, + TYPE_PYTHAS = 2, + TYPE_SERPENTIS = 3, + TYPE_DISCIPLE = 4, + TYPE_MUTANUS = 5, + + NPC_NARALEX = 3679, + NPC_DISCIPLE = 3678, + + SAY_INTRO = -1043000, // Say when the first 4 encounter are DONE + + GO_MYSTERIOUS_CHEST = 180055, // used for quest 7944; spawns in the instance only if one of the players has the quest + + QUEST_FORTUNE_AWAITS = 7944, +}; + +class instance_wailing_caverns : public ScriptedInstance +{ + public: + instance_wailing_caverns(Map* pMap); + ~instance_wailing_caverns() {} + + void Initialize() override; + + void OnPlayerEnter(Player* pPlayer) override; + void OnCreatureCreate(Creature* pCreature) override; + void OnObjectCreate(GameObject* pGo) override; + + void SetData(uint32 uiType, uint32 uiData) override; + uint32 GetData(uint32 uiType) const override; + + const char* Save() const override { return m_strInstData.c_str(); } + void Load(const char* chrIn) override; + + protected: + uint32 m_auiEncounter[MAX_ENCOUNTER]; + std::string m_strInstData; +}; +#endif diff --git a/src/modules/SD2/scripts/kalimdor/winterspring.cpp b/src/modules/SD2/scripts/kalimdor/winterspring.cpp new file mode 100644 index 000000000..e01262817 --- /dev/null +++ b/src/modules/SD2/scripts/kalimdor/winterspring.cpp @@ -0,0 +1,473 @@ +/* Copyright (C) 2006 - 2013 ScriptDev2 + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along 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: 4901. +SDCategory: Winterspring +EndScriptData */ + +/* ContentData +npc_ranshalla +EndContentData */ + +#include "precompiled.h" +#include "escort_ai.h" + +/*###### +# npc_ranshalla +####*/ + +enum +{ + // Escort texts + SAY_QUEST_START = -1000739, + SAY_ENTER_OWL_THICKET = -1000707, + SAY_REACH_TORCH_1 = -1000708, + SAY_REACH_TORCH_2 = -1000709, + SAY_REACH_TORCH_3 = -1000710, + SAY_AFTER_TORCH_1 = -1000711, + SAY_AFTER_TORCH_2 = -1000712, + SAY_REACH_ALTAR_1 = -1000713, + SAY_REACH_ALTAR_2 = -1000714, + + // After lighting the altar cinematic + SAY_RANSHALLA_ALTAR_1 = -1000715, + SAY_RANSHALLA_ALTAR_2 = -1000716, + SAY_PRIESTESS_ALTAR_3 = -1000717, + SAY_PRIESTESS_ALTAR_4 = -1000718, + SAY_RANSHALLA_ALTAR_5 = -1000719, + SAY_RANSHALLA_ALTAR_6 = -1000720, + SAY_PRIESTESS_ALTAR_7 = -1000721, + SAY_PRIESTESS_ALTAR_8 = -1000722, + SAY_PRIESTESS_ALTAR_9 = -1000723, + SAY_PRIESTESS_ALTAR_10 = -1000724, + SAY_PRIESTESS_ALTAR_11 = -1000725, + SAY_PRIESTESS_ALTAR_12 = -1000726, + SAY_PRIESTESS_ALTAR_13 = -1000727, + SAY_PRIESTESS_ALTAR_14 = -1000728, + SAY_VOICE_ALTAR_15 = -1000729, + SAY_PRIESTESS_ALTAR_16 = -1000730, + SAY_PRIESTESS_ALTAR_17 = -1000731, + SAY_PRIESTESS_ALTAR_18 = -1000732, + SAY_PRIESTESS_ALTAR_19 = -1000733, + SAY_PRIESTESS_ALTAR_20 = -1000734, + SAY_PRIESTESS_ALTAR_21 = -1000735, + SAY_QUEST_END_1 = -1000736, + SAY_QUEST_END_2 = -1000737, + + EMOTE_CHANT_SPELL = -1000738, + + SPELL_LIGHT_TORCH = 18953, // channeled spell by Ranshalla while waiting for the torches / altar + + NPC_RANSHALLA = 10300, + NPC_PRIESTESS_ELUNE = 12116, + NPC_VOICE_ELUNE = 12152, + NPC_GUARDIAN_ELUNE = 12140, + + NPC_PRIESTESS_DATA_1 = 1, // dummy member for the first priestess (right) + NPC_PRIESTESS_DATA_2 = 2, // dummy member for the second priestess (left) + DATA_MOVE_PRIESTESS = 3, // dummy member to check the priestess movement + DATA_EVENT_END = 4, // dummy member to indicate the event end + + GO_ELUNE_ALTAR = 177404, + GO_ELUNE_FIRE = 177417, + GO_ELUNE_GEM = 177414, // is respawned in script + GO_ELUNE_LIGHT = 177415, // are respawned in script + + QUEST_GUARDIANS_ALTAR = 4901, +}; + +static const DialogueEntry aIntroDialogue[] = +{ + {SAY_REACH_ALTAR_1, NPC_RANSHALLA, 2000}, + {SAY_REACH_ALTAR_2, NPC_RANSHALLA, 3000}, + {NPC_RANSHALLA, 0, 0}, // start the altar channeling + {SAY_PRIESTESS_ALTAR_3, NPC_PRIESTESS_DATA_2, 1000}, + {SAY_PRIESTESS_ALTAR_4, NPC_PRIESTESS_DATA_1, 4000}, + {SAY_RANSHALLA_ALTAR_5, NPC_RANSHALLA, 4000}, + {SAY_RANSHALLA_ALTAR_6, NPC_RANSHALLA, 4000}, // start the escort here + {SAY_PRIESTESS_ALTAR_7, NPC_PRIESTESS_DATA_2, 4000}, + {SAY_PRIESTESS_ALTAR_8, NPC_PRIESTESS_DATA_2, 5000}, // show the gem + {GO_ELUNE_GEM, 0, 5000}, + {SAY_PRIESTESS_ALTAR_9, NPC_PRIESTESS_DATA_1, 4000}, // move priestess 1 near m_creature + {NPC_PRIESTESS_DATA_1, 0, 3000}, + {SAY_PRIESTESS_ALTAR_10, NPC_PRIESTESS_DATA_1, 5000}, + {SAY_PRIESTESS_ALTAR_11, NPC_PRIESTESS_DATA_1, 4000}, + {SAY_PRIESTESS_ALTAR_12, NPC_PRIESTESS_DATA_1, 5000}, + {SAY_PRIESTESS_ALTAR_13, NPC_PRIESTESS_DATA_1, 8000}, // summon voice and guard of elune + {NPC_VOICE_ELUNE, 0, 12000}, + {SAY_VOICE_ALTAR_15, NPC_VOICE_ELUNE, 5000}, // move priestess 2 near m_creature + {NPC_PRIESTESS_DATA_2, 0, 3000}, + {SAY_PRIESTESS_ALTAR_16, NPC_PRIESTESS_DATA_2, 4000}, + {SAY_PRIESTESS_ALTAR_17, NPC_PRIESTESS_DATA_2, 6000}, + {SAY_PRIESTESS_ALTAR_18, NPC_PRIESTESS_DATA_1, 5000}, + {SAY_PRIESTESS_ALTAR_19, NPC_PRIESTESS_DATA_1, 3000}, // move the owlbeast + {NPC_GUARDIAN_ELUNE, 0, 2000}, + {SAY_PRIESTESS_ALTAR_20, NPC_PRIESTESS_DATA_1, 4000}, // move the first priestess up + {SAY_PRIESTESS_ALTAR_21, NPC_PRIESTESS_DATA_2, 10000}, // move second priestess up + {DATA_MOVE_PRIESTESS, 0, 6000}, // despawn the gem + {DATA_EVENT_END, 0, 2000}, // turn towards the player + {SAY_QUEST_END_2, NPC_RANSHALLA, 0}, + {0, 0, 0}, +}; + +struct EventLocations +{ + float m_fX, m_fY, m_fZ, m_fO; +}; + +static EventLocations aWingThicketLocations[] = +{ + {5515.98f, -4903.43f, 846.30f, 4.58f}, // 0 right priestess summon loc + {5501.94f, -4920.20f, 848.69f, 6.15f}, // 1 left priestess summon loc + {5497.35f, -4906.49f, 850.83f, 2.76f}, // 2 guard of elune summon loc + {5518.38f, -4913.47f, 845.57f}, // 3 right priestess move loc + {5510.36f, -4921.17f, 846.33f}, // 4 left priestess move loc + {5511.31f, -4913.82f, 847.17f}, // 5 guard of elune move loc + {5518.51f, -4917.56f, 845.23f}, // 6 right priestess second move loc + {5514.40f, -4921.16f, 845.49f} // 7 left priestess second move loc +}; + +struct npc_ranshallaAI : public npc_escortAI, private DialogueHelper +{ + npc_ranshallaAI(Creature* pCreature) : npc_escortAI(pCreature), + DialogueHelper(aIntroDialogue) + { + Reset(); + } + + uint32 m_uiDelayTimer; + + ObjectGuid m_firstPriestessGuid; + ObjectGuid m_secondPriestessGuid; + ObjectGuid m_guardEluneGuid; + ObjectGuid m_voiceEluneGuid; + ObjectGuid m_altarGuid; + + void Reset() override + { + m_uiDelayTimer = 0; + } + + // Called when the player activates the torch / altar + void DoContinueEscort(bool bIsAltarWaypoint = false) + { + if (bIsAltarWaypoint) + DoScriptText(SAY_RANSHALLA_ALTAR_1, m_creature); + else + { + switch (urand(0, 1)) + { + case 0: DoScriptText(SAY_AFTER_TORCH_1, m_creature); break; + case 1: DoScriptText(SAY_AFTER_TORCH_2, m_creature); break; + } + } + + m_uiDelayTimer = 2000; + } + + // Called when Ranshalla starts to channel on a torch / altar + void DoChannelTorchSpell(bool bIsAltarWaypoint = false) + { + // Check if we are using the fire or the altar and remove the no_interact flag + if (bIsAltarWaypoint) + { + if (GameObject* pGo = GetClosestGameObjectWithEntry(m_creature, GO_ELUNE_ALTAR, 10.0f)) + { + pGo->RemoveFlag(GAMEOBJECT_FLAGS, GO_FLAG_NO_INTERACT); + m_creature->SetFacingToObject(pGo); + m_altarGuid = pGo->GetObjectGuid(); + } + } + else + { + if (GameObject* pGo = GetClosestGameObjectWithEntry(m_creature, GO_ELUNE_FIRE, 10.0f)) + pGo->RemoveFlag(GAMEOBJECT_FLAGS, GO_FLAG_NO_INTERACT); + } + + // Yell and set escort to pause + switch (urand(0, 2)) + { + case 0: DoScriptText(SAY_REACH_TORCH_1, m_creature); break; + case 1: DoScriptText(SAY_REACH_TORCH_2, m_creature); break; + case 2: DoScriptText(SAY_REACH_TORCH_3, m_creature); break; + } + + DoScriptText(EMOTE_CHANT_SPELL, m_creature); + DoCastSpellIfCan(m_creature, SPELL_LIGHT_TORCH); + SetEscortPaused(true); + } + + void DoSummonPriestess() + { + // Summon 2 Elune priestess and make each of them move to a different spot + if (Creature* pPriestess = m_creature->SummonCreature(NPC_PRIESTESS_ELUNE, aWingThicketLocations[0].m_fX, aWingThicketLocations[0].m_fY, aWingThicketLocations[0].m_fZ, aWingThicketLocations[0].m_fO, TEMPSUMMON_CORPSE_DESPAWN, 0)) + { + pPriestess->GetMotionMaster()->MovePoint(0, aWingThicketLocations[3].m_fX, aWingThicketLocations[3].m_fY, aWingThicketLocations[3].m_fZ); + m_firstPriestessGuid = pPriestess->GetObjectGuid(); + } + if (Creature* pPriestess = m_creature->SummonCreature(NPC_PRIESTESS_ELUNE, aWingThicketLocations[1].m_fX, aWingThicketLocations[1].m_fY, aWingThicketLocations[1].m_fZ, aWingThicketLocations[1].m_fO, TEMPSUMMON_CORPSE_DESPAWN, 0)) + { + // Left priestess should have a distinct move point because she is the one who starts the dialogue at point reach + pPriestess->GetMotionMaster()->MovePoint(1, aWingThicketLocations[4].m_fX, aWingThicketLocations[4].m_fY, aWingThicketLocations[4].m_fZ); + m_secondPriestessGuid = pPriestess->GetObjectGuid(); + } + } + + void SummonedMovementInform(Creature* pSummoned, uint32 uiType, uint32 uiPointId) override + { + if (uiType != POINT_MOTION_TYPE || pSummoned->GetEntry() != NPC_PRIESTESS_ELUNE || uiPointId != 1) + return; + + // Start the dialogue when the priestess reach the altar (they should both reach the point in the same time) + StartNextDialogueText(SAY_PRIESTESS_ALTAR_3); + } + + void WaypointReached(uint32 uiPointId) override + { + switch (uiPointId) + { + case 3: + DoScriptText(SAY_ENTER_OWL_THICKET, m_creature); + break; + case 10: // Cavern 1 + case 15: // Cavern 2 + case 20: // Cavern 3 + case 25: // Cavern 4 + case 36: // Cavern 5 + DoChannelTorchSpell(); + break; + case 39: + StartNextDialogueText(SAY_REACH_ALTAR_1); + SetEscortPaused(true); + break; + case 41: + { + // Search for all nearest lights and respawn them + std::list m_lEluneLights; + GetGameObjectListWithEntryInGrid(m_lEluneLights, m_creature, GO_ELUNE_LIGHT, 20.0f); + for (std::list::const_iterator itr = m_lEluneLights.begin(); itr != m_lEluneLights.end(); ++itr) + { + if ((*itr)->isSpawned()) + continue; + + (*itr)->SetRespawnTime(115); + (*itr)->Refresh(); + } + + if (GameObject* pAltar = m_creature->GetMap()->GetGameObject(m_altarGuid)) + m_creature->SetFacingToObject(pAltar); + break; + } + case 42: + // Summon the 2 priestess + SetEscortPaused(true); + DoSummonPriestess(); + DoScriptText(SAY_RANSHALLA_ALTAR_2, m_creature); + break; + case 44: + // Stop the escort and turn towards the altar + SetEscortPaused(true); + if (GameObject* pAltar = m_creature->GetMap()->GetGameObject(m_altarGuid)) + m_creature->SetFacingToObject(pAltar); + break; + } + } + + void JustDidDialogueStep(int32 iEntry) override + { + switch (iEntry) + { + case NPC_RANSHALLA: + // Start the altar channeling + DoChannelTorchSpell(true); + break; + case SAY_RANSHALLA_ALTAR_6: + SetEscortPaused(false); + break; + case SAY_PRIESTESS_ALTAR_8: + // make the gem respawn + if (GameObject* pGem = GetClosestGameObjectWithEntry(m_creature, GO_ELUNE_GEM, 10.0f)) + { + if (pGem->isSpawned()) + break; + + pGem->SetRespawnTime(90); + pGem->Refresh(); + } + break; + case SAY_PRIESTESS_ALTAR_9: + // move near the escort npc + if (Creature* pPriestess = m_creature->GetMap()->GetCreature(m_firstPriestessGuid)) + pPriestess->GetMotionMaster()->MovePoint(0, aWingThicketLocations[6].m_fX, aWingThicketLocations[6].m_fY, aWingThicketLocations[6].m_fZ); + break; + case SAY_PRIESTESS_ALTAR_13: + // summon the Guardian of Elune + if (Creature* pGuard = m_creature->SummonCreature(NPC_GUARDIAN_ELUNE, aWingThicketLocations[2].m_fX, aWingThicketLocations[2].m_fY, aWingThicketLocations[2].m_fZ, aWingThicketLocations[2].m_fO, TEMPSUMMON_CORPSE_DESPAWN, 0)) + { + pGuard->GetMotionMaster()->MovePoint(0, aWingThicketLocations[5].m_fX, aWingThicketLocations[5].m_fY, aWingThicketLocations[5].m_fZ); + m_guardEluneGuid = pGuard->GetObjectGuid(); + } + // summon the Voice of Elune + if (GameObject* pAltar = m_creature->GetMap()->GetGameObject(m_altarGuid)) + { + if (Creature* pVoice = m_creature->SummonCreature(NPC_VOICE_ELUNE, pAltar->GetPositionX(), pAltar->GetPositionY(), pAltar->GetPositionZ(), 0, TEMPSUMMON_TIMED_DESPAWN, 30000)) + m_voiceEluneGuid = pVoice->GetObjectGuid(); + } + break; + case SAY_VOICE_ALTAR_15: + // move near the escort npc and continue dialogue + if (Creature* pPriestess = m_creature->GetMap()->GetCreature(m_secondPriestessGuid)) + { + DoScriptText(SAY_PRIESTESS_ALTAR_14, pPriestess); + pPriestess->GetMotionMaster()->MovePoint(0, aWingThicketLocations[7].m_fX, aWingThicketLocations[7].m_fY, aWingThicketLocations[7].m_fZ); + } + break; + case SAY_PRIESTESS_ALTAR_19: + // make the voice of elune leave + if (Creature* pGuard = m_creature->GetMap()->GetCreature(m_guardEluneGuid)) + { + pGuard->GetMotionMaster()->MovePoint(0, aWingThicketLocations[2].m_fX, aWingThicketLocations[2].m_fY, aWingThicketLocations[2].m_fZ); + pGuard->ForcedDespawn(4000); + } + break; + case SAY_PRIESTESS_ALTAR_20: + // make the first priestess leave + if (Creature* pPriestess = m_creature->GetMap()->GetCreature(m_firstPriestessGuid)) + { + pPriestess->GetMotionMaster()->MovePoint(0, aWingThicketLocations[0].m_fX, aWingThicketLocations[0].m_fY, aWingThicketLocations[0].m_fZ); + pPriestess->ForcedDespawn(4000); + } + break; + case SAY_PRIESTESS_ALTAR_21: + // make the second priestess leave + if (Creature* pPriestess = m_creature->GetMap()->GetCreature(m_secondPriestessGuid)) + { + pPriestess->GetMotionMaster()->MovePoint(0, aWingThicketLocations[1].m_fX, aWingThicketLocations[1].m_fY, aWingThicketLocations[1].m_fZ); + pPriestess->ForcedDespawn(4000); + } + break; + case DATA_EVENT_END: + // Turn towards the player + if (Player* pPlayer = GetPlayerForEscort()) + { + m_creature->SetFacingToObject(pPlayer); + DoScriptText(SAY_QUEST_END_1, m_creature, pPlayer); + } + break; + case SAY_QUEST_END_2: + // Turn towards the altar and kneel - quest complete + if (GameObject* pAltar = m_creature->GetMap()->GetGameObject(m_altarGuid)) + m_creature->SetFacingToObject(pAltar); + m_creature->SetStandState(UNIT_STAND_STATE_KNEEL); + if (Player* pPlayer = GetPlayerForEscort()) + pPlayer->GroupEventHappens(QUEST_GUARDIANS_ALTAR, m_creature); + break; + } + } + + Creature* GetSpeakerByEntry(uint32 uiEntry) override + { + switch (uiEntry) + { + case NPC_RANSHALLA: return m_creature; + case NPC_VOICE_ELUNE: return m_creature->GetMap()->GetCreature(m_voiceEluneGuid); + case NPC_PRIESTESS_DATA_1: return m_creature->GetMap()->GetCreature(m_firstPriestessGuid); + case NPC_PRIESTESS_DATA_2: return m_creature->GetMap()->GetCreature(m_secondPriestessGuid); + + default: + return NULL; + } + } + + void UpdateEscortAI(const uint32 uiDiff) override + { + DialogueUpdate(uiDiff); + + if (m_uiDelayTimer) + { + if (m_uiDelayTimer <= uiDiff) + { + m_creature->InterruptNonMeleeSpells(false); + SetEscortPaused(false); + m_uiDelayTimer = 0; + } + else + m_uiDelayTimer -= uiDiff; + } + + if (!m_creature->SelectHostileTarget() || !m_creature->getVictim()) + return; + + DoMeleeAttackIfReady(); + } +}; + +CreatureAI* GetAI_npc_ranshalla(Creature* pCreature) +{ + return new npc_ranshallaAI(pCreature); +} + +bool QuestAccept_npc_ranshalla(Player* pPlayer, Creature* pCreature, const Quest* pQuest) +{ + if (pQuest->GetQuestId() == QUEST_GUARDIANS_ALTAR) + { + DoScriptText(SAY_QUEST_START, pCreature); + pCreature->SetFactionTemporary(FACTION_ESCORT_A_NEUTRAL_PASSIVE, TEMPFACTION_RESTORE_RESPAWN); + + if (npc_ranshallaAI* pEscortAI = dynamic_cast(pCreature->AI())) + pEscortAI->Start(false, pPlayer, pQuest, true); + + return true; + } + + return false; +} + +bool GOUse_go_elune_fire(Player* /*pPlayer*/, GameObject* pGo) +{ + // Check if we are using the torches or the altar + bool bIsAltar = false; + + if (pGo->GetEntry() == GO_ELUNE_ALTAR) + bIsAltar = true; + + if (Creature* pRanshalla = GetClosestCreatureWithEntry(pGo, NPC_RANSHALLA, 10.0f)) + { + if (npc_ranshallaAI* pEscortAI = dynamic_cast(pRanshalla->AI())) + pEscortAI->DoContinueEscort(bIsAltar); + } + + return false; +} + +void AddSC_winterspring() +{ + Script* pNewScript; + + pNewScript = new Script; + pNewScript->Name = "npc_ranshalla"; + pNewScript->GetAI = &GetAI_npc_ranshalla; + pNewScript->pQuestAcceptNPC = &QuestAccept_npc_ranshalla; + pNewScript->RegisterSelf(); + + pNewScript = new Script; + pNewScript->Name = "go_elune_fire"; + pNewScript->pGOUse = &GOUse_go_elune_fire; + pNewScript->RegisterSelf(); +} diff --git a/src/modules/SD2/scripts/kalimdor/zulfarrak/boss_zumrah.cpp b/src/modules/SD2/scripts/kalimdor/zulfarrak/boss_zumrah.cpp new file mode 100644 index 000000000..19b08e195 --- /dev/null +++ b/src/modules/SD2/scripts/kalimdor/zulfarrak/boss_zumrah.cpp @@ -0,0 +1,225 @@ +/* Copyright (C) 2006 - 2013 ScriptDev2 + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +/* ScriptData +SDName: boss_zumrah +SD%Complete: 100 +SDComment: +SDCategory: Zul'Farrak +EndScriptData */ + +#include "precompiled.h" +#include "zulfarrak.h" + +enum +{ + SAY_INTRO = -1209000, + SAY_AGGRO = -1209001, + SAY_KILL = -1209002, + SAY_SUMMON = -1209003, + + SPELL_SHADOW_BOLT = 12739, + SPELL_SHADOW_BOLT_VOLLEY = 15245, + SPELL_WARD_OF_ZUMRAH = 11086, + SPELL_HEALING_WAVE = 12491, + SPELL_SUMMON_ZOMBIES = 10247, // spell should be triggered by missing trap 128972 + + // NPC_WARD_OF_ZUMRAH = 7785, + // NPC_SKELETON_OF_ZUMRAH = 7786, + NPC_ZULFARRAK_ZOMBIE = 7286, // spawned by the graves + NPC_ZULFARRAK_DEAD_HERO = 7276, // spawned by the graves + + FACTION_HOSTILE = 14, +}; + +struct boss_zumrahAI : public ScriptedAI +{ + boss_zumrahAI(Creature* pCreature) : ScriptedAI(pCreature) + { + m_pInstance = (instance_zulfarrak*) pCreature->GetInstanceData(); + m_bHasTurnedHostile = false; + Reset(); + } + + instance_zulfarrak* m_pInstance; + + uint32 m_uiShadowBoltTimer; + uint32 m_uiShadowBoltVolleyTimer; + uint32 m_uiWardOfZumrahTimer; + uint32 m_uHealingWaveTimer; + uint32 m_uiSpawnZombieTimer; + + bool m_bHasTurnedHostile; + + void Reset() override + { + m_uiShadowBoltTimer = 1000; + m_uiShadowBoltVolleyTimer = urand(6000, 30000); + m_uiWardOfZumrahTimer = urand(7000, 20000); + m_uHealingWaveTimer = urand(10000, 15000); + m_uiSpawnZombieTimer = 1000; + } + + void Aggro(Unit* /*pWho*/) override + { + DoScriptText(SAY_AGGRO, m_creature); + } + + void KilledUnit(Unit* /*pVictim*/) override + { + DoScriptText(SAY_KILL, m_creature); + } + + void AttackStart(Unit* pWho) override + { + if (m_creature->Attack(pWho, true)) + { + m_creature->AddThreat(pWho); + m_creature->SetInCombatWith(pWho); + pWho->SetInCombatWith(m_creature); + DoStartMovement(pWho, 10.0f); + } + } + + void MoveInLineOfSight(Unit* pWho) override + { + if (!m_bHasTurnedHostile && pWho->GetTypeId() == TYPEID_PLAYER && m_creature->IsWithinDistInMap(pWho, 9.0f) && m_creature->IsWithinLOSInMap(pWho)) + { + m_creature->SetFactionTemporary(FACTION_HOSTILE, TEMPFACTION_NONE); + DoScriptText(SAY_INTRO, m_creature); + m_bHasTurnedHostile = true; + AttackStart(pWho); + } + + ScriptedAI::MoveInLineOfSight(pWho); + } + + void JustSummoned(Creature* pSummoned) override + { + if (pSummoned->GetEntry() == NPC_ZULFARRAK_ZOMBIE || pSummoned->GetEntry() == NPC_ZULFARRAK_DEAD_HERO) + pSummoned->AI()->AttackStart(m_creature->getVictim()); + } + + GameObject* SelectNearbyShallowGrave() + { + if (!m_pInstance) + return NULL; + + // Get the list of usable graves (not used already by players) + GuidList lTempList; + std::list lGravesInRange; + + m_pInstance->GetShallowGravesGuidList(lTempList); + for (GuidList::const_iterator itr = lTempList.begin(); itr != lTempList.end(); ++itr) + { + GameObject* pGo = m_creature->GetMap()->GetGameObject(*itr); + // Go spawned and no looting in process + if (pGo && pGo->isSpawned() && pGo->getLootState() == GO_READY) + lGravesInRange.push_back(pGo); + } + + if (lGravesInRange.empty()) + return NULL; + + // Sort the graves + lGravesInRange.sort(ObjectDistanceOrder(m_creature)); + + return *lGravesInRange.begin(); + } + + void UpdateAI(const uint32 uiDiff) override + { + if (!m_creature->SelectHostileTarget() || !m_creature->getVictim()) + return; + + if (m_uiSpawnZombieTimer) + { + if (m_uiSpawnZombieTimer <= uiDiff) + { + // Use a nearby grave to spawn zombies + if (GameObject* pGrave = SelectNearbyShallowGrave()) + { + m_creature->CastSpell(pGrave->GetPositionX(), pGrave->GetPositionY(), pGrave->GetPositionZ(), SPELL_SUMMON_ZOMBIES, true, NULL, NULL, pGrave->GetObjectGuid()); + pGrave->SetLootState(GO_JUST_DEACTIVATED); + + if (roll_chance_i(30)) + DoScriptText(SAY_SUMMON, m_creature); + + m_uiSpawnZombieTimer = 20000; + } + else // No Grave usable any more + m_uiSpawnZombieTimer = 0; + } + else + m_uiSpawnZombieTimer -= uiDiff; + } + + if (m_uiShadowBoltTimer < uiDiff) + { + if (Unit* pTarget = m_creature->SelectAttackingTarget(ATTACKING_TARGET_RANDOM, 0)) + { + if (DoCastSpellIfCan(pTarget, SPELL_SHADOW_BOLT) == CAST_OK) + m_uiShadowBoltTimer = urand(3500, 5000); + } + } + else + m_uiShadowBoltTimer -= uiDiff; + + if (m_uiShadowBoltVolleyTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature, SPELL_SHADOW_BOLT_VOLLEY) == CAST_OK) + m_uiShadowBoltVolleyTimer = urand(10000, 18000); + } + else + m_uiShadowBoltVolleyTimer -= uiDiff; + + if (m_uiWardOfZumrahTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature, SPELL_WARD_OF_ZUMRAH) == CAST_OK) + m_uiWardOfZumrahTimer = urand(15000, 32000); + } + else + m_uiWardOfZumrahTimer -= uiDiff; + + if (m_uHealingWaveTimer < uiDiff) + { + if (Unit* pTarget = DoSelectLowestHpFriendly(40.0f)) + { + if (DoCastSpellIfCan(pTarget, SPELL_HEALING_WAVE) == CAST_OK) + m_uHealingWaveTimer = urand(15000, 23000); + } + } + else + m_uHealingWaveTimer -= uiDiff; + + DoMeleeAttackIfReady(); + } +}; + +CreatureAI* GetAI_boss_zumrah(Creature* pCreature) +{ + return new boss_zumrahAI(pCreature); +} + +void AddSC_boss_zumrah() +{ + Script* pNewScript; + + pNewScript = new Script; + pNewScript->Name = "boss_zumrah"; + pNewScript->GetAI = &GetAI_boss_zumrah; + pNewScript->RegisterSelf(); +} diff --git a/src/modules/SD2/scripts/kalimdor/zulfarrak/instance_zulfarrak.cpp b/src/modules/SD2/scripts/kalimdor/zulfarrak/instance_zulfarrak.cpp new file mode 100644 index 000000000..17ba5fe44 --- /dev/null +++ b/src/modules/SD2/scripts/kalimdor/zulfarrak/instance_zulfarrak.cpp @@ -0,0 +1,257 @@ +/* Copyright (C) 2006 - 2013 ScriptDev2 +* This program is free software; you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation; either version 2 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along 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_zulfarrak +SD%Complete: 80% +SDComment: +SDCategory: Zul'Farrak +EndScriptData */ + +#include "precompiled.h" +#include "zulfarrak.h" + +instance_zulfarrak::instance_zulfarrak(Map* pMap) : ScriptedInstance(pMap), + m_uiPyramidEventTimer(0) +{ + Initialize(); +} + +void instance_zulfarrak::Initialize() +{ + memset(&m_auiEncounter, 0, sizeof(m_auiEncounter)); +} + +void instance_zulfarrak::OnCreatureCreate(Creature* pCreature) +{ + switch (pCreature->GetEntry()) + { + case NPC_ANTUSUL: + case NPC_SERGEANT_BLY: + m_mNpcEntryGuidStore[pCreature->GetEntry()] = pCreature->GetObjectGuid(); + break; + case NPC_SANDFURY_SLAVE: + case NPC_SANDFURY_DRUDGE: + case NPC_SANDFURY_CRETIN: + case NPC_SANDFURY_ACOLYTE: + case NPC_SANDFURY_ZEALOT: + m_lPyramidTrollsGuidList.push_back(pCreature->GetObjectGuid()); + break; + } +} + +void instance_zulfarrak::OnObjectCreate(GameObject* pGo) +{ + if (pGo->GetEntry() == GO_SHALLOW_GRAVE) + m_lShallowGravesGuidList.push_back(pGo->GetObjectGuid()); + else if (pGo->GetEntry() == GO_END_DOOR) + { + if (GetData(TYPE_PYRAMID_EVENT) == DONE) + pGo->SetGoState(GO_STATE_ACTIVE); + } +} + +void instance_zulfarrak::SetData(uint32 uiType, uint32 uiData) +{ + switch (uiType) + { + case TYPE_VELRATHA: + case TYPE_GAHZRILLA: + case TYPE_ANTUSUL: + case TYPE_THEKA: + case TYPE_ZUMRAH: + case TYPE_CHIEF_SANDSCALP: + m_auiEncounter[uiType] = uiData; + break; + case TYPE_NEKRUM: + m_auiEncounter[uiType] = uiData; + if (uiData == DONE && GetData(TYPE_SEZZZIZ) == DONE) + SetData(TYPE_PYRAMID_EVENT, DONE); + break; + case TYPE_SEZZZIZ: + m_auiEncounter[uiType] = uiData; + if (uiData == DONE && GetData(TYPE_NEKRUM) == DONE) + SetData(TYPE_PYRAMID_EVENT, DONE); + break; + case TYPE_PYRAMID_EVENT: + m_auiEncounter[uiType] = uiData; + if (uiData == IN_PROGRESS) + m_uiPyramidEventTimer = 20000; + else if (uiData == DONE) + m_uiPyramidEventTimer = 0; + break; + default: + return; + } + + 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_strInstData = saveStream.str(); + + SaveToDB(); + OUT_SAVE_INST_DATA_COMPLETE; + } +} + +void instance_zulfarrak::Load(const char* chrIn) +{ + if (!chrIn) + { + 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]; + + for (uint8 i = 0; i < MAX_ENCOUNTER; ++i) + { + if (m_auiEncounter[i] == IN_PROGRESS) + m_auiEncounter[i] = NOT_STARTED; + } + + OUT_LOAD_INST_DATA_COMPLETE; +} + +uint32 instance_zulfarrak::GetData(uint32 uiType) const +{ + if (uiType < MAX_ENCOUNTER) + return m_auiEncounter[uiType]; + + return 0; +} + +void instance_zulfarrak::OnCreatureEnterCombat(Creature* pCreature) +{ + switch (pCreature->GetEntry()) + { + case NPC_VELRATHA: SetData(TYPE_VELRATHA, IN_PROGRESS); break; + case NPC_GAHZRILLA: SetData(TYPE_GAHZRILLA, IN_PROGRESS); break; + case NPC_ANTUSUL: SetData(TYPE_ANTUSUL, IN_PROGRESS); break; + case NPC_THEKA: SetData(TYPE_THEKA, IN_PROGRESS); break; + case NPC_ZUMRAH: SetData(TYPE_ZUMRAH, IN_PROGRESS); break; + case NPC_NEKRUM: SetData(TYPE_NEKRUM, IN_PROGRESS); break; + case NPC_SEZZZIZ: SetData(TYPE_SEZZZIZ, IN_PROGRESS); break; + case NPC_CHIEF_SANDSCALP: SetData(TYPE_CHIEF_SANDSCALP, IN_PROGRESS); break; + } +} + +void instance_zulfarrak::OnCreatureEvade(Creature* pCreature) +{ + switch (pCreature->GetEntry()) + { + case NPC_VELRATHA: SetData(TYPE_VELRATHA, FAIL); break; + case NPC_GAHZRILLA: SetData(TYPE_GAHZRILLA, FAIL); break; + case NPC_ANTUSUL: SetData(TYPE_ANTUSUL, FAIL); break; + case NPC_THEKA: SetData(TYPE_THEKA, FAIL); break; + case NPC_ZUMRAH: SetData(TYPE_ZUMRAH, FAIL); break; + case NPC_NEKRUM: SetData(TYPE_NEKRUM, FAIL); break; + case NPC_SEZZZIZ: SetData(TYPE_SEZZZIZ, FAIL); break; + case NPC_CHIEF_SANDSCALP: SetData(TYPE_CHIEF_SANDSCALP, FAIL); break; + } +} + +void instance_zulfarrak::OnCreatureDeath(Creature* pCreature) +{ + switch (pCreature->GetEntry()) + { + case NPC_VELRATHA: SetData(TYPE_VELRATHA, DONE); break; + case NPC_GAHZRILLA: SetData(TYPE_GAHZRILLA, DONE); break; + case NPC_ANTUSUL: SetData(TYPE_ANTUSUL, DONE); break; + case NPC_THEKA: SetData(TYPE_THEKA, DONE); break; + case NPC_ZUMRAH: SetData(TYPE_ZUMRAH, DONE); break; + case NPC_NEKRUM: SetData(TYPE_NEKRUM, DONE); break; + case NPC_SEZZZIZ: SetData(TYPE_SEZZZIZ, DONE); break; + case NPC_CHIEF_SANDSCALP: SetData(TYPE_CHIEF_SANDSCALP, DONE); break; + } +} + +void instance_zulfarrak::Update(uint32 uiDiff) +{ + if (m_uiPyramidEventTimer) + { + if (m_uiPyramidEventTimer <= uiDiff) + { + if (m_lPyramidTrollsGuidList.empty()) + { + m_uiPyramidEventTimer = urand(3000, 10000); + return; + } + + GuidList::iterator iter = m_lPyramidTrollsGuidList.begin(); + advance(iter, urand(0, m_lPyramidTrollsGuidList.size() - 1)); + + // Remove the selected troll + ObjectGuid selectedGuid = *iter; + m_lPyramidTrollsGuidList.erase(iter); + + // Move the selected troll to the top of the pyramid. Note: the algorythm may be more complicated than this, but for the moment this will do. + if (Creature* pTroll = instance->GetCreature(selectedGuid)) + { + // Pick another one if already in combat or already killed + if (pTroll->getVictim() || !pTroll->IsAlive()) + { + m_uiPyramidEventTimer = urand(0, 2) ? urand(3000, 10000) : 1000; + return; + } + + float fX, fY, fZ; + if (Creature* pBly = GetSingleCreatureFromStorage(NPC_SERGEANT_BLY)) + { + // ToDo: research if there is anything special if these guys die + if (!pBly->IsAlive()) + { + m_uiPyramidEventTimer = 0; + return; + } + + pBly->GetRandomPoint(pBly->GetPositionX(), pBly->GetPositionY(), pBly->GetPositionZ(), 4.0f, fX, fY, fZ); + pTroll->SetWalk(false); + pTroll->GetMotionMaster()->MovePoint(0, fX, fY, fZ); + } + } + m_uiPyramidEventTimer = urand(0, 2) ? urand(3000, 10000) : 1000; + } + else + m_uiPyramidEventTimer -= uiDiff; + } +} + +InstanceData* GetInstanceData_instance_zulfarrak(Map* pMap) +{ + return new instance_zulfarrak(pMap); +} + +void AddSC_instance_zulfarrak() +{ + Script* pNewScript; + + pNewScript = new Script; + pNewScript->Name = "instance_zulfarrak"; + pNewScript->GetInstanceData = &GetInstanceData_instance_zulfarrak; + pNewScript->RegisterSelf(); +} diff --git a/src/modules/SD2/scripts/kalimdor/zulfarrak/zulfarrak.cpp b/src/modules/SD2/scripts/kalimdor/zulfarrak/zulfarrak.cpp new file mode 100644 index 000000000..6a54b557d --- /dev/null +++ b/src/modules/SD2/scripts/kalimdor/zulfarrak/zulfarrak.cpp @@ -0,0 +1,124 @@ +/* Copyright (C) 2006 - 2013 ScriptDev2 + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along 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: 100 +SDComment: +SDCategory: Zul'Farrak +EndScriptData */ + +/* ContentData +event_go_zulfarrak_gong +event_spell_unlocking +at_zulfarrak +EndContentData */ + +#include "precompiled.h" +#include "zulfarrak.h" + +/*###### +## event_go_zulfarrak_gong +######*/ + +bool ProcessEventId_event_go_zulfarrak_gong(uint32 /*uiEventId*/, Object* pSource, Object* /*pTarget*/, bool bIsStart) +{ + if (bIsStart && pSource->GetTypeId() == TYPEID_PLAYER) + { + if (instance_zulfarrak* pInstance = (instance_zulfarrak*)((Player*)pSource)->GetInstanceData()) + { + if (pInstance->GetData(TYPE_GAHZRILLA) == NOT_STARTED || pInstance->GetData(TYPE_GAHZRILLA) == FAIL) + { + pInstance->SetData(TYPE_GAHZRILLA, IN_PROGRESS); + return false; // Summon Gahz'rilla by Database Script + } + else + return true; // Prevent DB script summoning Gahz'rilla + } + } + return false; +} + +/*###### +## event_spell_unlocking +######*/ + +bool ProcessEventId_event_spell_unlocking(uint32 /*uiEventId*/, Object* pSource, Object* /*pTarget*/, bool bIsStart) +{ + if (bIsStart && pSource->GetTypeId() == TYPEID_PLAYER) + { + if (instance_zulfarrak* pInstance = (instance_zulfarrak*)((Player*)pSource)->GetInstanceData()) + { + if (pInstance->GetData(TYPE_PYRAMID_EVENT) == NOT_STARTED) + { + pInstance->SetData(TYPE_PYRAMID_EVENT, IN_PROGRESS); + return false; // Summon pyramid trolls by Database Script + } + else + return true; + } + } + return false; +} + +/*###### +## at_zulfarrak +######*/ + +bool AreaTrigger_at_zulfarrak(Player* pPlayer, AreaTriggerEntry const* pAt) +{ + if (pAt->id == AREATRIGGER_ANTUSUL) + { + if (pPlayer->isGameMaster() || pPlayer->IsDead()) + return false; + + instance_zulfarrak* pInstance = (instance_zulfarrak*)pPlayer->GetInstanceData(); + + if (!pInstance) + return false; + + if (pInstance->GetData(TYPE_ANTUSUL) == NOT_STARTED || pInstance->GetData(TYPE_ANTUSUL) == FAIL) + { + if (Creature* pAntuSul = pInstance->GetSingleCreatureFromStorage(NPC_ANTUSUL)) + { + if (pAntuSul->IsAlive()) + pAntuSul->AI()->AttackStart(pPlayer); + } + } + } + + return false; +} + +void AddSC_zulfarrak() +{ + Script* pNewScript; + + pNewScript = new Script; + pNewScript->Name = "event_go_zulfarrak_gong"; + pNewScript->pProcessEventId = &ProcessEventId_event_go_zulfarrak_gong; + pNewScript->RegisterSelf(); + + pNewScript = new Script; + pNewScript->Name = "event_spell_unlocking"; + pNewScript->pProcessEventId = &ProcessEventId_event_spell_unlocking; + pNewScript->RegisterSelf(); + + pNewScript = new Script; + pNewScript->Name = "at_zulfarrak"; + pNewScript->pAreaTrigger = &AreaTrigger_at_zulfarrak; + pNewScript->RegisterSelf(); +} diff --git a/src/modules/SD2/scripts/kalimdor/zulfarrak/zulfarrak.h b/src/modules/SD2/scripts/kalimdor/zulfarrak/zulfarrak.h new file mode 100644 index 000000000..805887aa8 --- /dev/null +++ b/src/modules/SD2/scripts/kalimdor/zulfarrak/zulfarrak.h @@ -0,0 +1,82 @@ +/* Copyright (C) 2006 - 2013 ScriptDev2 + * This program is free software licensed under GPL version 2 + * Please see the included DOCS/LICENSE.TXT for more information */ + +#ifndef DEF_ZULFARRAK_H +#define DEF_ZULFARRAK_H + +enum +{ + MAX_ENCOUNTER = 9, + + TYPE_VELRATHA = 0, + TYPE_GAHZRILLA = 1, + TYPE_ANTUSUL = 2, + TYPE_THEKA = 3, + TYPE_ZUMRAH = 4, + TYPE_NEKRUM = 5, + TYPE_SEZZZIZ = 6, + TYPE_CHIEF_SANDSCALP = 7, + TYPE_PYRAMID_EVENT = 8, + + NPC_VELRATHA = 7795, + NPC_GAHZRILLA = 7273, + NPC_ANTUSUL = 8127, + NPC_THEKA = 7272, + NPC_ZUMRAH = 7271, + NPC_NEKRUM = 7796, + NPC_SEZZZIZ = 7275, + NPC_CHIEF_SANDSCALP = 7267, + + NPC_SERGEANT_BLY = 7604, + NPC_SANDFURY_SLAVE = 7787, + NPC_SANDFURY_DRUDGE = 7788, + NPC_SANDFURY_CRETIN = 7789, + NPC_SANDFURY_ACOLYTE = 8876, + NPC_SANDFURY_ZEALOT = 8877, + + GO_SHALLOW_GRAVE = 128403, + GO_END_DOOR = 146084, + + // EVENT_ID_GONG_ZULFARRAK = 2488, // go 141832 + // EVENT_ID_UNLOCKING = 2609, // spell 10738 + + AREATRIGGER_ANTUSUL = 1447, +}; + +class instance_zulfarrak : public ScriptedInstance +{ + public: + instance_zulfarrak(Map* pMap); + ~instance_zulfarrak() {} + + void Initialize() override; + + void OnCreatureEnterCombat(Creature* pCreature) override; + void OnCreatureEvade(Creature* pCreature); + void OnCreatureDeath(Creature* pCreature) override; + + void OnCreatureCreate(Creature* pCreature) override; + void OnObjectCreate(GameObject* pGo) override; + + void SetData(uint32 uiType, uint32 uiData) override; + uint32 GetData(uint32 uiType) const override; + + void GetShallowGravesGuidList(GuidList& lList) { lList = m_lShallowGravesGuidList; } + + const char* Save() const override { return m_strInstData.c_str(); } + void Load(const char* chrIn) override; + + void Update(uint32 uiDiff) override; + + protected: + uint32 m_auiEncounter[MAX_ENCOUNTER]; + std::string m_strInstData; + + GuidList m_lShallowGravesGuidList; + GuidList m_lPyramidTrollsGuidList; + + uint32 m_uiPyramidEventTimer; +}; + +#endif diff --git a/src/modules/SD2/scripts/maelstrom/deepholm.cpp b/src/modules/SD2/scripts/maelstrom/deepholm.cpp new file mode 100644 index 000000000..769bae2bd --- /dev/null +++ b/src/modules/SD2/scripts/maelstrom/deepholm.cpp @@ -0,0 +1,35 @@ +/* Copyright (C) 2006 - 2013 ScriptDev2 + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +/* ScriptData +SDName: Deepholm +SD%Complete: 0 +SDComment: Placeholder +SDCategory: Deepholm +EndScriptData */ + +/* ContentData +EndContentData */ + +#include "precompiled.h" + +/*###### +# +######*/ + +void AddSC_deepholm() +{ +} diff --git a/src/modules/SD2/scripts/maelstrom/kezan.cpp b/src/modules/SD2/scripts/maelstrom/kezan.cpp new file mode 100644 index 000000000..ecc6ff41a --- /dev/null +++ b/src/modules/SD2/scripts/maelstrom/kezan.cpp @@ -0,0 +1,35 @@ +/* Copyright (C) 2006 - 2013 ScriptDev2 + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +/* ScriptData +SDName: Kezan +SD%Complete: 0 +SDComment: Placeholder +SDCategory: Kezan +EndScriptData */ + +/* ContentData +EndContentData */ + +#include "precompiled.h" + +/*###### +# +######*/ + +void AddSC_kezan() +{ +} diff --git a/src/modules/SD2/scripts/maelstrom/lost_isles.cpp b/src/modules/SD2/scripts/maelstrom/lost_isles.cpp new file mode 100644 index 000000000..f6ec9c895 --- /dev/null +++ b/src/modules/SD2/scripts/maelstrom/lost_isles.cpp @@ -0,0 +1,35 @@ +/* Copyright (C) 2006 - 2013 ScriptDev2 + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +/* ScriptData +SDName: Lost Isles +SD%Complete: 0 +SDComment: Placeholder +SDCategory: Lost Isles +EndScriptData */ + +/* ContentData +EndContentData */ + +#include "precompiled.h" + +/*###### +# +######*/ + +void AddSC_lost_isles() +{ +} diff --git a/src/modules/SD2/scripts/maelstrom/stonecore/boss_corborus.cpp b/src/modules/SD2/scripts/maelstrom/stonecore/boss_corborus.cpp new file mode 100644 index 000000000..284bf6f6d --- /dev/null +++ b/src/modules/SD2/scripts/maelstrom/stonecore/boss_corborus.cpp @@ -0,0 +1,28 @@ +/* Copyright (C) 2006 - 2013 ScriptDev2 + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +/* ScriptData +SDName: boss_corborus +SD%Complete: 0 +SDComment: Placeholder +SDCategory: Stonecore +EndScriptData */ + +#include "precompiled.h" + +void AddSC_boss_corborus() +{ +} diff --git a/src/modules/SD2/scripts/maelstrom/stonecore/boss_ozruk.cpp b/src/modules/SD2/scripts/maelstrom/stonecore/boss_ozruk.cpp new file mode 100644 index 000000000..3f793d367 --- /dev/null +++ b/src/modules/SD2/scripts/maelstrom/stonecore/boss_ozruk.cpp @@ -0,0 +1,28 @@ +/* Copyright (C) 2006 - 2013 ScriptDev2 + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +/* ScriptData +SDName: boss_ozruk +SD%Complete: 0 +SDComment: Placeholder +SDCategory: Stonecore +EndScriptData */ + +#include "precompiled.h" + +void AddSC_boss_ozruk() +{ +} diff --git a/src/modules/SD2/scripts/maelstrom/stonecore/boss_priestess_azil.cpp b/src/modules/SD2/scripts/maelstrom/stonecore/boss_priestess_azil.cpp new file mode 100644 index 000000000..857c4b94c --- /dev/null +++ b/src/modules/SD2/scripts/maelstrom/stonecore/boss_priestess_azil.cpp @@ -0,0 +1,28 @@ +/* Copyright (C) 2006 - 2013 ScriptDev2 + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with 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_azil +SD%Complete: 0 +SDComment: Placeholder +SDCategory: Stonecore +EndScriptData */ + +#include "precompiled.h" + +void AddSC_priestess_azil() +{ +} diff --git a/src/modules/SD2/scripts/maelstrom/stonecore/boss_slabhide.cpp b/src/modules/SD2/scripts/maelstrom/stonecore/boss_slabhide.cpp new file mode 100644 index 000000000..bb67f1b86 --- /dev/null +++ b/src/modules/SD2/scripts/maelstrom/stonecore/boss_slabhide.cpp @@ -0,0 +1,28 @@ +/* Copyright (C) 2006 - 2013 ScriptDev2 + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +/* ScriptData +SDName: boss_slabhide +SD%Complete: 0 +SDComment: Placeholder +SDCategory: Stonecore +EndScriptData */ + +#include "precompiled.h" + +void AddSC_boss_slabhide() +{ +} diff --git a/src/modules/SD2/scripts/maelstrom/stonecore/instance_stonecore.cpp b/src/modules/SD2/scripts/maelstrom/stonecore/instance_stonecore.cpp new file mode 100644 index 000000000..bcf11ef82 --- /dev/null +++ b/src/modules/SD2/scripts/maelstrom/stonecore/instance_stonecore.cpp @@ -0,0 +1,28 @@ +/* Copyright (C) 2006 - 2013 ScriptDev2 + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along 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_stonecore +SD%Complete: 0 +SDComment: Placeholder +SDCategory: Stonecore +EndScriptData */ + +#include "precompiled.h" + +void AddSC_instance_stonecore() +{ +} diff --git a/src/modules/SD2/scripts/maelstrom/stonecore/stonecore.h b/src/modules/SD2/scripts/maelstrom/stonecore/stonecore.h new file mode 100644 index 000000000..6b5f9ef47 --- /dev/null +++ b/src/modules/SD2/scripts/maelstrom/stonecore/stonecore.h @@ -0,0 +1,3 @@ +/* Copyright (C) 2006 - 2013 ScriptDev2 + * This program is free software licensed under GPL version 2 + * Please see the included DOCS/LICENSE.TXT for more information */ diff --git a/src/modules/SD2/scripts/northrend/azjol-nerub/ahnkahet/ahnkahet.h b/src/modules/SD2/scripts/northrend/azjol-nerub/ahnkahet/ahnkahet.h new file mode 100644 index 000000000..3b127fcd9 --- /dev/null +++ b/src/modules/SD2/scripts/northrend/azjol-nerub/ahnkahet/ahnkahet.h @@ -0,0 +1,119 @@ +/* Copyright (C) 2006 - 2013 ScriptDev2 + * This program is free software licensed under GPL version 2 + * Please see the included DOCS/LICENSE.TXT for more information */ + +#ifndef DEF_AHNKAHET_H +#define DEF_AHNKAHET_H +/* Encounters + * Elder Nadox = 1 + * Prince Taldram = 2 + * Jedoga Shadowseeker = 3 + * Herald Volazj = 4 + * Amanitar = 5 +*/ +enum +{ + MAX_ENCOUNTER = 5, + MAX_INITIATES = 15, + + TYPE_NADOX = 0, + TYPE_TALDARAM = 1, + TYPE_JEDOGA = 2, + TYPE_VOLAZJ = 3, + TYPE_AMANITAR = 4, + + DATA_INSANITY_PLAYER = 1, + + GO_DOOR_TALDARAM = 192236, + GO_ANCIENT_DEVICE_L = 193093, + GO_ANCIENT_DEVICE_R = 193094, + GO_VORTEX = 193564, + + NPC_ELDER_NADOX = 29309, + NPC_TALDARAM = 29308, + NPC_JEDOGA_SHADOWSEEKER = 29310, + NPC_AHNKAHAR_GUARDIAN_EGG = 30173, + NPC_AHNKAHAR_SWARM_EGG = 30172, + NPC_JEDOGA_CONTROLLER = 30181, + NPC_TWILIGHT_INITIATE = 30114, + + NPC_HERALD_VOLAZJ = 29311, + NPC_TWISTED_VISAGE_1 = 30621, + NPC_TWISTED_VISAGE_2 = 30622, + NPC_TWISTED_VISAGE_3 = 30623, + NPC_TWISTED_VISAGE_4 = 30624, + NPC_TWISTED_VISAGE_5 = 30625, + + SPELL_TWISTED_VISAGE_DEATH = 57555, + SPELL_INSANITY_SWITCH = 57538, + SPELL_INSANITY_CLEAR = 57558, + + SPELL_INSANITY_PHASE_16 = 57508, + SPELL_INSANITY_PHASE_32 = 57509, + SPELL_INSANITY_PHASE_64 = 57510, + SPELL_INSANITY_PHASE_128 = 57511, + SPELL_INSANITY_PHASE_256 = 57512, + + ACHIEV_START_VOLAZJ_ID = 20382, + + ACHIEV_CRIT_RESPECT_ELDERS = 7317, // Nadox, achiev 2038 + ACHIEV_CRIT_VOLUNTEER_WORK = 7359, // Jedoga, achiev 2056 +}; + +static const float aTaldaramLandingLoc[4] = {528.734f, -845.998f, 11.54f, 0.68f}; +static const float aJedogaLandingLoc[4] = {375.4977f, -707.3635f, -16.094f, 5.42f}; + +class instance_ahnkahet : public ScriptedInstance +{ + public: + instance_ahnkahet(Map* pMap); + + void Initialize() override; + + void OnCreatureCreate(Creature* pCreature) override; + void OnObjectCreate(GameObject* pGo) override; + + void OnCreatureEvade(Creature* pCreature) override; + void OnCreatureDeath(Creature* pCreature) override; + + void SetData(uint32 uiType, uint32 uiData) override; + uint32 GetData(uint32 uiType) const override; + + void SetData64(uint32 uiType, uint64 uiGuid) override; + + ObjectGuid SelectRandomGuardianEggGuid(); + ObjectGuid SelectRandomSwarmerEggGuid(); + ObjectGuid SelectJedogaSacrificeControllerGuid() { return m_jedogaSacrificeController; } + + void GetJedogaControllersList(GuidList& lList) { lList = m_lJedogaControllersGuidList; } + void GetJedogaEventControllersList(GuidList& lList) {lList = m_lJedogaEventControllersGuidList; } + + bool CheckAchievementCriteriaMeet(uint32 uiCriteriaId, Player const* pSource, Unit const* pTarget, uint32 uiMiscValue1 /* = 0*/) const override; + + const char* Save() const override { return m_strInstData.c_str(); } + void Load(const char* chrIn) override; + + private: + void HandleInsanityClear(); + void HandleInsanitySwitch(Player* pPlayer); + + uint32 m_auiEncounter[MAX_ENCOUNTER]; + std::string m_strInstData; + + bool m_bRespectElders; + bool m_bVolunteerWork; + + uint8 m_uiDevicesActivated; + uint8 m_uiInitiatesKilled; + uint8 m_uiTwistedVisageCount; + + ObjectGuid m_jedogaSacrificeController; + + GuidList m_GuardianEggList; + GuidList m_SwarmerEggList; + GuidList m_lJedogaControllersGuidList; + GuidList m_lJedogaEventControllersGuidList; + GuidList m_lInsanityPlayersGuidList; +}; + +#endif diff --git a/src/modules/SD2/scripts/northrend/azjol-nerub/ahnkahet/boss_amanitar.cpp b/src/modules/SD2/scripts/northrend/azjol-nerub/ahnkahet/boss_amanitar.cpp new file mode 100644 index 000000000..daf4479d3 --- /dev/null +++ b/src/modules/SD2/scripts/northrend/azjol-nerub/ahnkahet/boss_amanitar.cpp @@ -0,0 +1,227 @@ +/* Copyright (C) 2006 - 2013 ScriptDev2 + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +/* ScriptData +SDName: Boss_Amanitar +SD%Complete: 80 +SDComment: Mushrooms summoning may need improvements; +SDCategory: Ahn'kahet +EndScriptData */ + +#include "precompiled.h" +#include "ahnkahet.h" + +enum +{ + SPELL_BASH = 57094, + SPELL_VENOM_BOLT_VOLLEY = 57088, + SPELL_ENTANGLING_ROOTS = 57095, + SPELL_MINI = 57055, + SPELL_REMOVE_MUSHROOM_POWER = 57283, // purpose unk - this spell may remove the Mini aura from all players + + // Mushroom entries + NPC_HEALTHY_MUSHROOM = 30391, + NPC_POISONOUS_MUSHROOM = 30435, + + // Mushroom spells + SPELL_POISON_CLOUD = 57061, + SPELL_POTENT_FUNGUS = 56648, + SPELL_POISON_MUSHROOM_VISUAL = 56741, + SPELL_POWER_MUSHROOM_VISUAL = 56740, + SPELL_MUSHROOM_FORM = 31690, +}; + +static const float aMushroomPos[3] = {362.8f, -869.16f, -75.03f}; + +/*###### +## boss_amanitar +######*/ + +struct boss_amanitarAI : public ScriptedAI +{ + boss_amanitarAI(Creature* pCreature) : ScriptedAI(pCreature) + { + m_pInstance = (ScriptedInstance*)pCreature->GetInstanceData(); + Reset(); + } + + ScriptedInstance* m_pInstance; + + uint32 m_uiBashTimer; + uint32 m_uiVenomBoltTimer; + uint32 m_uiRootsTimer; + uint32 m_uiMiniTimer; + uint32 m_uiMushroomTimer; + + void Reset() override + { + m_uiBashTimer = urand(7000, 10000); + m_uiVenomBoltTimer = urand(10000, 15000); + m_uiRootsTimer = 20000; + m_uiMiniTimer = urand(20000, 25000); + m_uiMushroomTimer = urand(10000, 20000); + } + + void Aggro(Unit* /*pWho*/) override + { + DoSummonMushrooms(true); + + if (m_pInstance) + m_pInstance->SetData(TYPE_AMANITAR, IN_PROGRESS); + } + + void JustDied(Unit* /*pKiller*/) override + { + if (m_pInstance) + m_pInstance->SetData(TYPE_AMANITAR, DONE); + } + + void JustReachedHome() override + { + if (m_pInstance) + m_pInstance->SetData(TYPE_AMANITAR, FAIL); + } + + void JustSummoned(Creature* pSummoned) override + { + if (pSummoned->GetEntry() == NPC_POISONOUS_MUSHROOM) + pSummoned->CastSpell(pSummoned, SPELL_POISON_MUSHROOM_VISUAL, true); + else if (pSummoned->GetEntry() == NPC_HEALTHY_MUSHROOM) + pSummoned->CastSpell(pSummoned, SPELL_POWER_MUSHROOM_VISUAL, true); + + // ToDo: research if the mushrooms should have a grow effect! + pSummoned->CastSpell(pSummoned, SPELL_MUSHROOM_FORM, true); + } + + void SummonedCreatureJustDied(Creature* pSummoned) override + { + if (pSummoned->GetEntry() == NPC_POISONOUS_MUSHROOM) + pSummoned->CastSpell(pSummoned, SPELL_POISON_CLOUD, true); + else if (pSummoned->GetEntry() == NPC_HEALTHY_MUSHROOM) + pSummoned->CastSpell(pSummoned, SPELL_POTENT_FUNGUS, true); + } + + void DoSummonMushrooms(bool bIsFirstSummon) + { + // This implementation may not be 100% accurate; + // On aggro boss summons about 20 mushrooms; On timer it summons about 5 mushrooms per turn + // There is a 33% chance that the mushroom will be healthy + // The summon position is based on the center of the area coords + + float fX, fY, fZ; + uint32 uiMaxMushrooms = bIsFirstSummon ? 20 : 5; + + for (uint8 i = 0; i < uiMaxMushrooms; ++i) + { + uint32 uiMushroomEntry = roll_chance_i(33) ? NPC_HEALTHY_MUSHROOM : NPC_POISONOUS_MUSHROOM; + m_creature->GetRandomPoint(aMushroomPos[0], aMushroomPos[1], aMushroomPos[2], 30.0f, fX, fY, fZ); + m_creature->SummonCreature(uiMushroomEntry, fX, fY, fZ, 0.0f, TEMPSUMMON_DEAD_DESPAWN, 0); + } + } + + void UpdateAI(const uint32 uiDiff) override + { + if (!m_creature->SelectHostileTarget() || !m_creature->getVictim()) + return; + + if (m_uiBashTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature->getVictim(), SPELL_BASH) == CAST_OK) + m_uiBashTimer = urand(8000, 13000); + } + else + m_uiBashTimer -= uiDiff; + + if (m_uiVenomBoltTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature, SPELL_VENOM_BOLT_VOLLEY) == CAST_OK) + m_uiVenomBoltTimer = urand(15000, 20000); + } + else + m_uiVenomBoltTimer -= uiDiff; + + if (m_uiRootsTimer < uiDiff) + { + if (Unit* pTarget = m_creature->SelectAttackingTarget(ATTACKING_TARGET_RANDOM, 0)) + { + if (DoCastSpellIfCan(pTarget, SPELL_ENTANGLING_ROOTS) == CAST_OK) + m_uiRootsTimer = urand(20000, 25000); + } + } + else + m_uiRootsTimer -= uiDiff; + + if (m_uiMiniTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature, SPELL_MINI) == CAST_OK) + m_uiMiniTimer = 30000; + } + else + m_uiMiniTimer -= uiDiff; + + if (m_uiMushroomTimer < uiDiff) + { + DoSummonMushrooms(false); + m_uiMushroomTimer = urand(10000, 20000); + } + else + m_uiMushroomTimer -= uiDiff; + + // ToDo: Research if he requires out of combat area evade check + + DoMeleeAttackIfReady(); + } +}; + +CreatureAI* GetAI_boss_amanitar(Creature* pCreature) +{ + return new boss_amanitarAI(pCreature); +} + +/*###### +## npc_amanitar_mushroom +######*/ + +struct npc_amanitar_mushroomAI : public Scripted_NoMovementAI +{ + npc_amanitar_mushroomAI(Creature* pCreature) : Scripted_NoMovementAI(pCreature) { Reset(); } + + void Reset() override { } + + void AttackStart(Unit* /*pWho*/) override { } + void MoveInLineOfSight(Unit* /*pWho*/) override { } + void UpdateAI(const uint32 /*uiDiff*/) override { } +}; + +CreatureAI* GetAI_npc_amanitar_mushroom(Creature* pCreature) +{ + return new npc_amanitar_mushroomAI(pCreature); +} + +void AddSC_boss_amanitar() +{ + Script* pNewScript; + + pNewScript = new Script; + pNewScript->Name = "boss_amanitar"; + pNewScript->GetAI = &GetAI_boss_amanitar; + pNewScript->RegisterSelf(); + + pNewScript = new Script; + pNewScript->Name = "npc_amanitar_mushroom"; + pNewScript->GetAI = &GetAI_npc_amanitar_mushroom; + pNewScript->RegisterSelf(); +} diff --git a/src/modules/SD2/scripts/northrend/azjol-nerub/ahnkahet/boss_jedoga.cpp b/src/modules/SD2/scripts/northrend/azjol-nerub/ahnkahet/boss_jedoga.cpp new file mode 100644 index 000000000..8d31286ea --- /dev/null +++ b/src/modules/SD2/scripts/northrend/azjol-nerub/ahnkahet/boss_jedoga.cpp @@ -0,0 +1,472 @@ +/* Copyright (C) 2006 - 2013 ScriptDev2 + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with 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 +SD%Complete: 90 +SDComment: The movement points for the volunteers are not 100% blizzlike. On retail they use hardcoded points +SDCategory: Ahn'kahet +EndScriptData */ + +#include "precompiled.h" +#include "ahnkahet.h" + +enum +{ + SAY_AGGRO = -1619017, + SAY_CALL_SACRIFICE1 = -1619018, + SAY_CALL_SACRIFICE2 = -1619019, + SAY_SACRIFICE1 = -1619020, + SAY_SACRIFICE2 = -1619021, + SAY_SLAY_1 = -1619022, + SAY_SLAY_2 = -1619023, + SAY_SLAY_3 = -1619024, + SAY_DEATH = -1619025, + + // preaching 1-5 when it is used? + SAY_PREACHING1 = -1619026, + SAY_PREACHING2 = -1619027, + SAY_PREACHING3 = -1619028, + SAY_PREACHING4 = -1619029, + SAY_PREACHING5 = -1619030, + + SAY_VOLUNTEER_CHOOSEN = -1619031, // I have been choosen! + SAY_VOLUNTEER_SACRIFICED = -1619032, // I give myself to the master! + + SPELL_BEAM_VISUAL = 56312, + SPELL_SPHERE_VISUAL = 56075, // already included in creature_template_addon + SPELL_LIGHTNING_VISUAL = 56327, + + SPELL_CYCLONE_STRIKE = 56855, + SPELL_CYCLONE_STRIKE_H = 60030, + SPELL_LIGHTNING_BOLT = 56891, + SPELL_LIGHTNING_BOLT_H = 60032, + SPELL_THUNDERSHOCK = 56926, + SPELL_THUNDERSHOCK_H = 60029, + + SPELL_HOVER_FALL = 56100, + SPELL_SACRIFICE_VISUAL = 56133, + SPELL_SACRIFICE_BEAM = 56150, + SPELL_VOLUNTEER_SPHERE = 56102, + SPELL_PILLAR_LIGHTNING = 56868, + + NPC_TWILIGHT_VOLUNTEER = 30385, + + MAX_VOLUNTEERS_PER_SIDE = 13, + + POINT_ID_PREPARE = 1, + POINT_ID_SACRIFICE = 2, + POINT_ID_LEVITATE = 3, + POINT_ID_COMBAT = 4, +}; + +static const float aVolunteerPosition[2][3] = +{ + {456.09f, -724.34f, -31.58f}, + {410.11f, -785.46f, -31.58f}, +}; + +/*###### +## boss_jedoga +######*/ + +struct boss_jedogaAI : public ScriptedAI +{ + boss_jedogaAI(Creature* pCreature) : ScriptedAI(pCreature) + { + m_pInstance = (instance_ahnkahet*)pCreature->GetInstanceData(); + m_bIsRegularMode = pCreature->GetMap()->IsRegularDifficulty(); + m_bHasDoneIntro = false; + Reset(); + } + + instance_ahnkahet* m_pInstance; + bool m_bIsRegularMode; + + uint32 m_uiVisualTimer; + uint32 m_uiThundershockTimer; + uint32 m_uiCycloneStrikeTimer; + uint32 m_uiLightningBoltTimer; + uint8 m_uiSacrificeCount; + bool m_bSacrifice; + bool m_bIsSacrificing; + bool m_bHasDoneIntro; + + GuidList m_lVolunteerGuidList; + + void Reset() override + { + m_uiThundershockTimer = 40000; + m_uiCycloneStrikeTimer = 15000; + m_uiLightningBoltTimer = 7000; + m_uiVisualTimer = 5000; + m_bSacrifice = false; + m_bIsSacrificing = false; + + m_lVolunteerGuidList.clear(); + + SetCombatMovement(true); + m_creature->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE); + } + + ObjectGuid SelectRandomVolunteer() + { + if (m_lVolunteerGuidList.empty()) + return ObjectGuid(); + + GuidList::iterator iter = m_lVolunteerGuidList.begin(); + advance(iter, urand(0, m_lVolunteerGuidList.size() - 1)); + + return *iter; + } + + void Aggro(Unit* /*pWho*/) override + { + DoScriptText(SAY_AGGRO, m_creature); + DoCallVolunteers(); + + if (m_pInstance) + m_pInstance->SetData(TYPE_JEDOGA, IN_PROGRESS); + } + + void KilledUnit(Unit* /*pVictim*/) override + { + switch (urand(0, 2)) + { + case 0: DoScriptText(SAY_SLAY_1, m_creature); break; + case 1: DoScriptText(SAY_SLAY_2, m_creature); break; + case 2: DoScriptText(SAY_SLAY_3, m_creature); break; + } + } + + void JustDied(Unit* /*pKiller*/) override + { + DoScriptText(SAY_DEATH, m_creature); + + if (m_pInstance) + m_pInstance->SetData(TYPE_JEDOGA, DONE); + } + + void JustReachedHome() override + { + if (m_pInstance) + m_pInstance->SetData(TYPE_JEDOGA, FAIL); + } + + void MoveInLineOfSight(Unit* pWho) override + { + if (!m_bHasDoneIntro && pWho->GetTypeId() == TYPEID_PLAYER && m_creature->IsWithinDistInMap(pWho, 110.0f) && m_creature->IsWithinLOSInMap(pWho)) + { + switch (urand(0, 4)) + { + case 0: DoScriptText(SAY_PREACHING1, m_creature); break; + case 1: DoScriptText(SAY_PREACHING2, m_creature); break; + case 2: DoScriptText(SAY_PREACHING3, m_creature); break; + case 3: DoScriptText(SAY_PREACHING4, m_creature); break; + case 4: DoScriptText(SAY_PREACHING5, m_creature); break; + } + m_bHasDoneIntro = true; + } + + ScriptedAI::MoveInLineOfSight(pWho); + } + + // Helper function which summons all the Volunteers + void DoCallVolunteers() + { + // The volunteers should be summoned on the bottom of each stair in 2 lines - 7 in the front line and 6 in the back line + // However, because this would involve too many hardcoded coordinates we'll summon this on random point near the stairs + + float fX, fY, fZ; + for (uint8 j = 0; j < 2; ++j) + { + for (uint8 i = 0; i < MAX_VOLUNTEERS_PER_SIDE; ++i) + { + // In order to get a good movement position we need to handle the coordinates calculation here, inside the iteration. + m_creature->GetRandomPoint(aVolunteerPosition[j][0], aVolunteerPosition[j][1], aVolunteerPosition[j][2], 10.0f, fX, fY, fZ); + if (Creature* pVolunteer = m_creature->SummonCreature(NPC_TWILIGHT_VOLUNTEER, fX, fY, fZ, 0, TEMPSUMMON_DEAD_DESPAWN, 0)) + { + // Adjust coordinates based on the wave number and side + float fDist = i < 7 ? 20.0f : 30.0f; + float fAngle = 0; + if (!j) + fAngle = i < 7 ? (i - 2) * (3 * M_PI_F / 35) : (i - 6) * (M_PI_F / 16); + else + fAngle = i < 7 ? (i - 10) * (3 * M_PI_F / 35) : 3 * M_PI_F / 2 - (i - 6) * (M_PI_F / 16); + + m_creature->GetNearPoint(m_creature, fX, fY, fZ, 0, fDist, fAngle); + pVolunteer->GetMotionMaster()->MovePoint(POINT_ID_PREPARE, fX, fY, fZ); + } + } + } + + // Summon one more Volunteer for the center position + m_creature->GetRandomPoint(aVolunteerPosition[0][0], aVolunteerPosition[0][1], aVolunteerPosition[0][2], 10.0f, fX, fY, fZ); + if (Creature* pVolunteer = m_creature->SummonCreature(NPC_TWILIGHT_VOLUNTEER, fX, fY, fZ, 0, TEMPSUMMON_DEAD_DESPAWN, 0)) + { + m_creature->GetNearPoint(m_creature, fX, fY, fZ, 0, 20.0f, 7 * M_PI_F / 4); + pVolunteer->GetMotionMaster()->MovePoint(POINT_ID_PREPARE, fX, fY, fZ); + } + } + + void JustSummoned(Creature* pSummoned) override + { + if (pSummoned->GetEntry() == NPC_TWILIGHT_VOLUNTEER) + { + pSummoned->SetWalk(false); + pSummoned->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_UNK_6); + m_lVolunteerGuidList.push_back(pSummoned->GetObjectGuid()); + } + } + + void SummonedCreatureJustDied(Creature* pSummoned) override + { + if (pSummoned->GetEntry() == NPC_TWILIGHT_VOLUNTEER) + { + m_lVolunteerGuidList.remove(pSummoned->GetObjectGuid()); + + if (m_pInstance) + { + if (Creature* pTemp = m_creature->GetMap()->GetCreature(m_pInstance->SelectJedogaSacrificeControllerGuid())) + pTemp->RemoveAurasDueToSpell(SPELL_SACRIFICE_VISUAL); + } + + m_creature->GetMotionMaster()->MovePoint(POINT_ID_COMBAT, aJedogaLandingLoc[0], aJedogaLandingLoc[1], aJedogaLandingLoc[2]); + } + } + + void SummonedMovementInform(Creature* pSummoned, uint32 uiType, uint32 uiPointId) override + { + if (uiType != POINT_MOTION_TYPE || pSummoned->GetEntry() != NPC_TWILIGHT_VOLUNTEER) + return; + + if (uiPointId == POINT_ID_PREPARE) + { + pSummoned->CastSpell(pSummoned, SPELL_VOLUNTEER_SPHERE, true); + pSummoned->SetFacingToObject(m_creature); + pSummoned->SetStandState(UNIT_STAND_STATE_KNEEL); + } + else if (uiPointId == POINT_ID_SACRIFICE) + { + DoCastSpellIfCan(pSummoned, SPELL_SACRIFICE_BEAM); + DoScriptText(urand(0, 1) ? SAY_SACRIFICE1 : SAY_SACRIFICE2, m_creature); + } + } + + void MovementInform(uint32 uiMoveType, uint32 uiPointId) override + { + if (uiMoveType != POINT_MOTION_TYPE) + return; + + switch (uiPointId) + { + // Prepare for combat + case POINT_ID_PREPARE: + + m_creature->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE); + m_creature->RemoveAurasDueToSpell(SPELL_LIGHTNING_VISUAL); + m_creature->RemoveAurasDueToSpell(SPELL_SPHERE_VISUAL); + m_creature->SetLevitate(false); + break; + + // Prepare for sacrifice lift off + case POINT_ID_SACRIFICE: + DoCastSpellIfCan(m_creature, SPELL_HOVER_FALL); + m_creature->SetLevitate(true); + m_creature->GetMotionMaster()->MovePoint(POINT_ID_LEVITATE, m_creature->GetPositionX(), m_creature->GetPositionY(), m_creature->GetPositionZ() + 10.0f); + break; + + // Call a volunteer to sacrifice + case POINT_ID_LEVITATE: + if (Creature* pVolunteer = m_creature->GetMap()->GetCreature(SelectRandomVolunteer())) + { + DoScriptText(urand(0, 1) ? SAY_CALL_SACRIFICE1 : SAY_CALL_SACRIFICE2, m_creature); + DoScriptText(SAY_VOLUNTEER_CHOOSEN, pVolunteer); + + pVolunteer->RemoveAurasDueToSpell(SPELL_VOLUNTEER_SPHERE); + pVolunteer->SetStandState(UNIT_STAND_STATE_STAND); + pVolunteer->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE); + pVolunteer->CastSpell(pVolunteer, SPELL_PILLAR_LIGHTNING, false); + pVolunteer->SetWalk(true); + pVolunteer->GetMotionMaster()->MovePoint(POINT_ID_SACRIFICE, aJedogaLandingLoc[0], aJedogaLandingLoc[1], aJedogaLandingLoc[2]); + } + + // Set visual aura + if (m_pInstance) + { + if (Creature* pTemp = m_creature->GetMap()->GetCreature(m_pInstance->SelectJedogaSacrificeControllerGuid())) + pTemp->CastSpell(pTemp, SPELL_SACRIFICE_VISUAL, false); + } + break; + + // Resume combat + case POINT_ID_COMBAT: + m_creature->RemoveAurasDueToSpell(SPELL_HOVER_FALL); + m_creature->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE); + + m_bIsSacrificing = false; + SetCombatMovement(true); + m_creature->SetLevitate(false); + if (m_creature->getVictim()) + m_creature->GetMotionMaster()->MoveChase(m_creature->getVictim()); + break; + } + } + + void UpdateAI(const uint32 uiDiff) override + { + if (m_uiVisualTimer) + { + if (m_uiVisualTimer <= uiDiff) + { + GuidList lControllersList; + if (m_pInstance) + m_pInstance->GetJedogaEventControllersList(lControllersList); + + for (GuidList::const_iterator itr = lControllersList.begin(); itr != lControllersList.end(); ++itr) + { + if (Creature* pTemp = m_creature->GetMap()->GetCreature(*itr)) + pTemp->CastSpell(m_creature, SPELL_BEAM_VISUAL, false); + } + + if (DoCastSpellIfCan(m_creature, SPELL_LIGHTNING_VISUAL) == CAST_OK) + m_uiVisualTimer = 0; + } + else + m_uiVisualTimer -= uiDiff; + } + + if (!m_creature->SelectHostileTarget() || !m_creature->getVictim()) + return; + + // Don't use abilities while sacrificing + if (m_bIsSacrificing) + return; + + // Note: this was changed in 3.3.2 and now it does this only once + if (m_creature->GetHealthPercent() < 50.0f && !m_bSacrifice) + { + SetCombatMovement(false); + m_creature->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE); + m_creature->GetMotionMaster()->MovePoint(POINT_ID_SACRIFICE, aJedogaLandingLoc[0], aJedogaLandingLoc[1], aJedogaLandingLoc[2]); + m_bSacrifice = true; + m_bIsSacrificing = true; + } + + if (m_uiThundershockTimer < uiDiff) + { + if (Unit* pTarget = m_creature->SelectAttackingTarget(ATTACKING_TARGET_RANDOM, 0)) + DoCastSpellIfCan(pTarget, m_bIsRegularMode ? SPELL_THUNDERSHOCK : SPELL_THUNDERSHOCK_H); + + m_uiThundershockTimer = 40000; + } + else + m_uiThundershockTimer -= uiDiff; + + if (m_uiLightningBoltTimer < uiDiff) + { + if (Unit* pTarget = m_creature->SelectAttackingTarget(ATTACKING_TARGET_RANDOM, 0)) + DoCastSpellIfCan(pTarget, m_bIsRegularMode ? SPELL_LIGHTNING_BOLT : SPELL_LIGHTNING_BOLT_H); + + m_uiLightningBoltTimer = 7000; + } + else + m_uiLightningBoltTimer -= uiDiff; + + if (m_uiCycloneStrikeTimer < uiDiff) + { + DoCastSpellIfCan(m_creature->getVictim(), m_bIsRegularMode ? SPELL_CYCLONE_STRIKE : SPELL_CYCLONE_STRIKE_H); + m_uiCycloneStrikeTimer = 15000; + } + else + m_uiCycloneStrikeTimer -= uiDiff; + + DoMeleeAttackIfReady(); + } +}; + +CreatureAI* GetAI_boss_jedoga(Creature* pCreature) +{ + return new boss_jedogaAI(pCreature); +} + +/*###### +## npc_twilight_volunteer +######*/ + +struct npc_twilight_volunteerAI : public Scripted_NoMovementAI +{ + npc_twilight_volunteerAI(Creature* pCreature) : Scripted_NoMovementAI(pCreature) + { + m_pInstance = (ScriptedInstance*)pCreature->GetInstanceData(); + Reset(); + } + + ScriptedInstance* m_pInstance; + + void Reset() override { } + + void JustDied(Unit* pKiller) override + { + // If it's not killed by Jedoga then set the achiev to fail + if (pKiller->GetEntry() == NPC_JEDOGA_SHADOWSEEKER) + return; + + if (m_pInstance) + m_pInstance->SetData(TYPE_JEDOGA, SPECIAL); + } + + void AttackStart(Unit* /*pWho*/) override { } + void MoveInLineOfSight(Unit* /*pWho*/) override { } + void UpdateAI(const uint32 /*uiDiff*/) override { } +}; + +CreatureAI* GetAI_npc_twilight_volunteer(Creature* pCreature) +{ + return new npc_twilight_volunteerAI(pCreature); +} + +bool EffectAuraDummy_spell_aura_dummy_sacrifice_beam(const Aura* pAura, bool bApply) +{ + if (pAura->GetId() == SPELL_SACRIFICE_BEAM && pAura->GetEffIndex() == EFFECT_INDEX_0 && !bApply) + { + if (Creature* pTarget = (Creature*)pAura->GetTarget()) + { + if (ScriptedInstance* pInstance = (ScriptedInstance*)pTarget->GetInstanceData()) + { + if (Creature* pJedoga = pInstance->GetSingleCreatureFromStorage(NPC_JEDOGA_SHADOWSEEKER)) + pJedoga->DealDamage(pTarget, pTarget->GetHealth(), NULL, DIRECT_DAMAGE, SPELL_SCHOOL_MASK_NORMAL, NULL, false); + } + } + } + return true; +} + +void AddSC_boss_jedoga() +{ + Script* pNewScript; + + pNewScript = new Script; + pNewScript->Name = "boss_jedoga"; + pNewScript->GetAI = &GetAI_boss_jedoga; + pNewScript->RegisterSelf(); + + pNewScript = new Script; + pNewScript->Name = "npc_twilight_volunteer"; + pNewScript->GetAI = &GetAI_npc_twilight_volunteer; + pNewScript->pEffectAuraDummy = &EffectAuraDummy_spell_aura_dummy_sacrifice_beam; + pNewScript->RegisterSelf(); +} diff --git a/src/modules/SD2/scripts/northrend/azjol-nerub/ahnkahet/boss_nadox.cpp b/src/modules/SD2/scripts/northrend/azjol-nerub/ahnkahet/boss_nadox.cpp new file mode 100644 index 000000000..b8de33445 --- /dev/null +++ b/src/modules/SD2/scripts/northrend/azjol-nerub/ahnkahet/boss_nadox.cpp @@ -0,0 +1,255 @@ +/* Copyright (C) 2006 - 2013 ScriptDev2 + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +/* ScriptData +SDName: Boss_Nadox +SD%Complete: 90% +SDComment: Some adjustment may be required +SDCategory: Ahn'kahet +EndScriptData */ + +#include "precompiled.h" +#include "ahnkahet.h" + +enum +{ + SAY_AGGRO = -1619000, + SAY_SUMMON_EGG_1 = -1619001, + SAY_SUMMON_EGG_2 = -1619002, + SAY_SLAY_1 = -1619003, + SAY_SLAY_2 = -1619004, + SAY_SLAY_3 = -1619005, + SAY_DEATH = -1619006, + EMOTE_HATCH = -1619007, + + SPELL_BROOD_PLAGUE = 56130, + SPELL_BROOD_PLAGUE_H = 59467, + SPELL_BERSERK = 26662, + SPELL_BROOD_RAGE = 59465, + + SPELL_GUARDIAN_AURA = 56151, + + // JustSummoned is not called for spell summoned creatures + SPELL_SUMMON_SWARM_GUARDIAN = 56120, + SPELL_SUMMON_SWARMERS = 56119, + + NPC_AHNKAHAR_GUARDIAN = 30176, + NPC_AHNKAHAR_SWARMER = 30178 +}; + +/*###### +## mob_ahnkahat_egg +######*/ +struct mob_ahnkahar_eggAI : public ScriptedAI +{ + mob_ahnkahar_eggAI(Creature* pCreature) : ScriptedAI(pCreature) + { + m_pInstance = (ScriptedInstance*)pCreature->GetInstanceData(); + Reset(); + } + + ScriptedInstance* m_pInstance; + + void Reset() override {} + void MoveInLineOfSight(Unit* /*pWho*/) override {} + void AttackStart(Unit* /*pWho*/) override {} + + void JustSummoned(Creature* pSummoned) override + { + if (pSummoned->GetEntry() == NPC_AHNKAHAR_GUARDIAN) + { + pSummoned->CastSpell(pSummoned, SPELL_GUARDIAN_AURA, true); + DoScriptText(EMOTE_HATCH, m_creature); + } + + if (m_pInstance) + { + if (Creature* pElderNadox = m_pInstance->GetSingleCreatureFromStorage(NPC_ELDER_NADOX)) + { + float fPosX, fPosY, fPosZ; + pElderNadox->GetPosition(fPosX, fPosY, fPosZ); + pSummoned->SetWalk(false); + pSummoned->GetMotionMaster()->MovePoint(0, fPosX, fPosY, fPosZ); + } + } + } + + void SummonedCreatureJustDied(Creature* pSummoned) override + { + // If the Guardian is killed set the achiev criteria to false + if (pSummoned->GetEntry() == NPC_AHNKAHAR_GUARDIAN) + { + if (m_pInstance) + m_pInstance->SetData(TYPE_NADOX, SPECIAL); + } + } +}; + +CreatureAI* GetAI_mob_ahnkahar_egg(Creature* pCreature) +{ + return new mob_ahnkahar_eggAI(pCreature); +} + +/*###### +## boss_nadox +######*/ + +struct boss_nadoxAI : public ScriptedAI +{ + boss_nadoxAI(Creature* pCreature) : ScriptedAI(pCreature) + { + m_pInstance = (instance_ahnkahet*)pCreature->GetInstanceData(); + m_bIsRegularMode = pCreature->GetMap()->IsRegularDifficulty(); + Reset(); + } + + instance_ahnkahet* m_pInstance; + bool m_bIsRegularMode; + + bool m_bBerserk; + bool m_bGuardianSummoned; + uint32 m_uiBroodPlagueTimer; + uint32 m_uiBroodRageTimer; + uint32 m_uiSummonTimer; + + void Reset() override + { + m_bBerserk = false; + m_bGuardianSummoned = false; + m_uiSummonTimer = 5000; + m_uiBroodPlagueTimer = 15000; + m_uiBroodRageTimer = 20000; + } + + void Aggro(Unit* /*pWho*/) override + { + DoScriptText(SAY_AGGRO, m_creature); + + if (m_pInstance) + m_pInstance->SetData(TYPE_NADOX, IN_PROGRESS); + } + + void JustReachedHome() override + { + if (m_pInstance) + m_pInstance->SetData(TYPE_NADOX, FAIL); + } + + void KilledUnit(Unit* /*pVictim*/) override + { + switch (urand(0, 2)) + { + case 0: DoScriptText(SAY_SLAY_1, m_creature); break; + case 1: DoScriptText(SAY_SLAY_2, m_creature); break; + case 2: DoScriptText(SAY_SLAY_3, m_creature); break; + } + } + + void JustDied(Unit* /*pKiller*/) override + { + DoScriptText(SAY_DEATH, m_creature); + + if (m_pInstance) + m_pInstance->SetData(TYPE_NADOX, DONE); + } + + void UpdateAI(const uint32 uiDiff) override + { + if (!m_creature->SelectHostileTarget() || !m_creature->getVictim()) + return; + + if (!m_bGuardianSummoned && m_creature->GetHealthPercent() < 50.0f) + { + // guardian is summoned at 50% of boss HP + if (m_pInstance) + { + if (Creature* pGuardianEgg = m_creature->GetMap()->GetCreature(m_pInstance->SelectRandomGuardianEggGuid())) + pGuardianEgg->CastSpell(pGuardianEgg, SPELL_SUMMON_SWARM_GUARDIAN, false); + + m_bGuardianSummoned = true; + } + } + + if (m_uiSummonTimer < uiDiff) + { + if (roll_chance_i(50)) + DoScriptText(urand(0, 1) ? SAY_SUMMON_EGG_1 : SAY_SUMMON_EGG_2, m_creature); + + if (m_pInstance) + { + // There are 2 Swarmers summoned at a timer + if (Creature* pSwarmerEgg = m_creature->GetMap()->GetCreature(m_pInstance->SelectRandomSwarmerEggGuid())) + { + for (uint8 i = 0; i < 2; ++i) + pSwarmerEgg->CastSpell(pSwarmerEgg, SPELL_SUMMON_SWARMERS, false); + } + } + + m_uiSummonTimer = urand(5000, 10000); + } + else + m_uiSummonTimer -= uiDiff; + + if (m_uiBroodPlagueTimer < uiDiff) + { + if (Unit* pTarget = m_creature->SelectAttackingTarget(ATTACKING_TARGET_RANDOM, 0)) + DoCastSpellIfCan(pTarget, m_bIsRegularMode ? SPELL_BROOD_PLAGUE : SPELL_BROOD_PLAGUE_H); + + m_uiBroodPlagueTimer = 20000; + } + else + m_uiBroodPlagueTimer -= uiDiff; + + if (!m_bIsRegularMode) + { + if (m_uiBroodRageTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature, SPELL_BROOD_RAGE) == CAST_OK) + m_uiBroodRageTimer = 20000; + } + else + m_uiBroodRageTimer -= uiDiff; + } + + if (!m_bBerserk && m_creature->GetPositionZ() < 24.0) + { + if (DoCastSpellIfCan(m_creature, SPELL_BERSERK) == CAST_OK) + m_bBerserk = true; + } + + DoMeleeAttackIfReady(); + } +}; + +CreatureAI* GetAI_boss_nadox(Creature* pCreature) +{ + return new boss_nadoxAI(pCreature); +} + +void AddSC_boss_nadox() +{ + Script* pNewScript; + + pNewScript = new Script; + pNewScript->Name = "boss_nadox"; + pNewScript->GetAI = &GetAI_boss_nadox; + pNewScript->RegisterSelf(); + + pNewScript = new Script; + pNewScript->Name = "mob_ahnkahar_egg"; + pNewScript->GetAI = &GetAI_mob_ahnkahar_egg; + pNewScript->RegisterSelf(); +} diff --git a/src/modules/SD2/scripts/northrend/azjol-nerub/ahnkahet/boss_taldaram.cpp b/src/modules/SD2/scripts/northrend/azjol-nerub/ahnkahet/boss_taldaram.cpp new file mode 100644 index 000000000..dcef7e84e --- /dev/null +++ b/src/modules/SD2/scripts/northrend/azjol-nerub/ahnkahet/boss_taldaram.cpp @@ -0,0 +1,343 @@ +/* Copyright (C) 2006 - 2013 ScriptDev2 + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +/* ScriptData +SDName: Boss_Taldaram +SD%Complete: 90% +SDComment: Timers; +SDCategory: Ahn'kahet +EndScriptData */ + +#include "precompiled.h" +#include "ahnkahet.h" + +enum +{ + SAY_AGGRO = -1619008, + SAY_VANISH_1 = -1619009, + SAY_VANISH_2 = -1619010, + SAY_FEED_1 = -1619011, + SAY_FEED_2 = -1619012, + SAY_SLAY_1 = -1619013, + SAY_SLAY_2 = -1619014, + SAY_SLAY_3 = -1619015, + SAY_DEATH = -1619016, + + SPELL_BEAM_VISUAL = 60342, // Visual spell, used before Taltaram is lowered to the ground + SPELL_CONJURE_FLAME_SPHERE = 55931, + SPELL_FLAME_SPHERE_SUMMON_1 = 55895, // summons 30106 + SPELL_FLAME_SPHERE_SUMMON_2 = 59511, // summons 31686 + SPELL_FLAME_SPHERE_SUMMON_3 = 59512, // summons 31687 + SPELL_BLOODTHIRST = 55968, + SPELL_VANISH = 55964, + SPELL_EMBRACE_OF_THE_VAMPYR = 55959, + SPELL_EMBRACE_OF_THE_VAMPYR_H = 59513, + + // Spells used by the Flame Sphere + SPELL_FLAME_SPHERE_PERIODIC = 55926, + SPELL_FLAME_SPHERE_PERIODIC_H = 59508, + SPELL_FLAME_SPHERE_SPAWN_EFFECT = 55891, + SPELL_FLAME_SPHERE_VISUAL = 55928, + SPELL_FLAME_SPHERE_DEATH_EFFECT = 55947, +}; + +/*###### +## boss_taldaram +######*/ + +struct boss_taldaramAI : public ScriptedAI +{ + boss_taldaramAI(Creature* pCreature) : ScriptedAI(pCreature) + { + m_pInstance = (instance_ahnkahet*)pCreature->GetInstanceData(); + m_bIsRegularMode = pCreature->GetMap()->IsRegularDifficulty(); + // Don't set the visual timers if the devices are already activated (reload case) + m_uiVisualTimer = m_pInstance->GetData(TYPE_TALDARAM) == SPECIAL ? 0 : 1000; + Reset(); + } + + instance_ahnkahet* m_pInstance; + bool m_bIsRegularMode; + + bool m_bIsFirstAggro; + uint32 m_uiVisualTimer; + uint32 m_uiBloodthirstTimer; + uint32 m_uiFlameOrbTimer; + uint32 m_uiVanishTimer; + uint32 m_uiEmbraceTimer; + + GuidList m_lFlameOrbsGuidList; + + void Reset() override + { + // Timers seem to be very random... + m_uiBloodthirstTimer = urand(20000, 25000); + m_uiFlameOrbTimer = urand(15000, 20000); + m_uiVanishTimer = 0; + m_uiEmbraceTimer = 0; + m_bIsFirstAggro = false; + } + + void Aggro(Unit* /*pWho*/) override + { + // Aggro is called after the boss vanish expires. There is no need to call this multiple times + if (m_bIsFirstAggro) + return; + + DoScriptText(SAY_AGGRO, m_creature); + m_bIsFirstAggro = true; + + if (m_pInstance) + m_pInstance->SetData(TYPE_TALDARAM, IN_PROGRESS); + } + + void KilledUnit(Unit* /*pVictim*/) override + { + switch (urand(0, 2)) + { + case 0: DoScriptText(SAY_SLAY_1, m_creature); break; + case 1: DoScriptText(SAY_SLAY_2, m_creature); break; + case 2: DoScriptText(SAY_SLAY_3, m_creature); break; + } + } + + void JustDied(Unit* /*pKiller*/) override + { + DoScriptText(SAY_DEATH, m_creature); + + if (m_pInstance) + m_pInstance->SetData(TYPE_TALDARAM, DONE); + } + + void JustReachedHome() override + { + if (m_pInstance) + m_pInstance->SetData(TYPE_TALDARAM, FAIL); + } + + void EnterEvadeMode() override + { + // Don't allow him to evade during vanish + if (m_uiEmbraceTimer) + return; + + m_creature->RemoveAllAurasOnEvade(); + m_creature->DeleteThreatList(); + m_creature->CombatStop(true); + m_creature->LoadCreatureAddon(true); + + // should evade on the ground + if (m_creature->IsAlive()) + m_creature->GetMotionMaster()->MovePoint(1, aTaldaramLandingLoc[0], aTaldaramLandingLoc[1], aTaldaramLandingLoc[2]); + + m_creature->SetLootRecipient(NULL); + + Reset(); + } + + void MovementInform(uint32 uiMoveType, uint32 uiPointId) override + { + if (uiMoveType != POINT_MOTION_TYPE) + return; + + // Adjust orientation + if (uiPointId) + { + m_creature->SetLevitate(false); + m_creature->SetFacingTo(aTaldaramLandingLoc[3]); + } + } + + void JustSummoned(Creature* pSummoned) override + { + pSummoned->CastSpell(pSummoned, SPELL_FLAME_SPHERE_SPAWN_EFFECT, true); + pSummoned->CastSpell(pSummoned, SPELL_FLAME_SPHERE_VISUAL, true); + + m_lFlameOrbsGuidList.push_back(pSummoned->GetObjectGuid()); + } + + void SummonedCreatureDespawn(Creature* pSummoned) override + { + pSummoned->CastSpell(pSummoned, SPELL_FLAME_SPHERE_DEATH_EFFECT, true); + } + + // Wrapper which sends each sphere in a different direction + void DoSetSpheresInMotion() + { + float fX, fY; + uint8 uiIndex = m_bIsRegularMode ? urand(0, 2) : 0; + for (GuidList::const_iterator itr = m_lFlameOrbsGuidList.begin(); itr != m_lFlameOrbsGuidList.end(); ++itr) + { + if (Creature* pOrb = m_creature->GetMap()->GetCreature(*itr)) + { + pOrb->CastSpell(pOrb, m_bIsRegularMode ? SPELL_FLAME_SPHERE_PERIODIC : SPELL_FLAME_SPHERE_PERIODIC_H, true); + + pOrb->GetNearPoint2D(fX, fY, 70.0f, (2 * M_PI_F / 3)*uiIndex); + pOrb->GetMotionMaster()->MovePoint(0, fX, fY, pOrb->GetPositionZ()); + } + ++uiIndex; + } + } + + void UpdateAI(const uint32 uiDiff) override + { + if (m_uiVisualTimer) + { + if (m_uiVisualTimer <= uiDiff) + { + GuidList lControllersList; + if (m_pInstance) + m_pInstance->GetJedogaControllersList(lControllersList); + + for (GuidList::const_iterator itr = lControllersList.begin(); itr != lControllersList.end(); ++itr) + { + if (Creature* pTemp = m_creature->GetMap()->GetCreature(*itr)) + pTemp->CastSpell(m_creature, SPELL_BEAM_VISUAL, false); + } + m_uiVisualTimer = 0; + } + else + m_uiVisualTimer -= uiDiff; + } + + if (!m_creature->SelectHostileTarget() || !m_creature->getVictim()) + return; + + // Cast Embrace of the Vampyr after Vanish expires - note: because of the invisibility effect, the timers won't decrease during vanish + if (m_uiEmbraceTimer) + { + if (m_uiEmbraceTimer <= uiDiff) + { + if (Unit* pTarget = m_creature->SelectAttackingTarget(ATTACKING_TARGET_RANDOM, 0)) + { + if (DoCastSpellIfCan(pTarget, m_bIsRegularMode ? SPELL_EMBRACE_OF_THE_VAMPYR : SPELL_EMBRACE_OF_THE_VAMPYR_H) == CAST_OK) + { + DoScriptText(urand(0, 1) ? SAY_FEED_1 : SAY_FEED_2, m_creature); + m_uiEmbraceTimer = 0; + } + } + } + else + m_uiEmbraceTimer -= uiDiff; + + // do not use other abilities during vanish + return; + } + + if (m_uiVanishTimer) + { + if (m_uiVanishTimer <= uiDiff) + { + if (DoCastSpellIfCan(m_creature, SPELL_VANISH) == CAST_OK) + { + DoScriptText(urand(0, 1) ? SAY_VANISH_1 : SAY_VANISH_2, m_creature); + m_uiVanishTimer = 0; + m_uiEmbraceTimer = 2000; + } + } + else + m_uiVanishTimer -= uiDiff; + } + + if (m_uiBloodthirstTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature, SPELL_BLOODTHIRST) == CAST_OK) + m_uiBloodthirstTimer = urand(20000, 25000); + } + else + m_uiBloodthirstTimer -= uiDiff; + + if (m_uiFlameOrbTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature, SPELL_CONJURE_FLAME_SPHERE) == CAST_OK) + { + m_lFlameOrbsGuidList.clear(); + + // Flame speres are summoned above the boss + m_creature->CastSpell(m_creature->GetPositionX(), m_creature->GetPositionY(), m_creature->GetPositionZ() + 5.0f, SPELL_FLAME_SPHERE_SUMMON_1, true); + + // 2 more spheres on heroic + if (!m_bIsRegularMode) + { + m_creature->CastSpell(m_creature->GetPositionX(), m_creature->GetPositionY(), m_creature->GetPositionZ() + 5.0f, SPELL_FLAME_SPHERE_SUMMON_2, true); + m_creature->CastSpell(m_creature->GetPositionX(), m_creature->GetPositionY(), m_creature->GetPositionZ() + 5.0f, SPELL_FLAME_SPHERE_SUMMON_3, true); + } + + m_uiFlameOrbTimer = urand(50000, 60000); + m_uiVanishTimer = 12000; + } + } + else + m_uiFlameOrbTimer -= uiDiff; + + DoMeleeAttackIfReady(); + } +}; + +CreatureAI* GetAI_boss_taldaram(Creature* pCreature) +{ + return new boss_taldaramAI(pCreature); +} + +bool EffectDummyCreature_spell_conjure_flame_orbs(Unit* /*pCaster*/, uint32 uiSpellId, SpellEffectIndex uiEffIndex, Creature* pCreatureTarget, ObjectGuid /*originalCasterGuid*/) +{ + // always check spellid and effectindex + if (uiSpellId == SPELL_CONJURE_FLAME_SPHERE && uiEffIndex == EFFECT_INDEX_0) + { + if (boss_taldaramAI* pBossAI = dynamic_cast(pCreatureTarget->AI())) + pBossAI->DoSetSpheresInMotion(); + + // always return true when we are handling this spell and effect + return true; + } + + return false; +} + +/*###### +## go_nerubian_device +######*/ + +bool GOUse_go_nerubian_device(Player* /*pPlayer*/, GameObject* pGo) +{ + ScriptedInstance* pInstance = (ScriptedInstance*)pGo->GetInstanceData(); + + if (!pInstance) + return false; + + // Don't allow players to use the devices if encounter is already finished or in progress (reload case) + if (pInstance->GetData(TYPE_TALDARAM) == SPECIAL || pInstance->GetData(TYPE_TALDARAM) == DONE) + return false; + + pInstance->SetData(TYPE_TALDARAM, SPECIAL); + return false; +} + +void AddSC_boss_taldaram() +{ + Script* pNewScript; + + pNewScript = new Script; + pNewScript->Name = "boss_taldaram"; + pNewScript->GetAI = &GetAI_boss_taldaram; + pNewScript->pEffectDummyNPC = &EffectDummyCreature_spell_conjure_flame_orbs; + pNewScript->RegisterSelf(); + + pNewScript = new Script; + pNewScript->Name = "go_nerubian_device"; + pNewScript->pGOUse = &GOUse_go_nerubian_device; + pNewScript->RegisterSelf(); +} diff --git a/src/modules/SD2/scripts/northrend/azjol-nerub/ahnkahet/boss_volazj.cpp b/src/modules/SD2/scripts/northrend/azjol-nerub/ahnkahet/boss_volazj.cpp new file mode 100644 index 000000000..40b8348ea --- /dev/null +++ b/src/modules/SD2/scripts/northrend/azjol-nerub/ahnkahet/boss_volazj.cpp @@ -0,0 +1,297 @@ +/* Copyright (C) 2006 - 2013 ScriptDev2 + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +/* ScriptData +SDName: Boss_Volazj +SD%Complete: 50% +SDComment: Insanity NYI; Timers need adjustments +SDCategory: Ahn'kahet +EndScriptData */ + +#include "precompiled.h" +#include "ahnkahet.h" +#include "TemporarySummon.h" + +enum +{ + SAY_AGGRO = -1619033, + SAY_INSANITY = -1619034, + SAY_SLAY_1 = -1619035, + SAY_SLAY_2 = -1619036, + SAY_SLAY_3 = -1619037, + SAY_DEATH_1 = -1619038, // missing text + SAY_DEATH_2 = -1619039, + + SPELL_MIND_FLAY = 57941, + SPELL_MIND_FLAY_H = 59974, + SPELL_SHADOW_BOLT = 57942, + SPELL_SHADOW_BOLT_H = 59975, + SPELL_SHIVER = 57949, + SPELL_SHIVER_H = 59978, + + SPELL_WHISPER_AGGRO = 60291, + SPELL_WHISPER_INSANITY = 60292, + SPELL_WHISPER_SLAY_1 = 60293, + SPELL_WHISPER_SLAY_2 = 60294, + SPELL_WHISPER_SLAY_3 = 60295, + SPELL_WHISPER_DEATH_1 = 60296, + SPELL_WHISPER_DEATH_2 = 60297, + + SPELL_INSANITY = 57496, // start insanity phasing + SPELL_INSANITY_VISUAL = 57561, + + SPELL_TWISTED_VISAGE_SPAWN = 57506, + SPELL_TWISTED_VISAGE_SPAWN_H = 59982, + SPELL_TWISTED_VISAGE_EFFECT = 57507, + SPELL_TWISTED_VISAGE_PASSIVE = 57551, + + SPELL_SUMMON_VISAGE_1 = 57500, + SPELL_SUMMON_VISAGE_2 = 57501, + SPELL_SUMMON_VISAGE_3 = 57502, + SPELL_SUMMON_VISAGE_4 = 57503, + SPELL_SUMMON_VISAGE_5 = 57504, + + MAX_INSANITY_SPELLS = 5, +}; + +static const uint32 aInsanityPhaseSpells[MAX_INSANITY_SPELLS] = {SPELL_INSANITY_PHASE_16, SPELL_INSANITY_PHASE_32, SPELL_INSANITY_PHASE_64, SPELL_INSANITY_PHASE_128, SPELL_INSANITY_PHASE_256}; +static const uint32 aSpawnVisageSpells[MAX_INSANITY_SPELLS] = {SPELL_SUMMON_VISAGE_1, SPELL_SUMMON_VISAGE_2, SPELL_SUMMON_VISAGE_3, SPELL_SUMMON_VISAGE_4, SPELL_SUMMON_VISAGE_5}; + +/*###### +## boss_volazj +######*/ + +struct boss_volazjAI : public ScriptedAI +{ + boss_volazjAI(Creature* pCreature) : ScriptedAI(pCreature) + { + m_pInstance = (ScriptedInstance*)pCreature->GetInstanceData(); + m_bIsRegularMode = pCreature->GetMap()->IsRegularDifficulty(); + Reset(); + } + + ScriptedInstance* m_pInstance; + bool m_bIsRegularMode; + + uint8 m_uiCombatPhase; + uint32 m_uiMindFlayTimer; + uint32 m_uiShadowBoltTimer; + uint32 m_uiShiverTimer; + + uint8 m_uiInsanityIndex; + bool m_bIsInsanityInProgress; + + void Reset() override + { + m_uiCombatPhase = 1; + m_uiMindFlayTimer = 10000; + m_uiShadowBoltTimer = 5000; + m_uiShiverTimer = 18000; + + m_uiInsanityIndex = 0; + m_bIsInsanityInProgress = false; + + SetCombatMovement(true); + m_creature->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE); + } + + void Aggro(Unit* /*pWho*/) override + { + DoScriptText(SAY_AGGRO, m_creature); + DoCastSpellIfCan(m_creature, SPELL_WHISPER_AGGRO); + + if (m_pInstance) + { + m_pInstance->SetData(TYPE_VOLAZJ, IN_PROGRESS); + + // Start achievement only on first aggro + m_pInstance->DoStartTimedAchievement(ACHIEVEMENT_CRITERIA_TYPE_KILL_CREATURE, ACHIEV_START_VOLAZJ_ID); + } + } + + void KilledUnit(Unit* /*pVictim*/) override + { + switch (urand(0, 2)) + { + case 0: + DoScriptText(SAY_SLAY_1, m_creature); + DoCastSpellIfCan(m_creature, SPELL_WHISPER_SLAY_1); + break; + case 1: + DoScriptText(SAY_SLAY_2, m_creature); + DoCastSpellIfCan(m_creature, SPELL_WHISPER_SLAY_2); + break; + case 2: + DoScriptText(SAY_SLAY_3, m_creature); + DoCastSpellIfCan(m_creature, SPELL_WHISPER_SLAY_3); + break; + } + } + + void JustDied(Unit* /*pKiller*/) override + { + if (urand(0, 1)) + { + DoScriptText(SAY_DEATH_1, m_creature); + DoCastSpellIfCan(m_creature, SPELL_WHISPER_DEATH_1, CAST_TRIGGERED); + } + else + { + DoScriptText(SAY_DEATH_2, m_creature); + DoCastSpellIfCan(m_creature, SPELL_WHISPER_DEATH_2, CAST_TRIGGERED); + } + + if (m_pInstance) + m_pInstance->SetData(TYPE_VOLAZJ, DONE); + } + + void EnterEvadeMode() override + { + if (m_pInstance && m_pInstance->GetData(TYPE_VOLAZJ) == SPECIAL) + return; + + ScriptedAI::EnterEvadeMode(); + } + + void JustReachedHome() override + { + if (m_pInstance) + m_pInstance->SetData(TYPE_VOLAZJ, FAIL); + } + + void JustSummoned(Creature* pSummoned) override + { + pSummoned->CastSpell(pSummoned, SPELL_TWISTED_VISAGE_PASSIVE, true); + + if (pSummoned->IsTemporarySummon()) + { + TemporarySummon* pTemporary = (TemporarySummon*)pSummoned; + + if (Player* pPlayer = m_creature->GetMap()->GetPlayer(pTemporary->GetSummonerGuid())) + { + pPlayer->CastSpell(pSummoned, SPELL_TWISTED_VISAGE_EFFECT, true); + pSummoned->CastSpell(pPlayer, m_bIsRegularMode ? SPELL_TWISTED_VISAGE_SPAWN : SPELL_TWISTED_VISAGE_SPAWN_H, true); + + pSummoned->AI()->AttackStart(pPlayer); + } + } + } + + void SpellHitTarget(Unit* pTarget, const SpellEntry* pSpell) override + { + if (pSpell->Id == SPELL_INSANITY && pTarget->GetTypeId() == TYPEID_PLAYER) + { + // Apply this only for the first target hit + if (!m_creature->HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE)) + { + DoCastSpellIfCan(m_creature, SPELL_INSANITY_VISUAL, CAST_TRIGGERED); + DoCastSpellIfCan(m_creature, SPELL_WHISPER_INSANITY, CAST_TRIGGERED); + + DoScriptText(SAY_INSANITY, m_creature); + + m_creature->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE); + SetCombatMovement(false); + + if (m_pInstance) + m_pInstance->SetData(TYPE_VOLAZJ, SPECIAL); + + m_bIsInsanityInProgress = true; + } + + // Store the players in the instance, in order to better handle phasing + if (m_pInstance) + m_pInstance->SetData64(DATA_INSANITY_PLAYER, pTarget->GetObjectGuid()); + + // Phase and summon a Visage for each player + pTarget->CastSpell(pTarget, aInsanityPhaseSpells[m_uiInsanityIndex], true, 0, 0, m_creature->GetObjectGuid()); + pTarget->CastSpell(pTarget, aSpawnVisageSpells[m_uiInsanityIndex], true, 0, 0, m_creature->GetObjectGuid()); + ++m_uiInsanityIndex; + } + } + + void UpdateAI(const uint32 uiDiff) override + { + if (!m_creature->SelectHostileTarget() || !m_creature->getVictim()) + return; + + // Check for Insanity + if (m_bIsInsanityInProgress) + { + if (!m_creature->HasAura(SPELL_INSANITY_VISUAL)) + { + m_creature->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE); + SetCombatMovement(true); + m_bIsInsanityInProgress = false; + } + + // No other actions during insanity + return; + } + + if (m_creature->GetHealthPercent() < 100.0f - (float)m_uiCombatPhase * 33.4f) + { + if (DoCastSpellIfCan(m_creature, SPELL_INSANITY) == CAST_OK) + { + m_uiInsanityIndex = 0; + ++m_uiCombatPhase; + } + } + + if (m_uiMindFlayTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature->getVictim(), m_bIsRegularMode ? SPELL_MIND_FLAY : SPELL_MIND_FLAY_H) == CAST_OK) + m_uiMindFlayTimer = urand(10000, 20000); + } + else + m_uiMindFlayTimer -= uiDiff; + + if (m_uiShadowBoltTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature, m_bIsRegularMode ? SPELL_SHADOW_BOLT : SPELL_SHADOW_BOLT_H) == CAST_OK) + m_uiShadowBoltTimer = urand(8000, 13000); + } + else + m_uiShadowBoltTimer -= uiDiff; + + if (m_uiShiverTimer < uiDiff) + { + if (Unit* pTarget = m_creature->SelectAttackingTarget(ATTACKING_TARGET_RANDOM, 0)) + { + if (DoCastSpellIfCan(pTarget, m_bIsRegularMode ? SPELL_SHIVER : SPELL_SHIVER_H) == CAST_OK) + m_uiShiverTimer = 30000; + } + } + else + m_uiShiverTimer -= uiDiff; + + DoMeleeAttackIfReady(); + } +}; + +CreatureAI* GetAI_boss_volazj(Creature* pCreature) +{ + return new boss_volazjAI(pCreature); +} + +void AddSC_boss_volazj() +{ + Script* pNewScript; + + pNewScript = new Script; + pNewScript->Name = "boss_volazj"; + pNewScript->GetAI = &GetAI_boss_volazj; + pNewScript->RegisterSelf(); +} diff --git a/src/modules/SD2/scripts/northrend/azjol-nerub/ahnkahet/instance_ahnkahet.cpp b/src/modules/SD2/scripts/northrend/azjol-nerub/ahnkahet/instance_ahnkahet.cpp new file mode 100644 index 000000000..467e52b79 --- /dev/null +++ b/src/modules/SD2/scripts/northrend/azjol-nerub/ahnkahet/instance_ahnkahet.cpp @@ -0,0 +1,433 @@ +/* Copyright (C) 2006 - 2013 ScriptDev2 + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along 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_ahnkahet +SD%Complete: 75 +SDComment: +SDCategory: Ahn'kahet +EndScriptData */ + +#include "precompiled.h" +#include "ahnkahet.h" +#include "TemporarySummon.h" + +instance_ahnkahet::instance_ahnkahet(Map* pMap) : ScriptedInstance(pMap), + m_bRespectElders(false), + m_bVolunteerWork(false), + m_uiDevicesActivated(0), + m_uiInitiatesKilled(0), + m_uiTwistedVisageCount(0) +{ + Initialize(); +} + +void instance_ahnkahet::Initialize() +{ + memset(&m_auiEncounter, 0, sizeof(m_auiEncounter)); +} + +void instance_ahnkahet::OnCreatureCreate(Creature* pCreature) +{ + switch (pCreature->GetEntry()) + { + case NPC_ELDER_NADOX: + case NPC_TALDARAM: + case NPC_JEDOGA_SHADOWSEEKER: + case NPC_HERALD_VOLAZJ: + m_mNpcEntryGuidStore[pCreature->GetEntry()] = pCreature->GetObjectGuid(); + break; + case NPC_AHNKAHAR_GUARDIAN_EGG: + m_GuardianEggList.push_back(pCreature->GetObjectGuid()); + break; + case NPC_AHNKAHAR_SWARM_EGG: + m_SwarmerEggList.push_back(pCreature->GetObjectGuid()); + break; + case NPC_JEDOGA_CONTROLLER: + // Sort the controllers based on their purpose + if (pCreature->GetPositionZ() > 30.0f) + // Used for Taldaram visual + m_lJedogaControllersGuidList.push_back(pCreature->GetObjectGuid()); + else if (pCreature->GetPositionZ() > 20.0f) + // Used for Jedoga visual + m_lJedogaEventControllersGuidList.push_back(pCreature->GetObjectGuid()); + else if (pCreature->GetPositionZ() < -16.0f) + // Used for Jedoga sacrifice + m_jedogaSacrificeController = pCreature->GetObjectGuid(); + break; + case NPC_TWISTED_VISAGE_1: + case NPC_TWISTED_VISAGE_2: + case NPC_TWISTED_VISAGE_3: + case NPC_TWISTED_VISAGE_4: + case NPC_TWISTED_VISAGE_5: + ++m_uiTwistedVisageCount; + break; + } +} + +void instance_ahnkahet::OnObjectCreate(GameObject* pGo) +{ + switch (pGo->GetEntry()) + { + case GO_DOOR_TALDARAM: + if (m_auiEncounter[TYPE_TALDARAM] == DONE) + pGo->SetGoState(GO_STATE_ACTIVE); + break; + case GO_VORTEX: + if (m_auiEncounter[TYPE_TALDARAM] == SPECIAL) + pGo->SetGoState(GO_STATE_ACTIVE); + break; + + case GO_ANCIENT_DEVICE_L: + case GO_ANCIENT_DEVICE_R: + if (m_auiEncounter[TYPE_NADOX] == DONE) + pGo->RemoveFlag(GAMEOBJECT_FLAGS, GO_FLAG_NO_INTERACT); + break; + + default: + return; + } + m_mGoEntryGuidStore[pGo->GetEntry()] = pGo->GetObjectGuid(); +} + +void instance_ahnkahet::SetData(uint32 uiType, uint32 uiData) +{ + debug_log("SD2: Instance Ahn'Kahet: SetData received for type %u with data %u", uiType, uiData); + + switch (uiType) + { + case TYPE_NADOX: + m_auiEncounter[uiType] = uiData; + if (uiData == IN_PROGRESS) + m_bRespectElders = true; + else if (uiData == SPECIAL) + m_bRespectElders = false; + else if (uiData == DONE) + { + DoToggleGameObjectFlags(GO_ANCIENT_DEVICE_L, GO_FLAG_NO_INTERACT, false); + DoToggleGameObjectFlags(GO_ANCIENT_DEVICE_R, GO_FLAG_NO_INTERACT, false); + } + break; + case TYPE_TALDARAM: + if (uiData == SPECIAL) + { + ++m_uiDevicesActivated; + + if (m_uiDevicesActivated == 2) + { + m_auiEncounter[uiType] = uiData; + DoUseDoorOrButton(GO_VORTEX); + + // Lower Taldaram + if (Creature* pTaldaram = GetSingleCreatureFromStorage(NPC_TALDARAM)) + pTaldaram->GetMotionMaster()->MovePoint(1, aTaldaramLandingLoc[0], aTaldaramLandingLoc[1], aTaldaramLandingLoc[2]); + + // Interrupt the channeling + for (GuidList::const_iterator itr = m_lJedogaControllersGuidList.begin(); itr != m_lJedogaControllersGuidList.end(); ++itr) + { + if (Creature* pTemp = instance->GetCreature(*itr)) + pTemp->InterruptNonMeleeSpells(false); + } + } + } + else if (uiData == DONE) + { + m_auiEncounter[uiType] = uiData; + DoUseDoorOrButton(GO_DOOR_TALDARAM); + } + break; + case TYPE_JEDOGA: + m_auiEncounter[uiType] = uiData; + if (uiData == IN_PROGRESS) + m_bVolunteerWork = true; + else if (uiData == SPECIAL) + m_bVolunteerWork = false; + else if (uiData == FAIL) + m_uiInitiatesKilled = 0; + break; + case TYPE_AMANITAR: + m_auiEncounter[uiType] = uiData; + break; + case TYPE_VOLAZJ: + m_auiEncounter[uiType] = uiData; + if (uiData == IN_PROGRESS) + { + m_uiTwistedVisageCount = 0; + m_lInsanityPlayersGuidList.clear(); + } + break; + + default: + script_error_log("Instance Ahn'Kahet: ERROR SetData = %u for type %u does not exist/not implemented.", uiType, uiData); + break; + } + + // For some encounters Special data needs to be saved + if (uiData == DONE || (uiData == SPECIAL && uiType == TYPE_TALDARAM)) + { + OUT_SAVE_INST_DATA; + + std::ostringstream saveStream; + saveStream << m_auiEncounter[0] << " " << m_auiEncounter[1] << " " << m_auiEncounter[2] << " " << m_auiEncounter[3] + << " " << m_auiEncounter[4]; + + m_strInstData = saveStream.str(); + + SaveToDB(); + OUT_SAVE_INST_DATA_COMPLETE; + } +} + +void instance_ahnkahet::OnCreatureDeath(Creature* pCreature) +{ + switch (pCreature->GetEntry()) + { + case NPC_TWILIGHT_INITIATE: + ++m_uiInitiatesKilled; + + // If all initiates are killed, then land Jedoga and stop the channeling + if (m_uiInitiatesKilled == MAX_INITIATES) + { + if (Creature* pJedoga = GetSingleCreatureFromStorage(NPC_JEDOGA_SHADOWSEEKER)) + pJedoga->GetMotionMaster()->MovePoint(1, aJedogaLandingLoc[0], aJedogaLandingLoc[1], aJedogaLandingLoc[2]); + + for (GuidList::const_iterator itr = m_lJedogaEventControllersGuidList.begin(); itr != m_lJedogaEventControllersGuidList.end(); ++itr) + { + if (Creature* pTemp = instance->GetCreature(*itr)) + pTemp->InterruptNonMeleeSpells(false); + } + } + + break; + case NPC_TWISTED_VISAGE_1: + case NPC_TWISTED_VISAGE_2: + case NPC_TWISTED_VISAGE_3: + case NPC_TWISTED_VISAGE_4: + case NPC_TWISTED_VISAGE_5: + pCreature->CastSpell(pCreature, SPELL_TWISTED_VISAGE_DEATH, true); + + --m_uiTwistedVisageCount; + + // When all Twisted Visages were killed or despawned switch back to combat phase + if (!m_uiTwistedVisageCount) + { + // Clear Insanity + if (Creature* pVolazj = GetSingleCreatureFromStorage(NPC_HERALD_VOLAZJ)) + { + pVolazj->CastSpell(pVolazj, SPELL_INSANITY_CLEAR, true); + pVolazj->RemoveAllAuras(); + } + + // Clear insanity manually for now, because the spell won't hit phased players + HandleInsanityClear(); + + SetData(TYPE_VOLAZJ, IN_PROGRESS); + } + else + { + // Switch Insanity + if (Creature* pVolazj = GetSingleCreatureFromStorage(NPC_HERALD_VOLAZJ)) + pVolazj->CastSpell(pVolazj, SPELL_INSANITY_SWITCH, true); + + // Handle insanity switch manually, because the boss can't hit phased players + if (pCreature->IsTemporarySummon()) + { + TemporarySummon* pTemporary = (TemporarySummon*)pCreature; + + // Switch insanity phase for the master player + if (Player* pPlayer = instance->GetPlayer(pTemporary->GetSummonerGuid())) + HandleInsanitySwitch(pPlayer); + } + } + break; + } +} + +void instance_ahnkahet::OnCreatureEvade(Creature* pCreature) +{ + switch (pCreature->GetEntry()) + { + case NPC_TWISTED_VISAGE_1: + case NPC_TWISTED_VISAGE_2: + case NPC_TWISTED_VISAGE_3: + case NPC_TWISTED_VISAGE_4: + case NPC_TWISTED_VISAGE_5: + --m_uiTwistedVisageCount; + + // When all Twisted Visages were killed or despawned switch back to combat phase + if (!m_uiTwistedVisageCount) + { + // Clear Insanity + if (Creature* pVolazj = GetSingleCreatureFromStorage(NPC_HERALD_VOLAZJ)) + { + pVolazj->CastSpell(pVolazj, SPELL_INSANITY_CLEAR, true); + pVolazj->RemoveAllAuras(); + } + + // Clear insanity manually for now, because the spell won't hit phased players + HandleInsanityClear(); + + SetData(TYPE_VOLAZJ, IN_PROGRESS); + } + + pCreature->ForcedDespawn(); + break; + } +} + +void instance_ahnkahet::SetData64(uint32 uiData, uint64 uiGuid) +{ + // Store all the players hit by the insanity spell in order to use them for the phasing switch / clear + if (uiData == DATA_INSANITY_PLAYER) + { + if (Player* pPlayer = instance->GetPlayer(ObjectGuid(uiGuid))) + m_lInsanityPlayersGuidList.push_back(pPlayer->GetObjectGuid()); + } +} + +ObjectGuid instance_ahnkahet::SelectRandomGuardianEggGuid() +{ + if (m_GuardianEggList.empty()) + return ObjectGuid(); + + GuidList::iterator iter = m_GuardianEggList.begin(); + advance(iter, urand(0, m_GuardianEggList.size() - 1)); + + return *iter; +} + +ObjectGuid instance_ahnkahet::SelectRandomSwarmerEggGuid() +{ + if (m_SwarmerEggList.empty()) + return ObjectGuid(); + + GuidList::iterator iter = m_SwarmerEggList.begin(); + advance(iter, urand(0, m_SwarmerEggList.size() - 1)); + + return *iter; +} + +void instance_ahnkahet::HandleInsanityClear() +{ + for (GuidList::const_iterator itr = m_lInsanityPlayersGuidList.begin(); itr != m_lInsanityPlayersGuidList.end(); ++itr) + { + if (Player* pPlayer = instance->GetPlayer(*itr)) + pPlayer->RemoveSpellsCausingAura(SPELL_AURA_PHASE); + } +} + +void instance_ahnkahet::HandleInsanitySwitch(Player* pPhasedPlayer) +{ + // Get the phase aura id + std::list lAuraList = pPhasedPlayer->GetAurasByType(SPELL_AURA_PHASE); + if (lAuraList.empty()) + return; + + uint32 uiPhaseAura = (*lAuraList.begin())->GetId(); + + std::list lSamePhasePlayers; + std::vector vOtherPhasePlayers; + + // Sort the insanity players, into those which have same phase and others + for (GuidList::const_iterator itr = m_lInsanityPlayersGuidList.begin(); itr != m_lInsanityPlayersGuidList.end(); ++itr) + { + if (Player* pTemp = instance->GetPlayer(*itr)) + { + if (pTemp->HasAura(uiPhaseAura)) + lSamePhasePlayers.push_back(pTemp); + // Check only for alive players + else if (pTemp->IsAlive()) + vOtherPhasePlayers.push_back(pTemp); + } + } + + // This shouldn't happen + if (vOtherPhasePlayers.empty()) + return; + + // Get the phase aura of the new selected player + Player* pNewPlayer = vOtherPhasePlayers[urand(0, vOtherPhasePlayers.size() - 1)]; + + // Get the phase aura id + std::list lNewAuraList = pNewPlayer->GetAurasByType(SPELL_AURA_PHASE); + if (lNewAuraList.empty()) + return; + + uint32 uiNewPhaseAura = (*lNewAuraList.begin())->GetId(); + + // Move the same phase players to the new phase + for (std::list::const_iterator itr = lSamePhasePlayers.begin(); itr != lSamePhasePlayers.end(); ++itr) + (*itr)->CastSpell((*itr), uiNewPhaseAura, true); +} + +bool instance_ahnkahet::CheckAchievementCriteriaMeet(uint32 uiCriteriaId, Player const* /*pSource*/, Unit const* /*pTarget*/, uint32 /*uiMiscValue1 = 0*/) const +{ + switch (uiCriteriaId) + { + case ACHIEV_CRIT_RESPECT_ELDERS: + return m_bRespectElders; + case ACHIEV_CRIT_VOLUNTEER_WORK: + return m_bVolunteerWork; + + default: + return false; + } +} + +void instance_ahnkahet::Load(const char* chrIn) +{ + if (!chrIn) + { + 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]; + + for (uint8 i = 0; i < MAX_ENCOUNTER; ++i) + { + if (m_auiEncounter[i] == IN_PROGRESS) + m_auiEncounter[i] = NOT_STARTED; + } + + OUT_LOAD_INST_DATA_COMPLETE; +} + +uint32 instance_ahnkahet::GetData(uint32 uiType) const +{ + if (uiType < MAX_ENCOUNTER) + return m_auiEncounter[uiType]; + + return 0; +} + +InstanceData* GetInstanceData_instance_ahnkahet(Map* pMap) +{ + return new instance_ahnkahet(pMap); +} + +void AddSC_instance_ahnkahet() +{ + Script* pNewScript; + + pNewScript = new Script; + pNewScript->Name = "instance_ahnkahet"; + pNewScript->GetInstanceData = &GetInstanceData_instance_ahnkahet; + pNewScript->RegisterSelf(); +} diff --git a/src/modules/SD2/scripts/northrend/azjol-nerub/azjol-nerub/azjol-nerub.h b/src/modules/SD2/scripts/northrend/azjol-nerub/azjol-nerub/azjol-nerub.h new file mode 100644 index 000000000..e38520920 --- /dev/null +++ b/src/modules/SD2/scripts/northrend/azjol-nerub/azjol-nerub/azjol-nerub.h @@ -0,0 +1,111 @@ +/* Copyright (C) 2006 - 2013 ScriptDev2 + * This program is free software licensed under GPL version 2 + * Please see the included DOCS/LICENSE.TXT for more information */ + +#ifndef DEF_AZJOL_NERUB_H +#define DEF_AZJOL_NERUB_H + +enum +{ + MAX_ENCOUNTER = 3, + + TYPE_KRIKTHIR = 0, + TYPE_HADRONOX = 1, + TYPE_ANUBARAK = 2, + + NPC_KRIKTHIR = 28684, + NPC_HADRONOX = 28921, + NPC_ANUBARAK = 29120, + + SAY_SEND_GROUP_1 = -1601004, + SAY_SEND_GROUP_2 = -1601005, + SAY_SEND_GROUP_3 = -1601006, + + NPC_GASHRA = 28730, + NPC_NARJIL = 28729, + NPC_SILTHIK = 28731, + NPC_ANUBAR_CRUSHER = 28922, + + NPC_WORLD_TRIGGER = 22515, + NPC_WORLD_TRIGGER_LARGE = 23472, + + GO_DOOR_KRIKTHIR = 192395, + GO_DOOR_ANUBARAK_1 = 192396, + GO_DOOR_ANUBARAK_2 = 192397, + GO_DOOR_ANUBARAK_3 = 192398, + + SAY_CRUSHER_AGGRO = -1601025, + SAY_CRUSHER_SPECIAL = -1601026, + + ACHIEV_START_ANUB_ID = 20381, + + ACHIEV_CRITERIA_WATCH_DIE = 4240, // Krikthir, achiev 1296 + ACHIEV_CRITERIA_DENIED = 4244, // Hadronox, achiev 1297 +}; + +static const uint32 aWatchers[] = {NPC_GASHRA, NPC_NARJIL, NPC_SILTHIK}; + +// Used to sort the summont triggers +static const int aSortDistance[4] = { -90, 10, 20, 30}; + +class instance_azjol_nerub : public ScriptedInstance +{ + public: + instance_azjol_nerub(Map* pMap); + + void Initialize() override; + + void OnObjectCreate(GameObject* pGo) override; + void OnCreatureCreate(Creature* pCreature) override; + + void OnCreatureEnterCombat(Creature* pCreature) override; + void OnCreatureEvade(Creature* pCreature); + void OnCreatureDeath(Creature* pCreature) override; + + void SetData(uint32 uiType, uint32 uiData) override; + uint32 GetData(uint32 uiType) const override; + + bool CheckAchievementCriteriaMeet(uint32 uiCriteriaId, Player const* pSource, Unit const* pTarget, uint32 uiMiscValue1 /* = 0*/) const override; + + ObjectGuid GetRandomAssassinTrigger(); + ObjectGuid GetGuardianTrigger() { return m_guardianSummonTarget; } + ObjectGuid GetDarterTrigger() { return m_darterSummonTarget; } + ObjectGuid GetAnubTrigger() { return m_anubSummonTarget; } + + void GetHadronoxTriggerList(GuidList& lList) { lList = m_lSpiderTriggersGuids; } + void ResetHadronoxTriggers(); + + void SetHadronoxDeniedAchievCriteria(bool bIsMet) { m_bHadronoxDenied = bIsMet; } + + const char* Save() const override { return m_strInstData.c_str(); } + void Load(const char* chrIn) override; + + void Update(uint32 uiDiff) override; + + private: + void DoSendWatcherOrKrikthir(); + void DoSortWorldTriggers(); + + uint32 m_auiEncounter[MAX_ENCOUNTER]; + std::string m_strInstData; + + ObjectGuid m_playerGuid; + + // Hadronox triggers + GuidList m_lSpiderTriggersGuids; + + // Anub triggers + ObjectGuid m_darterSummonTarget; + ObjectGuid m_guardianSummonTarget; + ObjectGuid m_anubSummonTarget; + GuidVector m_vAssassinSummonTargetsVect; + GuidList m_lTriggerGuids; + + uint32 m_uiWatcherTimer; + uint32 m_uiGauntletEndTimer; + + bool m_bWatchHimDie; + bool m_bHadronoxDenied; + bool m_bGauntletStarted; +}; +#endif diff --git a/src/modules/SD2/scripts/northrend/azjol-nerub/azjol-nerub/boss_anubarak.cpp b/src/modules/SD2/scripts/northrend/azjol-nerub/azjol-nerub/boss_anubarak.cpp new file mode 100644 index 000000000..ae00bd834 --- /dev/null +++ b/src/modules/SD2/scripts/northrend/azjol-nerub/azjol-nerub/boss_anubarak.cpp @@ -0,0 +1,392 @@ +/* Copyright (C) 2006 - 2013 ScriptDev2 + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with 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 +SD%Complete: 80% +SDComment: Summoned creatures movement may need some adjustments - may be solved with movement maps +SDCategory: Azjol'Nerub +EndScriptData */ + +#include "precompiled.h" +#include "azjol-nerub.h" + +enum +{ + SAY_INTRO = -1601014, + SAY_AGGRO = -1601015, + SAY_KILL_1 = -1601016, + SAY_KILL_2 = -1601017, + SAY_KILL_3 = -1601018, + SAY_SUBMERGE_1 = -1601019, + SAY_SUBMERGE_2 = -1601020, + SAY_LOCUST_1 = -1601021, + SAY_LOCUST_2 = -1601022, + SAY_LOCUST_3 = -1601023, + SAY_DEATH = -1601024, + + SPELL_CARRION_BEETLES = 53520, + SPELL_LEECHING_SWARM = 53467, + SPELL_LEECHING_SWARM_H = 59430, + SPELL_IMPALE_AURA = 53456, // ticks at each 10 secs - summons 29184 + SPELL_POUND = 53472, + SPELL_POUND_H = 59433, + SPELL_SUBMERGE = 53421, + SPELL_EMERGE = 53500, + + // NOTES: + // The Assassin and Guardian summon spell should be 53609 and 53613 + // They are currently not used because of the ignore LoS issue in core + SPELL_SUMMON_ASSASSIN = 53610, // summons 29214 + SPELL_SUMMON_GUARDIAN = 53614, // summons 29216 + SPELL_SUMMON_VENOMANCER = 53615, // summons 29217 + SPELL_SUMMON_DARTER = 53599, // summons 29213 + + // impale spells + SPELL_IMPALE_VISUAL = 53455, + SPELL_IMPALE = 53454, + SPELL_IMPALE_H = 59446, + + NPC_ANUBAR_DARTER = 29213, + NPC_ANUBAR_ASSASSIN = 29214, + NPC_ANUBAR_GUARDIAN = 29216, + NPC_ANUBAR_VENOMANCER = 29217, + NPC_IMPALE_TARGET = 29184, + + PHASE_GROUND = 1, + PHASE_SUBMERGED = 2 +}; + + +/*###### +## boss_anubarak +######*/ + +struct boss_anubarakAI : public ScriptedAI +{ + boss_anubarakAI(Creature* pCreature) : ScriptedAI(pCreature) + { + m_pInstance = (instance_azjol_nerub*)pCreature->GetInstanceData(); + m_bIsRegularMode = pCreature->GetMap()->IsRegularDifficulty(); + m_bDoneIntro = false; + Reset(); + } + + instance_azjol_nerub* m_pInstance; + bool m_bIsRegularMode; + + uint8 m_uiPhase; + uint8 m_uiSubmergePhase; + + uint32 m_uiCarrionBeetlesTimer; + uint32 m_uiLeechingSwarmTimer; + uint32 m_uiPoundTimer; + uint32 m_uiEmergeTimer; + uint32 m_uiSummonTimer; + uint32 m_uiDarterTimer; + bool m_bIsFirstWave; + bool m_bDoneIntro; + + void Reset() override + { + m_uiPhase = PHASE_GROUND; + m_uiSubmergePhase = 1; + + m_uiCarrionBeetlesTimer = 8000; + m_uiLeechingSwarmTimer = 20000; + m_uiPoundTimer = 15000; + m_uiDarterTimer = 5000; + + m_creature->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE | UNIT_FLAG_NOT_SELECTABLE); + } + + void Aggro(Unit* /*pWho*/) override + { + DoScriptText(SAY_AGGRO, m_creature); + + if (m_pInstance) + m_pInstance->SetData(TYPE_ANUBARAK, IN_PROGRESS); + } + + void KilledUnit(Unit* /*pVictim*/) override + { + switch (urand(0, 2)) + { + case 0: DoScriptText(SAY_KILL_1, m_creature); break; + case 1: DoScriptText(SAY_KILL_2, m_creature); break; + case 2: DoScriptText(SAY_KILL_3, m_creature); break; + } + } + + void JustDied(Unit* /*pKiller*/) override + { + DoScriptText(SAY_DEATH, m_creature); + + if (m_pInstance) + m_pInstance->SetData(TYPE_ANUBARAK, DONE); + } + + void JustReachedHome() override + { + if (m_pInstance) + m_pInstance->SetData(TYPE_ANUBARAK, NOT_STARTED); + } + + void MoveInLineOfSight(Unit* pWho) override + { + if (!m_bDoneIntro && m_creature->IsWithinDistInMap(pWho, 60.0f)) + { + DoScriptText(SAY_INTRO, m_creature); + m_bDoneIntro = true; + } + + ScriptedAI::MoveInLineOfSight(pWho); + } + + void JustSummoned(Creature* pSummoned) override + { + if (!m_pInstance) + return; + + switch (pSummoned->GetEntry()) + { + case NPC_ANUBAR_GUARDIAN: + case NPC_ANUBAR_VENOMANCER: + pSummoned->SetWalk(false); + if (Creature* pTrigger = m_creature->GetMap()->GetCreature(m_pInstance->GetAnubTrigger())) + pSummoned->GetMotionMaster()->MovePoint(0, pTrigger->GetPositionX(), pTrigger->GetPositionY(), pTrigger->GetPositionZ()); + break; + case NPC_ANUBAR_DARTER: + case NPC_ANUBAR_ASSASSIN: + if (Creature* pTrigger = m_creature->GetMap()->GetCreature(m_pInstance->GetAnubTrigger())) + { + float fX, fY, fZ; + m_creature->GetRandomPoint(pTrigger->GetPositionX(), pTrigger->GetPositionY(), pTrigger->GetPositionZ(), 15.0f, fX, fY, fZ); + + pSummoned->SetWalk(false); + pSummoned->GetMotionMaster()->MovePoint(0, fX, fY, fZ); + } + break; + case NPC_IMPALE_TARGET: + pSummoned->CastSpell(pSummoned, SPELL_IMPALE_VISUAL, true); + break; + default: + break; + } + } + + void UpdateAI(const uint32 uiDiff) override + { + if (!m_creature->SelectHostileTarget() || !m_creature->getVictim()) + return; + + if (m_uiPhase == PHASE_GROUND) + { + if (m_uiLeechingSwarmTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature, m_bIsRegularMode ? SPELL_LEECHING_SWARM : SPELL_LEECHING_SWARM_H) == CAST_OK) + { + switch (urand(0, 2)) + { + case 0: DoScriptText(SAY_LOCUST_1, m_creature); break; + case 1: DoScriptText(SAY_LOCUST_2, m_creature); break; + case 2: DoScriptText(SAY_LOCUST_3, m_creature); break; + } + + m_uiLeechingSwarmTimer = 19000; + } + } + else + m_uiLeechingSwarmTimer -= uiDiff; + + if (m_uiCarrionBeetlesTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature, SPELL_CARRION_BEETLES) == CAST_OK) + m_uiCarrionBeetlesTimer = 25000; + } + else + m_uiCarrionBeetlesTimer -= uiDiff; + + if (m_uiPoundTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature, m_bIsRegularMode ? SPELL_POUND : SPELL_POUND_H) == CAST_OK) + m_uiPoundTimer = 16000; + } + else + m_uiPoundTimer -= uiDiff; + + if (m_creature->GetHealthPercent() < 100 - 25 * m_uiSubmergePhase) + { + DoCastSpellIfCan(m_creature, SPELL_IMPALE_AURA, CAST_TRIGGERED); + DoCastSpellIfCan(m_creature, SPELL_SUBMERGE, CAST_TRIGGERED); + DoScriptText(urand(0, 1) ? SAY_SUBMERGE_1 : SAY_SUBMERGE_2, m_creature); + m_creature->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE | UNIT_FLAG_NOT_SELECTABLE); + m_uiPhase = PHASE_SUBMERGED; + m_bIsFirstWave = true; + m_uiSummonTimer = 5000; + + // Emerge timers aren't the same. They depend on the submerge phase + switch (m_uiSubmergePhase) + { + case 1: + m_uiEmergeTimer = 20000; + break; + case 2: + m_uiEmergeTimer = 45000; + break; + case 3: + m_uiEmergeTimer = 50000; + break; + } + ++m_uiSubmergePhase; + } + + DoMeleeAttackIfReady(); + } + else if (m_uiPhase == PHASE_SUBMERGED) + { + if (m_uiSummonTimer < uiDiff) + { + if (!m_pInstance) + return; + + // Summon 2 Assassins + for (uint8 i = 0; i < 2; ++i) + { + if (Creature* pTrigger = m_creature->GetMap()->GetCreature(m_pInstance->GetRandomAssassinTrigger())) + pTrigger->CastSpell(pTrigger, SPELL_SUMMON_ASSASSIN, true, NULL, NULL, m_creature->GetObjectGuid()); + } + + // on the first wave summon a guardian; on the second wave summon a venonmancer + if (Creature* pTrigger = m_creature->GetMap()->GetCreature(m_pInstance->GetGuardianTrigger())) + { + pTrigger->CastSpell(pTrigger, m_bIsFirstWave ? SPELL_SUMMON_GUARDIAN : SPELL_SUMMON_VENOMANCER, true, NULL, NULL, m_creature->GetObjectGuid()); + m_bIsFirstWave = false; + } + + m_uiSummonTimer = 26000; + } + else + m_uiSummonTimer -= uiDiff; + + // only on the last submerge phase + if (m_uiSubmergePhase == 4) + { + if (m_uiDarterTimer < uiDiff) + { + if (!m_pInstance) + return; + + if (Creature* pTrigger = m_creature->GetMap()->GetCreature(m_pInstance->GetDarterTrigger())) + { + pTrigger->CastSpell(pTrigger, SPELL_SUMMON_DARTER, true, NULL, NULL, m_creature->GetObjectGuid()); + m_uiDarterTimer = urand(10000, 15000); + } + } + else + m_uiDarterTimer -= uiDiff; + } + + if (m_uiEmergeTimer < uiDiff) + { + DoCastSpellIfCan(m_creature, SPELL_EMERGE, CAST_INTERRUPT_PREVIOUS); + m_creature->RemoveAurasDueToSpell(SPELL_SUBMERGE); + m_creature->RemoveAurasDueToSpell(SPELL_IMPALE_AURA); + m_creature->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE | UNIT_FLAG_NOT_SELECTABLE); + m_uiPhase = PHASE_GROUND; + } + else + m_uiEmergeTimer -= uiDiff; + } + + EnterEvadeIfOutOfCombatArea(uiDiff); + } +}; + +CreatureAI* GetAI_boss_anubarak(Creature* pCreature) +{ + return new boss_anubarakAI(pCreature); +} + +/*###### +## npc_impale_target +######*/ + +struct npc_impale_targetAI : public Scripted_NoMovementAI +{ + npc_impale_targetAI(Creature* pCreature) : Scripted_NoMovementAI(pCreature) + { + m_pInstance = (ScriptedInstance*)pCreature->GetInstanceData(); + m_bIsRegularMode = pCreature->GetMap()->IsRegularDifficulty(); + Reset(); + } + + ScriptedInstance* m_pInstance; + bool m_bIsRegularMode; + + uint32 m_uiImpaleTimer; + + void Reset() override + { + m_uiImpaleTimer = 3000; + } + + void AttackStart(Unit* /*pWho*/) override { } + void MoveInLineOfSight(Unit* /*pWho*/) override { } + + void UpdateAI(const uint32 uiDiff) override + { + if (m_uiImpaleTimer) + { + if (m_uiImpaleTimer <= uiDiff) + { + if (!m_pInstance) + return; + + m_creature->RemoveAurasDueToSpell(SPELL_IMPALE_VISUAL); + + // The impale is cast by Anub on the impale target + if (Creature* pAnub = m_pInstance->GetSingleCreatureFromStorage(NPC_ANUBARAK)) + pAnub->CastSpell(m_creature, m_bIsRegularMode ? SPELL_IMPALE : SPELL_IMPALE_H, true); + + m_creature->ForcedDespawn(3000); + m_uiImpaleTimer = 0; + } + else + m_uiImpaleTimer -= uiDiff; + } + } +}; + +CreatureAI* GetAI_npc_impale_target(Creature* pCreature) +{ + return new npc_impale_targetAI(pCreature); +} + +void AddSC_boss_anubarak() +{ + Script* pNewScript; + + pNewScript = new Script; + pNewScript->Name = "boss_anubarak"; + pNewScript->GetAI = &GetAI_boss_anubarak; + pNewScript->RegisterSelf(); + + pNewScript = new Script; + pNewScript->Name = "npc_impale_target"; + pNewScript->GetAI = &GetAI_npc_impale_target; + pNewScript->RegisterSelf(); +} diff --git a/src/modules/SD2/scripts/northrend/azjol-nerub/azjol-nerub/boss_hadronox.cpp b/src/modules/SD2/scripts/northrend/azjol-nerub/azjol-nerub/boss_hadronox.cpp new file mode 100644 index 000000000..d08bff204 --- /dev/null +++ b/src/modules/SD2/scripts/northrend/azjol-nerub/azjol-nerub/boss_hadronox.cpp @@ -0,0 +1,299 @@ +/* Copyright (C) 2006 - 2013 ScriptDev2 + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with 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: 90% +SDComment: Some details and timers can be improved. +SDCategory: Azjol'Nerub +EndScriptData */ + +#include "precompiled.h" +#include "azjol-nerub.h" + +enum +{ + EMOTE_MOVE_TUNNEL = -1601013, + + SPELL_TAUNT = 53799, + SPELL_PIERCE_ARMOR = 53418, + SPELL_ACID_CLOUD = 53400, + SPELL_ACID_CLOUD_H = 59419, + SPELL_LEECH_POISON = 53030, + SPELL_LEECH_POISON_H = 59417, + SPELL_WEB_GRAB = 57731, + SPELL_WEB_GRAB_H = 59421, + + // Gauntlet spells + SPELL_SUMMON_CHAMPION = 53035, + SPELL_SUMMON_NECROMANCER = 53036, + SPELL_SUMMON_CRYPT_FIEND = 53037, + SPELL_WEB_FRONT_DOORS = 53177, // sends event 19101 + SPELL_WEB_SIDE_DOORS = 53185, // sends event 19102 - it seems that this isn't actually used here + + MAX_SPIDERS = 9, +}; + +static const uint32 aSpiderEntries[MAX_SPIDERS] = {28924, 28925, 29051, 29062, 29063, 29064, 29096, 29097, 29098}; + +/* ##### Gauntlet description ##### + * This is the timed gauntlet - waves of non-elite spiders will spawn from the 3 doors located a little above the main room + * They will make their way down to fight Hadronox but she will head to the main room, fighting the spiders + * When Hadronox enters the main room, she will web the doors, and no more spiders will spawn. + */ + +/*###### +## boss_hadronox +######*/ + +struct boss_hadronoxAI : public ScriptedAI +{ + boss_hadronoxAI(Creature* pCreature) : ScriptedAI(pCreature) + { + m_pInstance = (instance_azjol_nerub*)pCreature->GetInstanceData(); + m_bIsRegularMode = pCreature->GetMap()->IsRegularDifficulty(); + m_uiGauntletStartTimer = 1000; + Reset(); + } + + instance_azjol_nerub* m_pInstance; + bool m_bIsRegularMode; + + uint32 m_uiGauntletStartTimer; + + uint32 m_uiAcidTimer; + uint32 m_uiLeechTimer; + uint32 m_uiPierceTimer; + uint32 m_uiGrabTimer; + uint32 m_uiTauntTimer; + + void Reset() override + { + m_uiAcidTimer = urand(10000, 14000); + m_uiLeechTimer = urand(3000, 9000); + m_uiPierceTimer = urand(1000, 3000); + m_uiGrabTimer = urand(15000, 19000); + m_uiTauntTimer = urand(2000, 5000); + } + + void Aggro(Unit* pWho) override + { + if (pWho->GetTypeId() == TYPEID_PLAYER && m_pInstance) + m_pInstance->SetData(TYPE_HADRONOX, IN_PROGRESS); + } + + void AttackStart(Unit* pWho) override + { + // No more attacks during the movement upstairs + if ((m_pInstance && m_pInstance->GetData(TYPE_HADRONOX) == SPECIAL) && pWho->GetTypeId() != TYPEID_PLAYER) + return; + + ScriptedAI::AttackStart(pWho); + } + + void MoveInLineOfSight(Unit* pWho) override + { + // Force the spiders to attack him + if (pWho->GetTypeId() == TYPEID_UNIT && m_creature->IsWithinDistInMap(pWho, 2 * ATTACK_DISTANCE) && !pWho->getVictim()) + { + for (uint8 i = 0; i < MAX_SPIDERS; ++i) + { + if (pWho->GetEntry() == aSpiderEntries[i]) + ((Creature*)pWho)->AI()->AttackStart(m_creature); + } + } + + // No more attacks during the movement upstairs + if ((m_pInstance && m_pInstance->GetData(TYPE_HADRONOX) == SPECIAL) && pWho->GetTypeId() != TYPEID_PLAYER) + return; + + ScriptedAI::MoveInLineOfSight(pWho); + } + + void KilledUnit(Unit* /*pVictim*/) override + { + m_creature->SetHealth(m_creature->GetHealth() + (m_creature->GetMaxHealth() * 0.1)); + } + + void JustDied(Unit* /*pKiller*/) override + { + if (m_pInstance) + m_pInstance->SetData(TYPE_HADRONOX, DONE); + } + + void EnterEvadeMode() override + { + m_creature->RemoveAllAurasOnEvade(); + m_creature->DeleteThreatList(); + m_creature->CombatStop(true); + m_creature->LoadCreatureAddon(true); + + m_creature->SetLootRecipient(NULL); + + Reset(); + + if (!m_creature->IsAlive() || !m_pInstance) + return; + + // Moving upstairs, don't disturb + if (m_pInstance->GetData(TYPE_HADRONOX) == SPECIAL) + { + m_creature->GetMotionMaster()->MoveWaypoint(); + DoScriptText(EMOTE_MOVE_TUNNEL, m_creature); + } + // Stay upstairs if evade from players + else if (m_pInstance->GetData(TYPE_HADRONOX) == IN_PROGRESS) + m_creature->GetMotionMaster()->MovePoint(1, 530.42f, 560.003f, 733.0308f); + else + m_creature->GetMotionMaster()->MoveTargetedHome(); + } + + void MovementInform(uint32 uiMoveType, uint32 uiPointId) override + { + // Mark as failed if evaded while upstairs + if (uiMoveType == POINT_MOTION_TYPE && uiPointId) + { + if (m_pInstance) + m_pInstance->SetData(TYPE_HADRONOX, FAIL); + } + // Web the doors when upstairs + else if (uiMoveType == WAYPOINT_MOTION_TYPE && uiPointId == 10) + { + if (DoCastSpellIfCan(m_creature, SPELL_WEB_FRONT_DOORS, CAST_TRIGGERED) == CAST_OK) + { + // These should be handled by the scripted event + if (m_pInstance) + { + m_pInstance->SetData(TYPE_HADRONOX, IN_PROGRESS); + m_pInstance->ResetHadronoxTriggers(); + m_pInstance->SetHadronoxDeniedAchievCriteria(false); + } + + // No more movement + m_creature->GetMotionMaster()->MoveIdle(); + } + } + } + + void JustSummoned(Creature* pSummoned) override + { + // Allow the spawns to make a few steps so we can use move maps + pSummoned->SetWalk(false); + pSummoned->GetMotionMaster()->MoveWaypoint(); + } + + void UpdateAI(const uint32 uiDiff) override + { + if (m_uiGauntletStartTimer) + { + if (m_uiGauntletStartTimer <= uiDiff) + { + if (!m_pInstance) + { + script_error_log("Instance Azjol-Nerub: ERROR Failed to load instance data for this instace."); + return; + } + + GuidList m_lTriggersGuids; + m_pInstance->GetHadronoxTriggerList(m_lTriggersGuids); + + // Need to force the triggers to cast this with Hadronox Guid so we can control the summons better + for (GuidList::const_iterator itr = m_lTriggersGuids.begin(); itr != m_lTriggersGuids.end(); ++itr) + { + if (Creature* pTrigger = m_creature->GetMap()->GetCreature(*itr)) + { + pTrigger->CastSpell(pTrigger, SPELL_SUMMON_CHAMPION, true, NULL, NULL, m_creature->GetObjectGuid()); + pTrigger->CastSpell(pTrigger, SPELL_SUMMON_NECROMANCER, true, NULL, NULL, m_creature->GetObjectGuid()); + pTrigger->CastSpell(pTrigger, SPELL_SUMMON_CRYPT_FIEND, true, NULL, NULL, m_creature->GetObjectGuid()); + } + } + + m_uiGauntletStartTimer = 0; + } + else + m_uiGauntletStartTimer -= uiDiff; + } + + if (!m_creature->SelectHostileTarget() || !m_creature->getVictim()) + return; + + if (m_uiPierceTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature->getVictim(), SPELL_PIERCE_ARMOR) == CAST_OK) + m_uiPierceTimer = urand(8000, 15000); + } + else + m_uiPierceTimer -= uiDiff; + + if (m_uiAcidTimer < uiDiff) + { + if (Unit* pTarget = m_creature->SelectAttackingTarget(ATTACKING_TARGET_RANDOM, 0)) + { + if (DoCastSpellIfCan(pTarget, m_bIsRegularMode ? SPELL_ACID_CLOUD : SPELL_ACID_CLOUD_H) == CAST_OK) + m_uiAcidTimer = urand(10000, 15000); + } + } + else + m_uiAcidTimer -= uiDiff; + + if (m_uiLeechTimer < uiDiff) + { + if (Unit* pTarget = m_creature->SelectAttackingTarget(ATTACKING_TARGET_RANDOM, 0)) + { + if (DoCastSpellIfCan(pTarget, m_bIsRegularMode ? SPELL_LEECH_POISON : SPELL_LEECH_POISON_H) == CAST_OK) + m_uiLeechTimer = urand(10000, 15000); + } + } + else + m_uiLeechTimer -= uiDiff; + + if (m_uiGrabTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature, m_bIsRegularMode ? SPELL_WEB_GRAB : SPELL_WEB_GRAB_H) == CAST_OK) + m_uiGrabTimer = urand(25000, 30000); + } + else + m_uiGrabTimer -= uiDiff; + + if (m_uiTauntTimer < uiDiff) + { + if (Unit* pTarget = m_creature->SelectAttackingTarget(ATTACKING_TARGET_RANDOM, 0)) + { + if (DoCastSpellIfCan(pTarget, SPELL_TAUNT) == CAST_OK) + m_uiTauntTimer = urand(7000, 14000); + } + } + else + m_uiTauntTimer -= uiDiff; + + DoMeleeAttackIfReady(); + } +}; + +CreatureAI* GetAI_boss_hadronox(Creature* pCreature) +{ + return new boss_hadronoxAI(pCreature); +} + +void AddSC_boss_hadronox() +{ + Script* pNewScript; + + pNewScript = new Script; + pNewScript->Name = "boss_hadronox"; + pNewScript->GetAI = &GetAI_boss_hadronox; + pNewScript->RegisterSelf(); +} diff --git a/src/modules/SD2/scripts/northrend/azjol-nerub/azjol-nerub/boss_krikthir.cpp b/src/modules/SD2/scripts/northrend/azjol-nerub/azjol-nerub/boss_krikthir.cpp new file mode 100644 index 000000000..e399f3e57 --- /dev/null +++ b/src/modules/SD2/scripts/northrend/azjol-nerub/azjol-nerub/boss_krikthir.cpp @@ -0,0 +1,187 @@ +/* Copyright (C) 2006 - 2013 ScriptDev2 + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with 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 +SD%Complete: 90% +SDComment: Implement Achievement +SDCategory: Azjol'Nerub +EndScriptData */ + +#include "precompiled.h" +#include "azjol-nerub.h" + +enum +{ + SAY_AGGRO = -1601000, + SAY_KILL_1 = -1601001, + SAY_KILL_2 = -1601002, + SAY_KILL_3 = -1601003, + SAY_PREFIGHT_1 = -1601007, + SAY_PREFIGHT_2 = -1601008, + SAY_PREFIGHT_3 = -1601009, + SAY_SWARM_1 = -1601010, + SAY_SWARM_2 = -1601011, + SAY_DEATH = -1601012, + EMOTE_BOSS_GENERIC_FRENZY = -1000005, + + SPELL_SWARM = 52440, + SPELL_CURSE_OF_FATIGUE = 52592, + SPELL_CURSE_OF_FATIGUE_H = 59368, + SPELL_MINDFLAY = 52586, + SPELL_MINDFLAY_H = 59367, + SPELL_FRENZY = 28747, + + NPC_SKITTERING_SWARMER = 28735, + NPC_SKITTERING_INFECTOR = 28736 +}; + +/*###### +## boss_krikthir +######*/ + +struct boss_krikthirAI : public ScriptedAI +{ + boss_krikthirAI(Creature* pCreature) : ScriptedAI(pCreature) + { + m_pInstance = (instance_azjol_nerub*)pCreature->GetInstanceData(); + m_bIsRegularMode = pCreature->GetMap()->IsRegularDifficulty(); + Reset(); + } + + instance_azjol_nerub* m_pInstance; + bool m_bIsRegularMode; + + bool m_bFrenzy; + bool m_bIntroSpeech; + + uint32 m_uiSwarmTimer; + uint32 m_uiCurseTimer; + uint32 m_uiMindFlayTimer; + + void Reset() override + { + m_uiSwarmTimer = 15000; + m_uiCurseTimer = 20000; + m_uiMindFlayTimer = 8000; + + m_bIntroSpeech = false; + m_bFrenzy = false; + } + + void Aggro(Unit* /*pWho*/) override + { + DoScriptText(SAY_AGGRO, m_creature); + } + + void KilledUnit(Unit* /*pVictim*/) override + { + switch (urand(0, 2)) + { + case 0: DoScriptText(SAY_KILL_1, m_creature); break; + case 1: DoScriptText(SAY_KILL_2, m_creature); break; + case 2: DoScriptText(SAY_KILL_3, m_creature); break; + } + } + + void MoveInLineOfSight(Unit* pWho) override + { + if (!m_bIntroSpeech && pWho->GetTypeId() == TYPEID_PLAYER && m_creature->IsWithinDistInMap(pWho, DEFAULT_VISIBILITY_INSTANCE) && m_creature->IsWithinLOSInMap(pWho)) + { + switch (urand(0, 2)) + { + case 0: DoScriptText(SAY_PREFIGHT_1, m_creature); break; + case 1: DoScriptText(SAY_PREFIGHT_2, m_creature); break; + case 2: DoScriptText(SAY_PREFIGHT_3, m_creature); break; + } + m_bIntroSpeech = true; + } + } + + void JustDied(Unit* /*pKiller*/) override + { + DoScriptText(SAY_DEATH, m_creature); + + if (m_pInstance) + m_pInstance->SetData(TYPE_KRIKTHIR, DONE); + } + + void JustSummoned(Creature* pSummoned) override + { + uint32 uiEntry = pSummoned->GetEntry(); + if (uiEntry == NPC_SKITTERING_SWARMER || uiEntry == NPC_SKITTERING_INFECTOR) + pSummoned->AI()->AttackStart(m_creature->getVictim()); + } + + void UpdateAI(const uint32 uiDiff) override + { + if (!m_creature->SelectHostileTarget() || !m_creature->getVictim()) + return; + + if (!m_bFrenzy && m_creature->GetHealthPercent() <= 10.0f) + { + if (DoCastSpellIfCan(m_creature, SPELL_FRENZY) == CAST_OK) + { + DoScriptText(EMOTE_BOSS_GENERIC_FRENZY, m_creature); + m_bFrenzy = true; + } + } + + if (m_uiCurseTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature->getVictim(), m_bIsRegularMode ? SPELL_CURSE_OF_FATIGUE : SPELL_CURSE_OF_FATIGUE_H) == CAST_OK) + m_uiCurseTimer = 20000; + } + else + m_uiCurseTimer -= uiDiff; + + if (m_uiMindFlayTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature->getVictim(), m_bIsRegularMode ? SPELL_MINDFLAY : SPELL_MINDFLAY_H) == CAST_OK) + m_uiMindFlayTimer = 8000; + } + else + m_uiMindFlayTimer -= uiDiff; + + if (m_uiSwarmTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature, SPELL_SWARM) == CAST_OK) + { + DoScriptText(urand(0, 1) ? SAY_SWARM_1 : SAY_SWARM_2, m_creature); + m_uiSwarmTimer = 15000; + } + } + else + m_uiSwarmTimer -= uiDiff; + + DoMeleeAttackIfReady(); + } +}; + +CreatureAI* GetAI_boss_krikthir(Creature* pCreature) +{ + return new boss_krikthirAI(pCreature); +} + +void AddSC_boss_krikthir() +{ + Script* pNewScript; + + pNewScript = new Script; + pNewScript->Name = "boss_krikthir"; + pNewScript->GetAI = &GetAI_boss_krikthir; + pNewScript->RegisterSelf(); +} diff --git a/src/modules/SD2/scripts/northrend/azjol-nerub/azjol-nerub/instance_azjol-nerub.cpp b/src/modules/SD2/scripts/northrend/azjol-nerub/azjol-nerub/instance_azjol-nerub.cpp new file mode 100644 index 000000000..4a8eb8f3b --- /dev/null +++ b/src/modules/SD2/scripts/northrend/azjol-nerub/azjol-nerub/instance_azjol-nerub.cpp @@ -0,0 +1,368 @@ +/* Copyright (C) 2006 - 2013 ScriptDev2 + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along 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: 50 +SDComment: +SDCategory: Azjol-Nerub +EndScriptData */ + +#include "precompiled.h" +#include "azjol-nerub.h" + +instance_azjol_nerub::instance_azjol_nerub(Map* pMap) : ScriptedInstance(pMap), + m_uiWatcherTimer(0), + m_uiGauntletEndTimer(0), + m_bWatchHimDie(true), + m_bHadronoxDenied(true), + m_bGauntletStarted(false) +{ + Initialize(); +} + +void instance_azjol_nerub::Initialize() +{ + memset(&m_auiEncounter, 0, sizeof(m_auiEncounter)); +} + +void instance_azjol_nerub::OnObjectCreate(GameObject* pGo) +{ + switch (pGo->GetEntry()) + { + case GO_DOOR_KRIKTHIR: + if (m_auiEncounter[TYPE_KRIKTHIR] == DONE) + pGo->SetGoState(GO_STATE_ACTIVE); + break; + case GO_DOOR_ANUBARAK_1: + case GO_DOOR_ANUBARAK_2: + case GO_DOOR_ANUBARAK_3: + break; + + default: + return; + } + m_mGoEntryGuidStore[pGo->GetEntry()] = pGo->GetObjectGuid(); +} + +void instance_azjol_nerub::OnCreatureCreate(Creature* pCreature) +{ + switch (pCreature->GetEntry()) + { + case NPC_KRIKTHIR: + case NPC_GASHRA: + case NPC_NARJIL: + case NPC_SILTHIK: + case NPC_HADRONOX: + case NPC_ANUBARAK: + m_mNpcEntryGuidStore[pCreature->GetEntry()] = pCreature->GetObjectGuid(); + break; + case NPC_WORLD_TRIGGER: + m_lTriggerGuids.push_back(pCreature->GetObjectGuid()); + break; + case NPC_WORLD_TRIGGER_LARGE: + m_lSpiderTriggersGuids.push_back(pCreature->GetObjectGuid()); + break; + } +} + +void instance_azjol_nerub::OnCreatureDeath(Creature* pCreature) +{ + uint32 uiEntry = pCreature->GetEntry(); + if (uiEntry == NPC_GASHRA || uiEntry == NPC_NARJIL || uiEntry == NPC_SILTHIK) + { + if (m_auiEncounter[TYPE_KRIKTHIR] == NOT_STARTED) + m_uiWatcherTimer = 5000; + + // Set achiev criteriat to false if one of the watchers dies + m_bWatchHimDie = false; + } +} + +void instance_azjol_nerub::OnCreatureEnterCombat(Creature* pCreature) +{ + uint32 uiEntry = pCreature->GetEntry(); + + if (uiEntry == NPC_GASHRA || uiEntry == NPC_NARJIL || uiEntry == NPC_SILTHIK) + { + // Creature enter combat is not equal to having a victim yet. + if (!m_playerGuid && pCreature->getVictim()) + m_playerGuid = pCreature->getVictim()->GetCharmerOrOwnerPlayerOrPlayerItself()->GetObjectGuid(); + } + else if (uiEntry == NPC_ANUBAR_CRUSHER) + { + // Only for the first try + if (m_bGauntletStarted) + return; + + DoScriptText(SAY_CRUSHER_AGGRO, pCreature); + + // Spawn 2 more crushers - note these are not the exact spawn coords, but we need to use this workaround for better movement + if (Creature* pCrusher = pCreature->SummonCreature(NPC_ANUBAR_CRUSHER, 485.25f, 611.46f, 771.42f, 4.74f, TEMPSUMMON_DEAD_DESPAWN, 0)) + { + pCrusher->SetWalk(false); + pCrusher->GetMotionMaster()->MovePoint(0, 517.51f, 561.439f, 734.0306f); + pCrusher->HandleEmote(EMOTE_STATE_READYUNARMED); + } + if (Creature* pCrusher = pCreature->SummonCreature(NPC_ANUBAR_CRUSHER, 575.21f, 611.47f, 771.46f, 3.59f, TEMPSUMMON_DEAD_DESPAWN, 0)) + { + pCrusher->SetWalk(false); + pCrusher->GetMotionMaster()->MovePoint(0, 543.414f, 551.728f, 732.0522f); + pCrusher->HandleEmote(EMOTE_STATE_READYUNARMED); + } + + // Spawn 2 more crushers and start the countdown + m_uiGauntletEndTimer = 2 * MINUTE * IN_MILLISECONDS; + m_bGauntletStarted = true; + } +} + +void instance_azjol_nerub::OnCreatureEvade(Creature* pCreature) +{ + uint32 uiEntry = pCreature->GetEntry(); + if (uiEntry == NPC_GASHRA || uiEntry == NPC_NARJIL || uiEntry == NPC_SILTHIK) + m_playerGuid.Clear(); +} + +void instance_azjol_nerub::Update(uint32 uiDiff) +{ + if (m_uiWatcherTimer) + { + if (m_uiWatcherTimer <= uiDiff) + { + DoSendWatcherOrKrikthir(); + m_uiWatcherTimer = 0; + } + else + m_uiWatcherTimer -= uiDiff; + } + + if (m_uiGauntletEndTimer) + { + if (m_uiGauntletEndTimer <= uiDiff) + { + if (GetData(TYPE_HADRONOX) == IN_PROGRESS) + { + m_uiGauntletEndTimer = 0; + return; + } + + SetData(TYPE_HADRONOX, SPECIAL); + + // Allow him to evade - this will start the waypoint movement + if (Creature* pHadronox = GetSingleCreatureFromStorage(NPC_HADRONOX)) + pHadronox->AI()->EnterEvadeMode(); + + m_uiGauntletEndTimer = 0; + } + else + m_uiGauntletEndTimer -= uiDiff; + } +} + +void instance_azjol_nerub::DoSendWatcherOrKrikthir() +{ + Creature* pAttacker = NULL; + Creature* pKrikthir = GetSingleCreatureFromStorage(NPC_KRIKTHIR); + + if (!pKrikthir) + return; + + for (uint8 i = 0; i < countof(aWatchers); ++i) + { + if (Creature* pTemp = GetSingleCreatureFromStorage(aWatchers[i])) + { + if (pTemp->IsAlive()) + { + if (pAttacker && urand(0, 1)) + continue; + else + pAttacker = pTemp; + } + } + } + + if (pAttacker) + { + switch (urand(0, 2)) + { + case 0: DoScriptText(SAY_SEND_GROUP_1, pKrikthir); break; + case 1: DoScriptText(SAY_SEND_GROUP_2, pKrikthir); break; + case 2: DoScriptText(SAY_SEND_GROUP_3, pKrikthir); break; + } + } + else + pAttacker = pKrikthir; + + if (Player* pTarget = instance->GetPlayer(m_playerGuid)) + { + if (pTarget->IsAlive()) + pAttacker->AI()->AttackStart(pTarget); + } +} + +void instance_azjol_nerub::DoSortWorldTriggers() +{ + if (Creature* pAnub = GetSingleCreatureFromStorage(NPC_ANUBARAK)) + { + float fZ = pAnub->GetPositionZ(); + float fTriggZ = 0; + + for (GuidList::const_iterator itr = m_lTriggerGuids.begin(); itr != m_lTriggerGuids.end(); ++itr) + { + if (Creature* pTrigg = instance->GetCreature(*itr)) + { + // Sort only triggers in a range of 100 + if (pTrigg->GetPositionY() < pAnub->GetPositionY() + 110) + { + fTriggZ = pTrigg->GetPositionZ(); + + // One npc below the platform + if (fTriggZ < fZ + aSortDistance[0]) + m_darterSummonTarget = pTrigg->GetObjectGuid(); + // One npc on the boss platform - used to handle the summoned movement + else if (fTriggZ < fZ + aSortDistance[1]) + m_anubSummonTarget = pTrigg->GetObjectGuid(); + // One npc on the upper pathway + else if (fTriggZ < fZ + aSortDistance[2]) + m_guardianSummonTarget = pTrigg->GetObjectGuid(); + // Eight npcs on the upper ledges + else if (fTriggZ < fZ + aSortDistance[3]) + m_vAssassinSummonTargetsVect.push_back(pTrigg->GetObjectGuid()); + } + } + } + } +} + +ObjectGuid instance_azjol_nerub::GetRandomAssassinTrigger() +{ + // Get a random summon target + if (m_vAssassinSummonTargetsVect.size() > 0) + return m_vAssassinSummonTargetsVect[urand(0, m_vAssassinSummonTargetsVect.size() - 1)]; + else + return ObjectGuid(); +} + +void instance_azjol_nerub::ResetHadronoxTriggers() +{ + // Drop the summon auras from the triggers + for (GuidList::const_iterator itr = m_lSpiderTriggersGuids.begin(); itr != m_lSpiderTriggersGuids.end(); ++itr) + { + if (Creature* pTrigger = instance->GetCreature(*itr)) + pTrigger->RemoveAllAurasOnEvade(); + } +} + +void instance_azjol_nerub::SetData(uint32 uiType, uint32 uiData) +{ + switch (uiType) + { + case TYPE_KRIKTHIR: + m_auiEncounter[uiType] = uiData; + if (uiData == DONE) + DoUseDoorOrButton(GO_DOOR_KRIKTHIR); + break; + case TYPE_HADRONOX: + m_auiEncounter[uiType] = uiData; + if (uiData == DONE) + ResetHadronoxTriggers(); + break; + case TYPE_ANUBARAK: + m_auiEncounter[uiType] = uiData; + DoUseDoorOrButton(GO_DOOR_ANUBARAK_1); + DoUseDoorOrButton(GO_DOOR_ANUBARAK_2); + DoUseDoorOrButton(GO_DOOR_ANUBARAK_3); + if (uiData == IN_PROGRESS) + { + DoSortWorldTriggers(); + DoStartTimedAchievement(ACHIEVEMENT_CRITERIA_TYPE_KILL_CREATURE, ACHIEV_START_ANUB_ID); + } + break; + } + + if (uiData == DONE) + { + OUT_SAVE_INST_DATA; + + std::ostringstream saveStream; + saveStream << m_auiEncounter[0] << " " << m_auiEncounter[1] << " " << m_auiEncounter[2]; + + m_strInstData = saveStream.str(); + + SaveToDB(); + OUT_SAVE_INST_DATA_COMPLETE; + } +} + +uint32 instance_azjol_nerub::GetData(uint32 uiType) const +{ + if (uiType < MAX_ENCOUNTER) + return m_auiEncounter[uiType]; + + return 0; +} + +bool instance_azjol_nerub::CheckAchievementCriteriaMeet(uint32 uiCriteriaId, Player const* /*pSource*/, Unit const* /*pTarget*/, uint32 /*uiMiscValue1*/ /* = 0*/) const +{ + switch (uiCriteriaId) + { + case ACHIEV_CRITERIA_WATCH_DIE: + return m_bWatchHimDie; + case ACHIEV_CRITERIA_DENIED: + return m_bHadronoxDenied; + + default: + return false; + } +} + +void instance_azjol_nerub::Load(const char* chrIn) +{ + if (!chrIn) + { + 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]; + + 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_azjol_nerub(Map* pMap) +{ + return new instance_azjol_nerub(pMap); +} + +void AddSC_instance_azjol_nerub() +{ + Script* pNewScript; + + pNewScript = new Script; + pNewScript->Name = "instance_azjol-nerub"; + pNewScript->GetInstanceData = &GetInstanceData_instance_azjol_nerub; + pNewScript->RegisterSelf(); +} diff --git a/src/modules/SD2/scripts/northrend/borean_tundra.cpp b/src/modules/SD2/scripts/northrend/borean_tundra.cpp new file mode 100644 index 000000000..d9d34f8fa --- /dev/null +++ b/src/modules/SD2/scripts/northrend/borean_tundra.cpp @@ -0,0 +1,550 @@ +/* Copyright (C) 2006 - 2013 ScriptDev2 + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * World of Warcraft, and all World of Warcraft or Warcraft art, images, + * and lore are copyrighted by Blizzard Entertainment, Inc. + */ + +/* ScriptData +SDName: Borean_Tundra +SD%Complete: 100 +SDComment: Quest support: 11865, 11728, 11897, 11570 +SDCategory: Borean Tundra +EndScriptData */ + +/* ContentData +npc_nesingwary_trapper +npc_sinkhole_kill_credit +npc_lurgglbr +EndContentData */ + +#include "precompiled.h" +#include "escort_ai.h" +#include "TemporarySummon.h" + +/*###### +## npc_nesingwary_trapper +######*/ + +enum +{ + NPC_NESINGWARY_TRAPPER = 25835, + GO_QUALITY_FUR = 187983, + + SAY_PHRASE_1 = -1000599, + SAY_PHRASE_2 = -1000600, + SAY_PHRASE_3 = -1000601, + SAY_PHRASE_4 = -1000602 +}; + +struct npc_nesingwary_trapperAI : public ScriptedAI +{ + npc_nesingwary_trapperAI(Creature* pCreature) : ScriptedAI(pCreature) { Reset(); } + + uint8 m_uiPhase; + uint32 m_uiPhaseTimer; + ObjectGuid m_playerGuid; + ObjectGuid m_trapGuid; + + void Reset() override + { + m_uiPhase = 0; + m_uiPhaseTimer = 0; + m_playerGuid.Clear(); + m_trapGuid.Clear(); + } + + void MoveInLineOfSight(Unit* pWho) override + { + if (!m_uiPhase && pWho->GetTypeId() == TYPEID_PLAYER && m_creature->IsWithinDistInMap(pWho, 20.0f)) + { + m_uiPhase = 1; + m_uiPhaseTimer = 1000; + m_playerGuid = pWho->GetObjectGuid(); + + if (m_creature->IsTemporarySummon()) + { + // Get the summoner trap + if (GameObject* pTrap = m_creature->GetMap()->GetGameObject(((TemporarySummon*)m_creature)->GetSummonerGuid())) + m_trapGuid = pTrap->GetObjectGuid(); + } + } + + ScriptedAI::MoveInLineOfSight(pWho); + } + + void MovementInform(uint32 uiType, uint32 uiPointId) override + { + if (uiType != POINT_MOTION_TYPE || !uiPointId) + return; + + if (GameObject* pTrap = m_creature->GetMap()->GetGameObject(m_trapGuid)) + { + // respawn the Quality Fur + if (GameObject* pGoFur = GetClosestGameObjectWithEntry(pTrap, GO_QUALITY_FUR, INTERACTION_DISTANCE)) + { + if (!pGoFur->isSpawned()) + { + pGoFur->SetRespawnTime(10); + pGoFur->Refresh(); + } + } + } + + m_uiPhaseTimer = 2000; + } + + void UpdateAI(const uint32 uiDiff) override + { + if (!m_creature->getVictim() && m_uiPhaseTimer) + { + if (m_uiPhaseTimer <= uiDiff) + { + switch (m_uiPhase) + { + case 1: + if (GameObject* pTrap = m_creature->GetMap()->GetGameObject(m_trapGuid)) + { + float fX, fY, fZ; + pTrap->GetContactPoint(m_creature, fX, fY, fZ); + + m_creature->SetWalk(false); + m_creature->GetMotionMaster()->MovePoint(1, fX, fY, fZ); + } + m_uiPhaseTimer = 0; + break; + case 2: + switch (urand(0, 3)) + { + case 0: DoScriptText(SAY_PHRASE_1, m_creature); break; + case 1: DoScriptText(SAY_PHRASE_2, m_creature); break; + case 2: DoScriptText(SAY_PHRASE_3, m_creature); break; + case 3: DoScriptText(SAY_PHRASE_4, m_creature); break; + } + m_creature->HandleEmote(EMOTE_ONESHOT_LOOT); + m_uiPhaseTimer = 3000; + break; + case 3: + if (GameObject* pTrap = m_creature->GetMap()->GetGameObject(m_trapGuid)) + { + pTrap->Use(m_creature); + + if (Player* pPlayer = m_creature->GetMap()->GetPlayer(m_playerGuid)) + { + if (pPlayer->IsAlive()) + pPlayer->KilledMonsterCredit(m_creature->GetEntry()); + } + } + m_uiPhaseTimer = 0; + break; + } + ++m_uiPhase; + } + else + m_uiPhaseTimer -= uiDiff; + } + } +}; + +CreatureAI* GetAI_npc_nesingwary_trapper(Creature* pCreature) +{ + return new npc_nesingwary_trapperAI(pCreature); +} + +/*##### +# npc_oil_stained_wolf +#####*/ + +enum +{ + SPELL_THROW_WOLF_BAIT = 53326, + SPELL_PLACE_WOLF_BAIT = 46072, // doesn't appear to be used for anything + SPELL_HAS_EATEN = 46073, + SPELL_SUMMON_DROPPINGS = 46075, + + FACTION_MONSTER = 634, + + POINT_DEST = 1 +}; + +struct npc_oil_stained_wolfAI : public ScriptedAI +{ + npc_oil_stained_wolfAI(Creature* pCreature) : ScriptedAI(pCreature) { Reset(); } + + bool m_bCanCrapInPublic; + uint32 m_uiPooTimer; + + void Reset() override + { + m_bCanCrapInPublic = false; + m_uiPooTimer = 0; + } + + void MovementInform(uint32 uiType, uint32 uiPointId) override + { + if (uiType != POINT_MOTION_TYPE) + return; + + if (uiPointId == POINT_DEST) + { + DoCastSpellIfCan(m_creature, SPELL_HAS_EATEN); + m_uiPooTimer = 4000; + } + } + + void UpdateAI(const uint32 uiDiff) override + { + if (!m_creature->SelectHostileTarget() || !m_creature->getVictim()) + { + if (m_uiPooTimer) + { + if (m_uiPooTimer <= uiDiff) + { + if (m_bCanCrapInPublic) + { + DoCastSpellIfCan(m_creature, SPELL_SUMMON_DROPPINGS); + m_creature->GetMotionMaster()->Clear(); + Reset(); + } + else + { + m_creature->HandleEmote(EMOTE_ONESHOT_BATTLEROAR); + m_bCanCrapInPublic = true; + m_uiPooTimer = 3000; + } + } + else + m_uiPooTimer -= uiDiff; + } + + return; + } + + DoMeleeAttackIfReady(); + } +}; + +CreatureAI* GetAI_npc_oil_stained_wolf(Creature* pCreature) +{ + return new npc_oil_stained_wolfAI(pCreature); +} + +bool EffectDummyCreature_npc_oil_stained_wolf(Unit* pCaster, uint32 uiSpellId, SpellEffectIndex uiEffIndex, Creature* pCreatureTarget, ObjectGuid /*originalCasterGuid*/) +{ + if (uiSpellId == SPELL_THROW_WOLF_BAIT) + { + if (uiEffIndex == EFFECT_INDEX_0 && pCreatureTarget->getFaction() != FACTION_MONSTER && !pCreatureTarget->HasAura(SPELL_HAS_EATEN)) + { + pCreatureTarget->SetFactionTemporary(FACTION_MONSTER); + pCreatureTarget->SetWalk(false); + + pCreatureTarget->GetMotionMaster()->MoveIdle(); + + float fX, fY, fZ; + pCaster->GetContactPoint(pCreatureTarget, fX, fY, fZ, CONTACT_DISTANCE); + pCreatureTarget->GetMotionMaster()->MovePoint(POINT_DEST, fX, fY, fZ); + return true; + } + } + + return false; +} + +bool EffectAuraDummy_npc_oil_stained_wolf(const Aura* pAura, bool bApply) +{ + if (pAura->GetId() == SPELL_HAS_EATEN) + { + if (pAura->GetEffIndex() != EFFECT_INDEX_0) + return false; + + if (bApply) + { + pAura->GetTarget()->HandleEmote(EMOTE_ONESHOT_CUSTOMSPELL01); + } + else + { + Creature* pCreature = (Creature*)pAura->GetTarget(); + pCreature->setFaction(pCreature->GetCreatureInfo()->faction_A); + } + + return true; + } + + return false; +} + +/*##### +# npc_sinkhole_kill_credit +#####*/ + +enum +{ + SPELL_SUMMON_EXPLOSIVES_CART_FIRE = 46799, + SPELL_SUMMON_SCOURGE_BURROWER = 46800, + SPELL_COSMETIC_HUGE_EXPLOSION = 46225, + SPELL_CANNON_FIRE = 42445, +}; + +struct npc_sinkhole_kill_creditAI : public ScriptedAI +{ + npc_sinkhole_kill_creditAI(Creature* pCreature) : ScriptedAI(pCreature) { Reset(); } + + ObjectGuid m_cartGuid; + ObjectGuid m_wormGuid; + uint32 m_uiCartTimer; + uint32 m_uiCartPhase; + + void Reset() override + { + m_cartGuid.Clear(); + m_wormGuid.Clear(); + m_uiCartTimer = 2000; + m_uiCartPhase = 0; + } + + void JustSummoned(Creature* pSummoned) override + { + m_wormGuid = pSummoned->GetObjectGuid(); + } + + void JustSummoned(GameObject* pGo) override + { + // Go is not really needed, but ok to use as a check point so only one "event" can be processed at a time + if (m_cartGuid) + return; + + // Expecting summoned from mangos dummy effect 46797 + m_cartGuid = pGo->GetObjectGuid(); + } + + void UpdateAI(const uint32 uiDiff) override + { + if (m_cartGuid) + { + if (m_uiCartTimer <= uiDiff) + { + switch (m_uiCartPhase) + { + case 0: + DoCastSpellIfCan(m_creature, SPELL_SUMMON_EXPLOSIVES_CART_FIRE); + m_uiCartTimer = 4000; + break; + case 1: + // Unclear if these should be in a dummy effect or not. + // The order of spells are correct though. + DoCastSpellIfCan(m_creature, SPELL_COSMETIC_HUGE_EXPLOSION, CAST_TRIGGERED); + DoCastSpellIfCan(m_creature, SPELL_CANNON_FIRE, CAST_TRIGGERED); + break; + case 2: + DoCastSpellIfCan(m_creature, SPELL_SUMMON_SCOURGE_BURROWER); + m_uiCartTimer = 2000; + break; + case 3: + if (Creature* pWorm = m_creature->GetMap()->GetCreature(m_wormGuid)) + { + pWorm->SetDeathState(JUST_DIED); + pWorm->SetHealth(0); + } + m_uiCartTimer = 10000; + break; + case 4: + if (Creature* pWorm = m_creature->GetMap()->GetCreature(m_wormGuid)) + pWorm->RemoveCorpse(); + + Reset(); + return; + } + + ++m_uiCartPhase; + } + else + m_uiCartTimer -= uiDiff; + } + } +}; + +CreatureAI* GetAI_npc_sinkhole_kill_credit(Creature* pCreature) +{ + return new npc_sinkhole_kill_creditAI(pCreature); +} + +/*###### +## npc_lurgglbr +######*/ + +enum +{ + QUEST_ESCAPE_FROM_WINTERFIN_CAVERNS = 11570, + GO_CAGE = 187369, + + SAY_START_1 = -1000575, + SAY_START_2 = -1000576, + SAY_END_1 = -1000577, + SAY_END_2 = -1000578 +}; + +struct npc_lurgglbrAI : public npc_escortAI +{ + npc_lurgglbrAI(Creature* pCreature) : npc_escortAI(pCreature) + { + m_uiSayTimer = 0; + m_uiSpeech = 0; + Reset(); + } + + uint32 m_uiSayTimer; + uint8 m_uiSpeech; + + void Reset() override + { + if (!HasEscortState(STATE_ESCORT_ESCORTING)) + { + m_uiSayTimer = 0; + m_uiSpeech = 0; + } + } + + void JustStartedEscort() override + { + if (GameObject* pCage = GetClosestGameObjectWithEntry(m_creature, GO_CAGE, INTERACTION_DISTANCE)) + { + if (pCage->GetGoState() == GO_STATE_READY) + pCage->Use(m_creature); + } + } + + void WaypointStart(uint32 uiPointId) override + { + switch (uiPointId) + { + case 1: + if (Player* pPlayer = GetPlayerForEscort()) + DoScriptText(SAY_START_2, m_creature, pPlayer); + + // Cage actually closes here, however it's normally determined by GO template and auto close time + + break; + } + } + + void WaypointReached(uint32 uiPointId) override + { + switch (uiPointId) + { + case 0: + if (Player* pPlayer = GetPlayerForEscort()) + { + m_creature->SetFacingToObject(pPlayer); + DoScriptText(SAY_START_1, m_creature, pPlayer); + } + break; + case 25: + if (Player* pPlayer = GetPlayerForEscort()) + { + DoScriptText(SAY_END_1, m_creature, pPlayer); + m_uiSayTimer = 3000; + } + break; + } + } + + void UpdateEscortAI(const uint32 uiDiff) override + { + if (!m_creature->SelectHostileTarget() || !m_creature->getVictim()) + { + if (m_uiSayTimer) + { + if (m_uiSayTimer <= uiDiff) + { + Player* pPlayer = GetPlayerForEscort(); + + if (!pPlayer) + { + m_uiSayTimer = 0; + return; + } + + m_creature->SetFacingToObject(pPlayer); + + switch (m_uiSpeech) + { + case 0: + DoScriptText(SAY_END_2, m_creature, pPlayer); + m_uiSayTimer = 3000; + break; + case 1: + pPlayer->GroupEventHappens(QUEST_ESCAPE_FROM_WINTERFIN_CAVERNS, m_creature); + m_uiSayTimer = 0; + break; + } + + ++m_uiSpeech; + } + else + m_uiSayTimer -= uiDiff; + } + + return; + } + + DoMeleeAttackIfReady(); + } +}; + +bool QuestAccept_npc_lurgglbr(Player* pPlayer, Creature* pCreature, const Quest* pQuest) +{ + if (pQuest->GetQuestId() == QUEST_ESCAPE_FROM_WINTERFIN_CAVERNS) + { + if (npc_lurgglbrAI* pEscortAI = dynamic_cast(pCreature->AI())) + { + pCreature->SetFactionTemporary(FACTION_ESCORT_N_NEUTRAL_PASSIVE, TEMPFACTION_RESTORE_RESPAWN); + pEscortAI->Start(false, pPlayer, pQuest); + } + } + return true; +} + +CreatureAI* GetAI_npc_lurgglbr(Creature* pCreature) +{ + return new npc_lurgglbrAI(pCreature); +} + +void AddSC_borean_tundra() +{ + Script* pNewScript; + + pNewScript = new Script; + pNewScript->Name = "npc_nesingwary_trapper"; + pNewScript->GetAI = &GetAI_npc_nesingwary_trapper; + pNewScript->RegisterSelf(); + + pNewScript = new Script; + pNewScript->Name = "npc_oil_stained_wolf"; + pNewScript->GetAI = &GetAI_npc_oil_stained_wolf; + pNewScript->pEffectDummyNPC = &EffectDummyCreature_npc_oil_stained_wolf; + pNewScript->pEffectAuraDummy = &EffectAuraDummy_npc_oil_stained_wolf; + pNewScript->RegisterSelf(); + + pNewScript = new Script; + pNewScript->Name = "npc_sinkhole_kill_credit"; + pNewScript->GetAI = &GetAI_npc_sinkhole_kill_credit; + pNewScript->RegisterSelf(); + + pNewScript = new Script; + pNewScript->Name = "npc_lurgglbr"; + pNewScript->GetAI = &GetAI_npc_lurgglbr; + pNewScript->pQuestAcceptNPC = &QuestAccept_npc_lurgglbr; + pNewScript->RegisterSelf(); +} diff --git a/src/modules/SD2/scripts/northrend/crusaders_coliseum/trial_of_the_champion/boss_grand_champions.cpp b/src/modules/SD2/scripts/northrend/crusaders_coliseum/trial_of_the_champion/boss_grand_champions.cpp new file mode 100644 index 000000000..f0e1e2034 --- /dev/null +++ b/src/modules/SD2/scripts/northrend/crusaders_coliseum/trial_of_the_champion/boss_grand_champions.cpp @@ -0,0 +1,29 @@ +/* Copyright (C) 2006 - 2013 ScriptDev2 + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +/* ScriptData +SDName: grand_champions +SD%Complete: 0 +SDComment: +SDCategory: Crusader Coliseum, Trial of the Champion +EndScriptData */ + +#include "precompiled.h" +#include "trial_of_the_champion.h" + +void AddSC_boss_grand_champions() +{ +} diff --git a/src/modules/SD2/scripts/northrend/crusaders_coliseum/trial_of_the_champion/instance_trial_of_the_champion.cpp b/src/modules/SD2/scripts/northrend/crusaders_coliseum/trial_of_the_champion/instance_trial_of_the_champion.cpp new file mode 100644 index 000000000..b1137a127 --- /dev/null +++ b/src/modules/SD2/scripts/northrend/crusaders_coliseum/trial_of_the_champion/instance_trial_of_the_champion.cpp @@ -0,0 +1,35 @@ +/* Copyright (C) 2006 - 2013 ScriptDev2 + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along 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_trial_of_the_champion +SD%Complete: 0 +SDComment: +SDCategory: Crusader Coliseum, Trial of the Champion +EndScriptData */ + +#include "precompiled.h" +#include "trial_of_the_champion.h" + +/* Trial of the Champion encounters: +0 - Grand Champions +1 - Argent Champion +2 - Black Knight +*/ + +void AddSC_instance_trial_of_the_champion() +{ +} diff --git a/src/modules/SD2/scripts/northrend/crusaders_coliseum/trial_of_the_champion/trial_of_the_champion.cpp b/src/modules/SD2/scripts/northrend/crusaders_coliseum/trial_of_the_champion/trial_of_the_champion.cpp new file mode 100644 index 000000000..ed82aa8d6 --- /dev/null +++ b/src/modules/SD2/scripts/northrend/crusaders_coliseum/trial_of_the_champion/trial_of_the_champion.cpp @@ -0,0 +1,29 @@ +/* Copyright (C) 2006 - 2013 ScriptDev2 + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +/* ScriptData +SDName: trial_of_the_champion +SD%Complete: 0 +SDComment: +SDCategory: Crusader Coliseum, Trial of the Champion +EndScriptData */ + +#include "precompiled.h" +#include "trial_of_the_champion.h" + +void AddSC_trial_of_the_champion() +{ +} diff --git a/src/modules/SD2/scripts/northrend/crusaders_coliseum/trial_of_the_champion/trial_of_the_champion.h b/src/modules/SD2/scripts/northrend/crusaders_coliseum/trial_of_the_champion/trial_of_the_champion.h new file mode 100644 index 000000000..c891b24d2 --- /dev/null +++ b/src/modules/SD2/scripts/northrend/crusaders_coliseum/trial_of_the_champion/trial_of_the_champion.h @@ -0,0 +1,17 @@ +/* Copyright (C) 2006 - 2013 ScriptDev2 + * This program is free software licensed under GPL version 2 + * Please see the included DOCS/LICENSE.TXT for more information */ + +#ifndef DEF_TRIAL_OF_THE_CHAMPION_H +#define DEF_TRIAL_OF_THE_CHAMPION_H + +enum +{ + MAX_ENCOUNTER = 3, + + TYPE_GRAND_CHAMPIONS = 0, + TYPE_ARGENT_CHAMPION = 1, + TYPE_BLACK_KNIGHT = 2, +}; + +#endif diff --git a/src/modules/SD2/scripts/northrend/crusaders_coliseum/trial_of_the_crusader/boss_anubarak_trial.cpp b/src/modules/SD2/scripts/northrend/crusaders_coliseum/trial_of_the_crusader/boss_anubarak_trial.cpp new file mode 100644 index 000000000..42f9394a6 --- /dev/null +++ b/src/modules/SD2/scripts/northrend/crusaders_coliseum/trial_of_the_crusader/boss_anubarak_trial.cpp @@ -0,0 +1,643 @@ +/* Copyright (C) 2006 - 2013 ScriptDev2 + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with 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_trial +SD%Complete: 100 +SDComment: +SDCategory: Crusader Coliseum +EndScriptData */ + +#include "precompiled.h" +#include "trial_of_the_crusader.h" + +enum +{ + SAY_INTRO = -1649038, + SAY_AGGRO = -1649064, + SAY_SLAY_1 = -1649065, + SAY_SLAY_2 = -1649066, + SAY_DEATH = -1649067, + SAY_BERSERK = -1649068, + SAY_SUBMERGE = -1649069, + SAY_LEECHING_SWARM = -1649070, + + EMOTE_BURROW = -1649071, + EMOTE_PURSUE = -1649072, + EMOTE_EMERGE = -1649073, + EMOTE_LEECHING_SWARM = -1649074, + + // Anub'arak + SPELL_FREEZING_SLASH = 66012, + SPELL_PENETRATING_COLD = 66013, + SPELL_SUMMON_NERUBIAN_BURROWER = 66332, + SPELL_SUMMON_SCARAB = 66339, + SPELL_SUBMERGE = 65981, + SPELL_EMERGE = 65982, + SPELL_TELEPORT_TO_SPIKE = 66170, // used when the underground phase ends + SPELL_CLEAR_ALL_DEBUFFS = 34098, + SPELL_SUMMON_SPIKES = 66169, + SPELL_LEECHING_SWARM = 66118, + SPELL_BERSERK = 26662, + + // Pursuing Spikes + SPELL_PURSUING_SPIKES_FAIL = 66181, + SPELL_PURSUING_SPIKES_DUMMY = 67470, // target selection spell + SPELL_PURSUING_SPIKES_SPEED1 = 65920, + // SPELL_PURSUING_SPIKES_GROUND = 65921, // included in creature_template_addon + SPELL_PURSUING_SPIKES_SPEED2 = 65922, + SPELL_PURSUING_SPIKES_SPEED3 = 65923, + SPELL_MARK = 67574, + + // Frostsphere + SPELL_PERMAFROST_VISUAL = 65882, // triggers 65872 + SPELL_PERMAFROST_DUMMY = 65872, // dummy spell which handles the spike fail event + SPELL_PERMAFROST_TRANSFORM = 66185, + SPELL_PERMAFROST_SLOW = 66193, // slow spell + SPELL_FROSTSPHERE_VISUAL = 67539, + + POINT_GROUND = 0, + + // npcs + NPC_SCARAB = 34605, + NPC_FROSTSPHERE = 34606, + NPC_NERUBIAN_BURROWER = 34607, + NPC_ANUBARAK_SPIKE = 34660, + NPC_BURROW = 34862, + + MAX_FROSTSPHERES = 6, + MAX_BURROWS = 4 +}; + +enum Phases +{ + PHASE_GROUND = 0, + PHASE_UNDERGROUND = 1, + PHASE_LEECHING_SWARM = 2, + PHASE_SUBMERGING = 3, // virtual use only while casting SPELL_SUBMERGE (triggered by script!) +}; + +enum PursuingSpikesPhases +{ + PHASE_NO_MOVEMENT = 0, + PHASE_IMPALE_NORMAL = 1, + PHASE_IMPALE_MIDDLE = 2, + PHASE_IMPALE_FAST = 3 +}; + +static const float aFrostSphereSpawnPositions[MAX_FROSTSPHERES][3] = +{ + { 701.4270f, 126.4739f, 158.0205f }, + { 712.5711f, 160.9948f, 158.4367f }, + { 736.0243f, 113.4201f, 158.0225f }, + { 747.9201f, 155.0920f, 158.0613f }, + { 769.6285f, 121.1024f, 158.0504f }, + { 779.8038f, 150.658f, 158.1426f } +}; + +static const float aBurrowSpawnPositions[MAX_BURROWS][4] = +{ + { 735.4028f, 75.35764f, 142.2023f, 2.094395f }, + { 692.9202f, 184.809f, 142.2026f, 5.358161f }, + { 688.2066f, 102.8472f, 142.2023f, 0.6457718f }, + { 740.5452f, 189.1129f, 142.1972f, 3.752458f } +}; + +/*###### +## boss_anubarak_trial +######*/ + +struct boss_anubarak_trialAI : public ScriptedAI +{ + boss_anubarak_trialAI(Creature* pCreature) : ScriptedAI(pCreature) + { + m_pInstance = (instance_trial_of_the_crusader*)pCreature->GetInstanceData(); + m_bDidIntroYell = false; + Reset(); + } + + instance_trial_of_the_crusader* m_pInstance; + + Phases m_Phase; + + uint32 m_PhaseSwitchTimer; + uint32 m_uiFreezingSlashTimer; + uint32 m_uiPenetratingColdTimer; + uint32 m_uiBurrowerSummonTimer; + uint32 m_uiBerserkTimer; + bool m_bDidIntroYell; + + ObjectGuid m_PursuingSpikesGuid; + GuidVector m_vSpheresGuidVector; + + void Reset() override + { + m_Phase = PHASE_GROUND; + m_PhaseSwitchTimer = 80000; + m_uiFreezingSlashTimer = 20000; + m_uiPenetratingColdTimer = urand(15000, 25000); + m_uiBurrowerSummonTimer = 10000; + m_uiBerserkTimer = 10 * MINUTE * IN_MILLISECONDS; + + m_vSpheresGuidVector.clear(); + m_vSpheresGuidVector.resize(MAX_FROSTSPHERES, ObjectGuid()); + } + + void JustReachedHome() override + { + if (m_pInstance) + m_pInstance->SetData(TYPE_ANUBARAK, FAIL); + } + + void JustDied(Unit* /*pKiller*/) override + { + DoScriptText(SAY_DEATH, m_creature); + + if (m_pInstance) + m_pInstance->SetData(TYPE_ANUBARAK, DONE); + } + + void MoveInLineOfSight(Unit* pWho) override + { + if (!m_bDidIntroYell && pWho->GetTypeId() == TYPEID_PLAYER && !((Player*)pWho)->isGameMaster() && + !m_creature->IsInEvadeMode() && pWho->IsWithinDistInMap(m_creature, 100) && pWho->IsWithinLOSInMap(m_creature)) + { + DoScriptText(SAY_INTRO, m_creature); + + m_creature->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE | UNIT_FLAG_NOT_SELECTABLE); + m_creature->RemoveAurasDueToSpell(SPELL_SUBMERGE); + DoCastSpellIfCan(m_creature, SPELL_EMERGE); + + m_bDidIntroYell = true; + return; + } + + ScriptedAI::MoveInLineOfSight(pWho); + } + + void Aggro(Unit* /*pWho*/) override + { + DoScriptText(SAY_AGGRO, m_creature); + + // Summon the spheres on random points + for (uint8 i = 0; i < MAX_FROSTSPHERES; ++i) + { + if (Creature* pTemp = m_creature->SummonCreature(NPC_FROSTSPHERE, aFrostSphereSpawnPositions[i][0], aFrostSphereSpawnPositions[i][1], aFrostSphereSpawnPositions[i][2], 0, TEMPSUMMON_DEAD_DESPAWN, 0)) + m_vSpheresGuidVector[i] = pTemp->GetObjectGuid(); + } + + // It's not clear if these should be spawned by DB or summoned + for (uint8 i = 0; i < MAX_BURROWS; ++i) + m_creature->SummonCreature(NPC_BURROW, aBurrowSpawnPositions[i][0], aBurrowSpawnPositions[i][1], aBurrowSpawnPositions[i][2], aBurrowSpawnPositions[i][3], TEMPSUMMON_DEAD_DESPAWN, 0); + + if (m_pInstance) + m_pInstance->SetData(TYPE_ANUBARAK, IN_PROGRESS); + } + + void KilledUnit(Unit* pVictim) override + { + if (pVictim->GetTypeId() != TYPEID_PLAYER) + return; + + DoScriptText((urand(0, 1)) ? SAY_SLAY_1 : SAY_SLAY_2, m_creature); + } + + void SpellHit(Unit* /*pCaster*/, const SpellEntry* pSpell) override + { + if (pSpell->Id == SPELL_SUBMERGE) + { + m_creature->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE); + + // Extra check here, because AnubArak must be submerged by default + if (m_Phase != PHASE_SUBMERGING) + return; + + m_Phase = PHASE_UNDERGROUND; + + // Refresh spheres only on normal difficulty + if (m_pInstance && !m_pInstance->IsHeroicDifficulty()) + DoRefreshSpheres(); + + DoCastSpellIfCan(m_creature, SPELL_CLEAR_ALL_DEBUFFS, CAST_TRIGGERED); + DoCastSpellIfCan(m_creature, SPELL_SUMMON_SPIKES, CAST_TRIGGERED); + DoCastSpellIfCan(m_creature, SPELL_SUMMON_SCARAB, CAST_TRIGGERED); + } + } + + void JustSummoned(Creature* pSummoned) override + { + switch (pSummoned->GetEntry()) + { + case NPC_ANUBARAK_SPIKE: + m_PursuingSpikesGuid = pSummoned->GetObjectGuid(); + // no break here + case NPC_NERUBIAN_BURROWER: + case NPC_SCARAB: + pSummoned->AI()->AttackStart(m_creature->getVictim()); + break; + } + } + + // Wrapper to refresh frost spheres - it's not very clear how ofter should this happen + void DoRefreshSpheres() + { + for (uint8 i = 0; i < MAX_FROSTSPHERES; ++i) + { + // If the sphere is alive and hasn't transfomed to permafrost yet summon a new one + Creature* pTemp = m_creature->GetMap()->GetCreature(m_vSpheresGuidVector[i]); + if (pTemp && !pTemp->HasAura(SPELL_PERMAFROST_TRANSFORM)) + continue; + + // Summon a new frost sphere instead of the killed one + if (Creature* pTemp = m_creature->SummonCreature(NPC_FROSTSPHERE, aFrostSphereSpawnPositions[i][0], aFrostSphereSpawnPositions[i][1], aFrostSphereSpawnPositions[i][2], 0, TEMPSUMMON_DEAD_DESPAWN, 0)) + m_vSpheresGuidVector[i] = pTemp->GetObjectGuid(); + } + } + + // Wrapper to despawn the Spikes + void DoDespawnPursuingSpikes() + { + if (Creature* pPursuingSpikes = m_creature->GetMap()->GetCreature(m_PursuingSpikesGuid)) + pPursuingSpikes->ForcedDespawn(); + + m_PursuingSpikesGuid.Clear(); + } + + void UpdateAI(const uint32 uiDiff) override + { + if (!m_creature->SelectHostileTarget() || !m_creature->getVictim()) + return; + + switch (m_Phase) + { + case PHASE_GROUND: + + // Switch to underground phase on timer + if (m_PhaseSwitchTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature, SPELL_SUBMERGE) == CAST_OK) + { + DoScriptText(SAY_SUBMERGE, m_creature); + DoScriptText(EMOTE_BURROW, m_creature); + m_PhaseSwitchTimer = 63000; + m_Phase = PHASE_SUBMERGING; + return; + } + } + else + m_PhaseSwitchTimer -= uiDiff; + + // Switch to phase 3 when below 30% + if (m_creature->GetHealthPercent() <= 30.0f) + { + if (DoCastSpellIfCan(m_creature, SPELL_LEECHING_SWARM) == CAST_OK) + { + DoScriptText(SAY_LEECHING_SWARM, m_creature); + DoScriptText(EMOTE_LEECHING_SWARM, m_creature); + m_Phase = PHASE_LEECHING_SWARM; + } + } + + // No break - the spells are used in both phase 1 and 3 + case PHASE_LEECHING_SWARM: + + if (m_uiFreezingSlashTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature->getVictim(), SPELL_FREEZING_SLASH) == CAST_OK) + m_uiFreezingSlashTimer = 20000; + } + else + m_uiFreezingSlashTimer -= uiDiff; + + if (m_uiPenetratingColdTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature, SPELL_PENETRATING_COLD) == CAST_OK) + m_uiPenetratingColdTimer = 15000; + } + else + m_uiPenetratingColdTimer -= uiDiff; + + // The Borrowers are summoned in Ground phase only on normal mode or during Ground and Swarm phase on heroic mode + if (m_Phase == PHASE_GROUND || (m_pInstance && m_pInstance->IsHeroicDifficulty())) + { + if (m_uiBurrowerSummonTimer < uiDiff) + { + // The number of targets is handled in core, based on difficulty + if (DoCastSpellIfCan(m_creature, SPELL_SUMMON_NERUBIAN_BURROWER) == CAST_OK) + m_uiBurrowerSummonTimer = 45000; + } + else + m_uiBurrowerSummonTimer -= uiDiff; + } + + DoMeleeAttackIfReady(); + + break; + + case PHASE_UNDERGROUND: + + // Underground phase is finished + if (m_PhaseSwitchTimer < uiDiff) + { + DoCastSpellIfCan(m_creature, SPELL_EMERGE, CAST_TRIGGERED); + DoCastSpellIfCan(m_creature, SPELL_TELEPORT_TO_SPIKE, CAST_TRIGGERED); + DoScriptText(EMOTE_EMERGE, m_creature); + DoDespawnPursuingSpikes(); + + m_creature->RemoveAurasDueToSpell(SPELL_SUBMERGE); + m_creature->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE); + + // Refresh spheres only on normal difficulty + if (m_pInstance && !m_pInstance->IsHeroicDifficulty()) + DoRefreshSpheres(); + + m_PhaseSwitchTimer = 80000; + m_Phase = PHASE_GROUND; + } + else + m_PhaseSwitchTimer -= uiDiff; + + break; + case PHASE_SUBMERGING: // Do nothing, but continue berserk timer + break; + } + + if (m_uiBerserkTimer) + { + if (m_uiBerserkTimer <= uiDiff) + { + if (m_Phase != PHASE_UNDERGROUND) + { + if (DoCastSpellIfCan(m_creature, SPELL_BERSERK) == CAST_OK) + { + DoScriptText(SAY_BERSERK, m_creature); + m_uiBerserkTimer = 0; + } + } + } + else + m_uiBerserkTimer -= uiDiff; + } + } +}; + +CreatureAI* GetAI_boss_anubarak_trial(Creature* pCreature) +{ + return new boss_anubarak_trialAI(pCreature); +} + +/*###### +## npc_anubarak_trial_spike +######*/ + +struct npc_anubarak_trial_spikeAI : public ScriptedAI +{ + npc_anubarak_trial_spikeAI(Creature* pCreature) : ScriptedAI(pCreature) { Reset(); } + + PursuingSpikesPhases m_Phase; + uint32 m_PhaseSwitchTimer; + + void Reset() override + { + m_Phase = PHASE_NO_MOVEMENT; + m_PhaseSwitchTimer = 5000; + + SetCombatMovement(false); + } + + void SpellHitTarget(Unit* pTarget, const SpellEntry* pSpell) override + { + if (pSpell->Id == SPELL_PURSUING_SPIKES_DUMMY && pTarget->GetTypeId() == TYPEID_PLAYER) + { + DoScriptText(EMOTE_PURSUE, m_creature, pTarget); + DoCastSpellIfCan(pTarget, SPELL_MARK, CAST_TRIGGERED); + DoStartMovement(pTarget); + } + } + + // Handle permafrost hit from dummy spell + void PermafrostHit(Creature* pPermafrost) + { + // To prevent more than one call + if (m_Phase == PHASE_NO_MOVEMENT) + return; + + // Remove the speed auras + switch (m_Phase) + { + case PHASE_IMPALE_NORMAL: + m_creature->RemoveAurasDueToSpell(SPELL_PURSUING_SPIKES_SPEED1); + break; + case PHASE_IMPALE_MIDDLE: + m_creature->RemoveAurasDueToSpell(SPELL_PURSUING_SPIKES_SPEED2); + break; + case PHASE_IMPALE_FAST: + m_creature->RemoveAurasDueToSpell(SPELL_PURSUING_SPIKES_SPEED3); + break; + } + + // Set Spike fail animation and despawn + DoCastSpellIfCan(m_creature, SPELL_PURSUING_SPIKES_FAIL, CAST_TRIGGERED); + + if (pPermafrost) + pPermafrost->ForcedDespawn(2000); + + // After the spikes hit the icy surface they can't move for about ~5 seconds + m_Phase = PHASE_NO_MOVEMENT; + m_PhaseSwitchTimer = 5000; + DoResetThreat(); + + SetCombatMovement(false); + m_creature->GetMotionMaster()->Clear(); + m_creature->GetMotionMaster()->MoveIdle(); + } + + void UpdateAI(const uint32 uiDiff) override + { + if (!m_creature->SelectHostileTarget() || !m_creature->getVictim()) + return; + + if (m_PhaseSwitchTimer) + { + if (m_PhaseSwitchTimer <= uiDiff) + { + switch (m_Phase) + { + case PHASE_NO_MOVEMENT: + if (DoCastSpellIfCan(m_creature, SPELL_PURSUING_SPIKES_SPEED1) == CAST_OK) + { + DoCastSpellIfCan(m_creature, SPELL_PURSUING_SPIKES_DUMMY, CAST_TRIGGERED); + + m_Phase = PHASE_IMPALE_NORMAL; + m_PhaseSwitchTimer = 7000; + } + break; + case PHASE_IMPALE_NORMAL: + if (DoCastSpellIfCan(m_creature, SPELL_PURSUING_SPIKES_SPEED2) == CAST_OK) + { + m_Phase = PHASE_IMPALE_MIDDLE; + m_PhaseSwitchTimer = 7000; + } + break; + case PHASE_IMPALE_MIDDLE: + if (DoCastSpellIfCan(m_creature, SPELL_PURSUING_SPIKES_SPEED3) == CAST_OK) + { + m_Phase = PHASE_IMPALE_FAST; + m_PhaseSwitchTimer = 0; + } + break; + } + } + else + m_PhaseSwitchTimer -= uiDiff; + } + } +}; + +CreatureAI* GetAI_npc_anubarak_trial_spike(Creature* pCreature) +{ + return new npc_anubarak_trial_spikeAI(pCreature); +} + +bool EffectDummyCreature_spell_dummy_permafrost(Unit* pCaster, uint32 uiSpellId, SpellEffectIndex uiEffIndex, Creature* pCreatureTarget, ObjectGuid /*originalCasterGuid*/) +{ + // always check spellid and effectindex + if (uiSpellId == SPELL_PERMAFROST_DUMMY && uiEffIndex == EFFECT_INDEX_0) + { + if (npc_anubarak_trial_spikeAI* pSpikeAI = dynamic_cast(pCreatureTarget->AI())) + pSpikeAI->PermafrostHit((Creature*)pCaster); + + // always return true when we are handling this spell and effect + return true; + } + + return false; +} + +/*###### +## npc_anubarak_trial_frostsphere +######*/ + +struct npc_anubarak_trial_frostsphereAI : public Scripted_NoMovementAI +{ + npc_anubarak_trial_frostsphereAI(Creature* pCreature) : Scripted_NoMovementAI(pCreature) { Reset(); } + + bool m_bPermafrost; + + void Reset() override + { + m_bPermafrost = false; + + m_creature->GetMotionMaster()->MoveRandomAroundPoint(m_creature->GetPositionX(), m_creature->GetPositionY(), m_creature->GetPositionZ(), 15.0f); + } + + void MoveInLineOfSight(Unit* /*pWho*/) override { } + void AttackStart(Unit* /*pWho*/) override { } + + void DamageTaken(Unit* pDoneBy, uint32& uiDamage) override + { + if (uiDamage < m_creature->GetHealth()) + return; + + // Set fake death in order to apply permafrost + uiDamage = 0; + + if (m_bPermafrost) + return; + + m_creature->InterruptNonMeleeSpells(false); + m_creature->SetHealth(0); + m_creature->StopMoving(); + m_creature->ClearComboPointHolders(); + m_creature->RemoveAllAurasOnDeath(); + m_creature->ModifyAuraState(AURA_STATE_HEALTHLESS_20_PERCENT, false); + m_creature->ModifyAuraState(AURA_STATE_HEALTHLESS_35_PERCENT, false); + m_creature->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE); + m_creature->ClearAllReactives(); + m_creature->SetTargetGuid(ObjectGuid()); + + // Set proper Z position + m_creature->SetWalk(false); + float fZ = pDoneBy->GetPositionZ(); + MaNGOS::NormalizeMapCoord(fZ); + + // Note: This should be fall movement + m_creature->GetMotionMaster()->Clear(); + m_creature->GetMotionMaster()->MovePoint(1, m_creature->GetPositionX(), m_creature->GetPositionY(), fZ); + m_bPermafrost = true; + } + + void MovementInform(uint32 uiMotionType, uint32 uiPointId) override + { + if (uiMotionType != POINT_MOTION_TYPE || !uiPointId) + return; + + DoCastSpellIfCan(m_creature, SPELL_PERMAFROST_VISUAL, CAST_TRIGGERED); + DoCastSpellIfCan(m_creature, SPELL_PERMAFROST_TRANSFORM, CAST_TRIGGERED); + DoCastSpellIfCan(m_creature, SPELL_PERMAFROST_SLOW, CAST_TRIGGERED); + } +}; + +CreatureAI* GetAI_npc_anubarak_trial_frostsphere(Creature* pCreature) +{ + return new npc_anubarak_trial_frostsphereAI(pCreature); +} + +/*###### +## npc_nerubian_borrow +######*/ + +// TODO Remove this 'script' when combat movement can be proper prevented from core-side +struct npc_nerubian_borrowAI : public Scripted_NoMovementAI +{ + npc_nerubian_borrowAI(Creature* pCreature) : Scripted_NoMovementAI(pCreature) { Reset(); } + + void Reset() override { } + void MoveInLineOfSight(Unit* /*pWho*/) override { } + void AttackStart(Unit* /*pWho*/) override { } + void UpdateAI(const uint32 /*uiDiff*/) override { } +}; + +CreatureAI* GetAI_npc_nerubian_borrow(Creature* pCreature) +{ + return new npc_nerubian_borrowAI(pCreature); +} + +void AddSC_boss_anubarak_trial() +{ + Script* pNewScript; + + pNewScript = new Script; + pNewScript->Name = "boss_anubarak_trial"; + pNewScript->GetAI = &GetAI_boss_anubarak_trial; + pNewScript->RegisterSelf(); + + pNewScript = new Script; + pNewScript->Name = "npc_anubarak_spike"; + pNewScript->GetAI = &GetAI_npc_anubarak_trial_spike; + pNewScript->pEffectDummyNPC = &EffectDummyCreature_spell_dummy_permafrost; + pNewScript->RegisterSelf(); + + pNewScript = new Script; + pNewScript->Name = "npc_frost_sphere"; + pNewScript->GetAI = &GetAI_npc_anubarak_trial_frostsphere; + pNewScript->RegisterSelf(); + + pNewScript = new Script; + pNewScript->Name = "npc_nerubian_borrow"; + pNewScript->GetAI = &GetAI_npc_nerubian_borrow; + pNewScript->RegisterSelf(); +} diff --git a/src/modules/SD2/scripts/northrend/crusaders_coliseum/trial_of_the_crusader/boss_faction_champions.cpp b/src/modules/SD2/scripts/northrend/crusaders_coliseum/trial_of_the_crusader/boss_faction_champions.cpp new file mode 100644 index 000000000..c96032af5 --- /dev/null +++ b/src/modules/SD2/scripts/northrend/crusaders_coliseum/trial_of_the_crusader/boss_faction_champions.cpp @@ -0,0 +1,42 @@ +/* Copyright (C) 2006 - 2013 ScriptDev2 + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +/* ScriptData +SDName: faction_champions +SD%Complete: 0 +SDComment: +SDCategory: Crusader Coliseum +EndScriptData */ + +#include "precompiled.h" +#include "trial_of_the_crusader.h" + +enum +{ + SAY_GARROSH_PVP_A_SLAY_1 = -1649048, + SAY_GARROSH_PVP_A_SLAY_2 = -1649049, + SAY_GARROSH_PVP_A_SLAY_3 = -1649050, + SAY_GARROSH_PVP_A_SLAY_4 = -1649051, + + SAY_VARIAN_PVP_H_SLAY_1 = -1649052, + SAY_VARIAN_PVP_H_SLAY_2 = -1649053, + SAY_VARIAN_PVP_H_SLAY_3 = -1649054, + SAY_VARIAN_PVP_H_SLAY_4 = -1649055, +}; + +void AddSC_boss_faction_champions() +{ +} diff --git a/src/modules/SD2/scripts/northrend/crusaders_coliseum/trial_of_the_crusader/boss_jaraxxus.cpp b/src/modules/SD2/scripts/northrend/crusaders_coliseum/trial_of_the_crusader/boss_jaraxxus.cpp new file mode 100644 index 000000000..2aaaf9249 --- /dev/null +++ b/src/modules/SD2/scripts/northrend/crusaders_coliseum/trial_of_the_crusader/boss_jaraxxus.cpp @@ -0,0 +1,274 @@ +/* Copyright (C) 2006 - 2013 ScriptDev2 + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +/* ScriptData +SDName: trial_of_the_crusader +SD%Complete: 75 +SDComment: Evade handling must be fixed, this is currently totally wrong. Some issues with emotes and texts, generic improvements related to spells can be missing +SDCategory: Crusader Coliseum +EndScriptData */ + +#include "precompiled.h" +#include "trial_of_the_crusader.h" + +/*###### +## boss_jaraxxus +######*/ + +enum +{ + SAY_AGGRO = -1649040, + SAY_SLAY_1 = -1649041, + SAY_SLAY_2 = -1649042, + SAY_BERSERK = -1649044, + SAY_INCINERATE = -1649045, + SAY_MISTRESS = -1649046, + SAY_INFERNO = -1649047, + + // boss spells + SPELL_JARAXXUS_HITTIN_YA = 66327, + SPELL_FEL_FIREBALL = 66532, + SPELL_FEL_LIGHTNING = 66528, + SPELL_INCINERATE_FLESH = 66237, + SPELL_BURNING_INFERNO = 66242, + SPELL_LEGION_FLAME = 66197, + SPELL_INFERNAL_ERUPTION = 66258, // summons a volcano + SPELL_NETHER_PORTAL_SUMMON = 66269, // summons a nether portal + SPELL_NETHER_PORTAL = 66263, // spell casted by the portal + SPELL_ERUPTION = 66252, // spell casted by the volcano + SPELL_NETHER_POWER = 67009, + SPELL_BERSERK = 26662, + + // npcs + NPC_INFERNAL_VOLCANO = 34813, + NPC_NETHER_PORTAL = 34825 +}; + +struct boss_jaraxxusAI : public ScriptedAI +{ + boss_jaraxxusAI(Creature* pCreature) : ScriptedAI(pCreature) + { + m_pInstance = (ScriptedInstance*)pCreature->GetInstanceData(); + Reset(); + } + + ScriptedInstance* m_pInstance; + + uint32 m_uiFelFireballTimer; + uint32 m_uiFelLightningTimer; + uint32 m_uiIncinerateFleshTimer; + uint32 m_uiBurningInfernoTimer; + uint32 m_uiLegionFlameTimer; + uint32 m_uiSummonTimer; + uint32 m_uiNetherPowerTimer; + uint32 m_uiBerserkTimer; + bool m_bVolcanoSummon; + + void Reset() override + { + m_uiFelFireballTimer = urand(20000, 25000); // maybe too early, and too often! + m_uiFelLightningTimer = urand(5000, 8000); + m_uiIncinerateFleshTimer = 25000; + m_uiLegionFlameTimer = 10000; + m_uiSummonTimer = 20000; + m_uiNetherPowerTimer = urand(20000, 30000); + m_uiBerserkTimer = 10 * MINUTE * IN_MILLISECONDS; + + m_bVolcanoSummon = true; + + DoCastSpellIfCan(m_creature, SPELL_JARAXXUS_HITTIN_YA); + } + + void JustReachedHome() override + { + if (m_pInstance) + m_pInstance->SetData(TYPE_JARAXXUS, FAIL); + + m_creature->ForcedDespawn(); + } + + void JustDied(Unit* /*pKiller*/) override + { + if (m_pInstance) + m_pInstance->SetData(TYPE_JARAXXUS, DONE); + } + + void Aggro(Unit* pWho) override + { + if (pWho->GetEntry() == NPC_FIZZLEBANG) + return; + + if (m_pInstance) + m_pInstance->SetData(TYPE_JARAXXUS, IN_PROGRESS); + + DoCastSpellIfCan(m_creature, SPELL_NETHER_POWER); + m_creature->SetInCombatWithZone(); + } + + void EnterEvadeMode() override + { + if (m_pInstance && m_pInstance->GetData(TYPE_JARAXXUS) != IN_PROGRESS) + return; + + ScriptedAI::EnterEvadeMode(); + } + + void KilledUnit(Unit* pVictim) override + { + if (pVictim->GetEntry() == NPC_FIZZLEBANG) + return; + + DoScriptText(urand(0, 1) ? SAY_SLAY_1 : SAY_SLAY_2, m_creature); + } + + void JustSummoned(Creature* pSummoned) override + { + switch (pSummoned->GetEntry()) + { + case NPC_INFERNAL_VOLCANO: + pSummoned->CastSpell(pSummoned, SPELL_ERUPTION, true, NULL, NULL, m_creature->GetObjectGuid()); + break; + case NPC_NETHER_PORTAL: + pSummoned->CastSpell(pSummoned, SPELL_NETHER_PORTAL, true, NULL, NULL, m_creature->GetObjectGuid()); + break; + } + } + + void MovementInform(uint32 uiMovementType, uint32 uiPointId) override + { + if (uiMovementType != POINT_MOTION_TYPE) + return; + + if (m_pInstance && uiPointId == POINT_COMBAT_POSITION) + if (Creature* pFizzlebang = m_pInstance->GetSingleCreatureFromStorage(NPC_FIZZLEBANG)) + m_creature->SetFacingToObject(pFizzlebang); + } + + void UpdateAI(const uint32 uiDiff) override + { + if (!m_creature->SelectHostileTarget() || !m_creature->getVictim()) + return; + + // Spells + if (m_uiIncinerateFleshTimer < uiDiff) + { + if (Unit* pTarget = m_creature->SelectAttackingTarget(ATTACKING_TARGET_RANDOM, 0)) + { + if (DoCastSpellIfCan(pTarget, SPELL_INCINERATE_FLESH) == CAST_OK) + m_uiIncinerateFleshTimer = 25000; + } + } + else + m_uiIncinerateFleshTimer -= uiDiff; + + if (m_uiFelFireballTimer < uiDiff) + { + if (Unit* pTarget = m_creature->SelectAttackingTarget(ATTACKING_TARGET_TOPAGGRO, 0)) + { + if (DoCastSpellIfCan(pTarget, SPELL_FEL_FIREBALL) == CAST_OK) + m_uiFelFireballTimer = urand(20000, 30000); + } + } + else + m_uiFelFireballTimer -= uiDiff; + + if (m_uiFelLightningTimer < uiDiff) + { + if (Unit* pTarget = m_creature->SelectAttackingTarget(ATTACKING_TARGET_RANDOM, 0)) + { + if (DoCastSpellIfCan(pTarget, SPELL_FEL_LIGHTNING) == CAST_OK) + m_uiFelLightningTimer = urand(10000, 18000); + } + } + else + m_uiFelLightningTimer -= uiDiff; + + if (m_uiSummonTimer < uiDiff) + { + if (m_bVolcanoSummon) + { + if (DoCastSpellIfCan(m_creature, SPELL_NETHER_PORTAL_SUMMON) == CAST_OK) + { + // TODO missing emote? + // DoScriptText(EMOTE_PORTAL, m_creature); + m_bVolcanoSummon = false; + m_uiSummonTimer = 60000; + } + } + // summon volcano + else + { + if (DoCastSpellIfCan(m_creature, SPELL_INFERNAL_ERUPTION) == CAST_OK) + { + // TODO missing emote? + // DoScriptText(EMOTE_VOLCANO, m_creature); + m_bVolcanoSummon = true; + m_uiSummonTimer = 60000; + } + } + } + else + m_uiSummonTimer -= uiDiff; + + if (m_uiLegionFlameTimer < uiDiff) + { + if (Unit* pTarget = m_creature->SelectAttackingTarget(ATTACKING_TARGET_RANDOM, 0)) + { + if (DoCastSpellIfCan(pTarget, SPELL_LEGION_FLAME) == CAST_OK) + m_uiLegionFlameTimer = 30000; + } + } + else + m_uiLegionFlameTimer -= uiDiff; + + if (m_uiNetherPowerTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature, SPELL_NETHER_POWER) == CAST_OK) + m_uiNetherPowerTimer = 42000; + } + else + m_uiNetherPowerTimer -= uiDiff; + + // berserk + if (m_uiBerserkTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature, SPELL_BERSERK) == CAST_OK) + { + DoScriptText(SAY_BERSERK, m_creature); + m_uiBerserkTimer = 60000; + } + } + else + m_uiBerserkTimer -= uiDiff; + + DoMeleeAttackIfReady(); + } +}; + +CreatureAI* GetAI_boss_jaraxxus(Creature* pCreature) +{ + return new boss_jaraxxusAI(pCreature); +} + +void AddSC_boss_jaraxxus() +{ + Script* pNewScript; + + pNewScript = new Script; + pNewScript->Name = "boss_jaraxxus"; + pNewScript->GetAI = &GetAI_boss_jaraxxus; + pNewScript->RegisterSelf(); +} diff --git a/src/modules/SD2/scripts/northrend/crusaders_coliseum/trial_of_the_crusader/boss_northrend_beasts.cpp b/src/modules/SD2/scripts/northrend/crusaders_coliseum/trial_of_the_crusader/boss_northrend_beasts.cpp new file mode 100644 index 000000000..7643cacc6 --- /dev/null +++ b/src/modules/SD2/scripts/northrend/crusaders_coliseum/trial_of_the_crusader/boss_northrend_beasts.cpp @@ -0,0 +1,413 @@ +/* Copyright (C) 2006 - 2013 ScriptDev2 + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +/* ScriptData +SDName: +SD%Complete: 0 +SDComment: +SDCategory: +EndScriptData */ + +#include "precompiled.h" +#include "trial_of_the_crusader.h" + +/*###### +## npc_beast_combat_stalker +######*/ + +enum +{ + SAY_TIRION_BEAST_2 = -1649005, + SAY_TIRION_BEAST_3 = -1649006, + + SPELL_BERSERK = 26662, + + PHASE_GORMOK = 0, + PHASE_WORMS = 1, + PHASE_ICEHOWL = 2, +}; + +struct npc_beast_combat_stalkerAI : public Scripted_NoMovementAI +{ + npc_beast_combat_stalkerAI(Creature* pCreature) : Scripted_NoMovementAI(pCreature) + { + m_pInstance = (ScriptedInstance*)pCreature->GetInstanceData(); + Reset(); + m_creature->SetInCombatWithZone(); + } + + ScriptedInstance* m_pInstance; + ObjectGuid m_aSummonedBossGuid[4]; + bool m_bFirstWormDied; + uint32 m_uiBerserkTimer; + uint32 m_uiAttackDelayTimer; + uint32 m_uiNextBeastTimer; + uint32 m_uiPhase; + + void Reset() override + { + m_uiAttackDelayTimer = 0; + m_uiNextBeastTimer = 0; + m_bFirstWormDied = false; + m_uiPhase = PHASE_GORMOK; + + if (m_creature->GetMap()->GetDifficulty() == RAID_DIFFICULTY_10MAN_NORMAL || m_creature->GetMap()->GetDifficulty() == RAID_DIFFICULTY_25MAN_NORMAL) + m_uiBerserkTimer = 15 * MINUTE * IN_MILLISECONDS; + else + m_uiBerserkTimer = 9 * MINUTE * IN_MILLISECONDS; + } + + void MoveInLineOfSight(Unit* /*pWho*/) override {} + + void DamageTaken(Unit* /*pDealer*/, uint32& uiDamage) override + { + uiDamage = 0; + } + + void EnterEvadeMode() override + { + if (m_pInstance) + m_pInstance->SetData(TYPE_NORTHREND_BEASTS, FAIL); + + for (uint8 i = 0; i < 4; ++i) + { + if (Creature* pBoss = m_creature->GetMap()->GetCreature(m_aSummonedBossGuid[i])) + pBoss->ForcedDespawn(); + } + + m_creature->ForcedDespawn(); + } + + void Aggro(Unit* /*pWho*/) override + { + if (m_pInstance) + m_pInstance->SetData(TYPE_NORTHREND_BEASTS, IN_PROGRESS); + } + + void JustSummoned(Creature* pSummoned) override + { + switch (pSummoned->GetEntry()) + { + case NPC_GORMOK: m_uiPhase = PHASE_GORMOK; break; + case NPC_DREADSCALE: m_uiPhase = PHASE_WORMS; break; + case NPC_ICEHOWL: m_uiPhase = PHASE_ICEHOWL; break; + case NPC_ACIDMAW: + // Cast emerge and delayed set in combat? + pSummoned->SetInCombatWithZone(); + m_aSummonedBossGuid[3] = pSummoned->GetObjectGuid(); + return; + } + + m_aSummonedBossGuid[m_uiPhase] = pSummoned->GetObjectGuid(); + + pSummoned->GetMotionMaster()->MovePoint(m_uiPhase, aMovePositions[m_uiPhase][0], aMovePositions[m_uiPhase][1], aMovePositions[m_uiPhase][2]); + + // Next beasts are summoned only for heroic modes + if (m_creature->GetMap()->GetDifficulty() == RAID_DIFFICULTY_10MAN_HEROIC || m_creature->GetMap()->GetDifficulty() == RAID_DIFFICULTY_25MAN_HEROIC) + m_uiNextBeastTimer = 150 * IN_MILLISECONDS; // 2 min 30 + + m_uiAttackDelayTimer = 15000; // TODO, must be checked. + } + + // Only for Dreadscale and Icehowl + void DoSummonNextBeast(uint32 uiBeastEntry) + { + if (uiBeastEntry == NPC_DREADSCALE) + { + if (Creature* pTirion = m_pInstance->GetSingleCreatureFromStorage(NPC_TIRION_A)) + DoScriptText(SAY_TIRION_BEAST_2, pTirion); + + m_creature->SummonCreature(NPC_DREADSCALE, aSpawnPositions[2][0], aSpawnPositions[2][1], aSpawnPositions[2][2], aSpawnPositions[2][3], TEMPSUMMON_DEAD_DESPAWN, 0); + } + else + { + if (Creature* pTirion = m_pInstance->GetSingleCreatureFromStorage(NPC_TIRION_A)) + DoScriptText(SAY_TIRION_BEAST_3, pTirion); + + m_creature->SummonCreature(NPC_ICEHOWL, aSpawnPositions[4][0], aSpawnPositions[4][1], aSpawnPositions[4][2], aSpawnPositions[4][3], TEMPSUMMON_DEAD_DESPAWN, 0); + } + } + + void SummonedCreatureJustDied(Creature* pSummoned) override + { + if (!m_pInstance) + return; + + switch (pSummoned->GetEntry()) + { + case NPC_GORMOK: + if (m_uiPhase == PHASE_GORMOK) + DoSummonNextBeast(NPC_DREADSCALE); + break; + + case NPC_DREADSCALE: + case NPC_ACIDMAW: + if (m_bFirstWormDied && m_uiPhase == PHASE_WORMS) + DoSummonNextBeast(NPC_ICEHOWL); + else + m_bFirstWormDied = true; + break; + + case NPC_ICEHOWL: + m_pInstance->SetData(TYPE_NORTHREND_BEASTS, DONE); + m_creature->ForcedDespawn(); + break; + } + } + + void UpdateAI(const uint32 uiDiff) override + { + if (m_uiNextBeastTimer) + { + if (m_uiNextBeastTimer <= uiDiff) + { + if (m_uiPhase == PHASE_GORMOK) + DoSummonNextBeast(NPC_DREADSCALE); + else if (m_uiPhase == PHASE_WORMS) + DoSummonNextBeast(NPC_ICEHOWL); + + m_uiNextBeastTimer = 0; + } + else + m_uiNextBeastTimer -= uiDiff; + } + + if (m_uiAttackDelayTimer) + { + if (m_uiAttackDelayTimer <= uiDiff) + { + if (m_uiPhase == PHASE_WORMS) + m_creature->SummonCreature(NPC_ACIDMAW, aSpawnPositions[3][0], aSpawnPositions[3][1], aSpawnPositions[3][2], aSpawnPositions[3][3], TEMPSUMMON_DEAD_DESPAWN, 0); + + if (Creature* pBeast = m_creature->GetMap()->GetCreature(m_aSummonedBossGuid[m_uiPhase])) + pBeast->SetInCombatWithZone(); + + m_uiAttackDelayTimer = 0; + } + else + m_uiAttackDelayTimer -= uiDiff; + } + + if (m_uiBerserkTimer) + { + if (m_uiBerserkTimer < uiDiff) + { + for (uint8 i = 0; i < 4; ++i) + { + Creature* pBoss = m_creature->GetMap()->GetCreature(m_aSummonedBossGuid[i]); + if (pBoss && pBoss->IsAlive()) + pBoss->CastSpell(pBoss, SPELL_BERSERK, true); + } + } + else + m_uiBerserkTimer -= uiDiff; + } + + m_creature->SelectHostileTarget(); + } +}; + +CreatureAI* GetAI_npc_beast_combat_stalker(Creature* pCreature) +{ + return new npc_beast_combat_stalkerAI(pCreature); +} + +/*###### +## boss_gormok, vehicle driven by 34800 (multiple times) +######*/ + +struct boss_gormokAI : public ScriptedAI +{ + boss_gormokAI(Creature* pCreature) : ScriptedAI(pCreature) + { + m_pInstance = (ScriptedInstance*)pCreature->GetInstanceData(); + Reset(); + } + + ScriptedInstance* m_pInstance; + + void Reset() override {} + + void JustReachedHome() override + { + } + + void Aggro(Unit* /*pWho*/) override + { + } + + void UpdateAI(const uint32 /*uiDiff*/) override + { + if (!m_creature->SelectHostileTarget() || !m_creature->getVictim()) + return; + + DoMeleeAttackIfReady(); + } +}; + +CreatureAI* GetAI_boss_gormok(Creature* pCreature) +{ + return new boss_gormokAI(pCreature); +} + +/*###### +## boss_acidmaw +######*/ + +struct boss_acidmawAI : public ScriptedAI +{ + boss_acidmawAI(Creature* pCreature) : ScriptedAI(pCreature) + { + m_pInstance = (ScriptedInstance*)pCreature->GetInstanceData(); + Reset(); + } + + ScriptedInstance* m_pInstance; + + void Reset() override {} + + void JustReachedHome() override + { + } + + void Aggro(Unit* /*pWho*/) override + { + } + + void UpdateAI(const uint32 /*uiDiff*/) override + { + if (!m_creature->SelectHostileTarget() || !m_creature->getVictim()) + return; + + DoMeleeAttackIfReady(); + } +}; + +CreatureAI* GetAI_boss_acidmaw(Creature* pCreature) +{ + return new boss_acidmawAI(pCreature); +} + +/*###### +## boss_dreadscale +######*/ + +struct boss_dreadscaleAI : public ScriptedAI +{ + boss_dreadscaleAI(Creature* pCreature) : ScriptedAI(pCreature) + { + m_pInstance = (ScriptedInstance*)pCreature->GetInstanceData(); + Reset(); + } + + ScriptedInstance* m_pInstance; + + void Reset() override {} + + void JustReachedHome() override + { + } + + void Aggro(Unit* /*pWho*/) override + { + } + + void UpdateAI(const uint32 /*uiDiff*/) override + { + if (!m_creature->SelectHostileTarget() || !m_creature->getVictim()) + return; + + DoMeleeAttackIfReady(); + } +}; + +CreatureAI* GetAI_boss_dreadscale(Creature* pCreature) +{ + return new boss_dreadscaleAI(pCreature); +} + +/*###### +## boss_icehowl +######*/ + +enum +{ + EMOTE_MASSIVE_CRASH = -1649039, +}; + +struct boss_icehowlAI : public ScriptedAI +{ + boss_icehowlAI(Creature* pCreature) : ScriptedAI(pCreature) + { + m_pInstance = (ScriptedInstance*)pCreature->GetInstanceData(); + Reset(); + } + + ScriptedInstance* m_pInstance; + + void Reset() override {} + + void JustReachedHome() override + { + } + + void Aggro(Unit* /*pWho*/) override + { + } + + void UpdateAI(const uint32 /*uiDiff*/) override + { + if (!m_creature->SelectHostileTarget() || !m_creature->getVictim()) + return; + + DoMeleeAttackIfReady(); + } +}; + +CreatureAI* GetAI_boss_icehowl(Creature* pCreature) +{ + return new boss_icehowlAI(pCreature); +} + +void AddSC_northrend_beasts() +{ + Script* pNewScript; + + pNewScript = new Script; + pNewScript->Name = "npc_beast_combat_stalker"; + pNewScript->GetAI = &GetAI_npc_beast_combat_stalker; + pNewScript->RegisterSelf(); + + pNewScript = new Script; + pNewScript->Name = "boss_gormok"; + pNewScript->GetAI = &GetAI_boss_gormok; + pNewScript->RegisterSelf(); + + pNewScript = new Script; + pNewScript->Name = "boss_acidmaw"; + pNewScript->GetAI = &GetAI_boss_acidmaw; + pNewScript->RegisterSelf(); + + pNewScript = new Script; + pNewScript->Name = "boss_dreadscale"; + pNewScript->GetAI = &GetAI_boss_dreadscale; + pNewScript->RegisterSelf(); + + pNewScript = new Script; + pNewScript->Name = "boss_icehowl"; + pNewScript->GetAI = &GetAI_boss_icehowl; + pNewScript->RegisterSelf(); +} diff --git a/src/modules/SD2/scripts/northrend/crusaders_coliseum/trial_of_the_crusader/boss_twin_valkyr.cpp b/src/modules/SD2/scripts/northrend/crusaders_coliseum/trial_of_the_crusader/boss_twin_valkyr.cpp new file mode 100644 index 000000000..056f85e45 --- /dev/null +++ b/src/modules/SD2/scripts/northrend/crusaders_coliseum/trial_of_the_crusader/boss_twin_valkyr.cpp @@ -0,0 +1,112 @@ +/* Copyright (C) 2006 - 2013 ScriptDev2 + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +/* ScriptData +SDName: trial_of_the_crusader +SD%Complete: 0 +SDComment: +SDCategory: Crusader Coliseum +EndScriptData */ + +#include "precompiled.h" +#include "trial_of_the_crusader.h" + +enum +{ + SAY_AGGRO = -1649056, + SAY_BERSERK = -1649057, + SAY_COLORSWITCH = -1649058, + SAY_DEATH = -1649059, + SAY_SLAY_1 = -1649060, + SAY_SLAY_2 = -1649061, + SAY_TO_BLACK = -1649062, + SAY_TO_WHITE = -1649063, +}; + +/*###### +## boss_fjola +######*/ + +struct boss_fjolaAI : public ScriptedAI +{ + boss_fjolaAI(Creature* pCreature) : ScriptedAI(pCreature) {Reset();} + + ScriptedInstance* m_pInstance; + + void Reset() override {} + + void Aggro(Unit* /*pWho*/) override + { + m_creature->SetInCombatWithZone(); + } + + void UpdateAI(const uint32 /*uiDiff*/) override + { + if (!m_creature->SelectHostileTarget() || !m_creature->getVictim()) + return; + + DoMeleeAttackIfReady(); + } +}; + +CreatureAI* GetAI_boss_fjola(Creature* pCreature) +{ + return new boss_fjolaAI(pCreature); +} + +/*###### +## boss_eydis +######*/ + +struct boss_eydisAI : public ScriptedAI +{ + boss_eydisAI(Creature* pCreature) : ScriptedAI(pCreature) {Reset();} + + void Reset() override {} + + void Aggro(Unit* /*pWho*/) override + { + m_creature->SetInCombatWithZone(); + } + + void UpdateAI(const uint32 /*uiDiff*/) override + { + if (!m_creature->SelectHostileTarget() || !m_creature->getVictim()) + return; + + DoMeleeAttackIfReady(); + } +}; + +CreatureAI* GetAI_boss_eydis(Creature* pCreature) +{ + return new boss_eydisAI(pCreature); +} + +void AddSC_twin_valkyr() +{ + Script* pNewScript; + + pNewScript = new Script; + pNewScript->Name = "boss_fjola"; + pNewScript->GetAI = &GetAI_boss_fjola; + pNewScript->RegisterSelf(); + + pNewScript = new Script; + pNewScript->Name = "boss_eydis"; + pNewScript->GetAI = &GetAI_boss_fjola; + pNewScript->RegisterSelf(); +} diff --git a/src/modules/SD2/scripts/northrend/crusaders_coliseum/trial_of_the_crusader/instance_trial_of_the_crusader.cpp b/src/modules/SD2/scripts/northrend/crusaders_coliseum/trial_of_the_crusader/instance_trial_of_the_crusader.cpp new file mode 100644 index 000000000..c03738a98 --- /dev/null +++ b/src/modules/SD2/scripts/northrend/crusaders_coliseum/trial_of_the_crusader/instance_trial_of_the_crusader.cpp @@ -0,0 +1,539 @@ +/* Copyright (C) 2006 - 2013 ScriptDev2 + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along 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_trial_of_the_crusader +SD%Complete: 100 +SDComment: +SDCategory: Crusader Coliseum +EndScriptData */ + +#include "precompiled.h" +#include "trial_of_the_crusader.h" + +/* Trial Of The Crusader encounters: +0 - Wipe Count +1 - Northrend Beasts +2 - Jaraxxus +3 - Faction Champions +4 - Twin Valkyr +5 - Anubarak +*/ + +enum +{ + SAY_TIRION_RAID_INTRO_LONG = -1649000, + SAY_RAID_TRIALS_INTRO = -1649001, + + // Northrend Beasts + SAY_TIRION_BEAST_1 = -1649002, + SAY_VARIAN_BEAST_1 = -1649003, + SAY_GARROSH_BEAST_1 = -1649004, + SAY_TIRION_BEAST_SLAY = -1649007, + SAY_TIRION_BEAST_WIPE = -1649008, + + // Jaraxxus Encounter + SAY_TIRION_JARAXXUS_INTRO_1 = -1649009, + SAY_WILFRED_JARAXXUS_INTRO_1 = -1649010, + SAY_WILFRED_JARAXXUS_INTRO_2 = -1649011, + SAY_WILFRED_JARAXXUS_INTRO_3 = -1649012, + SAY_JARAXXUS_JARAXXAS_INTRO_1 = -1649013, + SAY_WILFRED_DEATH = -1649014, + SAY_TIRION_JARAXXUS_INTRO_2 = -1649015, + SAY_JARAXXUS_DEATH = -1649043, + SAY_TIRION_JARAXXUS_EXIT_1 = -1649016, + SAY_GARROSH_JARAXXUS_EXIT_1 = -1649017, + SAY_VARIAN_JARAXXUS_SLAY = -1649018, + SAY_TIRION_JARAXXUS_EXIT_2 = -1649019, + + // Faction-Champions + SAY_TIRION_PVP_INTRO_1 = -1649020, + SAY_GARROSH_PVP_A_INTRO_1 = -1649021, + SAY_VARIAN_PVP_H_INTRO_1 = -1649022, + SAY_TIRION_PVP_INTRO_2 = -1649023, + SAY_VARIAN_PVP_H_INTRO_2 = -1649024, + SAY_GARROSH_PVP_A_INTRO_2 = -1649025, + SAY_VARIAN_PVP_A_WIN = -1649026, + SAY_GARROSH_PVP_H_WIN = -1649027, + SAY_TIRION_PVP_WIN = -1649028, + + // Twin Valkyrs + SAY_TIRION_TWINS_INTRO = -1649029, + SAY_RAID_INTRO_SHORT = -1649030, + SAY_VARIAN_TWINS_A_WIN = -1649031, + SAY_GARROSH_TWINS_H_WIN = -1649032, + SAY_TIRION_TWINS_WIN = -1649033, + + // Anub'Arak Encounter + SAY_LKING_ANUB_INTRO_1 = -1649034, + SAY_TIRION_ABUN_INTRO_1 = -1649035, + SAY_LKING_ANUB_INTRO_2 = -1649036, + SAY_LKING_ANUB_INTRO_3 = -1649037, + + // Epilogue + SAY_TIRION_EPILOGUE = -1649075, +}; + +static const DialogueEntryTwoSide aTocDialogues[] = +{ + // Beasts related, summons in between not handled here + {TYPE_NORTHREND_BEASTS, 0, 0, 0, 24000}, + {SAY_TIRION_BEAST_1, NPC_TIRION_A, 0, 0, 12000}, + {SAY_VARIAN_BEAST_1, NPC_VARIAN, SAY_GARROSH_BEAST_1, NPC_GARROSH, 0}, + {SAY_TIRION_BEAST_SLAY, NPC_TIRION_A, 0, 0, 8000}, + {NPC_RAMSEY_2, 0, 0, 0, 0}, + {SAY_TIRION_BEAST_WIPE, NPC_TIRION_A, 0, 0, 8000}, + {NPC_RAMSEY_1, 0, 0, 0, 0}, + // Jaruxxus (Intro) + {TYPE_JARAXXUS, 0, 0, 0, 1000}, + {SAY_TIRION_JARAXXUS_INTRO_1, NPC_TIRION_A, 0, 0, 6000}, + {NPC_FIZZLEBANG, 0, 0, 0, 26000}, + {SAY_WILFRED_JARAXXUS_INTRO_1, NPC_FIZZLEBANG, 0, 0, 10000}, + {SAY_WILFRED_JARAXXUS_INTRO_2, NPC_FIZZLEBANG, 0, 0, 7000}, + {EVENT_OPEN_PORTAL, 0, 0, 0, 5000}, + {SAY_WILFRED_JARAXXUS_INTRO_3, NPC_FIZZLEBANG, 0, 0, 12000}, // Summon also Jaraxxus + {SAY_JARAXXUS_JARAXXAS_INTRO_1, NPC_JARAXXUS, 0, 0, 6000}, + {SAY_WILFRED_DEATH, NPC_FIZZLEBANG, 0, 0, 1000}, + {EVENT_KILL_FIZZLEBANG, 0, 0, 0, 5000}, // Kill Fizzlebang + {SAY_TIRION_JARAXXUS_INTRO_2, NPC_TIRION_A, 0, 0, 6000}, + {EVENT_JARAXXUS_START_ATTACK, 0, 0, 0, 0}, + // Jaruxxus (Outro) + {SAY_JARAXXUS_DEATH, NPC_JARAXXUS, 0, 0, 6000}, // Jaraxxus Death + {SAY_TIRION_JARAXXUS_EXIT_1, NPC_TIRION_A, 0, 0, 5000}, + {SAY_GARROSH_JARAXXUS_EXIT_1, NPC_GARROSH, 0, 0, 11000}, + {SAY_VARIAN_JARAXXUS_SLAY, NPC_VARIAN, 0, 0, 8000}, + {SAY_TIRION_JARAXXUS_EXIT_2, NPC_TIRION_A, 0, 0, 19000}, + {NPC_RAMSEY_3, 0, 0, 0, 0}, + // Grand Champions + {SAY_TIRION_PVP_INTRO_1, NPC_TIRION_A, 0, 0, 9000}, + {SAY_GARROSH_PVP_A_INTRO_1, NPC_GARROSH, SAY_VARIAN_PVP_H_INTRO_1, NPC_VARIAN, 14000}, + {SAY_TIRION_PVP_INTRO_2, NPC_TIRION_A, 0, 0, 1000}, + {TYPE_FACTION_CHAMPIONS, 0, 0, 0, 5000}, + {SAY_GARROSH_PVP_A_INTRO_2, NPC_GARROSH, SAY_VARIAN_PVP_H_INTRO_2, NPC_VARIAN, 0}, + {SAY_VARIAN_PVP_A_WIN, NPC_VARIAN, SAY_GARROSH_PVP_H_WIN, NPC_GARROSH, 4000}, + {SAY_TIRION_PVP_WIN, NPC_TIRION_A, 0, 0, 27000}, + {NPC_RAMSEY_4, 0, 0, 0, 0}, + // Twin Valkyrs + {TYPE_TWIN_VALKYR, 0, 0, 0, 17000}, + {EVENT_SUMMON_TWINS, 0, 0, 0, 0}, + {EVENT_TWINS_KILLED, 0, 0, 0, 2000}, + {NPC_RAMSEY_5, 0, 0, 0, 4000}, + {SAY_VARIAN_TWINS_A_WIN, NPC_VARIAN, SAY_GARROSH_TWINS_H_WIN, NPC_GARROSH, 1000}, + {SAY_TIRION_TWINS_WIN, NPC_TIRION_A, 0, 0, 0}, + // Anub'arak + {TYPE_ANUBARAK, 0, 0, 0, 19000}, + {SAY_LKING_ANUB_INTRO_1, NPC_THE_LICHKING, 0, 0, 4000}, + {EVENT_ARTHAS_PORTAL, 0, 0, 0, 2000}, + {EVENT_SUMMON_THE_LICHKING, 0, 0, 0, 3000}, + {SAY_TIRION_ABUN_INTRO_1, NPC_TIRION_A, 0, 0, 8000}, + {SAY_LKING_ANUB_INTRO_2, NPC_THE_LICHKING_VISUAL, 0, 0, 18500}, + {EVENT_DESTROY_FLOOR, 0, 0, 0, 2500}, + {SAY_LKING_ANUB_INTRO_3, NPC_THE_LICHKING, 0, 0, 0}, + {0, 0, 0, 0 , 0} +}; + +instance_trial_of_the_crusader::instance_trial_of_the_crusader(Map* pMap) : ScriptedInstance(pMap), DialogueHelper(aTocDialogues), + m_uiTeam(TEAM_NONE) +{ + Initialize(); +} + +void instance_trial_of_the_crusader::Initialize() +{ + memset(&m_auiEncounter, 0, sizeof(m_auiEncounter)); + InitializeDialogueHelper(this); +} + +bool instance_trial_of_the_crusader::IsEncounterInProgress() const +{ + for (uint8 i = TYPE_NORTHREND_BEASTS; i < MAX_ENCOUNTER; ++i) + { + if (m_auiEncounter[i] == IN_PROGRESS) + return true; + } + + return false; +} + +void instance_trial_of_the_crusader::OnCreatureCreate(Creature* pCreature) +{ + switch (pCreature->GetEntry()) + { + case NPC_FIZZLEBANG: + DoUseDoorOrButton(GO_MAIN_GATE); + case NPC_TIRION_A: + case NPC_TIRION_B: + case NPC_VARIAN: + case NPC_GARROSH: + case NPC_JARAXXUS: + case NPC_OPEN_PORTAL_TARGET: + case NPC_EYDIS: + case NPC_WORLD_TRIGGER_LARGE: + case NPC_THE_LICHKING: + case NPC_THE_LICHKING_VISUAL: + break; + default: + return; + } + + m_mNpcEntryGuidStore[pCreature->GetEntry()] = pCreature->GetObjectGuid(); +} + +void instance_trial_of_the_crusader::OnObjectCreate(GameObject* pGo) +{ + switch (pGo->GetEntry()) + { + case GO_COLISEUM_FLOOR: + if (m_auiEncounter[TYPE_TWIN_VALKYR] == DONE) + { + pGo->SetDisplayId(DISPLAYID_DESTROYED_FLOOR); + pGo->SetFlag(GAMEOBJECT_FLAGS, GO_FLAG_UNK_10 | GO_FLAG_NODESPAWN); + pGo->SetGoState(GO_STATE_ACTIVE); + } + break; + case GO_TRIBUTE_CHEST_10H_01: + case GO_TRIBUTE_CHEST_10H_25: + case GO_TRIBUTE_CHEST_10H_45: + case GO_TRIBUTE_CHEST_10H_50: + case GO_TRIBUTE_CHEST_25H_01: + case GO_TRIBUTE_CHEST_25H_25: + case GO_TRIBUTE_CHEST_25H_45: + case GO_TRIBUTE_CHEST_25H_50: + case GO_MAIN_GATE: + case GO_WEB_DOOR: + case GO_PORTAL_DALARAN: + break; + case GO_CRUSADERS_CACHE: + case GO_CRUSADERS_CACHE_25: + case GO_CRUSADERS_CACHE_10_H: + case GO_CRUSADERS_CACHE_25_H: + m_mGoEntryGuidStore[GO_CRUSADERS_CACHE] = pGo->GetObjectGuid(); + return; + default: + return; + } + m_mGoEntryGuidStore[pGo->GetEntry()] = pGo->GetObjectGuid(); +} + +void instance_trial_of_the_crusader::OnPlayerEnter(Player* pPlayer) +{ + if (m_uiTeam) + return; + + m_uiTeam = pPlayer->GetTeam(); + SetDialogueSide(m_uiTeam == ALLIANCE); + + DoSummonRamsey(0); + + // Show wipe world state on heroic difficulty + if (IsHeroicDifficulty()) + { + pPlayer->SendUpdateWorldState(WORLD_STATE_WIPES, 1); + pPlayer->SendUpdateWorldState(WORLD_STATE_WIPES_COUNT, MAX_WIPES_ALLOWED >= GetData(TYPE_WIPE_COUNT) ? MAX_WIPES_ALLOWED - GetData(TYPE_WIPE_COUNT) : 0); + } +} + +void instance_trial_of_the_crusader::SetData(uint32 uiType, uint32 uiData) +{ + switch (uiType) + { + case TYPE_WIPE_COUNT: + // Update data before updating worldstate + m_auiEncounter[uiType] = uiData; + if (IsHeroicDifficulty()) + DoUpdateWorldState(WORLD_STATE_WIPES_COUNT, MAX_WIPES_ALLOWED >= GetData(TYPE_WIPE_COUNT) ? MAX_WIPES_ALLOWED - GetData(TYPE_WIPE_COUNT) : 0); + break; + case TYPE_NORTHREND_BEASTS: + if (uiData == SPECIAL) + { + if (Creature* pTirion = GetSingleCreatureFromStorage(NPC_TIRION_A)) + DoScriptText(m_auiEncounter[uiType] != FAIL ? SAY_TIRION_RAID_INTRO_LONG : SAY_RAID_TRIALS_INTRO, pTirion); + StartNextDialogueText(TYPE_NORTHREND_BEASTS); + } + else if (uiData == FAIL) + { + SetData(TYPE_WIPE_COUNT, m_auiEncounter[TYPE_WIPE_COUNT] + 1); + StartNextDialogueText(SAY_TIRION_BEAST_WIPE); + } + else if (uiData == DONE) + StartNextDialogueText(SAY_TIRION_BEAST_SLAY); + m_auiEncounter[uiType] = uiData; + break; + case TYPE_JARAXXUS: + if (uiData == SPECIAL) + // TODO - What happen in wipe case? + StartNextDialogueText(TYPE_JARAXXUS); + else if (uiData == FAIL) + { + SetData(TYPE_WIPE_COUNT, m_auiEncounter[TYPE_WIPE_COUNT] + 1); + StartNextDialogueText(NPC_RAMSEY_2); + } + else if (uiData == DONE) + StartNextDialogueText(SAY_JARAXXUS_DEATH); + m_auiEncounter[uiType] = uiData; + break; + case TYPE_FACTION_CHAMPIONS: + if (uiData == SPECIAL) + StartNextDialogueText(m_auiEncounter[uiType] != FAIL ? SAY_TIRION_PVP_INTRO_1 : TYPE_FACTION_CHAMPIONS); + else if (uiData == FAIL) + { + SetData(TYPE_WIPE_COUNT, m_auiEncounter[TYPE_WIPE_COUNT] + 1); + StartNextDialogueText(NPC_RAMSEY_3); + } + else if (uiData == DONE) + { + DoRespawnGameObject(GO_CRUSADERS_CACHE, 60 * MINUTE); + StartNextDialogueText(SAY_VARIAN_PVP_A_WIN); + } + m_auiEncounter[uiType] = uiData; + break; + case TYPE_TWIN_VALKYR: + if (uiData == SPECIAL) + { + if (Creature* pTirion = GetSingleCreatureFromStorage(NPC_TIRION_A)) + DoScriptText(m_auiEncounter[uiType] != FAIL ? SAY_TIRION_TWINS_INTRO : SAY_RAID_INTRO_SHORT, pTirion); + StartNextDialogueText(TYPE_TWIN_VALKYR); + } + else if (uiData == FAIL) + { + SetData(TYPE_WIPE_COUNT, m_auiEncounter[TYPE_WIPE_COUNT] + 1); + StartNextDialogueText(NPC_RAMSEY_4); + } + else if (uiData == DONE) + StartNextDialogueText(EVENT_TWINS_KILLED); + m_auiEncounter[uiType] = uiData; + break; + case TYPE_ANUBARAK: + if (uiData == SPECIAL) + StartNextDialogueText(TYPE_ANUBARAK); + else if (uiData == FAIL) + SetData(TYPE_WIPE_COUNT, m_auiEncounter[TYPE_WIPE_COUNT] + 1); + else if (uiData == DONE) + DoHandleEventEpilogue(); + // Handle combat door + if (uiData != SPECIAL) + DoUseDoorOrButton(GO_WEB_DOOR); + m_auiEncounter[uiType] = uiData; + break; + default: + script_error_log("Instance Trial of The Crusader: ERROR SetData = %u for type %u does not exist/not implemented.", uiType, uiData); + return; + } + + if (uiData == DONE || uiType == TYPE_WIPE_COUNT) + { + 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_strInstData = saveStream.str(); + + SaveToDB(); + OUT_SAVE_INST_DATA_COMPLETE; + } +} + +uint32 instance_trial_of_the_crusader::GetData(uint32 uiType) const +{ + if (uiType < MAX_ENCOUNTER) + return m_auiEncounter[uiType]; + + return 0; +} + +void instance_trial_of_the_crusader::Load(const char* chrIn) +{ + if (!chrIn) + { + 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]; + + for (uint8 i = TYPE_NORTHREND_BEASTS; 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; +} + +void instance_trial_of_the_crusader::DoSummonRamsey(uint32 uiEntry) +{ + Player* pPlayer = GetPlayerInMap(); + if (!pPlayer) + return; + + if (uiEntry) + ; + // For initial case, figure which Ramsay to summon + else if (m_auiEncounter[TYPE_TWIN_VALKYR] == DONE) + uiEntry = NPC_RAMSEY_5; + else if (m_auiEncounter[TYPE_FACTION_CHAMPIONS] == DONE) + uiEntry = NPC_RAMSEY_4; + else if (m_auiEncounter[TYPE_JARAXXUS] == DONE) + uiEntry = NPC_RAMSEY_3; + else if (m_auiEncounter[TYPE_NORTHREND_BEASTS] == DONE) + uiEntry = NPC_RAMSEY_2; + else + uiEntry = NPC_RAMSEY_1; + + pPlayer->SummonCreature(uiEntry, aRamsayPositions[0][0], aRamsayPositions[0][1], aRamsayPositions[0][2], aRamsayPositions[0][3], TEMPSUMMON_DEAD_DESPAWN, 0); +} + +void instance_trial_of_the_crusader::DoHandleEventEpilogue() +{ + Player* pPlayer = GetPlayerInMap(); + if (!pPlayer) + return; + + // Spawn Tirion and the mage + if (Creature* pTirion = pPlayer->SummonCreature(NPC_TIRION_B, aSpawnPositions[12][0], aSpawnPositions[12][1], aSpawnPositions[12][2], aSpawnPositions[12][3], TEMPSUMMON_CORPSE_DESPAWN, 0)) + DoScriptText(SAY_TIRION_EPILOGUE, pTirion); + + pPlayer->SummonCreature(NPC_ARGENT_MAGE, aSpawnPositions[13][0], aSpawnPositions[13][1], aSpawnPositions[13][2], aSpawnPositions[13][3], TEMPSUMMON_CORPSE_DESPAWN, 0); + + DoRespawnGameObject(GO_PORTAL_DALARAN, 60 * MINUTE); + + // Spawn the chest for heroic difficulty + if (IsHeroicDifficulty()) + { + if (GetData(TYPE_WIPE_COUNT) == 0) + DoRespawnGameObject(Is25ManDifficulty() ? GO_TRIBUTE_CHEST_25H_50 : GO_TRIBUTE_CHEST_10H_50, 60 * MINUTE); + else if (GetData(TYPE_WIPE_COUNT) < 5) + DoRespawnGameObject(Is25ManDifficulty() ? GO_TRIBUTE_CHEST_25H_45 : GO_TRIBUTE_CHEST_10H_45, 60 * MINUTE); + else if (GetData(TYPE_WIPE_COUNT) < 25) + DoRespawnGameObject(Is25ManDifficulty() ? GO_TRIBUTE_CHEST_25H_25 : GO_TRIBUTE_CHEST_10H_25, 60 * MINUTE); + else + DoRespawnGameObject(Is25ManDifficulty() ? GO_TRIBUTE_CHEST_25H_01 : GO_TRIBUTE_CHEST_10H_01, 60 * MINUTE); + } +} + +void instance_trial_of_the_crusader::JustDidDialogueStep(int32 iEntry) +{ + switch (iEntry) + { + case NPC_RAMSEY_1: + case NPC_RAMSEY_2: + case NPC_RAMSEY_3: + case NPC_RAMSEY_4: + case NPC_RAMSEY_5: + DoSummonRamsey(iEntry); + break; + case SAY_VARIAN_BEAST_1: + if (Player* pPlayer = GetPlayerInMap()) + { + if (Creature* pBeasts = pPlayer->SummonCreature(NPC_BEAST_COMBAT_STALKER, aSpawnPositions[0][0], aSpawnPositions[0][1], aSpawnPositions[0][2], aSpawnPositions[0][3], TEMPSUMMON_DEAD_DESPAWN, 0)) + pBeasts->SummonCreature(NPC_GORMOK, aSpawnPositions[1][0], aSpawnPositions[1][1], aSpawnPositions[1][2], aSpawnPositions[1][3], TEMPSUMMON_DEAD_DESPAWN, 0); + } + break; + case NPC_FIZZLEBANG: + if (Player* pPlayer = GetPlayerInMap()) + pPlayer->SummonCreature(NPC_FIZZLEBANG, aSpawnPositions[5][0], aSpawnPositions[5][1], aSpawnPositions[5][2], aSpawnPositions[5][3], TEMPSUMMON_DEAD_DESPAWN, 0); + break; + case SAY_WILFRED_JARAXXUS_INTRO_1: + DoUseDoorOrButton(GO_MAIN_GATE); // Close main gate + break; + case SAY_WILFRED_JARAXXUS_INTRO_2: + if (Creature* pFizzlebang = GetSingleCreatureFromStorage(NPC_FIZZLEBANG)) + { + pFizzlebang->SummonCreature(NPC_PURPLE_RUNE, aSpawnPositions[11][0], aSpawnPositions[11][1], aSpawnPositions[11][2], aSpawnPositions[11][3], TEMPSUMMON_TIMED_DESPAWN, 15000); + pFizzlebang->CastSpell(pFizzlebang, SPELL_OPEN_PORTAL, false); + } + break; + case EVENT_OPEN_PORTAL: + if (Creature* pOpenPortalTarget = GetSingleCreatureFromStorage(NPC_OPEN_PORTAL_TARGET)) + { + pOpenPortalTarget->CastSpell(pOpenPortalTarget, SPELL_WILFRED_PORTAL, true); + pOpenPortalTarget->ForcedDespawn(9000); + } + break; + case SAY_WILFRED_JARAXXUS_INTRO_3: + if (Player* pPlayer = GetPlayerInMap()) + if (Creature* pJaraxxus = pPlayer->SummonCreature(NPC_JARAXXUS, aSpawnPositions[6][0], aSpawnPositions[6][1], aSpawnPositions[6][2], aSpawnPositions[6][3], TEMPSUMMON_DEAD_DESPAWN, 0)) + pJaraxxus->GetMotionMaster()->MovePoint(POINT_COMBAT_POSITION, aMovePositions[3][0], aMovePositions[3][1], aMovePositions[3][2]); + break; + case EVENT_KILL_FIZZLEBANG: + if (Creature* pJaraxxus = GetSingleCreatureFromStorage(NPC_JARAXXUS)) + pJaraxxus->CastSpell(pJaraxxus, SPELL_FEL_LIGHTNING_KILL, true); + break; + case EVENT_JARAXXUS_START_ATTACK: + if (Creature* pJaraxxus = GetSingleCreatureFromStorage(NPC_JARAXXUS)) + pJaraxxus->SetInCombatWithZone(); + break; + case EVENT_SUMMON_TWINS: + if (Player* pPlayer = GetPlayerInMap()) + { + pPlayer->SummonCreature(NPC_FJOLA, aSpawnPositions[7][0], aSpawnPositions[7][1], aSpawnPositions[7][2], aSpawnPositions[7][3], TEMPSUMMON_DEAD_DESPAWN, 0); + pPlayer->SummonCreature(NPC_EYDIS, aSpawnPositions[8][0], aSpawnPositions[8][1], aSpawnPositions[8][2], aSpawnPositions[8][3], TEMPSUMMON_DEAD_DESPAWN, 0); + } + break; + case SAY_LKING_ANUB_INTRO_1: + if (Player* pPlayer = GetPlayerInMap()) + pPlayer->SummonCreature(NPC_WORLD_TRIGGER_LARGE, aSpawnPositions[9][0], aSpawnPositions[9][1], aSpawnPositions[9][2], aSpawnPositions[9][3], TEMPSUMMON_DEAD_DESPAWN, 0); + break; + case EVENT_ARTHAS_PORTAL: + if (Creature* pWorldTriggerLarge = GetSingleCreatureFromStorage(NPC_WORLD_TRIGGER_LARGE)) + pWorldTriggerLarge->CastSpell(pWorldTriggerLarge, SPELL_ARTHAS_PORTAL, true); + break; + case EVENT_SUMMON_THE_LICHKING: + if (Player* pPlayer = GetPlayerInMap()) + pPlayer->SummonCreature(NPC_THE_LICHKING_VISUAL, aSpawnPositions[10][0], aSpawnPositions[10][1], aSpawnPositions[10][2], aSpawnPositions[10][3], TEMPSUMMON_DEAD_DESPAWN, 0); + break; + case EVENT_DESTROY_FLOOR: + if (GameObject* pColiseumFloor = GetSingleGameObjectFromStorage(GO_COLISEUM_FLOOR)) + { + pColiseumFloor->SetDisplayId(DISPLAYID_DESTROYED_FLOOR); + pColiseumFloor->SetFlag(GAMEOBJECT_FLAGS, GO_FLAG_UNK_10 | GO_FLAG_NODESPAWN); + pColiseumFloor->SetGoState(GO_STATE_ACTIVE); + } + + if (Creature* pLichKingVisual = GetSingleCreatureFromStorage(NPC_THE_LICHKING_VISUAL)) + { + pLichKingVisual->CastSpell(pLichKingVisual, SPELL_FROSTNOVA, true); + // pLichKingVisual->CastSpell(pLichKingVisual, SPELL_CORPSE_TELEPORT, true); // NYI + pLichKingVisual->ForcedDespawn(); + } + + if (Creature* pLichKing = GetSingleCreatureFromStorage(NPC_THE_LICHKING)) + pLichKing->CastSpell(pLichKing, SPELL_DESTROY_FLOOR_KNOCKUP, true); + + if (Creature* pWorldTriggerLarge = GetSingleCreatureFromStorage(NPC_WORLD_TRIGGER_LARGE)) + pWorldTriggerLarge->ForcedDespawn(); + break; + } +} + +InstanceData* GetInstanceData_instance_trial_of_the_crusader(Map* pMap) +{ + return new instance_trial_of_the_crusader(pMap); +} + +void AddSC_instance_trial_of_the_crusader() +{ + Script* pNewScript; + + pNewScript = new Script; + pNewScript->Name = "instance_trial_of_the_crusader"; + pNewScript->GetInstanceData = &GetInstanceData_instance_trial_of_the_crusader; + pNewScript->RegisterSelf(); +} diff --git a/src/modules/SD2/scripts/northrend/crusaders_coliseum/trial_of_the_crusader/trial_of_the_crusader.cpp b/src/modules/SD2/scripts/northrend/crusaders_coliseum/trial_of_the_crusader/trial_of_the_crusader.cpp new file mode 100644 index 000000000..2b7bf3800 --- /dev/null +++ b/src/modules/SD2/scripts/northrend/crusaders_coliseum/trial_of_the_crusader/trial_of_the_crusader.cpp @@ -0,0 +1,218 @@ +/* Copyright (C) 2006 - 2013 ScriptDev2 + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +/* ScriptData +SDName: trial_of_the_crusader +SD%Complete: 0 +SDComment: +SDCategory: Crusader Coliseum +EndScriptData */ + +#include "precompiled.h" +#include "trial_of_the_crusader.h" + +enum +{ + GOSSIP_TEXT_BEAST_INIT = 14664, + GOSSIP_TEXT_BEAST_START = 14665, + GOSSIP_TEXT_BEAST_WIPE_INIT = 14667, + GOSSIP_TEXT_BEAST_WIPE_START = 14668, + + GOSSIP_TEXT_JARAXXUS_INIT = 14678, + GOSSIP_TEXT_JARAXXUS_START = 14680, + GOSSIP_TEXT_JARAXXUS_WIPE_INIT = 14679, + GOSSIP_TEXT_JARAXXUS_WIPE_START = 14682, + + GOSSIP_TEXT_PVP_INIT = 14813, + GOSSIP_TEXT_PVP_START = 14814, + GOSSIP_TEXT_PVP_WIPE_INIT = 14815, + GOSSIP_TEXT_PVP_WIPE_START = 14816, + + GOSSIP_TEXT_TWINS_INIT = 14819, + GOSSIP_TEXT_TWINS_START = 14821, + GOSSIP_TEXT_TWINS_WIPE_INIT = 14820, + GOSSIP_TEXT_TWINS_WIPE_START = 14822, + + GOSSIP_TEXT_ANUB_INIT = 14828, + GOSSIP_TEXT_ANUB_START = 14829, + + GOSSIP_ITEM_BEAST_INIT = -3649000, + GOSSIP_ITEM_BEAST_START = -3649001, + GOSSIP_ITEM_BEAST_WIPE_INIT = -3649002, + GOSSIP_ITEM_BEAST_WIPE_START = -3000000, + + GOSSIP_ITEM_JARAXXUS_INIT = -3649003, + GOSSIP_ITEM_JARAXXUS_START = -3000000, + GOSSIP_ITEM_JARAXXUS_WIPE_INIT = -3649004, + GOSSIP_ITEM_JARAXXUS_WIPE_START = -3000000, + + GOSSIP_ITEM_PVP_INIT = -3649005, + GOSSIP_ITEM_PVP_START = -3649006, + GOSSIP_ITEM_PVP_WIPE_INIT = -3000000, + GOSSIP_ITEM_PVP_WIPE_START = -3000000, + + GOSSIP_ITEM_TWINS_INIT = -3649007, + GOSSIP_ITEM_TWINS_START = -3649008, + GOSSIP_ITEM_TWINS_WIPE_INIT = -3000000, + GOSSIP_ITEM_TWINS_WIPE_START = -3000000, + + GOSSIP_ITEM_ANUB_INIT = -3649009, + GOSSIP_ITEM_ANUB_START = -3649010, +}; + +/*###### +## npc_barrett_ramsey +######*/ + +struct RamseyInfo +{ + uint32 uiEntry; + uint32 uiTextEntry; + int32 iGossipItem; + uint32 uiWipeTextEntry; + int32 iWipeGossipItem; + uint32 uiOptionId; // If . > 0 SetInstData(. , SPECIAL), else open new DiagMenu +}; + +static const RamseyInfo aRamseyInfo[] = +{ + {NPC_RAMSEY_1, GOSSIP_TEXT_BEAST_INIT, GOSSIP_ITEM_BEAST_INIT, GOSSIP_TEXT_BEAST_WIPE_INIT, GOSSIP_ITEM_BEAST_WIPE_INIT, 0}, + {NPC_RAMSEY_1, GOSSIP_TEXT_BEAST_START, GOSSIP_ITEM_BEAST_START, GOSSIP_TEXT_BEAST_WIPE_START, GOSSIP_ITEM_BEAST_WIPE_START, TYPE_NORTHREND_BEASTS}, + + {NPC_RAMSEY_2, GOSSIP_TEXT_JARAXXUS_INIT, GOSSIP_ITEM_JARAXXUS_INIT, GOSSIP_TEXT_JARAXXUS_WIPE_INIT, GOSSIP_ITEM_JARAXXUS_WIPE_INIT, 0}, + {NPC_RAMSEY_2, GOSSIP_TEXT_JARAXXUS_START, GOSSIP_ITEM_JARAXXUS_START, GOSSIP_TEXT_JARAXXUS_WIPE_START, GOSSIP_ITEM_JARAXXUS_WIPE_START, TYPE_JARAXXUS}, + + {NPC_RAMSEY_3, GOSSIP_TEXT_PVP_INIT, GOSSIP_ITEM_PVP_INIT, GOSSIP_TEXT_PVP_WIPE_INIT, GOSSIP_ITEM_PVP_WIPE_INIT, 0}, + {NPC_RAMSEY_3, GOSSIP_TEXT_PVP_START, GOSSIP_ITEM_PVP_START, GOSSIP_TEXT_PVP_WIPE_START, GOSSIP_ITEM_PVP_WIPE_START, TYPE_FACTION_CHAMPIONS}, + + {NPC_RAMSEY_4, GOSSIP_TEXT_TWINS_INIT, GOSSIP_ITEM_TWINS_INIT, GOSSIP_TEXT_TWINS_WIPE_INIT, GOSSIP_ITEM_TWINS_WIPE_INIT, 0}, + {NPC_RAMSEY_4, GOSSIP_TEXT_TWINS_START, GOSSIP_ITEM_TWINS_START, GOSSIP_TEXT_TWINS_WIPE_START, GOSSIP_ITEM_TWINS_WIPE_START, TYPE_TWIN_VALKYR}, + + {NPC_RAMSEY_5, GOSSIP_TEXT_ANUB_INIT, GOSSIP_ITEM_ANUB_INIT, 0, 0, 0}, + {NPC_RAMSEY_5, GOSSIP_TEXT_ANUB_START, GOSSIP_ITEM_ANUB_START, 0, 0, TYPE_ANUBARAK}, +}; + +struct npc_barrett_ramseyAI : public ScriptedAI +{ + npc_barrett_ramseyAI(Creature* pCreature) : ScriptedAI(pCreature) {Reset();} + + ScriptedInstance* m_pInstance; + + void Reset() override {} + + void MovementInform(uint32 uiType, uint32 uiPointId) override + { + if (uiType == POINT_MOTION_TYPE && uiPointId == 1) + m_creature->ForcedDespawn(); + } +}; + +bool GossipHello_npc_barrett_ramsey(Player* pPlayer, Creature* pCreature) +{ + ScriptedInstance* pInstance = (ScriptedInstance*)pCreature->GetInstanceData(); + uint8 uiPos = 0; + uint32 uiType = 0; + + for (uint8 i = 0; i < countof(aRamseyInfo); ++i) + { + if (pCreature->GetEntry() == aRamseyInfo[i].uiEntry) + { + if (!aRamseyInfo[i].uiOptionId) + uiPos = i; + else + { + uiType = aRamseyInfo[i].uiOptionId; + break; + } + } + } + + if (!uiType || !pInstance) + return true; + + if (pInstance->GetData(uiType) == FAIL) + { + pPlayer->ADD_GOSSIP_ITEM_ID(GOSSIP_ICON_CHAT, aRamseyInfo[uiPos].iWipeGossipItem, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF); + pPlayer->SEND_GOSSIP_MENU(aRamseyInfo[uiPos].uiWipeTextEntry, pCreature->GetObjectGuid()); + } + else + { + pPlayer->ADD_GOSSIP_ITEM_ID(GOSSIP_ICON_CHAT, aRamseyInfo[uiPos].iGossipItem, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF); + pPlayer->SEND_GOSSIP_MENU(aRamseyInfo[uiPos].uiTextEntry, pCreature->GetObjectGuid()); + } + + return true; +} + +bool GossipSelect_npc_barrett_ramsey(Player* pPlayer, Creature* pCreature, uint32 /*uiSender*/, uint32 uiAction) +{ + ScriptedInstance* pInstance = (ScriptedInstance*)pCreature->GetInstanceData(); + if (!pInstance) + return true; + + if (uiAction > GOSSIP_ACTION_INFO_DEF) + { + // Begin Event + uint32 uiType = uiAction - GOSSIP_ACTION_INFO_DEF; + if (pInstance->GetData(uiType) == FAIL || pInstance->GetData(uiType) == NOT_STARTED) + pInstance->SetData(uiAction - GOSSIP_ACTION_INFO_DEF, SPECIAL); + + pPlayer->CLOSE_GOSSIP_MENU(); + pCreature->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE); + pCreature->RemoveFlag(UNIT_NPC_FLAGS, UNIT_NPC_FLAG_GOSSIP); + pCreature->GetMotionMaster()->MovePoint(1, aRamsayPositions[1][0], aRamsayPositions[1][1], aRamsayPositions[1][2]); + + return true; + } + + for (uint8 i = 0; i < countof(aRamseyInfo); ++i) + { + if (pCreature->GetEntry() == aRamseyInfo[i].uiEntry && aRamseyInfo[i].uiOptionId) + { + if (pInstance->GetData(aRamseyInfo[i].uiOptionId) == FAIL) + { + pPlayer->ADD_GOSSIP_ITEM_ID(GOSSIP_ICON_CHAT, aRamseyInfo[i].iWipeGossipItem, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + aRamseyInfo[i].uiOptionId); + pPlayer->SEND_GOSSIP_MENU(aRamseyInfo[i].uiWipeTextEntry, pCreature->GetObjectGuid()); + } + else + { + pPlayer->ADD_GOSSIP_ITEM_ID(GOSSIP_ICON_CHAT, aRamseyInfo[i].iGossipItem, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + aRamseyInfo[i].uiOptionId); + pPlayer->SEND_GOSSIP_MENU(aRamseyInfo[i].uiTextEntry, pCreature->GetObjectGuid()); + } + + return true; + } + } + + return true; +} + +CreatureAI* GetAI_npc_barrett_ramsey(Creature* pCreature) +{ + return new npc_barrett_ramseyAI(pCreature); +} + +void AddSC_trial_of_the_crusader() +{ + Script* pNewScript; + + pNewScript = new Script; + pNewScript->Name = "npc_barrett_ramsey"; + pNewScript->GetAI = &GetAI_npc_barrett_ramsey; + pNewScript->pGossipHello = &GossipHello_npc_barrett_ramsey; + pNewScript->pGossipSelect = &GossipSelect_npc_barrett_ramsey; + pNewScript->RegisterSelf(); +} diff --git a/src/modules/SD2/scripts/northrend/crusaders_coliseum/trial_of_the_crusader/trial_of_the_crusader.h b/src/modules/SD2/scripts/northrend/crusaders_coliseum/trial_of_the_crusader/trial_of_the_crusader.h new file mode 100644 index 000000000..02ebc6349 --- /dev/null +++ b/src/modules/SD2/scripts/northrend/crusaders_coliseum/trial_of_the_crusader/trial_of_the_crusader.h @@ -0,0 +1,166 @@ +/* Copyright (C) 2006 - 2013 ScriptDev2 + * This program is free software licensed under GPL version 2 + * Please see the included DOCS/LICENSE.TXT for more information */ + +#ifndef DEF_TRIAL_OF_THE_CRUSADER_H +#define DEF_TRIAL_OF_THE_CRUSADER_H + +enum +{ + MAX_ENCOUNTER = 6, + MAX_WIPES_ALLOWED = 50, + + TYPE_WIPE_COUNT = 0, + TYPE_NORTHREND_BEASTS = 1, + TYPE_JARAXXUS = 2, + TYPE_FACTION_CHAMPIONS = 3, + TYPE_TWIN_VALKYR = 4, + TYPE_ANUBARAK = 5, + + EVENT_OPEN_PORTAL = 6, + EVENT_KILL_FIZZLEBANG = 7, + EVENT_JARAXXUS_START_ATTACK = 8, + EVENT_SUMMON_TWINS = 9, + EVENT_TWINS_KILLED = 10, + EVENT_ARTHAS_PORTAL = 11, + EVENT_SUMMON_THE_LICHKING = 12, + EVENT_DESTROY_FLOOR = 13, + + NPC_BEAST_COMBAT_STALKER = 36549, + NPC_GORMOK = 34796, + NPC_ACIDMAW = 35144, + NPC_DREADSCALE = 34799, + NPC_ICEHOWL = 34797, + NPC_JARAXXUS = 34780, + NPC_FJOLA = 34497, + NPC_EYDIS = 34496, + NPC_ANUBARAK = 34564, + + NPC_TIRION_A = 34996, + NPC_TIRION_B = 36095, // Summoned after his text (Champions, you're alive! Not only have you defeated every challenge of the Trial of the Crusader, but also thwarted Arthas' plans! Your skill and cunning will prove to be a powerful weapon against the Scourge. Well done! Allow one of the Crusade's mages to transport you to the surface!) is said.. + NPC_ARGENT_MAGE = 36097, // Summoned along with Tirion B + NPC_VARIAN = 34990, + NPC_GARROSH = 34995, + NPC_FIZZLEBANG = 35458, + NPC_OPEN_PORTAL_TARGET = 17965, + NPC_WORLD_TRIGGER_LARGE = 22517, // Used for Lich King summon event + NPC_THE_LICHKING = 16980, + NPC_THE_LICHKING_VISUAL = 35877, + NPC_RAMSEY_1 = 34816, + NPC_RAMSEY_2 = 35035, + NPC_RAMSEY_3 = 35766, + NPC_RAMSEY_4 = 35770, + NPC_RAMSEY_5 = 35771, + NPC_RAMSEY_6 = 35895, // Unknown what these three NPCs are used for, maybe horde events? + NPC_RAMSEY_7 = 35909, + NPC_RAMSEY_8 = 35910, + + NPC_PURPLE_RUNE = 35651, + + GO_MAIN_GATE = 195647, + GO_WEST_GATE = 195648, // entrance gate + GO_SOUTH_GATE = 195649, // south and north doors are used to allow the Champions to enter the arena + GO_NORTH_GATE = 195650, + GO_COLISEUM_FLOOR = 195527, + GO_WEB_DOOR = 195485, + GO_PORTAL_DALARAN = 195682, + + GO_CRUSADERS_CACHE = 195631, + GO_CRUSADERS_CACHE_25 = 195632, + GO_CRUSADERS_CACHE_10_H = 195633, + GO_CRUSADERS_CACHE_25_H = 195635, + + GO_TRIBUTE_CHEST_10H_01 = 195665, + GO_TRIBUTE_CHEST_10H_25 = 195666, + GO_TRIBUTE_CHEST_10H_45 = 195667, + GO_TRIBUTE_CHEST_10H_50 = 195668, + + GO_TRIBUTE_CHEST_25H_01 = 195669, + GO_TRIBUTE_CHEST_25H_25 = 195670, + GO_TRIBUTE_CHEST_25H_45 = 195671, + GO_TRIBUTE_CHEST_25H_50 = 195672, + + SPELL_OPEN_PORTAL = 67864, + SPELL_FEL_LIGHTNING_KILL = 67888, + SPELL_WILFRED_PORTAL = 68424, + SPELL_ARTHAS_PORTAL = 51807, + SPELL_FROSTNOVA = 68198, + SPELL_CORPSE_TELEPORT = 69016, // NYI + SPELL_DESTROY_FLOOR_KNOCKUP = 68193, + + DISPLAYID_DESTROYED_FLOOR = 9060, + POINT_COMBAT_POSITION = 10, + + WORLD_STATE_WIPES = 4390, + WORLD_STATE_WIPES_COUNT = 4389, +}; + +static const float aRamsayPositions[2][4] = +{ + {559.1528f, 90.55729f, 395.2734f, 5.078908f}, // Summon Position + {563.556f, 78.72571f, 395.2125f, 0.0f} // Movement Position +}; + +static const float aSpawnPositions[][4] = +{ + {563.8941f, 137.3333f, 405.8467f, 0.0f}, // Beast combat stalker (Summoned when SAY_VARIAN_BEAST_1) + {563.9358f, 229.8299f, 394.8061f, 4.694936f}, // Gormok (vehicle) (Summoned when SAY_VARIAN_BEAST_1) + {564.3301f, 232.1549f, 394.8188f, 1.621917f}, // Dreadscale (Summoned when Tirion says SAY_TIRION_BEAST_2) + {549.5139f, 170.1389f, 394.7965f, 5.009095f}, // Acidmaw (Summoned(?) 14s after Dreadscale) + {563.6081f, 228.1491f, 394.7057f, 4.664022f}, // Icehowl (Summoned when SAY_TIRION_BEAST_3) + {563.6007f, 208.5278f, 395.2696f, 4.729842f}, // Fizzlebang + {563.8264f, 140.6563f, 393.9861f, 4.694936f}, // Jaraxxus + {571.684f, 204.9028f, 399.263f, 4.590216f}, // Fjola + {555.4514f, 205.8889f, 399.2634f, 4.886922f}, // Eydis + {563.6996f, 175.9826f, 394.5042f, 4.694936f}, // World Trigger Large + {563.5712f, 174.8351f, 394.4954f, 4.712389f}, // Lich King + {563.6858f, 139.4323f, 393.9862f, 4.694936f}, // Purple Rune / Center Position + {648.9169f, 131.0209f, 141.6159f, 0.0f}, // Tirion B + {649.1610f, 142.0399f, 141.3060f, 0.0f}, // Argent mage +}; + +static const float aMovePositions[][3] = +{ + {563.748f, 179.766f, 394.4862f}, // Gormok + {576.5347f, 168.9514f, 394.7064f}, // Dreadscale + {563.8577f, 176.5885f, 394.4417f}, // Icehowl + {563.7223f, 131.2344f, 393.9901f}, // Jaraxxus +}; + +class instance_trial_of_the_crusader : public ScriptedInstance, private DialogueHelper +{ + public: + instance_trial_of_the_crusader(Map* pMap); + + void Initialize() override; + bool IsEncounterInProgress() const override; + + void OnCreatureCreate(Creature* pCreature) override; + void OnObjectCreate(GameObject* pGo) override; + + void OnPlayerEnter(Player* pPlayer) override; + + void SetData(uint32 uiType, uint32 uiData) override; + uint32 GetData(uint32 uiType) const override; + + // Difficulty wrappers + bool IsHeroicDifficulty() { return instance->GetDifficulty() == RAID_DIFFICULTY_10MAN_HEROIC || instance->GetDifficulty() == RAID_DIFFICULTY_25MAN_HEROIC; } + bool Is25ManDifficulty() { return instance->GetDifficulty() == RAID_DIFFICULTY_25MAN_NORMAL || instance->GetDifficulty() == RAID_DIFFICULTY_25MAN_HEROIC; } + + const char* Save() const override { return m_strInstData.c_str(); } + void Load(const char* chrIn) override; + + void Update(uint32 uiDiff) { DialogueUpdate(uiDiff); } + + private: + void DoSummonRamsey(uint32 uiEntry); + void JustDidDialogueStep(int32 iEntry) override; + void DoHandleEventEpilogue(); + + uint32 m_auiEncounter[MAX_ENCOUNTER]; + std::string m_strInstData; + + Team m_uiTeam; +}; + +#endif diff --git a/src/modules/SD2/scripts/northrend/dalaran.cpp b/src/modules/SD2/scripts/northrend/dalaran.cpp new file mode 100644 index 000000000..2f27f65af --- /dev/null +++ b/src/modules/SD2/scripts/northrend/dalaran.cpp @@ -0,0 +1,96 @@ +/* Copyright (C) 2006 - 2013 ScriptDev2 + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +/* ScriptData +SDName: Dalaran +SD%Complete: 100 +SDComment: +SDCategory: Dalaran +EndScriptData */ + +/* ContentData +npc_dalaran_guardian_mage +EndContentData */ + +#include "precompiled.h" + +enum +{ + SPELL_TRESPASSER_H = 54029, + SPELL_TRESPASSER_A = 54028, + + // Exception auras - used for quests 20439 and 24451 + SPELL_COVENANT_DISGUISE_1 = 70971, + SPELL_COVENANT_DISGUISE_2 = 70972, + SPELL_SUNREAVER_DISGUISE_1 = 70973, + SPELL_SUNREAVER_DISGUISE_2 = 70974, + + AREA_ID_SUNREAVER = 4616, + AREA_ID_SILVER_ENCLAVE = 4740 +}; + +struct npc_dalaran_guardian_mageAI : public ScriptedAI +{ + npc_dalaran_guardian_mageAI(Creature* pCreature) : ScriptedAI(pCreature) { Reset(); } + + void MoveInLineOfSight(Unit* pWho) override + { + if (m_creature->GetDistanceZ(pWho) > CREATURE_Z_ATTACK_RANGE) + return; + + if (pWho->IsTargetableForAttack() && m_creature->IsHostileTo(pWho)) + { + // exception for quests 20439 and 24451 + if (pWho->HasAura(SPELL_COVENANT_DISGUISE_1) || pWho->HasAura(SPELL_COVENANT_DISGUISE_2) || + pWho->HasAura(SPELL_SUNREAVER_DISGUISE_1) || pWho->HasAura(SPELL_SUNREAVER_DISGUISE_2)) + return; + + if (m_creature->IsWithinDistInMap(pWho, m_creature->GetAttackDistance(pWho)) && m_creature->IsWithinLOSInMap(pWho)) + { + if (Player* pPlayer = pWho->GetCharmerOrOwnerPlayerOrPlayerItself()) + { + // it's mentioned that pet may also be teleported, if so, we need to tune script to apply to those in addition. + + if (pPlayer->GetAreaId() == AREA_ID_SILVER_ENCLAVE) + DoCastSpellIfCan(pPlayer, SPELL_TRESPASSER_A); + else if (pPlayer->GetAreaId() == AREA_ID_SUNREAVER) + DoCastSpellIfCan(pPlayer, SPELL_TRESPASSER_H); + } + } + } + } + + void AttackedBy(Unit* /*pAttacker*/) override {} + + void Reset() override {} + + void UpdateAI(const uint32 /*uiDiff*/) override {} +}; + +CreatureAI* GetAI_npc_dalaran_guardian_mage(Creature* pCreature) +{ + return new npc_dalaran_guardian_mageAI(pCreature); +} + +void AddSC_dalaran() +{ + Script* pNewScript; + + pNewScript = new Script; + pNewScript->Name = "npc_dalaran_guardian_mage"; + pNewScript->GetAI = &GetAI_npc_dalaran_guardian_mage; + pNewScript->RegisterSelf(); +} diff --git a/src/modules/SD2/scripts/northrend/dragonblight.cpp b/src/modules/SD2/scripts/northrend/dragonblight.cpp new file mode 100644 index 000000000..1f21a2d87 --- /dev/null +++ b/src/modules/SD2/scripts/northrend/dragonblight.cpp @@ -0,0 +1,175 @@ +/* Copyright (C) 2006 - 2013 ScriptDev2 + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along 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: Quest support: 12166, 12261. +SDCategory: Dragonblight +EndScriptData */ + +/* ContentData +npc_destructive_ward +EndContentData */ + +#include "precompiled.h" + +/*###### +# npc_destructive_ward +#####*/ + +enum +{ + SAY_WARD_POWERUP = -1000664, + SAY_WARD_CHARGED = -1000665, + + SPELL_DESTRUCTIVE_PULSE = 48733, + SPELL_DESTRUCTIVE_BARRAGE = 48734, + SPELL_DESTRUCTIVE_WARD_POWERUP = 48735, + + SPELL_SUMMON_SMOLDERING_SKELETON = 48715, + SPELL_SUMMON_SMOLDERING_CONSTRUCT = 48718, + SPELL_DESTRUCTIVE_WARD_KILL_CREDIT = 52409, + + MAX_STACK = 1, +}; + +// Script is based on real event from you-know-where. +// Some sources show the event in a bit different way, for unknown reason. +// Devs decided to add it in the below way, until more details can be obtained. + +// It will be only two power-up's, where other sources has a different count (2-4 stacks has been observed) +// Probably caused by either a change in a patch (bugfix?) or the powerup has a condition (some +// sources suggest this, but without any explanation about what this might be) + +struct npc_destructive_wardAI : public Scripted_NoMovementAI +{ + npc_destructive_wardAI(Creature* pCreature) : Scripted_NoMovementAI(pCreature) + { + m_uiPowerTimer = 30000; + m_uiStack = 0; + m_uiSummonTimer = 2000; + m_bCanPulse = false; + m_bFirst = true; + Reset(); + } + + uint32 m_uiPowerTimer; + uint32 m_uiStack; + uint32 m_uiSummonTimer; + bool m_bFirst; + bool m_bCanPulse; + + void Reset() override { } + + void JustSummoned(Creature* pSummoned) override + { + pSummoned->AI()->AttackStart(m_creature); + } + + void UpdateAI(const uint32 uiDiff) override + { + if (m_bCanPulse) + { + if (DoCastSpellIfCan(m_creature, m_uiStack > MAX_STACK ? SPELL_DESTRUCTIVE_BARRAGE : SPELL_DESTRUCTIVE_PULSE) == CAST_OK) + m_bCanPulse = false; + } + + if (m_uiSummonTimer) + { + if (m_uiSummonTimer <= uiDiff) + { + if (m_bFirst) + m_uiSummonTimer = 25000; + else + m_uiSummonTimer = 0; + + switch (m_uiStack) + { + case 0: + DoCastSpellIfCan(m_creature, SPELL_SUMMON_SMOLDERING_SKELETON, CAST_TRIGGERED); + break; + case 1: + DoCastSpellIfCan(m_creature, SPELL_SUMMON_SMOLDERING_CONSTRUCT, CAST_TRIGGERED); + + if (m_bFirst) + break; + + DoCastSpellIfCan(m_creature, SPELL_SUMMON_SMOLDERING_CONSTRUCT, CAST_TRIGGERED); + break; + case 2: + if (m_bFirst) + break; + + DoCastSpellIfCan(m_creature, SPELL_SUMMON_SMOLDERING_SKELETON, CAST_TRIGGERED); + DoCastSpellIfCan(m_creature, SPELL_SUMMON_SMOLDERING_SKELETON, CAST_TRIGGERED); + DoCastSpellIfCan(m_creature, SPELL_SUMMON_SMOLDERING_CONSTRUCT, CAST_TRIGGERED); + break; + } + + m_bFirst = !m_bFirst; + } + else + m_uiSummonTimer -= uiDiff; + } + + if (!m_uiPowerTimer) + return; + + if (m_uiPowerTimer <= uiDiff) + { + if (m_uiStack > MAX_STACK) + { + if (DoCastSpellIfCan(m_creature, SPELL_DESTRUCTIVE_WARD_KILL_CREDIT) == CAST_OK) + { + DoScriptText(SAY_WARD_CHARGED, m_creature, m_creature->GetOwner()); + m_uiPowerTimer = 0; + m_uiSummonTimer = 0; + m_bCanPulse = true; + } + } + else if (DoCastSpellIfCan(m_creature, SPELL_DESTRUCTIVE_WARD_POWERUP) == CAST_OK) + { + DoScriptText(SAY_WARD_POWERUP, m_creature, m_creature->GetOwner()); + + m_uiPowerTimer = 30000; + m_uiSummonTimer = 2000; + + m_bFirst = true; + m_bCanPulse = true; // pulse right after each charge + + ++m_uiStack; + } + } + else + m_uiPowerTimer -= uiDiff; + } +}; + +CreatureAI* GetAI_npc_destructive_ward(Creature* pCreature) +{ + return new npc_destructive_wardAI(pCreature); +} + +void AddSC_dragonblight() +{ + Script* pNewScript; + + pNewScript = new Script; + pNewScript->Name = "npc_destructive_ward"; + pNewScript->GetAI = &GetAI_npc_destructive_ward; + pNewScript->RegisterSelf(); +} diff --git a/src/modules/SD2/scripts/northrend/draktharon_keep/boss_novos.cpp b/src/modules/SD2/scripts/northrend/draktharon_keep/boss_novos.cpp new file mode 100644 index 000000000..c8b8a1964 --- /dev/null +++ b/src/modules/SD2/scripts/northrend/draktharon_keep/boss_novos.cpp @@ -0,0 +1,421 @@ +/* Copyright (C) 2006 - 2013 ScriptDev2 + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +/* ScriptData +SDName: Boss_Novos +SD%Complete: 90% +SDComment: Summon Timers are vague +SDCategory: Drak'Tharon Keep +EndScriptData */ + +#include "precompiled.h" +#include "draktharon_keep.h" + +enum +{ + SAY_AGGRO = -1600005, + SAY_DEATH = -1600006, + SAY_KILL = -1600007, + SAY_ADDS = -1600008, + SAY_BUBBLE_1 = -1600009, + SAY_BUBBLE_2 = -1600010, + + EMOTE_ASSISTANCE = -1600011, + + SPELL_ARCANE_FIELD = 47346, + SPELL_IMMUNITY = 34098, + SPELL_SUMMON_MINIONS_H = 59910, + SPELL_FROSTBOLT = 49037, + SPELL_FROSTBOLT_H = 59855, + SPELL_ARCANE_BLAST = 49198, + SPELL_ARCANE_BLAST_H = 59909, + SPELL_BLIZZARD = 49034, + SPELL_BLIZZARD_H = 59854, + SPELL_TOUCH_OF_MISERY = 50090, // TODO - purpose of this spell (triggers SPELL_WRATH_OF_MISERY) unknown + SPELL_WRATH_OF_MISERY = 50089, + SPELL_WRATH_OF_MISERY_H = 59856, + + // SPELL_SUMMON_CRYSTAL_HANDLER = 49179, // Spell seems to be unused, perhaps only server-side, and especially no suitable positioned caster are found for this spell + SPELL_SUMMON_FETID_TROLL_CORPSE = 49103, + SPELL_SUMMON_HULKING_CORPSE = 49104, + SPELL_SUMMON_RISON_SHADOWCASTER = 49105, + + // Spells 'Crystal Handler Death' 47336, 55801, 55803, 55805 (defined in instance script) + + NPC_CRYSTAL_HANDLER = 26627, + NPC_HULKING_CORPSE = 27597, + NPC_FETID_TROLL_CORPSE = 27598, + NPC_RISON_SHADOWCASTER = 27600, + NPC_ROTTED_TROLL_CORPSE = 32786, // On heroic as effect of SPELL_SUMMON_MINIONS_H +}; + +// The Crystal Handlers are summoned around the two entrances of the room +static const float aHandlerSummonPos[2][3] = +{ + { -342.894836f, -727.016846f, 28.581081f}, + { -410.644653f, -731.826904f, 28.580412f} +}; + +/*###### +## boss_novos +######*/ + +enum Phases +{ + PHASE_SHIELDED = 0, + PHASE_WAITING = 1, + PHASE_NORMAL = 2, +}; + +struct boss_novosAI : public Scripted_NoMovementAI +{ + boss_novosAI(Creature* pCreature) : Scripted_NoMovementAI(pCreature) + { + m_pInstance = (instance_draktharon_keep*)pCreature->GetInstanceData(); + m_bIsRegularMode = pCreature->GetMap()->IsRegularDifficulty(); + Reset(); + } + + instance_draktharon_keep* m_pInstance; + bool m_bIsRegularMode; + + uint32 m_uiSummonHandlerTimer; // TODO the summoning timers are weak + uint32 m_uiSummonShadowcasterTimer; + uint32 m_uiSummonFetidTrollTimer; + uint32 m_uiSummonHulkingCorpseTimer; + uint32 m_uiPhaseTimer; + uint32 m_uiArcaneBlastTimer; + uint32 m_uiBlizzardTimer; + uint32 m_uiWrathTimer; + + uint8 m_uiSummonedHandlers; + uint8 m_uiLostCrystals; + Phases m_uiPhase; + + void Reset() override + { + m_uiSummonHandlerTimer = 25000; + m_uiSummonShadowcasterTimer = 3000; + m_uiSummonFetidTrollTimer = 10000; + m_uiSummonHulkingCorpseTimer = 30000; + m_uiPhaseTimer = 3000; + m_uiArcaneBlastTimer = urand(6000, 8000); + m_uiBlizzardTimer = urand(8000, 12000); + m_uiWrathTimer = urand(12000, 15000); + + m_uiSummonedHandlers = 0; + m_uiLostCrystals = 0; + // This ensures that in the shield phase m_pInstance is valid + m_uiPhase = m_pInstance ? PHASE_SHIELDED : PHASE_NORMAL; + + m_creature->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE); + } + + void LostOneCrystal() + { + ++m_uiLostCrystals; + + DoScriptText(urand(0, 1) ? SAY_BUBBLE_1 : SAY_BUBBLE_2, m_creature); + + if (m_uiLostCrystals == MAX_CRYSTALS) // Enter Phase 2 + m_uiPhase = PHASE_WAITING; + } + + void MoveInLineOfSight(Unit* pWho) override + { + // An Add reached the ground, if its z-pos is near the z pos of Novos + if (pWho->GetEntry() == NPC_HULKING_CORPSE || pWho->GetEntry() == NPC_FETID_TROLL_CORPSE || pWho->GetEntry() == NPC_RISON_SHADOWCASTER) + { + // Add reached ground, and the failure has not yet been reported + if (pWho->GetPositionZ() < m_creature->GetPositionZ() + 1.5f && m_pInstance && m_pInstance->GetData(TYPE_NOVOS) == IN_PROGRESS) + m_pInstance->SetData(TYPE_NOVOS, SPECIAL); + return; + } + + ScriptedAI::MoveInLineOfSight(pWho); + } + + void Aggro(Unit* /*pWho*/) override + { + DoScriptText(SAY_AGGRO, m_creature); + + DoCastSpellIfCan(m_creature, m_bIsRegularMode ? SPELL_ARCANE_BLAST : SPELL_ARCANE_BLAST_H, CAST_TRIGGERED); + + DoCastSpellIfCan(m_creature, SPELL_IMMUNITY, CAST_TRIGGERED); + m_creature->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE); + + DoCastSpellIfCan(m_creature, SPELL_ARCANE_FIELD); + + if (m_pInstance) + m_pInstance->SetData(TYPE_NOVOS, IN_PROGRESS); + } + + void KilledUnit(Unit* /*pVictim*/) override + { + DoScriptText(SAY_KILL, m_creature); + } + + void JustDied(Unit* /*pKiller*/) override + { + DoScriptText(SAY_DEATH, m_creature); + + if (m_pInstance) + m_pInstance->SetData(TYPE_NOVOS, DONE); + } + + void JustReachedHome() override + { + if (m_pInstance) + m_pInstance->SetData(TYPE_NOVOS, FAIL); + } + + void JustSummoned(Creature* pSummoned) override + { + switch (pSummoned->GetEntry()) + { + case NPC_CRYSTAL_HANDLER: + case NPC_ROTTED_TROLL_CORPSE: + if (Unit* pTarget = m_creature->SelectAttackingTarget(ATTACKING_TARGET_RANDOM, 0)) + pSummoned->AI()->AttackStart(pTarget); + break; + } + } + + void SummonedCreatureJustDied(Creature* pSummoned) override + { + if (pSummoned->GetEntry() == NPC_CRYSTAL_HANDLER) + { + uint8 uiIndex = 0; + if (m_pInstance) + { + if (Creature* pTarget = m_pInstance->GetNextCrystalTarget(pSummoned, uiIndex)) + pSummoned->CastSpell(pTarget, aCrystalHandlerDeathSpells[uiIndex], true, NULL, NULL, m_creature->GetObjectGuid()); + } + } + } + + void UpdateAI(const uint32 uiDiff) override + { + if (!m_creature->SelectHostileTarget() || !m_creature->getVictim()) + return; + + switch (m_uiPhase) + { + case PHASE_SHIELDED: // Event Phase, only summoning of mobs + if (m_uiSummonHandlerTimer < uiDiff) + { + float fX, fY, fZ; + ++m_uiSummonedHandlers; + m_creature->GetRandomPoint(aHandlerSummonPos[m_uiSummonedHandlers % 2][0], aHandlerSummonPos[m_uiSummonedHandlers % 2][1], aHandlerSummonPos[m_uiSummonedHandlers % 2][2], 10.0f, fX, fY, fZ); + m_creature->SummonCreature(NPC_CRYSTAL_HANDLER, fX, fY, fZ, 0.0f, TEMPSUMMON_DEAD_DESPAWN, 0); + + DoScriptText(SAY_ADDS, m_creature); + DoScriptText(EMOTE_ASSISTANCE, m_creature); + + m_uiSummonHandlerTimer = 40000; + } + else + m_uiSummonHandlerTimer -= uiDiff; + + if (m_uiSummonShadowcasterTimer < uiDiff) + { + if (Creature* pSummoner = m_pInstance->GetSummonDummy()) + pSummoner->CastSpell(pSummoner, SPELL_SUMMON_RISON_SHADOWCASTER, false, NULL, NULL, m_creature->GetObjectGuid()); + m_uiSummonShadowcasterTimer = 25000; + } + else + m_uiSummonShadowcasterTimer -= uiDiff; + + if (m_uiSummonFetidTrollTimer < uiDiff) + { + if (Creature* pSummoner = m_pInstance->GetSummonDummy()) + pSummoner->CastSpell(pSummoner, SPELL_SUMMON_FETID_TROLL_CORPSE, false, NULL, NULL, m_creature->GetObjectGuid()); + m_uiSummonFetidTrollTimer = 5000; + } + else + m_uiSummonFetidTrollTimer -= uiDiff; + + if (m_uiSummonHulkingCorpseTimer < uiDiff) + { + if (Creature* pSummoner = m_pInstance->GetSummonDummy()) + pSummoner->CastSpell(pSummoner, SPELL_SUMMON_HULKING_CORPSE, false, NULL, NULL, m_creature->GetObjectGuid()); + m_uiSummonHulkingCorpseTimer = 30000; + } + else + m_uiSummonHulkingCorpseTimer -= uiDiff; + + break; + + case PHASE_WAITING: // Short delay between last destroyed crystal and entering combat + if (m_uiPhaseTimer < uiDiff) + { + m_uiPhase = PHASE_NORMAL; + // Remove Immunity and Shield Aura + m_creature->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE); + m_creature->RemoveAllAuras(); + + if (!m_bIsRegularMode) + DoCastSpellIfCan(m_creature, SPELL_SUMMON_MINIONS_H, CAST_INTERRUPT_PREVIOUS); + else + m_creature->InterruptNonMeleeSpells(true); + } + else + m_uiPhaseTimer -= uiDiff; + + break; + + case PHASE_NORMAL: // Normal Phase, attack enemies + if (m_uiArcaneBlastTimer < uiDiff) + { + // TODO - might be possible that this spell is only casted, when there is an enemy in range + if (DoCastSpellIfCan(m_creature, m_bIsRegularMode ? SPELL_ARCANE_BLAST : SPELL_ARCANE_BLAST_H) == CAST_OK) + m_uiArcaneBlastTimer = urand(7000, 9000); + } + else + m_uiArcaneBlastTimer -= uiDiff; + + if (m_uiBlizzardTimer < uiDiff) + { + if (Unit* pTarget = m_creature->SelectAttackingTarget(ATTACKING_TARGET_RANDOM, 0)) + if (DoCastSpellIfCan(pTarget, m_bIsRegularMode ? SPELL_BLIZZARD : SPELL_BLIZZARD_H) == CAST_OK) + m_uiBlizzardTimer = urand(9000, 13500); + } + else + m_uiBlizzardTimer -= uiDiff; + + if (m_uiWrathTimer < uiDiff) + { + if (Unit* pTarget = m_creature->SelectAttackingTarget(ATTACKING_TARGET_RANDOM, 0)) + if (DoCastSpellIfCan(pTarget, m_bIsRegularMode ? SPELL_WRATH_OF_MISERY : SPELL_WRATH_OF_MISERY_H) == CAST_OK) + m_uiWrathTimer = urand(12500, 17200); + } + else + m_uiWrathTimer -= uiDiff; + + if (!m_creature->IsNonMeleeSpellCasted(true)) // TODO Use this additional check, because might want to change the random target to be a target that is in LoS (which then is expensive) + if (Unit* pTarget = m_creature->SelectAttackingTarget(ATTACKING_TARGET_RANDOM, 0)) + DoCastSpellIfCan(pTarget, m_bIsRegularMode ? SPELL_FROSTBOLT : SPELL_FROSTBOLT_H); + + break; + } + } +}; + +CreatureAI* GetAI_boss_novos(Creature* pCreature) +{ + return new boss_novosAI(pCreature); +} + +// Small helper script to handle summoned adds for Novos +struct npc_crystal_channel_targetAI : public ScriptedAI +{ + npc_crystal_channel_targetAI(Creature* pCreature) : ScriptedAI(pCreature) + { + m_pInstance = (instance_draktharon_keep*)pCreature->GetInstanceData(); + } + + instance_draktharon_keep* m_pInstance; + + void Reset() override {} + void MoveInLineOfSight(Unit* /*pWho*/) override {} + void AttackStart(Unit* /*pWho*/) override {} + void UpdateAI(const uint32 /*uiDiff*/) override {} + + void JustSummoned(Creature* pSummoned) override + { + if (pSummoned->GetEntry() == NPC_HULKING_CORPSE || pSummoned->GetEntry() == NPC_FETID_TROLL_CORPSE || pSummoned->GetEntry() == NPC_RISON_SHADOWCASTER) + { + // Let them move down the stairs + float fX, fY, fZ; + + // The end of the stairs is approximately at 1/3 of the way between summoning-position and novos, height of Novos + if (Creature* pNovos = m_pInstance->GetSingleCreatureFromStorage(NPC_NOVOS)) + { + m_creature->GetRandomPoint(0.70 * pNovos->GetPositionX() + 0.30 * pSummoned->GetPositionX(), 0.70 * pNovos->GetPositionY() + 0.30 * pSummoned->GetPositionY(), pNovos->GetPositionZ() + 1.5f, 4.0f, fX, fY, fZ); + pSummoned->GetMotionMaster()->MovePoint(1, fX, fY, fZ); + } + } + } + + void SummonedMovementInform(Creature* pSummoned, uint32 uiMotionType, uint32 uiPointId) override + { + if (uiPointId != 1 || uiMotionType != POINT_MOTION_TYPE || (pSummoned->GetEntry() != NPC_HULKING_CORPSE && pSummoned->GetEntry() != NPC_FETID_TROLL_CORPSE && pSummoned->GetEntry() != NPC_RISON_SHADOWCASTER)) + return; + + if (!pSummoned->IsInCombat() && m_pInstance) + { + if (Creature* pNovos = m_pInstance->GetSingleCreatureFromStorage(NPC_NOVOS)) + { + if (Unit* pTarget = pNovos->SelectAttackingTarget(ATTACKING_TARGET_RANDOM, 0)) + pSummoned->AI()->AttackStart(pTarget); + } + } + } +}; + +CreatureAI* GetAI_npc_crystal_channel_target(Creature* pCreature) +{ + return new npc_crystal_channel_targetAI(pCreature); +} + +// Handling of the dummy auras of Crystal Handler Death spells, on remove the Crystal needs to be opened +bool EffectAuraDummy_npc_crystal_channel_target(const Aura* pAura, bool bApply) +{ + for (uint8 i = 0; i < MAX_CRYSTALS; ++i) + { + if (pAura->GetId() == aCrystalHandlerDeathSpells[i]) + { + if (pAura->GetEffIndex() == EFFECT_INDEX_0 && !bApply) + { + if (Creature* pCreature = (Creature*)pAura->GetTarget()) + { + if (instance_draktharon_keep* pInstance = (instance_draktharon_keep*)pCreature->GetInstanceData()) + { + if (pInstance->GetData(TYPE_NOVOS) == NOT_STARTED || pInstance->GetData(TYPE_NOVOS) == FAIL) + return true; + + pInstance->DoHandleCrystal(i); + + // Inform Novos about removed + if (Creature* pNovos = pInstance->GetSingleCreatureFromStorage(NPC_NOVOS)) + if (boss_novosAI* pNovosAI = dynamic_cast(pNovos->AI())) + pNovosAI->LostOneCrystal(); + } + } + } + + return true; + } + } + + return false; +} + +void AddSC_boss_novos() +{ + Script* pNewScript; + + pNewScript = new Script; + pNewScript->Name = "boss_novos"; + pNewScript->GetAI = &GetAI_boss_novos; + pNewScript->RegisterSelf(); + + pNewScript = new Script; + pNewScript->Name = "npc_crystal_channel_target"; + pNewScript->GetAI = &GetAI_npc_crystal_channel_target; + pNewScript->pEffectAuraDummy = &EffectAuraDummy_npc_crystal_channel_target; + pNewScript->RegisterSelf(); +} diff --git a/src/modules/SD2/scripts/northrend/draktharon_keep/boss_tharonja.cpp b/src/modules/SD2/scripts/northrend/draktharon_keep/boss_tharonja.cpp new file mode 100644 index 000000000..a4ea31db5 --- /dev/null +++ b/src/modules/SD2/scripts/northrend/draktharon_keep/boss_tharonja.cpp @@ -0,0 +1,293 @@ +/* Copyright (C) 2006 - 2013 ScriptDev2 + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +/* ScriptData +SDName: Boss_Tharonja +SD%Complete: 80% +SDComment: Encounter mechanic is not verified, spell CLEAR_GIFT_OF_THARONJA need core support +SDCategory: Drak'Tharon Keep +EndScriptData */ + +#include "precompiled.h" +#include "ObjectMgr.h" +#include "draktharon_keep.h" + +enum +{ + SAY_AGGRO = -1600012, + SAY_KILL_1 = -1600013, + SAY_KILL_2 = -1600014, + SAY_FLESH_1 = -1600015, + SAY_FLESH_2 = -1600016, + SAY_SKELETON_1 = -1600017, + SAY_SKELETON_2 = -1600018, + SAY_DEATH = -1600019, + + SPELL_CURSE_OF_LIFE = 49527, + SPELL_CURSE_OF_LIFE_H = 59972, + SPELL_RAIN_OF_FIRE = 49518, + SPELL_RAIN_OF_FIRE_H = 59971, + SPELL_SHADOW_VOLLEY = 49528, + SPELL_SHADOW_VOLLEY_H = 59973, + SPELL_LIGHTNING_BREATH = 49537, + SPELL_LIGHTNING_BREATH_H = 59963, + SPELL_EYE_BEAM = 49544, + SPELL_EYE_BEAM_H = 59965, + SPELL_POISON_CLOUD = 49548, + SPELL_POISON_CLOUD_H = 59969, + SPELL_DECAY_FLESH = 49356, // Has also unknown dummy aura onto all players while transforming + SPELL_GIFT_OF_THARONJA = 52509, + SPELL_RETURN_FLESH = 53463, // Has also unknown dummy aura onto all players while transforming + SPELL_CLEAR_GIFT_OF_THARONJA = 53242, + + SPELL_ACHIEVEMENT_CHECK = 61863, // Exact Purpose unknown, but is criteria (BE_SPELL_TARGET(28) and BE_SPELL_TARGET2(69)) + + // Only used to change display ID, might infact be some sort of UpdateEntry - TODO, research! + NPC_THARONJA_SKELETAL = 26632, + NPC_THARONJA_FLESH = 27696, +}; + +enum Phases +{ + PHASE_SKELETAL = 0, + PHASE_TAKE_FLESH = 1, + PHASE_FLESH = 2, + PHASE_RETURN_FLESH = 3, + PHASE_SKELETAL_END = 4, +}; + +/*###### +## boss_tharonja +######*/ + +struct boss_tharonjaAI : public ScriptedAI +{ + boss_tharonjaAI(Creature* pCreature) : ScriptedAI(pCreature) + { + m_pInstance = (ScriptedInstance*)pCreature->GetInstanceData(); + m_bIsRegularMode = pCreature->GetMap()->IsRegularDifficulty(); + Reset(); + } + + ScriptedInstance* m_pInstance; + bool m_bIsRegularMode; + + Phases m_uiPhase; + + uint32 m_uiCurseLifeTimer; + uint32 m_uiRainFireTimer; + uint32 m_uiShadowVolleyTimer; + uint32 m_uiLightningBreathTimer; + uint32 m_uiEyeBeamTimer; + uint32 m_uiPoisonCloudTimer; + uint32 m_uiReturnFleshTimer; + + void Reset() override + { + m_uiPhase = PHASE_SKELETAL; + + m_uiCurseLifeTimer = urand(15000, 20000); + m_uiRainFireTimer = urand(16000, 23000); // This timer is not very accurate + m_uiShadowVolleyTimer = urand(8000, 10000); + m_uiLightningBreathTimer = urand(3000, 4000); + m_uiEyeBeamTimer = urand(15000, 18000); // This timer is not very accurate + m_uiPoisonCloudTimer = urand(9000, 11000); // This timer is not very accurate + m_uiReturnFleshTimer = 26000; + } + + void Aggro(Unit* /*pWho*/) override + { + DoScriptText(SAY_AGGRO, m_creature); + + if (m_pInstance) + m_pInstance->SetData(TYPE_THARONJA, IN_PROGRESS); + } + + void KilledUnit(Unit* /*pVictim*/) override + { + DoScriptText(urand(0, 1) ? SAY_KILL_1 : SAY_KILL_2, m_creature); + } + + void JustDied(Unit* /*pKiller*/) override + { + DoScriptText(SAY_DEATH, m_creature); + + DoCastSpellIfCan(m_creature, SPELL_ACHIEVEMENT_CHECK, CAST_TRIGGERED | CAST_FORCE_CAST); + + // TODO check if this spell casting is infact also needed on phase-switch or only here (possible that there is also some sort of hp% dependency + if (m_uiPhase == PHASE_FLESH) + DoCastSpellIfCan(m_creature, SPELL_CLEAR_GIFT_OF_THARONJA, CAST_TRIGGERED | CAST_FORCE_CAST); + + if (m_pInstance) + m_pInstance->SetData(TYPE_THARONJA, DONE); + } + + void JustReachedHome() override + { + // Reset Display ID + if (CreatureInfo const* pCreatureInfo = GetCreatureTemplateStore(NPC_THARONJA_SKELETAL)) + { + uint32 uiDisplayId = Creature::ChooseDisplayId(pCreatureInfo); + if (m_creature->GetDisplayId() != uiDisplayId) + m_creature->SetDisplayId(uiDisplayId); + } + + if (m_pInstance) + m_pInstance->SetData(TYPE_THARONJA, FAIL); + } + + void UpdateAI(const uint32 uiDiff) override + { + if (!m_creature->SelectHostileTarget() || !m_creature->getVictim()) + return; + + switch (m_uiPhase) + { + case PHASE_SKELETAL: + // Phase switching at 50% (was in older patch versions multiple times, but from 335 on only once) + if (m_creature->GetHealthPercent() < 50) + { + if (DoCastSpellIfCan(m_creature, SPELL_DECAY_FLESH, CAST_INTERRUPT_PREVIOUS) == CAST_OK) + { + DoScriptText(urand(0, 1) ? SAY_FLESH_1 : SAY_FLESH_2, m_creature); + m_uiPhase = PHASE_TAKE_FLESH; + + return; // return here, as there is nothing more to be done in this phase + } + } + + // No break here, the last phase is exactly like the first, but he doesn't change anymore + case PHASE_SKELETAL_END: + if (m_uiCurseLifeTimer < uiDiff) + { + if (Unit* pTarget = m_creature->SelectAttackingTarget(ATTACKING_TARGET_RANDOM, 0)) + if (DoCastSpellIfCan(pTarget, m_bIsRegularMode ? SPELL_CURSE_OF_LIFE : SPELL_CURSE_OF_LIFE_H) == CAST_OK) + m_uiCurseLifeTimer = urand(12000, 18000); + } + else + m_uiCurseLifeTimer -= uiDiff; + + if (m_uiRainFireTimer < uiDiff) + { + if (Unit* pTarget = m_creature->SelectAttackingTarget(ATTACKING_TARGET_RANDOM, 0)) + if (DoCastSpellIfCan(pTarget, m_bIsRegularMode ? SPELL_RAIN_OF_FIRE : SPELL_RAIN_OF_FIRE_H) == CAST_OK) + m_uiRainFireTimer = urand(22000, 29000); + } + else + m_uiRainFireTimer -= uiDiff; + + if (m_uiShadowVolleyTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature, m_bIsRegularMode ? SPELL_SHADOW_VOLLEY : SPELL_SHADOW_VOLLEY_H) == CAST_OK) + m_uiShadowVolleyTimer = urand(6000, 12000); + } + else + m_uiShadowVolleyTimer -= uiDiff; + + DoMeleeAttackIfReady(); + break; + + case PHASE_FLESH: + // This is not entirely clear if this _might_ also be triggered HP-dependend + if (m_uiReturnFleshTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature, SPELL_RETURN_FLESH, CAST_INTERRUPT_PREVIOUS) == CAST_OK) + { + DoScriptText(urand(0, 1) ? SAY_SKELETON_1 : SAY_SKELETON_2, m_creature); + m_uiReturnFleshTimer = 26000; + m_uiPhase = PHASE_RETURN_FLESH; + + return; // return here, as there is nothing more to be done in this phase + } + } + else + m_uiReturnFleshTimer -= uiDiff; + + if (m_uiPoisonCloudTimer < uiDiff) + { + if (Unit* pTarget = m_creature->SelectAttackingTarget(ATTACKING_TARGET_RANDOM, 0)) + if (DoCastSpellIfCan(pTarget, m_bIsRegularMode ? SPELL_POISON_CLOUD : SPELL_POISON_CLOUD_H) == CAST_OK) + m_uiPoisonCloudTimer = urand(7000, 12000); + } + else + m_uiPoisonCloudTimer -= uiDiff; + + if (m_uiLightningBreathTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature->getVictim(), m_bIsRegularMode ? SPELL_LIGHTNING_BREATH : SPELL_LIGHTNING_BREATH_H) == CAST_OK) + m_uiLightningBreathTimer = urand(5000, 8000); + } + else + m_uiLightningBreathTimer -= uiDiff; + + if (m_uiEyeBeamTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature->getVictim(), m_bIsRegularMode ? SPELL_EYE_BEAM : SPELL_EYE_BEAM_H) == CAST_OK) + m_uiEyeBeamTimer = urand(12000, 15000); + } + else + m_uiEyeBeamTimer -= uiDiff; + + DoMeleeAttackIfReady(); + break; + + case PHASE_TAKE_FLESH: + // Turn players into skeletons + if (DoCastSpellIfCan(m_creature, SPELL_GIFT_OF_THARONJA) == CAST_OK) + { + // Change modell - might be UpdateEntry + if (CreatureInfo const* pCreatureInfo = GetCreatureTemplateStore(NPC_THARONJA_FLESH)) + { + uint32 uiDisplayId = Creature::ChooseDisplayId(pCreatureInfo); + m_creature->SetDisplayId(uiDisplayId); + } + + m_uiPhase = PHASE_FLESH; + } + break; + + case PHASE_RETURN_FLESH: + // Turn players into normal + if (DoCastSpellIfCan(m_creature, SPELL_CLEAR_GIFT_OF_THARONJA) == CAST_OK) + { + // Change modell - might be UpdateEntry + if (CreatureInfo const* pCreatureInfo = GetCreatureTemplateStore(NPC_THARONJA_SKELETAL)) + { + uint32 uiDisplayId = Creature::ChooseDisplayId(pCreatureInfo); + m_creature->SetDisplayId(uiDisplayId); + } + + m_uiPhase = PHASE_SKELETAL_END; + } + break; + } + } +}; + +CreatureAI* GetAI_boss_tharonja(Creature* pCreature) +{ + return new boss_tharonjaAI(pCreature); +} + +void AddSC_boss_tharonja() +{ + Script* pNewScript; + + pNewScript = new Script; + pNewScript->Name = "boss_tharonja"; + pNewScript->GetAI = &GetAI_boss_tharonja; + pNewScript->RegisterSelf(); +} diff --git a/src/modules/SD2/scripts/northrend/draktharon_keep/boss_trollgore.cpp b/src/modules/SD2/scripts/northrend/draktharon_keep/boss_trollgore.cpp new file mode 100644 index 000000000..bf9da7cb0 --- /dev/null +++ b/src/modules/SD2/scripts/northrend/draktharon_keep/boss_trollgore.cpp @@ -0,0 +1,245 @@ +/* Copyright (C) 2006 - 2013 ScriptDev2 + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +/* ScriptData +SDName: Boss_Trollgore +SD%Complete: 80% +SDComment: Some details related to the summoned creatures need more adjustments +SDCategory: Drak'Tharon Keep +EndScriptData */ + +#include "precompiled.h" +#include "draktharon_keep.h" + +enum +{ + SAY_AGGRO = -1600000, + SAY_CONSUME = -1600001, + SAY_DEATH = -1600002, + SAY_EXPLODE = -1600003, + SAY_KILL = -1600004, + + SPELL_CRUSH = 49639, + SPELL_INFECTED_WOUND = 49637, + SPELL_CORPSE_EXPLODE = 49555, + SPELL_CORPSE_EXPLODE_H = 59807, + SPELL_CONSUME = 49380, + SPELL_CONSUME_H = 59803, + SPELL_CONSUME_BUFF = 49381, // used to measure the achiev + SPELL_CONSUME_BUFF_H = 59805, + + SPELL_SUMMON_INVADER_1 = 49456, // summon 27709 + SPELL_SUMMON_INVADER_2 = 49457, // summon 27753 + // SPELL_SUMMON_INVADER_3 = 49458, // summon 27754 + SPELL_INVADER_TAUNT = 49405, // triggers 49406 + + MAX_CONSOME_STACKS = 10, +}; + +/*###### +## boss_trollgore +######*/ + +struct boss_trollgoreAI : public ScriptedAI +{ + boss_trollgoreAI(Creature* pCreature) : ScriptedAI(pCreature) + { + m_pInstance = (instance_draktharon_keep*)pCreature->GetInstanceData(); + m_bIsRegularMode = pCreature->GetMap()->IsRegularDifficulty(); + Reset(); + } + + instance_draktharon_keep* m_pInstance; + bool m_bIsRegularMode; + + uint8 m_uiConsumeStacks; + + uint32 m_uiConsumeTimer; + uint32 m_uiCrushTimer; + uint32 m_uiInfectedWoundTimer; + uint32 m_uiWaveTimer; + uint32 m_uiCorpseExplodeTimer; + + GuidVector m_vTriggers; + + void Reset() override + { + m_uiCorpseExplodeTimer = 20000; + m_uiConsumeTimer = 15000; + m_uiCrushTimer = 10000; + m_uiInfectedWoundTimer = 5000; + m_uiWaveTimer = 0; + m_uiConsumeStacks = 0; + } + + void Aggro(Unit* /*pWho*/) override + { + DoScriptText(SAY_AGGRO, m_creature); + + if (m_pInstance) + { + m_pInstance->SetData(TYPE_TROLLGORE, IN_PROGRESS); + m_pInstance->GetTrollgoreOutsideTriggers(m_vTriggers); + } + } + + void KilledUnit(Unit* pVictim) override + { + if (pVictim->GetCharmerOrOwnerPlayerOrPlayerItself()) + DoScriptText(SAY_KILL, m_creature); + } + + void JustDied(Unit* /*pKiller*/) override + { + DoScriptText(SAY_DEATH, m_creature); + + if (m_pInstance) + m_pInstance->SetData(TYPE_TROLLGORE, DONE); + } + + void JustReachedHome() override + { + if (m_pInstance) + m_pInstance->SetData(TYPE_TROLLGORE, FAIL); + } + + void SpellHit(Unit* /*pTarget*/, const SpellEntry* pSpell) override + { + if (pSpell->Id == SPELL_CONSUME_BUFF || pSpell->Id == SPELL_CONSUME_BUFF_H) + { + ++m_uiConsumeStacks; + + // if the boss has 10 stacks then set the achiev to fail + if (m_uiConsumeStacks == MAX_CONSOME_STACKS) + { + if (m_pInstance) + m_pInstance->SetData(TYPE_TROLLGORE, SPECIAL); + } + } + } + + void JustSummoned(Creature* pSummoned) override + { + // This spell taunts the boss and the boss taunts back + pSummoned->CastSpell(m_creature, SPELL_INVADER_TAUNT, true); + } + + // Wrapper to handle the drakkari invaders summon + void DoSummonDrakkariInvaders() + { + if (!m_pInstance) + return; + + // check if there are there are at least 2 triggers in the vector + if (m_vTriggers.size() < 2) + return; + + if (roll_chance_i(30)) + { + // Summon a troll in the corner and 2 trolls in the air + if (Creature* pTrigger = m_creature->GetMap()->GetCreature(m_pInstance->GetTrollgoreCornerTrigger())) + pTrigger->CastSpell(pTrigger, roll_chance_i(20) ? SPELL_SUMMON_INVADER_1 : SPELL_SUMMON_INVADER_2, true, NULL, NULL, m_creature->GetObjectGuid()); + + // get two random outside triggers + uint8 uiMaxTriggers = m_vTriggers.size(); + uint8 uiPos1 = urand(0, uiMaxTriggers - 1); + uint8 uiPos2 = (uiPos1 + urand(1, uiMaxTriggers - 1)) % uiMaxTriggers; + + if (Creature* pTrigger = m_creature->GetMap()->GetCreature(m_vTriggers[uiPos1])) + pTrigger->CastSpell(pTrigger, roll_chance_i(30) ? SPELL_SUMMON_INVADER_1 : SPELL_SUMMON_INVADER_2, true, NULL, NULL, m_creature->GetObjectGuid()); + if (Creature* pTrigger = m_creature->GetMap()->GetCreature(m_vTriggers[uiPos2])) + pTrigger->CastSpell(pTrigger, roll_chance_i(30) ? SPELL_SUMMON_INVADER_1 : SPELL_SUMMON_INVADER_2, true, NULL, NULL, m_creature->GetObjectGuid()); + } + else + { + // Summon 3 trolls in the air + for (uint8 i = 0; i < m_vTriggers.size(); ++i) + { + if (Creature* pTrigger = m_creature->GetMap()->GetCreature(m_vTriggers[i])) + pTrigger->CastSpell(pTrigger, roll_chance_i(30) ? SPELL_SUMMON_INVADER_1 : SPELL_SUMMON_INVADER_2, true, NULL, NULL, m_creature->GetObjectGuid()); + } + } + } + + void UpdateAI(const uint32 uiDiff) override + { + if (!m_creature->SelectHostileTarget() || !m_creature->getVictim()) + return; + + if (m_uiCrushTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature->getVictim(), SPELL_CRUSH) == CAST_OK) + m_uiCrushTimer = 10000; + } + else + m_uiCrushTimer -= uiDiff; + + if (m_uiInfectedWoundTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature->getVictim(), SPELL_INFECTED_WOUND) == CAST_OK) + m_uiInfectedWoundTimer = urand(20000, 30000); + } + else + m_uiInfectedWoundTimer -= uiDiff; + + if (m_uiWaveTimer < uiDiff) + { + DoSummonDrakkariInvaders(); + m_uiWaveTimer = 30000; + } + else + m_uiWaveTimer -= uiDiff; + + if (m_uiConsumeTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature, m_bIsRegularMode ? SPELL_CONSUME : SPELL_CONSUME_H) == CAST_OK) + { + DoScriptText(SAY_CONSUME, m_creature); + m_uiConsumeTimer = 15000; + } + } + else + m_uiConsumeTimer -= uiDiff; + + if (m_uiCorpseExplodeTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature, m_bIsRegularMode ? SPELL_CORPSE_EXPLODE : SPELL_CORPSE_EXPLODE_H) == CAST_OK) + { + DoScriptText(SAY_EXPLODE, m_creature); + m_uiCorpseExplodeTimer = 10000; + } + } + else + m_uiCorpseExplodeTimer -= uiDiff; + + DoMeleeAttackIfReady(); + } +}; + +CreatureAI* GetAI_boss_trollgore(Creature* pCreature) +{ + return new boss_trollgoreAI(pCreature); +} + +void AddSC_boss_trollgore() +{ + Script* pNewScript; + + pNewScript = new Script; + pNewScript->Name = "boss_trollgore"; + pNewScript->GetAI = &GetAI_boss_trollgore; + pNewScript->RegisterSelf(); +} diff --git a/src/modules/SD2/scripts/northrend/draktharon_keep/draktharon_keep.h b/src/modules/SD2/scripts/northrend/draktharon_keep/draktharon_keep.h new file mode 100644 index 000000000..144b9067a --- /dev/null +++ b/src/modules/SD2/scripts/northrend/draktharon_keep/draktharon_keep.h @@ -0,0 +1,105 @@ +/* Copyright (C) 2006 - 2013 ScriptDev2 + * This program is free software licensed under GPL version 2 + * Please see the included DOCS/LICENSE.TXT for more information */ + +#ifndef DEF_DRAKTHARON_KEEP_H +#define DEF_DRAKTHARON_KEEP_H + +enum +{ + MAX_ENCOUNTER = 4, + + TYPE_TROLLGORE = 0, + TYPE_NOVOS = 1, + TYPE_KING_DRED = 2, + TYPE_THARONJA = 3, + + NPC_NOVOS = 26631, + NPC_KING_DRED = 27483, + + // Adds of King Dred Encounter - deaths counted for achievement + NPC_DRAKKARI_GUTRIPPER = 26641, + NPC_DRAKKARI_SCYTHECLAW = 26628, + NPC_WORLD_TRIGGER = 22515, + + // Novos Encounter + SPELL_BEAM_CHANNEL = 52106, + SPELL_CRYSTAL_HANDLER_DEATH_1 = 47336, + SPELL_CRYSTAL_HANDLER_DEATH_2 = 55801, + SPELL_CRYSTAL_HANDLER_DEATH_3 = 55803, + SPELL_CRYSTAL_HANDLER_DEATH_4 = 55805, + + MAX_CRYSTALS = 4, + NPC_CRYSTAL_CHANNEL_TARGET = 26712, + GO_CRYSTAL_SW = 189299, + GO_CRYSTAL_NE = 189300, + GO_CRYSTAL_NW = 189301, + GO_CRYSTAL_SE = 189302, + + // Achievement Criterias to be handled with SD2 + ACHIEV_CRIT_BETTER_OFF_DREAD = 7318, + ACHIEV_CRIT_CONSUME_JUNCTION = 7579, + ACHIEV_CRIT_OH_NOVOS = 7361, +}; + +static const uint32 aCrystalHandlerDeathSpells[MAX_CRYSTALS] = +{SPELL_CRYSTAL_HANDLER_DEATH_1, SPELL_CRYSTAL_HANDLER_DEATH_2, SPELL_CRYSTAL_HANDLER_DEATH_3, SPELL_CRYSTAL_HANDLER_DEATH_4}; + +struct NovosCrystalInfo +{ + ObjectGuid m_crystalGuid; + ObjectGuid m_channelGuid; + bool m_bWasUsed; +}; + +class instance_draktharon_keep : public ScriptedInstance +{ + public: + instance_draktharon_keep(Map* pMap); + ~instance_draktharon_keep() {} + + void Initialize() override; + + void SetData(uint32 uiType, uint32 uiData) override; + uint32 GetData(uint32 uiType) const override; + + void OnCreatureEnterCombat(Creature* pCreature) override; + void OnCreatureEvade(Creature* pCreature); + void OnCreatureDeath(Creature* pCreature) override; + + void OnCreatureCreate(Creature* pCreature) override; + void OnObjectCreate(GameObject* pGo) override; + + void GetTrollgoreOutsideTriggers(GuidVector& vTriggers) { vTriggers = m_vTriggerGuids; } + ObjectGuid GetTrollgoreCornerTrigger() { return m_trollgoreCornerTriggerGuid; } + + bool CheckAchievementCriteriaMeet(uint32 uiCriteriaId, Player const* pSource, Unit const* pTarget, uint32 uiMiscValue1 /* = 0*/) const override; + + const char* Save() const override { return m_strInstData.c_str(); } + void Load(const char* chrIn) override; + + Creature* GetNextCrystalTarget(Creature* pCrystalHandler, uint8& uiIndex); + void DoHandleCrystal(uint8 uiIndex); + Creature* GetSummonDummy(); + + protected: + void DoSortNovosDummies(); + + uint32 m_auiEncounter[MAX_ENCOUNTER]; + std::string m_strInstData; + + uint32 m_uiDreadAddsKilled; + bool m_bNovosAddGrounded; + bool m_bTrollgoreConsume; + + ObjectGuid m_novosChannelGuid; + ObjectGuid m_trollgoreCornerTriggerGuid; + + NovosCrystalInfo m_aNovosCrystalInfo[MAX_CRYSTALS]; + + GuidVector m_vSummonDummyGuids; + GuidList m_lNovosDummyGuids; + GuidVector m_vTriggerGuids; +}; + +#endif diff --git a/src/modules/SD2/scripts/northrend/draktharon_keep/instance_draktharon_keep.cpp b/src/modules/SD2/scripts/northrend/draktharon_keep/instance_draktharon_keep.cpp new file mode 100644 index 000000000..ba5ec920a --- /dev/null +++ b/src/modules/SD2/scripts/northrend/draktharon_keep/instance_draktharon_keep.cpp @@ -0,0 +1,349 @@ +/* Copyright (C) 2006 - 2013 ScriptDev2 + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along 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_draktharon_keep +SD%Complete: 50% +SDComment: +SDCategory: Drak'Tharon Keep +EndScriptData */ + +#include "precompiled.h" +#include "draktharon_keep.h" + +instance_draktharon_keep::instance_draktharon_keep(Map* pMap) : ScriptedInstance(pMap), + m_uiDreadAddsKilled(0), + m_bNovosAddGrounded(false), + m_bTrollgoreConsume(true) +{ + Initialize(); +} + +void instance_draktharon_keep::Initialize() +{ + memset(&m_auiEncounter, 0, sizeof(m_auiEncounter)); +} + +void instance_draktharon_keep::OnCreatureEnterCombat(Creature* pCreature) +{ + if (pCreature->GetEntry() == NPC_KING_DRED) + SetData(TYPE_KING_DRED, IN_PROGRESS); +} + +void instance_draktharon_keep::OnCreatureEvade(Creature* pCreature) +{ + if (pCreature->GetEntry() == NPC_KING_DRED) + SetData(TYPE_KING_DRED, FAIL); +} + +void instance_draktharon_keep::OnCreatureDeath(Creature* pCreature) +{ + if ((pCreature->GetEntry() == NPC_DRAKKARI_GUTRIPPER || pCreature->GetEntry() == NPC_DRAKKARI_SCYTHECLAW) && m_auiEncounter[TYPE_KING_DRED] == IN_PROGRESS) + ++m_uiDreadAddsKilled; + + if (pCreature->GetEntry() == NPC_KING_DRED) + SetData(TYPE_KING_DRED, DONE); +} + +void instance_draktharon_keep::OnCreatureCreate(Creature* pCreature) +{ + switch (pCreature->GetEntry()) + { + case NPC_NOVOS: + m_mNpcEntryGuidStore[NPC_NOVOS] = pCreature->GetObjectGuid(); + break; + case NPC_CRYSTAL_CHANNEL_TARGET: + m_lNovosDummyGuids.push_back(pCreature->GetObjectGuid()); + break; + case NPC_WORLD_TRIGGER: + if (pCreature->GetPositionZ() > 30.0f) + m_vTriggerGuids.push_back(pCreature->GetObjectGuid()); + else + m_trollgoreCornerTriggerGuid = pCreature->GetObjectGuid(); + break; + } +} + +void instance_draktharon_keep::OnObjectCreate(GameObject* pGo) +{ + switch (pGo->GetEntry()) + { + case GO_CRYSTAL_SW: m_aNovosCrystalInfo[0].m_crystalGuid = pGo->GetObjectGuid(); break; + case GO_CRYSTAL_NW: m_aNovosCrystalInfo[1].m_crystalGuid = pGo->GetObjectGuid(); break; + case GO_CRYSTAL_SE: m_aNovosCrystalInfo[2].m_crystalGuid = pGo->GetObjectGuid(); break; + case GO_CRYSTAL_NE: m_aNovosCrystalInfo[3].m_crystalGuid = pGo->GetObjectGuid(); break; + } +} + +void instance_draktharon_keep::DoSortNovosDummies() +{ + // Sorting once is good enough + if (m_lNovosDummyGuids.empty()) + return; + + Creature* pNovos = GetSingleCreatureFromStorage(NPC_NOVOS); + if (!pNovos) + return; + + // First sort the Dummies to the Crystals + for (uint8 i = 0; i < MAX_CRYSTALS; ++i) + { + GameObject* pCrystal = instance->GetGameObject(m_aNovosCrystalInfo[i].m_crystalGuid); + if (!pCrystal) + continue; + + for (GuidList::iterator itr = m_lNovosDummyGuids.begin(); itr != m_lNovosDummyGuids.end();) + { + Creature* pDummy = instance->GetCreature(*itr); + if (!pDummy) + { + m_lNovosDummyGuids.erase(itr++); + continue; + } + + // Check if dummy fits to crystal + if (pCrystal->IsWithinDistInMap(pDummy, INTERACTION_DISTANCE, false)) + { + m_aNovosCrystalInfo[i].m_channelGuid = pDummy->GetObjectGuid(); + m_lNovosDummyGuids.erase(itr); + break; + } + + ++itr; + } + } + + // Find the crystal channel target (above Novos) + float fNovosX, fNovosY, fNovosZ; + pNovos->GetRespawnCoord(fNovosX, fNovosY, fNovosZ); + for (GuidList::iterator itr = m_lNovosDummyGuids.begin(); itr != m_lNovosDummyGuids.end();) + { + Creature* pDummy = instance->GetCreature(*itr); + if (!pDummy) + { + m_lNovosDummyGuids.erase(itr++); + continue; + } + + // As the wanted dummy is exactly above Novos, check small range, and only 2d + if (pDummy->IsWithinDist2d(fNovosX, fNovosY, 5.0f)) + { + m_novosChannelGuid = pDummy->GetObjectGuid(); + m_lNovosDummyGuids.erase(itr); + break; + } + + ++itr; + } + + // Summon positions (at end of stairs) + for (GuidList::iterator itr = m_lNovosDummyGuids.begin(); itr != m_lNovosDummyGuids.end();) + { + Creature* pDummy = instance->GetCreature(*itr); + if (!pDummy) + { + m_lNovosDummyGuids.erase(itr++); + continue; + } + + // The wanted dummies are quite above Novos + if (pDummy->GetPositionZ() > fNovosZ + 20.0f) + { + m_vSummonDummyGuids.push_back(pDummy->GetObjectGuid()); + m_lNovosDummyGuids.erase(itr++); + } + else + ++itr; + } + + // Clear remaining (unused) dummies + m_lNovosDummyGuids.clear(); +} + +Creature* instance_draktharon_keep::GetNextCrystalTarget(Creature* pCrystalHandler, uint8& uiIndex) +{ + Creature* pTarget = NULL; + uiIndex = 0; + + for (uint8 i = 0; i < MAX_CRYSTALS; ++i) + { + Creature* pDummy = instance->GetCreature(m_aNovosCrystalInfo[i].m_channelGuid); + // Return the nearest 'unused' crystal dummy + // unused means, that the crystal was not already used, and the dummy-npc doesn't have the aura that will trigger the use on remove + if (pDummy && !m_aNovosCrystalInfo[i].m_bWasUsed && (!pTarget || pCrystalHandler->GetDistanceOrder(pDummy, pTarget)) && !pDummy->HasAura(aCrystalHandlerDeathSpells[i])) + { + pTarget = pDummy; + uiIndex = i; + } + } + + return pTarget; +} + +void instance_draktharon_keep::DoHandleCrystal(uint8 uiIndex) +{ + m_aNovosCrystalInfo[uiIndex].m_bWasUsed = true; + + DoUseDoorOrButton(m_aNovosCrystalInfo[uiIndex].m_crystalGuid); + + if (Creature* pDummy = instance->GetCreature(m_aNovosCrystalInfo[uiIndex].m_channelGuid)) + pDummy->InterruptNonMeleeSpells(false); +} + +Creature* instance_draktharon_keep::GetSummonDummy() +{ + if (m_vSummonDummyGuids.empty()) + return NULL; + + return instance->GetCreature(m_vSummonDummyGuids[urand(0, m_vSummonDummyGuids.size() - 1)]); +} + +bool instance_draktharon_keep::CheckAchievementCriteriaMeet(uint32 uiCriteriaId, Player const* /*pSource*/, Unit const* /*pTarget*/, uint32 /*uiMiscValue1 = 0*/) const +{ + switch (uiCriteriaId) + { + case ACHIEV_CRIT_BETTER_OFF_DREAD: return m_uiDreadAddsKilled >= 6; + case ACHIEV_CRIT_OH_NOVOS: return !m_bNovosAddGrounded; + case ACHIEV_CRIT_CONSUME_JUNCTION: return m_bTrollgoreConsume; + default: + return false; + } +} + +void instance_draktharon_keep::SetData(uint32 uiType, uint32 uiData) +{ + switch (uiType) + { + case TYPE_TROLLGORE: + if (uiData == IN_PROGRESS) + m_bTrollgoreConsume = true; + if (uiData == SPECIAL) + m_bTrollgoreConsume = false; + m_auiEncounter[uiType] = uiData; + break; + case TYPE_NOVOS: + if (uiData == IN_PROGRESS) + { + // Sort the dummies + DoSortNovosDummies(); + + // Cast some visual spells + Creature* pTarget = instance->GetCreature(m_novosChannelGuid); + for (uint8 i = 0; i < MAX_CRYSTALS; ++i) + { + Creature* pCaster = instance->GetCreature(m_aNovosCrystalInfo[i].m_channelGuid); + if (pCaster && pTarget) + pCaster->CastSpell(pTarget, SPELL_BEAM_CHANNEL, false); + + m_aNovosCrystalInfo[i].m_bWasUsed = false; + } + + // Achievement related + m_bNovosAddGrounded = false; + } + else if (uiData == SPECIAL) + { + // Achievement related + m_bNovosAddGrounded = true; + } + else if (uiData == FAIL) + { + // Interrupt casted spells + for (uint8 i = 0; i < MAX_CRYSTALS; ++i) + { + Creature* pDummy = instance->GetCreature(m_aNovosCrystalInfo[i].m_channelGuid); + if (pDummy) + pDummy->InterruptNonMeleeSpells(false); + // And reset used crystals + if (m_aNovosCrystalInfo[i].m_bWasUsed) + DoUseDoorOrButton(m_aNovosCrystalInfo[i].m_crystalGuid); + } + } + m_auiEncounter[uiType] = uiData; + break; + case TYPE_KING_DRED: + if (uiData == IN_PROGRESS) + m_uiDreadAddsKilled = 0; + m_auiEncounter[uiType] = uiData; + break; + case TYPE_THARONJA: + m_auiEncounter[uiType] = uiData; + 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_strInstData = saveStream.str(); + + SaveToDB(); + OUT_SAVE_INST_DATA_COMPLETE; + } +} + +void instance_draktharon_keep::Load(const char* chrIn) +{ + if (!chrIn) + { + 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 < MAX_ENCOUNTER; ++i) + { + if (m_auiEncounter[i] == IN_PROGRESS) + m_auiEncounter[i] = NOT_STARTED; + } + + OUT_LOAD_INST_DATA_COMPLETE; +} + +uint32 instance_draktharon_keep::GetData(uint32 uiType) const +{ + switch (uiType) + { + case TYPE_TROLLGORE: return m_auiEncounter[uiType]; + case TYPE_NOVOS: return m_auiEncounter[uiType]; + case TYPE_KING_DRED: return m_auiEncounter[uiType]; + case TYPE_THARONJA: return m_auiEncounter[uiType]; + default: + return 0; + } +} + +InstanceData* GetInstanceData_instance_draktharon_keep(Map* pMap) +{ + return new instance_draktharon_keep(pMap); +} + +void AddSC_instance_draktharon_keep() +{ + Script* pNewScript; + + pNewScript = new Script; + pNewScript->Name = "instance_draktharon_keep"; + pNewScript->GetInstanceData = &GetInstanceData_instance_draktharon_keep; + pNewScript->RegisterSelf(); +} diff --git a/src/modules/SD2/scripts/northrend/grizzly_hills.cpp b/src/modules/SD2/scripts/northrend/grizzly_hills.cpp new file mode 100644 index 000000000..b1a7a1774 --- /dev/null +++ b/src/modules/SD2/scripts/northrend/grizzly_hills.cpp @@ -0,0 +1,107 @@ +/* Copyright (C) 2006 - 2013 ScriptDev2 + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along 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: +SDComment: Quest support: 12138, 12198 +SDCategory: Grizzly Hills +EndScriptData */ + +/* ContentData +npc_depleted_war_golem +EndContentData */ + +#include "precompiled.h" +#include "pet_ai.h" + +/*###### +## npc_depleted_war_golem +######*/ + +enum +{ + SAY_GOLEM_CHARGE = -1000626, + SAY_GOLEM_COMPLETE = -1000627, + + NPC_LIGHTNING_SENTRY = 26407, + + SPELL_CHARGE_GOLEM = 47799, + SPELL_GOLEM_CHARGE_CREDIT = 47797, +}; + +struct npc_depleted_war_golemAI : public ScriptedPetAI +{ + npc_depleted_war_golemAI(Creature* pCreature) : ScriptedPetAI(pCreature) { Reset(); } + + void Reset() override { } + + void OwnerKilledUnit(Unit* pVictim) override + { + if (pVictim->GetTypeId() == TYPEID_UNIT && pVictim->GetEntry() == NPC_LIGHTNING_SENTRY) + { + // Is distance expected? + if (m_creature->IsWithinDistInMap(pVictim, 10.0f)) + m_creature->CastSpell(m_creature, SPELL_CHARGE_GOLEM, true); + } + } +}; + +CreatureAI* GetAI_npc_depleted_war_golem(Creature* pCreature) +{ + return new npc_depleted_war_golemAI(pCreature); +} + +bool EffectAuraDummy_npc_depleted_war_golem(const Aura* pAura, bool bApply) +{ + if (pAura->GetId() != SPELL_CHARGE_GOLEM) + return true; + + Creature* pCreature = (Creature*)pAura->GetTarget(); + + if (!pCreature) + return true; + + if (pAura->GetEffIndex() == EFFECT_INDEX_0) + { + if (bApply) + { + DoScriptText(SAY_GOLEM_CHARGE, pCreature); + pCreature->addUnitState(UNIT_STAT_STUNNED); + } + else + { + DoScriptText(SAY_GOLEM_COMPLETE, pCreature); + pCreature->clearUnitState(UNIT_STAT_STUNNED); + + // targets master + pCreature->CastSpell(pCreature, SPELL_GOLEM_CHARGE_CREDIT, true); + } + } + + return true; +} + +void AddSC_grizzly_hills() +{ + Script* pNewScript; + + pNewScript = new Script; + pNewScript->Name = "npc_depleted_war_golem"; + pNewScript->GetAI = &GetAI_npc_depleted_war_golem; + pNewScript->pEffectAuraDummy = &EffectAuraDummy_npc_depleted_war_golem; + pNewScript->RegisterSelf(); +} diff --git a/src/modules/SD2/scripts/northrend/gundrak/boss_colossus.cpp b/src/modules/SD2/scripts/northrend/gundrak/boss_colossus.cpp new file mode 100644 index 000000000..bc5aa6fcc --- /dev/null +++ b/src/modules/SD2/scripts/northrend/gundrak/boss_colossus.cpp @@ -0,0 +1,426 @@ +/* Copyright (C) 2006 - 2013 ScriptDev2 + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +/* ScriptData +SDName: Boss_Colossus +SD%Complete: 95% +SDComment: Timers; May need small adjustments +SDCategory: Gundrak +EndScriptData */ + +#include "precompiled.h" +#include "gundrak.h" + +enum +{ + EMOTE_SURGE = -1604008, + EMOTE_SEEP = -1604009, + EMOTE_GLOW = -1604010, + + // collosus' abilities + SPELL_FREEZE_ANIM = 16245, // Colossus stun aura + SPELL_EMERGE = 54850, + SPELL_MIGHTY_BLOW = 54719, + SPELL_MORTAL_STRIKES = 54715, + SPELL_MORTAL_STRIKES_H = 59454, + + // elemental's abilities + SPELL_MERGE = 54878, + SPELL_SURGE = 54801, + SPELL_MOJO_VOLLEY = 59453, + SPELL_MOJO_VOLLEY_H = 54849, + + // Living Mojo spells + SPELL_MOJO_WAVE = 55626, + SPELL_MOJO_WAVE_H = 58993, + SPELL_MOJO_PUDDLE = 55627, + SPELL_MOJO_PUDDLE_H = 58994, + + MAX_COLOSSUS_MOJOS = 5, +}; + +/*###### +## boss_drakkari_elemental +######*/ + +struct boss_drakkari_elementalAI : public ScriptedAI +{ + boss_drakkari_elementalAI(Creature* pCreature) : ScriptedAI(pCreature) + { + m_pInstance = (instance_gundrak*)pCreature->GetInstanceData(); + m_bIsRegularMode = pCreature->GetMap()->IsRegularDifficulty(); + Reset(); + } + + instance_gundrak* m_pInstance; + bool m_bIsRegularMode; + bool m_bIsFirstEmerge; + + uint32 m_uiSurgeTimer; + + void Reset() override + { + m_bIsFirstEmerge = true; + m_uiSurgeTimer = urand(9000, 13000); + } + + void Aggro(Unit* /*pWho*/) override + { + DoCastSpellIfCan(m_creature, m_bIsRegularMode ? SPELL_MOJO_VOLLEY : SPELL_MOJO_VOLLEY_H); + } + + void DamageTaken(Unit* /*pDoneBy*/, uint32& /*uiDamage*/) override + { + if (!m_bIsFirstEmerge) + return; + + if (m_creature->GetHealthPercent() < 50.0f) + { + DoCastSpellIfCan(m_creature, SPELL_MERGE, CAST_INTERRUPT_PREVIOUS); + m_bIsFirstEmerge = false; + } + } + + void JustReachedHome() override + { + if (m_pInstance) + { + if (Creature* pColossus = m_pInstance->GetSingleCreatureFromStorage(NPC_COLOSSUS)) + pColossus->AI()->EnterEvadeMode(); + } + + m_creature->ForcedDespawn(); + } + + void JustDied(Unit* /*pKiller*/) override + { + if (m_pInstance) + { + // kill colossus on death - this will finish the encounter + if (Creature* pColossus = m_pInstance->GetSingleCreatureFromStorage(NPC_COLOSSUS)) + pColossus->DealDamage(pColossus, pColossus->GetHealth(), NULL, DIRECT_DAMAGE, SPELL_SCHOOL_MASK_NORMAL, NULL, false); + } + } + + // Set the second emerge of the Elemental + void DoPrepareSecondEmerge() + { + m_bIsFirstEmerge = false; + m_creature->SetHealth(m_creature->GetMaxHealth()*.5); + } + + void UpdateAI(const uint32 uiDiff) override + { + if (!m_creature->SelectHostileTarget() || !m_creature->getVictim()) + return; + + if (m_uiSurgeTimer < uiDiff) + { + if (Unit* pTarget = m_creature->SelectAttackingTarget(ATTACKING_TARGET_RANDOM, 0)) + { + if (DoCastSpellIfCan(pTarget, SPELL_SURGE) == CAST_OK) + m_uiSurgeTimer = urand(12000, 17000); + } + } + else + m_uiSurgeTimer -= uiDiff; + + DoMeleeAttackIfReady(); + } +}; + +CreatureAI* GetAI_boss_drakkari_elemental(Creature* pCreature) +{ + return new boss_drakkari_elementalAI(pCreature); +} + +/*###### +## boss_drakkari_colossus +######*/ + +struct boss_drakkari_colossusAI : public ScriptedAI +{ + boss_drakkari_colossusAI(Creature* pCreature) : ScriptedAI(pCreature) + { + m_pInstance = (instance_gundrak*)pCreature->GetInstanceData(); + m_bIsRegularMode = pCreature->GetMap()->IsRegularDifficulty(); + Reset(); + } + + instance_gundrak* m_pInstance; + bool m_bIsRegularMode; + bool m_bFirstEmerge; + + uint32 m_uiMightyBlowTimer; + uint32 m_uiColossusStartTimer; + uint8 m_uiMojosGathered; + + void Reset() override + { + m_bFirstEmerge = true; + m_uiMightyBlowTimer = 10000; + m_uiColossusStartTimer = 0; + m_uiMojosGathered = 0; + + // Reset unit flags + SetCombatMovement(true); + m_creature->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_PASSIVE); + m_creature->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE); + } + + void Aggro(Unit* /*pWho*/) override + { + DoCastSpellIfCan(m_creature, m_bIsRegularMode ? SPELL_MORTAL_STRIKES : SPELL_MORTAL_STRIKES_H); + + if (m_pInstance) + m_pInstance->SetData(TYPE_COLOSSUS, IN_PROGRESS); + } + + void JustDied(Unit* /*pKiller*/) override + { + if (m_pInstance) + m_pInstance->SetData(TYPE_COLOSSUS, DONE); + } + + void JustReachedHome() override + { + if (m_pInstance) + m_pInstance->SetData(TYPE_COLOSSUS, FAIL); + } + + void SpellHit(Unit* pCaster, const SpellEntry* pSpell) override + { + if (pSpell->Id == SPELL_MERGE) + { + // re-activate colossus here + m_creature->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE); + m_creature->RemoveAurasDueToSpell(SPELL_FREEZE_ANIM); + + SetCombatMovement(true); + m_creature->GetMotionMaster()->Clear(); + m_creature->GetMotionMaster()->MoveChase(m_creature->getVictim()); + ((Creature*)pCaster)->ForcedDespawn(); + } + } + + void JustSummoned(Creature* pSummoned) override + { + if (pSummoned->GetEntry() == NPC_ELEMENTAL) + { + // If this is the second summon, then set the health to half + if (!m_bFirstEmerge) + { + if (boss_drakkari_elementalAI* pBossAI = dynamic_cast(pSummoned->AI())) + pBossAI->DoPrepareSecondEmerge(); + } + + m_bFirstEmerge = false; + if (m_creature->getVictim()) + pSummoned->AI()->AttackStart(m_creature->getVictim()); + } + } + + void DamageTaken(Unit* /*pDoneBy*/, uint32& uiDamage) override + { + if (m_bFirstEmerge && m_creature->GetHealthPercent() < 50.0f) + DoEmergeElemental(); + else if (uiDamage >= m_creature->GetHealth()) + { + uiDamage = 0; + DoEmergeElemental(); + } + } + + void DoEmergeElemental() + { + // Avoid casting the merge spell twice + if (m_creature->HasAura(SPELL_FREEZE_ANIM)) + return; + + if (DoCastSpellIfCan(m_creature, SPELL_EMERGE, CAST_INTERRUPT_PREVIOUS) == CAST_OK) + { + SetCombatMovement(false); + m_creature->GetMotionMaster()->MoveIdle(); + m_creature->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE); + DoCastSpellIfCan(m_creature, SPELL_FREEZE_ANIM, CAST_TRIGGERED); + } + } + + // Wrapper to prepare the Colossus + void DoPrepareColossus() + { + ++m_uiMojosGathered; + + if (m_uiMojosGathered == MAX_COLOSSUS_MOJOS) + m_uiColossusStartTimer = 1000; + } + + void UpdateAI(const uint32 uiDiff) override + { + if (m_uiColossusStartTimer) + { + if (m_uiColossusStartTimer <= uiDiff) + { + m_creature->RemoveAurasDueToSpell(SPELL_FREEZE_ANIM); + m_creature->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_PASSIVE); + m_uiColossusStartTimer = 0; + } + else + m_uiColossusStartTimer -= uiDiff; + } + + if (!m_creature->SelectHostileTarget() || !m_creature->getVictim()) + return; + + if (m_uiMightyBlowTimer < uiDiff) + { + DoCastSpellIfCan(m_creature->getVictim(), SPELL_MIGHTY_BLOW); + m_uiMightyBlowTimer = 10000; + } + else + m_uiMightyBlowTimer -= uiDiff; + + DoMeleeAttackIfReady(); + } +}; + +CreatureAI* GetAI_boss_drakkari_colossus(Creature* pCreature) +{ + return new boss_drakkari_colossusAI(pCreature); +} + +/*###### +## npc_living_mojo +######*/ + +struct npc_living_mojoAI : public ScriptedAI +{ + npc_living_mojoAI(Creature* pCreature) : ScriptedAI(pCreature) + { + m_bIsRegularMode = pCreature->GetMap()->IsRegularDifficulty(); + m_pInstance = (instance_gundrak*)pCreature->GetInstanceData(); + m_bIsPartOfColossus = pCreature->GetPositionX() > 1650.0f ? true : false; + Reset(); + } + + instance_gundrak* m_pInstance; + bool m_bIsRegularMode; + bool m_bIsPartOfColossus; + + uint32 m_uiMojoWaveTimer; + + void Reset() override + { + m_uiMojoWaveTimer = urand(10000, 13000); + } + + void AttackStart(Unit* pWho) override + { + // Don't attack if is part of the Colossus event + if (m_bIsPartOfColossus) + return; + + ScriptedAI::AttackStart(pWho); + } + + void MovementInform(uint32 uiType, uint32 uiPointId) override + { + if (uiType != POINT_MOTION_TYPE) + return; + + if (uiPointId) + { + m_creature->ForcedDespawn(1000); + + if (m_pInstance) + { + // Prepare to set the Colossus in combat + if (Creature* pColossus = m_pInstance->GetSingleCreatureFromStorage(NPC_COLOSSUS)) + { + if (boss_drakkari_colossusAI* pBossAI = dynamic_cast(pColossus->AI())) + pBossAI->DoPrepareColossus(); + } + } + } + } + + void EnterEvadeMode() override + { + if (!m_bIsPartOfColossus) + ScriptedAI::EnterEvadeMode(); + // Force the Mojo to move to the Colossus position + else + { + if (m_pInstance) + { + float fX, fY, fZ; + m_creature->GetPosition(fX, fY, fZ); + + if (Creature* pColossus = m_pInstance->GetSingleCreatureFromStorage(NPC_COLOSSUS)) + pColossus->GetPosition(fX, fY, fZ); + + m_creature->SetWalk(false); + m_creature->GetMotionMaster()->MovePoint(1, fX, fY, fZ); + } + } + } + + void JustDied(Unit* /*pKiller*/) override + { + DoCastSpellIfCan(m_creature, m_bIsRegularMode ? SPELL_MOJO_PUDDLE : SPELL_MOJO_PUDDLE_H, CAST_TRIGGERED); + } + + void UpdateAI(const uint32 uiDiff) override + { + if (!m_creature->SelectHostileTarget() || !m_creature->getVictim()) + return; + + if (m_uiMojoWaveTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature, m_bIsRegularMode ? SPELL_MOJO_WAVE : SPELL_MOJO_WAVE_H) == CAST_OK) + m_uiMojoWaveTimer = urand(15000, 18000); + } + else + m_uiMojoWaveTimer -= uiDiff; + + DoMeleeAttackIfReady(); + } +}; + +CreatureAI* GetAI_npc_living_mojo(Creature* pCreature) +{ + return new npc_living_mojoAI(pCreature); +}; + +void AddSC_boss_colossus() +{ + Script* pNewScript; + + pNewScript = new Script; + pNewScript->Name = "boss_drakkari_colossus"; + pNewScript->GetAI = &GetAI_boss_drakkari_colossus; + pNewScript->RegisterSelf(); + + pNewScript = new Script; + pNewScript->Name = "boss_drakkari_elemental"; + pNewScript->GetAI = &GetAI_boss_drakkari_elemental; + pNewScript->RegisterSelf(); + + pNewScript = new Script; + pNewScript->Name = "npc_living_mojo"; + pNewScript->GetAI = &GetAI_npc_living_mojo; + pNewScript->RegisterSelf(); +} diff --git a/src/modules/SD2/scripts/northrend/gundrak/boss_eck.cpp b/src/modules/SD2/scripts/northrend/gundrak/boss_eck.cpp new file mode 100644 index 000000000..318679377 --- /dev/null +++ b/src/modules/SD2/scripts/northrend/gundrak/boss_eck.cpp @@ -0,0 +1,155 @@ +/* Copyright (C) 2006 - 2013 ScriptDev2 + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +/* ScriptData +SDName: boss_eck +SD%Complete: 80% +SDComment: Timers need improval, Spring Spells are not clear. +SDCategory: Gundrak +EndScriptData */ + +#include "precompiled.h" +#include "gundrak.h" + +enum +{ + SPELL_ECK_BITE = 55813, + SPELL_ECK_SPIT = 55814, + SPELL_ECK_SPRING = 55815, + SPELL_ECK_BERSERK = 55816, + SPELL_ECK_RESIDUE = 55817, + SPELL_ECK_SPRING_ALT = 55837, // Purpose unknown +}; + +/*###### +## boss_eck +######*/ + +struct boss_eckAI : public ScriptedAI +{ + boss_eckAI(Creature* pCreature) : ScriptedAI(pCreature) + { + m_pInstance = (instance_gundrak*)pCreature->GetInstanceData(); + Reset(); + } + + instance_gundrak* m_pInstance; + bool m_bIsBerserk; + + uint32 m_uiSpitTimer; + uint32 m_uiSpringTimer; + uint32 m_uiBiteTimer; + uint32 m_uiBerserkTimer; + + void Reset() override + { + m_uiSpitTimer = urand(10000, 20000); + m_uiSpringTimer = urand(15000, 25000); + m_uiBiteTimer = urand(5000, 15000); + m_uiBerserkTimer = urand(60000, 90000); // Enrange at 20% HP or after 60-90 seconds + m_bIsBerserk = false; + } + + void Aggro(Unit* /*pWho*/) override + { + if (m_pInstance) + m_pInstance->SetData(TYPE_ECK, IN_PROGRESS); + } + + void JustDied(Unit* /*pKiller*/) override + { + if (m_pInstance) + m_pInstance->SetData(TYPE_ECK, DONE); + } + + void JustReachedHome() override + { + if (m_pInstance) + m_pInstance->SetData(TYPE_ECK, FAIL); + } + + // As the Eck Spite spell has no dummy or similar effect, applying the residue aura has to be done with spellHitTarget + void SpellHitTarget(Unit* pUnit, const SpellEntry* pSpellEntry) override + { + if (pSpellEntry->Id == SPELL_ECK_SPIT && pUnit->GetTypeId() == TYPEID_PLAYER && !pUnit->HasAura(SPELL_ECK_RESIDUE)) + pUnit->CastSpell(pUnit, SPELL_ECK_RESIDUE, true); + } + + void UpdateAI(const uint32 uiDiff) override + { + if (!m_creature->SelectHostileTarget() || !m_creature->getVictim()) + return; + + if (m_uiSpitTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature, SPELL_ECK_SPIT) == CAST_OK) + m_uiSpitTimer = urand(10000, 20000); + } + else + m_uiSpitTimer -= uiDiff; + + if (m_uiSpringTimer < uiDiff) + { + Unit* pTarget = m_creature->SelectAttackingTarget(ATTACKING_TARGET_RANDOM, 1); + if (!pTarget) + pTarget = m_creature->getVictim(); + + if (DoCastSpellIfCan(pTarget, SPELL_ECK_SPRING) == CAST_OK) + { + DoResetThreat(); + m_uiSpringTimer = urand(15000, 25000); + } + } + else + m_uiSpringTimer -= uiDiff; + + if (m_uiBiteTimer < uiDiff) + { + DoCastSpellIfCan(m_creature->getVictim(), SPELL_ECK_BITE); + m_uiBiteTimer = urand(5000, 15000); + } + else + m_uiBiteTimer -= uiDiff; + + if (!m_bIsBerserk) // Go into Berserk after time, or when below 20% health + { + if (m_creature->GetHealthPercent() <= 20.0f || m_uiBerserkTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature, SPELL_ECK_BERSERK) == CAST_OK) + m_bIsBerserk = true; + } + else + m_uiBerserkTimer -= uiDiff; + } + + DoMeleeAttackIfReady(); + } +}; + +CreatureAI* GetAI_boss_eck(Creature* pCreature) +{ + return new boss_eckAI(pCreature); +} + +void AddSC_boss_eck() +{ + Script* pNewScript; + + pNewScript = new Script; + pNewScript->Name = "boss_eck"; + pNewScript->GetAI = &GetAI_boss_eck; + pNewScript->RegisterSelf(); +} diff --git a/src/modules/SD2/scripts/northrend/gundrak/boss_galdarah.cpp b/src/modules/SD2/scripts/northrend/gundrak/boss_galdarah.cpp new file mode 100644 index 000000000..54b5b1e8a --- /dev/null +++ b/src/modules/SD2/scripts/northrend/gundrak/boss_galdarah.cpp @@ -0,0 +1,271 @@ +/* Copyright (C) 2006 - 2013 ScriptDev2 + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +/* ScriptData +SDName: Boss_Galdarah +SD%Complete: 80% +SDComment: achievements need to be implemented, channeling before engage is missing +SDCategory: Gundrak +EndScriptData */ + +#include "precompiled.h" +#include "gundrak.h" + +enum +{ + SAY_AGGRO = -1604019, + SAY_TRANSFORM_1 = -1604020, + SAY_TRANSFORM_2 = -1604021, + SAY_SUMMON_1 = -1604022, + SAY_SUMMON_2 = -1604023, + SAY_SUMMON_3 = -1604024, + SAY_SLAY_1 = -1604025, + SAY_SLAY_2 = -1604026, + SAY_SLAY_3 = -1604027, + SAY_DEATH = -1604028, + + EMOTE_IMPALED = -1604030, + + NPC_RHINO_SPIRIT = 29791, + SPELL_STAMPEDE_RHINO = 55220, + SPELL_STAMPEDE_RHINO_H = 59823, + + // troll form spells + SPELL_STAMPEDE = 55218, + SPELL_WHIRLING_SLASH = 55250, + SPELL_WHIRLING_SLASH_H = 59824, + SPELL_RHINO_TRANSFORM = 55297, + SPELL_PUNCTURE = 55276, + SPELL_PUNCTURE_H = 59826, + + // rhino form spells + SPELL_TROLL_TRANSFORM = 55299, + SPELL_ENRAGE = 55285, + SPELL_ENRAGE_H = 59828, + SPELL_IMPALING_CHARGE = 54956, + SPELL_IMPALING_CHARGE_H = 59827, + SPELL_STOMP = 55292, + SPELL_STOMP_H = 59829, +}; + +/*###### +## boss_galdarah +######*/ + +struct boss_galdarahAI : public ScriptedAI +{ + boss_galdarahAI(Creature* pCreature) : ScriptedAI(pCreature) + { + m_pInstance = (instance_gundrak*)pCreature->GetInstanceData(); + m_bIsRegularMode = pCreature->GetMap()->IsRegularDifficulty(); + Reset(); + } + + instance_gundrak* m_pInstance; + bool m_bIsRegularMode; + bool m_bIsTrollPhase; + + uint32 m_uiStampedeTimer; + uint32 m_uiPhaseChangeTimer; + uint32 m_uiSpecialAbilityTimer; // Impaling Charge and Whirling Slash + uint32 m_uiPunctureTimer; + uint32 m_uiStompTimer; + uint32 m_uiEnrageTimer; + uint8 m_uiAbilityCount; + + void Reset() override + { + m_bIsTrollPhase = true; + + m_uiStampedeTimer = 10000; + m_uiSpecialAbilityTimer = 12000; + m_uiPunctureTimer = 25000; + m_uiPhaseChangeTimer = 7000; + m_uiAbilityCount = 0; + } + + void Aggro(Unit* /*pWho*/) override + { + DoScriptText(SAY_AGGRO, m_creature); + + if (m_pInstance) + m_pInstance->SetData(TYPE_GALDARAH , IN_PROGRESS); + } + + void KilledUnit(Unit* /*pVictim*/) override + { + switch (urand(0, 2)) + { + case 0: DoScriptText(SAY_SLAY_1, m_creature); break; + case 1: DoScriptText(SAY_SLAY_2, m_creature); break; + case 2: DoScriptText(SAY_SLAY_3, m_creature); break; + } + } + + void JustReachedHome() override + { + if (m_pInstance) + m_pInstance->SetData(TYPE_GALDARAH, FAIL); + } + + void JustDied(Unit* /*pKiller*/) override + { + DoScriptText(SAY_DEATH, m_creature); + + if (m_pInstance) + m_pInstance->SetData(TYPE_GALDARAH, DONE); + } + + void JustSummoned(Creature* pSummoned) override + { + if (pSummoned->GetEntry() == NPC_RHINO_SPIRIT) + { + if (Unit* pTarget = m_creature->SelectAttackingTarget(ATTACKING_TARGET_RANDOM, 1, m_bIsRegularMode ? SPELL_STAMPEDE_RHINO : SPELL_STAMPEDE_RHINO_H, SELECT_FLAG_PLAYER)) + { + pSummoned->CastSpell(pTarget, m_bIsRegularMode ? SPELL_STAMPEDE_RHINO : SPELL_STAMPEDE_RHINO_H, false, NULL, NULL, m_creature->GetObjectGuid()); + + // Store the player guid in order to count it for the achievement + if (m_pInstance) + m_pInstance->SetData(TYPE_ACHIEV_SHARE_LOVE, pTarget->GetGUIDLow()); + } + } + } + + void DoPhaseSwitch() + { + if (!m_bIsTrollPhase) + m_creature->RemoveAurasDueToSpell(SPELL_RHINO_TRANSFORM); + + m_bIsTrollPhase = !m_bIsTrollPhase; + + if (m_bIsTrollPhase) + DoCastSpellIfCan(m_creature, SPELL_TROLL_TRANSFORM); + else + { + DoScriptText(urand(0, 1) ? SAY_TRANSFORM_1 : SAY_TRANSFORM_2, m_creature); + DoCastSpellIfCan(m_creature, SPELL_RHINO_TRANSFORM); + + m_uiEnrageTimer = 4000; + m_uiStompTimer = 1000; + } + + m_uiAbilityCount = 0; + m_uiPhaseChangeTimer = 7000; + m_uiSpecialAbilityTimer = 12000; + } + + void UpdateAI(const uint32 uiDiff) override + { + if (!m_creature->SelectHostileTarget() || !m_creature->getVictim()) + return; + + if (m_uiAbilityCount == 2) + { + if (m_uiPhaseChangeTimer < uiDiff) + DoPhaseSwitch(); + else + m_uiPhaseChangeTimer -= uiDiff; + } + + if (m_bIsTrollPhase) + { + if (m_uiPunctureTimer < uiDiff) + { + DoCastSpellIfCan(m_creature->getVictim(), m_bIsRegularMode ? SPELL_PUNCTURE : SPELL_PUNCTURE_H); + m_uiPunctureTimer = 25000; + } + else + m_uiPunctureTimer -= uiDiff; + + if (m_uiStampedeTimer < uiDiff) + { + switch (urand(0, 2)) + { + case 0: DoScriptText(SAY_SUMMON_1, m_creature); break; + case 1: DoScriptText(SAY_SUMMON_2, m_creature); break; + case 2: DoScriptText(SAY_SUMMON_3, m_creature); break; + } + + DoCastSpellIfCan(m_creature->getVictim(), SPELL_STAMPEDE); + m_uiStampedeTimer = 15000; + } + else + m_uiStampedeTimer -= uiDiff; + + if (m_uiSpecialAbilityTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature->getVictim(), m_bIsRegularMode ? SPELL_WHIRLING_SLASH : SPELL_WHIRLING_SLASH_H) == CAST_OK) + m_uiSpecialAbilityTimer = 12000; + + ++m_uiAbilityCount; + } + else + m_uiSpecialAbilityTimer -= uiDiff; + } + else + { + if (m_uiEnrageTimer < uiDiff) + { + DoCastSpellIfCan(m_creature, m_bIsRegularMode ? SPELL_ENRAGE : SPELL_ENRAGE_H); + m_uiEnrageTimer = 15000; + } + else + m_uiEnrageTimer -= uiDiff; + + if (m_uiStompTimer < uiDiff) + { + DoCastSpellIfCan(m_creature, m_bIsRegularMode ? SPELL_STOMP : SPELL_STOMP_H); + m_uiStompTimer = 10000; + } + else + m_uiStompTimer -= uiDiff; + + if (m_uiSpecialAbilityTimer < uiDiff) + { + Unit* pTarget = m_creature->SelectAttackingTarget(ATTACKING_TARGET_RANDOM, 1); + if (!pTarget) + pTarget = m_creature->getVictim(); + + if (DoCastSpellIfCan(pTarget, m_bIsRegularMode ? SPELL_IMPALING_CHARGE : SPELL_IMPALING_CHARGE_H) == CAST_OK) + { + DoScriptText(EMOTE_IMPALED, m_creature, pTarget); + m_uiSpecialAbilityTimer = 12000; + + ++m_uiAbilityCount; + } + } + else + m_uiSpecialAbilityTimer -= uiDiff; + } + + DoMeleeAttackIfReady(); + } +}; + +CreatureAI* GetAI_boss_galdarah(Creature* pCreature) +{ + return new boss_galdarahAI(pCreature); +} + +void AddSC_boss_galdarah() +{ + Script* pNewScript; + + pNewScript = new Script; + pNewScript->Name = "boss_galdarah"; + pNewScript->GetAI = &GetAI_boss_galdarah; + pNewScript->RegisterSelf(); +} diff --git a/src/modules/SD2/scripts/northrend/gundrak/boss_moorabi.cpp b/src/modules/SD2/scripts/northrend/gundrak/boss_moorabi.cpp new file mode 100644 index 000000000..79f521753 --- /dev/null +++ b/src/modules/SD2/scripts/northrend/gundrak/boss_moorabi.cpp @@ -0,0 +1,191 @@ +/* Copyright (C) 2006 - 2013 ScriptDev2 + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +/* ScriptData +SDName: Boss_Moorabi +SD%Complete: 20% +SDComment: +SDCategory: Gundrak +EndScriptData */ + +#include "precompiled.h" +#include "gundrak.h" + +enum +{ + SAY_AGGRO = -1604011, + SAY_QUAKE = -1604012, + SAY_TRANSFORM = -1604013, + SAY_SLAY_1 = -1604014, + SAY_SLAY_2 = -1604015, + SAY_SLAY_3 = -1604016, + SAY_DEATH = -1604017, + EMOTE_TRANSFORM = -1604018, + EMOTE_TRANSFORMED = -1604029, + + // Troll form + SPELL_DETERMINED_STAB = 55104, + SPELL_MOJO_FRENZY = 55163, + SPELL_GROUND_TREMOR = 55142, + SPELL_NUMBING_SHOUT = 55106, + SPELL_TRANSFORMATION = 55098, + + // Mammoth + SPELL_DETERMINED_GORE = 55102, + SPELL_DETERMINED_GORE_H = 59444, + SPELL_QUAKE = 55101, + SPELL_NUMBING_ROAR = 55100, +}; + +/*###### +## boss_moorabi +######*/ + +struct boss_moorabiAI : public ScriptedAI +{ + boss_moorabiAI(Creature* pCreature) : ScriptedAI(pCreature) + { + m_pInstance = (instance_gundrak*)pCreature->GetInstanceData(); + m_bIsRegularMode = pCreature->GetMap()->IsRegularDifficulty(); + Reset(); + } + + instance_gundrak* m_pInstance; + bool m_bIsRegularMode; + + uint32 m_uiStabTimer; // used for stab and gore + uint32 m_uiQuakeTimer; // used for quake and ground tremor + uint32 m_uiRoarTimer; // both roars on it + uint32 m_uiTransformationTimer; + uint32 m_uiPreviousTimer; + + bool m_bMammothPhase; + + void Reset() override + { + m_bMammothPhase = false; + + m_uiStabTimer = 8000; + m_uiQuakeTimer = 1000; + m_uiRoarTimer = 7000; + m_uiTransformationTimer = 10000; + m_uiPreviousTimer = 10000; + } + + void Aggro(Unit* /*pWho*/) override + { + DoScriptText(SAY_AGGRO, m_creature); + DoCastSpellIfCan(m_creature, SPELL_MOJO_FRENZY); + + if (m_pInstance) + m_pInstance->SetData(TYPE_MOORABI, IN_PROGRESS); + } + + void KilledUnit(Unit* /*pVictim*/) override + { + switch (urand(0, 2)) + { + case 0: DoScriptText(SAY_SLAY_1, m_creature); break; + case 1: DoScriptText(SAY_SLAY_2, m_creature); break; + case 2: DoScriptText(SAY_SLAY_3, m_creature); break; + } + } + + void JustDied(Unit* /*pKiller*/) override + { + DoScriptText(SAY_DEATH, m_creature); + + if (m_pInstance) + m_pInstance->SetData(TYPE_MOORABI, DONE); + } + + void UpdateAI(const uint32 uiDiff) override + { + if (!m_creature->SelectHostileTarget() || !m_creature->getVictim()) + return; + + if (m_creature->HasAura(SPELL_TRANSFORMATION) && !m_bMammothPhase) + { + DoScriptText(EMOTE_TRANSFORMED, m_creature); + m_bMammothPhase = true; + + // Set the achievement to failed + if (m_pInstance) + m_pInstance->SetLessRabiAchievementCriteria(false); + } + + if (m_uiRoarTimer < uiDiff) + { + DoCastSpellIfCan(m_creature->getVictim(), m_bMammothPhase ? SPELL_NUMBING_ROAR : SPELL_NUMBING_SHOUT); + m_uiRoarTimer = 20000; + } + else + m_uiRoarTimer -= uiDiff; + + if (m_uiQuakeTimer < uiDiff) + { + DoScriptText(SAY_QUAKE, m_creature); + DoCastSpellIfCan(m_creature->getVictim(), m_bMammothPhase ? SPELL_QUAKE : SPELL_GROUND_TREMOR); + m_uiQuakeTimer = m_bMammothPhase ? 13000 : 18000; + } + else + m_uiQuakeTimer -= uiDiff; + + if (m_uiStabTimer < uiDiff) + { + if (m_bMammothPhase) + DoCastSpellIfCan(m_creature->getVictim(), m_bIsRegularMode ? SPELL_DETERMINED_GORE : SPELL_DETERMINED_GORE_H); + else + DoCastSpellIfCan(m_creature->getVictim(), SPELL_DETERMINED_STAB); + + m_uiStabTimer = 7000; + } + else + m_uiStabTimer -= uiDiff; + + // check only in troll phase + if (!m_bMammothPhase) + { + if (m_uiTransformationTimer < uiDiff) + { + DoScriptText(SAY_TRANSFORM, m_creature); + DoScriptText(EMOTE_TRANSFORM, m_creature); + DoCastSpellIfCan(m_creature, SPELL_TRANSFORMATION); + m_uiPreviousTimer *= 0.8; + m_uiTransformationTimer = m_uiPreviousTimer; + } + else + m_uiTransformationTimer -= uiDiff; + } + + DoMeleeAttackIfReady(); + } +}; + +CreatureAI* GetAI_boss_moorabi(Creature* pCreature) +{ + return new boss_moorabiAI(pCreature); +} + +void AddSC_boss_moorabi() +{ + Script* pNewScript; + + pNewScript = new Script; + pNewScript->Name = "boss_moorabi"; + pNewScript->GetAI = &GetAI_boss_moorabi; + pNewScript->RegisterSelf(); +} diff --git a/src/modules/SD2/scripts/northrend/gundrak/boss_sladran.cpp b/src/modules/SD2/scripts/northrend/gundrak/boss_sladran.cpp new file mode 100644 index 000000000..b6c7b2595 --- /dev/null +++ b/src/modules/SD2/scripts/northrend/gundrak/boss_sladran.cpp @@ -0,0 +1,219 @@ +/* Copyright (C) 2006 - 2013 ScriptDev2 + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +/* ScriptData +SDName: Boss_Sladran +SD%Complete: 75% +SDComment: +SDCategory: Gundrak +EndScriptData */ + +#include "precompiled.h" +#include "gundrak.h" + +enum +{ + SAY_AGGRO = -1604000, + SAY_SUMMON_SNAKE = -1604001, + SAY_SUMMON_CONSTRICTOR = -1604002, + SAY_SLAY_1 = -1604003, + SAY_SLAY_2 = -1604004, + SAY_SLAY_3 = -1604005, + SAY_DEATH = -1604006, + EMOTE_NOVA = -1604007, + + // Slad'Ran spells + SPELL_POISON_NOVA = 55081, + SPELL_POISON_NOVA_H = 59842, + SPELL_POWERFUL_BITE = 48287, + SPELL_POWERFUL_BITE_H = 59840, + SPELL_VENOM_BOLT = 54970, + SPELL_VENOM_BOLT_H = 59839, + + // Summon spells + SPELL_SUMMON_VIPER = 55060, + SPELL_SUMMON_CONSTRICTOR = 54969, + + // Constrictor spells + SPELL_GRIP_OF_SLADRAN = 55093, + SPELL_GRIP_OF_SLADRAN_H = 61474, + + // Snake Wrap spells - mechanics unk + SPELL_SNAKE_WRAP = 55099, + SPELL_SNAKE_WRAP_H = 61475, + SPELL_SNAKE_WRAP_SUMMON = 55126, + SPELL_SNAKE_WRAP_SUMMON_H = 61476, + SPELL_SNAKE_WRAP_EFFECT = 55128, + SPELL_SNAKE_WRAP_SNAKES = 55127, // kills all snakes + + NPC_SLADRAN_CONSTRICTOR = 29713, + NPC_SLADRAN_VIPER = 29680, + NPC_SNAKE_WRAP = 29742, +}; + +/*###### +## boss_sladran +######*/ + +struct boss_sladranAI : public ScriptedAI +{ + boss_sladranAI(Creature* pCreature) : ScriptedAI(pCreature) + { + m_pInstance = (instance_gundrak*)pCreature->GetInstanceData(); + m_bIsRegularMode = pCreature->GetMap()->IsRegularDifficulty(); + Reset(); + } + + instance_gundrak* m_pInstance; + bool m_bIsRegularMode; + + uint32 m_uiSummonTimer; + uint32 m_uiPoisonNovaTimer; + uint32 m_uiPowerfulBiteTimer; + uint32 m_uiVenomBoltTimer; + + void Reset() override + { + m_uiSummonTimer = m_bIsRegularMode ? 5000 : 3000; + m_uiPoisonNovaTimer = 22000; + m_uiPowerfulBiteTimer = 10000; + m_uiVenomBoltTimer = 15000; + } + + void Aggro(Unit* /*pWho*/) override + { + DoScriptText(SAY_AGGRO, m_creature); + + if (m_pInstance) + m_pInstance->SetData(TYPE_SLADRAN, IN_PROGRESS); + } + + void KilledUnit(Unit* /*pVictim*/) override + { + switch (urand(0, 2)) + { + case 0: DoScriptText(SAY_SLAY_1, m_creature); break; + case 1: DoScriptText(SAY_SLAY_2, m_creature); break; + case 2: DoScriptText(SAY_SLAY_3, m_creature); break; + } + } + + void JustDied(Unit* /*pKiller*/) override + { + DoScriptText(SAY_DEATH, m_creature); + + if (m_pInstance) + m_pInstance->SetData(TYPE_SLADRAN, DONE); + } + + void JustReachedHome() override + { + if (m_pInstance) + m_pInstance->SetData(TYPE_SLADRAN, FAIL); + } + + void JustSummoned(Creature* pSummoned) override + { + if (pSummoned->GetEntry() != NPC_SLADRAN_CONSTRICTOR && pSummoned->GetEntry() != NPC_SLADRAN_VIPER) + return; + + pSummoned->SetWalk(false); + pSummoned->GetMotionMaster()->MovePoint(0, m_creature->GetPositionX(), m_creature->GetPositionY(), m_creature->GetPositionZ(), false); + } + + void UpdateAI(const uint32 uiDiff) override + { + if (!m_creature->SelectHostileTarget() || !m_creature->getVictim()) + return; + + if (m_uiPoisonNovaTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature, m_bIsRegularMode ? SPELL_POISON_NOVA : SPELL_POISON_NOVA_H) == CAST_OK) + { + DoScriptText(EMOTE_NOVA, m_creature); + m_uiPoisonNovaTimer = 22000; + } + } + else + m_uiPoisonNovaTimer -= uiDiff; + + if (m_uiSummonTimer < uiDiff) + { + if (!m_pInstance) + return; + + if (Creature* pSummonTarget = m_creature->GetMap()->GetCreature(m_pInstance->SelectRandomSladranTargetGuid())) + { + if (urand(0, 3)) + { + // we don't want to get spammed + if (!urand(0, 4)) + DoScriptText(SAY_SUMMON_CONSTRICTOR, m_creature); + + pSummonTarget->CastSpell(pSummonTarget, SPELL_SUMMON_CONSTRICTOR, false, NULL, NULL, m_creature->GetObjectGuid()); + } + else + { + // we don't want to get spammed + if (!urand(0, 4)) + DoScriptText(SAY_SUMMON_SNAKE, m_creature); + + pSummonTarget->CastSpell(pSummonTarget, SPELL_SUMMON_VIPER, false, NULL, NULL, m_creature->GetObjectGuid()); + } + } + + m_uiSummonTimer = m_bIsRegularMode ? 5000 : 3000; + } + else + m_uiSummonTimer -= uiDiff; + + if (m_uiPowerfulBiteTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature->getVictim(), m_bIsRegularMode ? SPELL_POWERFUL_BITE : SPELL_POWERFUL_BITE_H) == CAST_OK) + m_uiPowerfulBiteTimer = 10000; + } + else + m_uiPowerfulBiteTimer -= uiDiff; + + if (m_uiVenomBoltTimer < uiDiff) + { + if (Unit* pTarget = m_creature->SelectAttackingTarget(ATTACKING_TARGET_RANDOM, 0)) + { + if (DoCastSpellIfCan(pTarget, m_bIsRegularMode ? SPELL_VENOM_BOLT : SPELL_VENOM_BOLT_H) == CAST_OK) + m_uiVenomBoltTimer = 15000; + } + } + else + m_uiVenomBoltTimer -= uiDiff; + + DoMeleeAttackIfReady(); + } +}; + +CreatureAI* GetAI_boss_sladran(Creature* pCreature) +{ + return new boss_sladranAI(pCreature); +} + +void AddSC_boss_sladran() +{ + Script* pNewScript; + + pNewScript = new Script; + pNewScript->Name = "boss_sladran"; + pNewScript->GetAI = &GetAI_boss_sladran; + pNewScript->RegisterSelf(); +} diff --git a/src/modules/SD2/scripts/northrend/gundrak/gundrak.h b/src/modules/SD2/scripts/northrend/gundrak/gundrak.h new file mode 100644 index 000000000..e6f602631 --- /dev/null +++ b/src/modules/SD2/scripts/northrend/gundrak/gundrak.h @@ -0,0 +1,120 @@ +/* Copyright (C) 2006 - 2013 ScriptDev2 + * This program is free software licensed under GPL version 2 + * Please see the included DOCS/LICENSE.TXT for more information */ + +#ifndef DEF_GUNDRAK_H +#define DEF_GUNDRAK_H +/* Encounters + * Slad'ran = 0 + * Moorabi = 1 + * Drakkari Colossus = 2 + * Gal'darah = 3 + * Eck the Ferocious = 4 +*/ +enum +{ + MAX_ENCOUNTER = 5, + MIN_LOVE_SHARE_PLAYERS = 5, + + TYPE_SLADRAN = 0, + TYPE_MOORABI = 1, + TYPE_COLOSSUS = 2, + TYPE_GALDARAH = 3, + TYPE_ECK = 4, + + // Used to handle achievements + TYPE_ACHIEV_WHY_SNAKES = 5, + TYPE_ACHIEV_SHARE_LOVE = 6, + + NPC_SLADRAN = 29304, + NPC_MOORABI = 29305, + NPC_COLOSSUS = 29307, + NPC_ELEMENTAL = 29573, + NPC_LIVIN_MOJO = 29830, + NPC_GALDARAH = 29306, + NPC_ECK = 29932, + NPC_INVISIBLE_STALKER = 30298, // Caster and Target for visual spells on altar use + NPC_SLADRAN_SUMMON_T = 29682, + + GO_ECK_DOOR = 192632, + GO_ECK_UNDERWATER_DOOR = 192569, + GO_GALDARAH_DOOR = 192568, + GO_EXIT_DOOR_L = 193208, + GO_EXIT_DOOR_R = 193209, + + GO_ALTAR_OF_SLADRAN = 192518, + GO_ALTAR_OF_MOORABI = 192519, + GO_ALTAR_OF_COLOSSUS = 192520, + + GO_SNAKE_KEY = 192564, + GO_TROLL_KEY = 192567, + GO_MAMMOTH_KEY = 192565, + GO_RHINO_KEY = 192566, + + GO_BRIDGE = 193188, + GO_COLLISION = 192633, + + SPELL_BEAM_MAMMOTH = 57068, + SPELL_BEAM_SNAKE = 57071, + SPELL_BEAM_ELEMENTAL = 57072, + + TIMER_VISUAL_ALTAR = 3000, + TIMER_VISUAL_BEAM = 2500, + TIMER_VISUAL_KEY = 2000, + + ACHIEV_CRIT_LESS_RABI = 7319, // Moorabi achiev 2040 + ACHIEV_CRIT_WHY_SNAKES = 7363, // Sladran achiev 2058 + ACHIEV_CRIT_SHARE_LOVE = 7583, // Galdarah achiev 2152 +}; + +typedef std::map TypeTimerMap; +typedef std::pair TypeTimerPair; + +class instance_gundrak : public ScriptedInstance +{ + public: + instance_gundrak(Map* pMap); + ~instance_gundrak() {} + + void Initialize() override; + + void OnCreatureCreate(Creature* pCreature) override; + void OnObjectCreate(GameObject* pGo) override; + + void OnCreatureEnterCombat(Creature* pCreature) override; + + void SetData(uint32 uiType, uint32 uiData) override; + uint32 GetData(uint32 uiType) const override; + + const char* Save() const override { return m_strInstData.c_str(); } + void Load(const char* chrIn) override; + + ObjectGuid SelectRandomSladranTargetGuid(); + + void SetLessRabiAchievementCriteria(bool bIsMet) { m_bLessRabi = bIsMet; } + bool CheckAchievementCriteriaMeet(uint32 uiCriteriaId, Player const* pSource, Unit const* pTarget, uint32 uiMiscValue1 /* = 0*/) const override; + + void Update(uint32 uiDiff) override; + + protected: + void DoAltarVisualEffect(uint8 uiType); + uint32 m_auiEncounter[MAX_ENCOUNTER]; + std::string m_strInstData; + + TypeTimerMap m_mAltarInProgress; + TypeTimerMap m_mBeamInProgress; + TypeTimerMap m_mKeyInProgress; + + GuidList m_luiStalkerGUIDs; + GuidList m_lSummonTargetsGuids; + GuidVector m_vStalkerCasterGuids; + GuidVector m_vStalkerTargetGuids; + GuidSet m_sColossusMojosGuids; + + bool m_bLessRabi; + + std::set m_uisShareLoveAchievPlayers; + std::set m_uisWhySnakesAchievPlayers; +}; + +#endif diff --git a/src/modules/SD2/scripts/northrend/gundrak/instance_gundrak.cpp b/src/modules/SD2/scripts/northrend/gundrak/instance_gundrak.cpp new file mode 100644 index 000000000..9f8985c18 --- /dev/null +++ b/src/modules/SD2/scripts/northrend/gundrak/instance_gundrak.cpp @@ -0,0 +1,488 @@ +/* Copyright (C) 2006 - 2013 ScriptDev2 + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along 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_gundrak +SD%Complete: 80 +SDComment: Reload case for bridge support is missing, achievement support is missing +SDCategory: Gundrak +EndScriptData */ + +#include "precompiled.h" +#include "gundrak.h" + +bool GOUse_go_gundrak_altar(Player* /*pPlayer*/, GameObject* pGo) +{ + ScriptedInstance* pInstance = (ScriptedInstance*)pGo->GetInstanceData(); + + if (!pInstance) + return false; + + switch (pGo->GetEntry()) + { + case GO_ALTAR_OF_SLADRAN: pInstance->SetData(TYPE_SLADRAN, SPECIAL); break; + case GO_ALTAR_OF_MOORABI: pInstance->SetData(TYPE_MOORABI, SPECIAL); break; + case GO_ALTAR_OF_COLOSSUS: pInstance->SetData(TYPE_COLOSSUS, SPECIAL); break; + } + + pGo->UseDoorOrButton(0, true); + return true; +} + +instance_gundrak::instance_gundrak(Map* pMap) : ScriptedInstance(pMap), + m_bLessRabi(false) +{ + Initialize(); +} + +void instance_gundrak::Initialize() +{ + memset(&m_auiEncounter, 0, sizeof(m_auiEncounter)); + + m_vStalkerCasterGuids.reserve(3); + m_vStalkerTargetGuids.reserve(3); +} + +void instance_gundrak::OnCreatureCreate(Creature* pCreature) +{ + switch (pCreature->GetEntry()) + { + case NPC_SLADRAN: + case NPC_ELEMENTAL: + case NPC_COLOSSUS: + m_mNpcEntryGuidStore[pCreature->GetEntry()] = pCreature->GetObjectGuid(); + break; + + case NPC_INVISIBLE_STALKER: + m_luiStalkerGUIDs.push_back(pCreature->GetObjectGuid()); + break; + case NPC_SLADRAN_SUMMON_T: + m_lSummonTargetsGuids.push_back(pCreature->GetObjectGuid()); + break; + + case NPC_LIVIN_MOJO: + // Store only the Mojos used to activate the Colossus + if (pCreature->GetPositionX() > 1650.0f) + m_sColossusMojosGuids.insert(pCreature->GetObjectGuid()); + break; + } +} + +/* TODO: Reload case need some love! +* Problem is to get the bridge/ collision work correct in relaod case. +* To provide correct functionality(expecting testers to activate all altars in reload case), the Keys aren't loaded, too +* TODO: When fixed, also remove the SPECIAL->DONE data translation in Load(). +* +* For the Keys should be used something like this, and for bridge and collision similar +* +* if (m_auiEncounter[0] == SPECIAL && m_auiEncounter[1] == SPECIAL && m_auiEncounter[2] == SPECIAL) +* pGo->SetGoState(GO_STATE_ACTIVE_ALTERNATIVE); +* else +* pGo->SetGoState(GO_STATE_READY); +*/ + +void instance_gundrak::OnObjectCreate(GameObject* pGo) +{ + switch (pGo->GetEntry()) + { + case GO_ECK_DOOR: + if (m_auiEncounter[TYPE_MOORABI] == DONE && !instance->IsRegularDifficulty()) + pGo->SetGoState(GO_STATE_ACTIVE); + break; + case GO_ECK_UNDERWATER_DOOR: + if (m_auiEncounter[TYPE_ECK] == DONE) + pGo->SetGoState(GO_STATE_ACTIVE); + break; + case GO_GALDARAH_DOOR: + pGo->SetGoState(GO_STATE_ACTIVE); + break; + case GO_EXIT_DOOR_L: + if (m_auiEncounter[TYPE_GALDARAH] == DONE) + pGo->SetGoState(GO_STATE_ACTIVE); + break; + case GO_EXIT_DOOR_R: + if (m_auiEncounter[TYPE_GALDARAH] == DONE) + pGo->SetGoState(GO_STATE_ACTIVE); + break; + case GO_ALTAR_OF_SLADRAN: + if (m_auiEncounter[TYPE_SLADRAN] == DONE) + pGo->RemoveFlag(GAMEOBJECT_FLAGS, GO_FLAG_NO_INTERACT); + break; + case GO_ALTAR_OF_MOORABI: + if (m_auiEncounter[TYPE_MOORABI] == DONE) + pGo->RemoveFlag(GAMEOBJECT_FLAGS, GO_FLAG_NO_INTERACT); + break; + case GO_ALTAR_OF_COLOSSUS: + if (m_auiEncounter[TYPE_COLOSSUS] == DONE) + pGo->RemoveFlag(GAMEOBJECT_FLAGS, GO_FLAG_NO_INTERACT); + break; + case GO_SNAKE_KEY: + case GO_TROLL_KEY: + case GO_MAMMOTH_KEY: + case GO_RHINO_KEY: + case GO_BRIDGE: + case GO_COLLISION: + break; + + default: + return; + } + m_mGoEntryGuidStore[pGo->GetEntry()] = pGo->GetObjectGuid(); +} + +void instance_gundrak::Load(const char* chrIn) +{ + if (!chrIn) + { + OUT_LOAD_INST_DATA_FAIL; + return; + } + + OUT_LOAD_INST_DATA(chrIn); + + std::istringstream loadStream(chrIn); + loadStream >> m_auiEncounter[TYPE_SLADRAN] >> m_auiEncounter[TYPE_MOORABI] >> m_auiEncounter[TYPE_COLOSSUS] >> m_auiEncounter[TYPE_GALDARAH] >> m_auiEncounter[TYPE_ECK]; + + for (uint8 i = 0; i < MAX_ENCOUNTER; ++i) + { + if (m_auiEncounter[i] == IN_PROGRESS) + m_auiEncounter[i] = NOT_STARTED; + + // TODO: REMOVE when bridge/ collision reloading correctly working + if (m_auiEncounter[i] == SPECIAL) + m_auiEncounter[i] = DONE; + } + + OUT_LOAD_INST_DATA_COMPLETE; +} + +void instance_gundrak::SetData(uint32 uiType, uint32 uiData) +{ + debug_log("SD2: Instance Gundrak: SetData received for type %u with data %u", uiType, uiData); + + switch (uiType) + { + case TYPE_SLADRAN: + m_auiEncounter[TYPE_SLADRAN] = uiData; + if (uiData == DONE) + if (GameObject* pGo = GetSingleGameObjectFromStorage(GO_ALTAR_OF_SLADRAN)) + pGo->RemoveFlag(GAMEOBJECT_FLAGS, GO_FLAG_NO_INTERACT); + if (uiData == FAIL) + m_uisWhySnakesAchievPlayers.clear(); + if (uiData == SPECIAL) + m_mAltarInProgress.insert(TypeTimerPair(TYPE_SLADRAN, TIMER_VISUAL_ALTAR)); + break; + case TYPE_MOORABI: + m_auiEncounter[TYPE_MOORABI] = uiData; + if (uiData == DONE) + { + if (!instance->IsRegularDifficulty()) + DoUseDoorOrButton(GO_ECK_DOOR); + if (GameObject* pGo = GetSingleGameObjectFromStorage(GO_ALTAR_OF_MOORABI)) + pGo->RemoveFlag(GAMEOBJECT_FLAGS, GO_FLAG_NO_INTERACT); + } + if (uiData == IN_PROGRESS) + SetLessRabiAchievementCriteria(true); + if (uiData == SPECIAL) + m_mAltarInProgress.insert(TypeTimerPair(TYPE_MOORABI, TIMER_VISUAL_ALTAR)); + break; + case TYPE_COLOSSUS: + m_auiEncounter[TYPE_COLOSSUS] = uiData; + if (uiData == DONE) + if (GameObject* pGo = GetSingleGameObjectFromStorage(GO_ALTAR_OF_COLOSSUS)) + pGo->RemoveFlag(GAMEOBJECT_FLAGS, GO_FLAG_NO_INTERACT); + if (uiData == FAIL) + { + for (GuidSet::const_iterator itr = m_sColossusMojosGuids.begin(); itr != m_sColossusMojosGuids.end(); ++itr) + { + if (Creature* pMojo = instance->GetCreature(*itr)) + pMojo->Respawn(); + } + } + if (uiData == SPECIAL) + m_mAltarInProgress.insert(TypeTimerPair(TYPE_COLOSSUS, TIMER_VISUAL_ALTAR)); + break; + case TYPE_GALDARAH: + m_auiEncounter[TYPE_GALDARAH] = uiData; + DoUseDoorOrButton(GO_GALDARAH_DOOR); + if (uiData == DONE) + { + DoUseDoorOrButton(GO_EXIT_DOOR_L); + DoUseDoorOrButton(GO_EXIT_DOOR_R); + } + if (uiData == FAIL) + m_uisShareLoveAchievPlayers.clear(); + break; + case TYPE_ECK: + m_auiEncounter[TYPE_ECK] = uiData; + if (uiData == DONE) + DoUseDoorOrButton(GO_ECK_UNDERWATER_DOOR); + break; + case TYPE_ACHIEV_WHY_SNAKES: + // insert the players who failed the achiev and haven't been already inserted in the set + if (m_uisWhySnakesAchievPlayers.find(uiData) == m_uisWhySnakesAchievPlayers.end()) + m_uisWhySnakesAchievPlayers.insert(uiData); + break; + case TYPE_ACHIEV_SHARE_LOVE: + // insert players who got stampeled and haven't been already inserted in the set + if (m_uisShareLoveAchievPlayers.find(uiData) == m_uisShareLoveAchievPlayers.end()) + m_uisShareLoveAchievPlayers.insert(uiData); + break; + default: + script_error_log("Instance Gundrak: ERROR SetData = %u for type %u does not exist/not implemented.", uiType, uiData); + return; + } + + if (uiData == DONE || uiData == SPECIAL) // Save activated altars, too + { + OUT_SAVE_INST_DATA; + + std::ostringstream saveStream; + saveStream << m_auiEncounter[TYPE_SLADRAN] << " " << m_auiEncounter[TYPE_MOORABI] << " " << m_auiEncounter[TYPE_COLOSSUS] << " " << m_auiEncounter[TYPE_GALDARAH] << " " + << m_auiEncounter[TYPE_ECK]; + + m_strInstData = saveStream.str(); + + SaveToDB(); + OUT_SAVE_INST_DATA_COMPLETE; + } +} + +uint32 instance_gundrak::GetData(uint32 uiType) const +{ + if (uiType < MAX_ENCOUNTER) + return m_auiEncounter[uiType]; + + return 0; +} + +bool instance_gundrak::CheckAchievementCriteriaMeet(uint32 uiCriteriaId, Player const* /*pSource*/, Unit const* /*pTarget*/, uint32 /*uiMiscValue1 = 0*/) const +{ + switch (uiCriteriaId) + { + case ACHIEV_CRIT_LESS_RABI: + return m_bLessRabi; + case ACHIEV_CRIT_SHARE_LOVE: + // Return true if all the players in the group got stampeled + return m_uisShareLoveAchievPlayers.size() == MIN_LOVE_SHARE_PLAYERS; + // ToDo: enable this criteria when the script will be implemented + // case ACHIEV_CRIT_WHY_SNAKES: + // // Return true if not found in the set + // return m_uisWhySnakesAchievPlayers.find(pSource->GetGUIDLow()) == m_uisWhySnakesAchievPlayers.end(); + + default: + return false; + } +} + +void instance_gundrak::OnCreatureEnterCombat(Creature* pCreature) +{ + if (pCreature->GetEntry() == NPC_LIVIN_MOJO) + { + // If not found in the set, or the event is already started, return + if (m_sColossusMojosGuids.find(pCreature->GetObjectGuid()) == m_sColossusMojosGuids.end()) + return; + + // Move all 4 Mojos to evade and move to the Colossus position + for (GuidSet::const_iterator itr = m_sColossusMojosGuids.begin(); itr != m_sColossusMojosGuids.end(); ++itr) + { + if (Creature* pMojo = instance->GetCreature(*itr)) + pMojo->AI()->EnterEvadeMode(); + } + } +} + +ObjectGuid instance_gundrak::SelectRandomSladranTargetGuid() +{ + if (m_lSummonTargetsGuids.empty()) + return ObjectGuid(); + + GuidList::iterator iter = m_lSummonTargetsGuids.begin(); + advance(iter, urand(0, m_lSummonTargetsGuids.size() - 1)); + + return *iter; +} + +static bool sortFromEastToWest(Creature* pFirst, Creature* pSecond) +{ + return pFirst && pSecond && pFirst->GetPositionY() < pSecond->GetPositionY(); +} + +void instance_gundrak::DoAltarVisualEffect(uint8 uiType) +{ + // Sort the lists if not yet done + if (!m_luiStalkerGUIDs.empty()) + { + float fHeight = 10.0f; // A bit higher than the altar is needed + if (GameObject* pCollusAltar = GetSingleGameObjectFromStorage(GO_ALTAR_OF_COLOSSUS)) + fHeight += pCollusAltar->GetPositionZ(); + + std::list lStalkerTargets, lStalkerCasters; + for (GuidList::const_iterator itr = m_luiStalkerGUIDs.begin(); itr != m_luiStalkerGUIDs.end(); ++itr) + { + if (Creature* pStalker = instance->GetCreature(*itr)) + { + if (pStalker->GetPositionZ() > fHeight) + lStalkerTargets.push_back(pStalker); + else + lStalkerCasters.push_back(pStalker); + } + } + m_luiStalkerGUIDs.clear(); + + lStalkerTargets.sort(sortFromEastToWest); + lStalkerCasters.sort(sortFromEastToWest); + + for (std::list::const_iterator itr = lStalkerTargets.begin(); itr != lStalkerTargets.end(); ++itr) + m_vStalkerTargetGuids.push_back((*itr)->GetObjectGuid()); + for (std::list::const_iterator itr = lStalkerCasters.begin(); itr != lStalkerCasters.end(); ++itr) + m_vStalkerCasterGuids.push_back((*itr)->GetObjectGuid()); + } + + // Verify that the DB has enough trigger spawned + if (m_vStalkerTargetGuids.size() < 3 || m_vStalkerCasterGuids.size() < 3) + return; + + // Get the Index from the bosses + uint8 uiIndex = 0; + switch (uiType) + { + case TYPE_SLADRAN: uiIndex = 0; break; + case TYPE_COLOSSUS: uiIndex = 1; break; + case TYPE_MOORABI: uiIndex = 2; break; + default: + return; + } + + Creature* pTarget = instance->GetCreature(m_vStalkerTargetGuids[uiIndex]); + Creature* pCaster = instance->GetCreature(m_vStalkerCasterGuids[uiIndex]); + + if (!pTarget || !pCaster) + return; + + uint32 auiFireBeamSpells[3] = {SPELL_BEAM_SNAKE, SPELL_BEAM_ELEMENTAL, SPELL_BEAM_MAMMOTH}; + + // Cast from Caster to Target + pCaster->CastSpell(pTarget, auiFireBeamSpells[uiIndex], false); +} + +void instance_gundrak::Update(uint32 uiDiff) +{ + // Possible multible altars used at the same time, process their timers + if (!m_mAltarInProgress.empty()) + { + for (TypeTimerMap::iterator itr = m_mAltarInProgress.begin(); itr != m_mAltarInProgress.end();) + { + if (itr->second < uiDiff) + { + // Do Visual Effect + DoAltarVisualEffect(itr->first); + // Set Timer for Beam-Duration + m_mBeamInProgress.insert(TypeTimerPair(itr->first, TIMER_VISUAL_BEAM)); + // Remove this timer, as processed + m_mAltarInProgress.erase(itr++); + } + else + { + itr->second -= uiDiff; + ++itr; + } + } + } + + // Possible multible beams used at the same time, process their timers + if (!m_mBeamInProgress.empty()) + { + for (TypeTimerMap::iterator itr = m_mBeamInProgress.begin(); itr != m_mBeamInProgress.end();) + { + if (itr->second < uiDiff) + { + // Use Key + switch (itr->first) + { + case TYPE_SLADRAN: DoUseDoorOrButton(GO_SNAKE_KEY); break; + case TYPE_MOORABI: DoUseDoorOrButton(GO_MAMMOTH_KEY); break; + case TYPE_COLOSSUS: DoUseDoorOrButton(GO_TROLL_KEY); break; + } + // Set Timer for Beam-Duration + m_mKeyInProgress.insert(TypeTimerPair(itr->first, TIMER_VISUAL_KEY)); + m_mBeamInProgress.erase(itr++); + } + else + { + itr->second -= uiDiff; + ++itr; + } + } + } + + // Activate Bridge if all Three Encounters are used + if (!m_mKeyInProgress.empty()) + { + for (TypeTimerMap::iterator itr = m_mKeyInProgress.begin(); itr != m_mKeyInProgress.end();) + { + if (itr->second < uiDiff) + { + // Activate Bridge (and all other Keys) if we are on the last Key, and all other keys are already set + if (m_auiEncounter[0] == SPECIAL && m_auiEncounter[1] == SPECIAL && m_auiEncounter[2] == SPECIAL + && m_mAltarInProgress.empty() && m_mBeamInProgress.empty() && m_mKeyInProgress.size() == 1) + { + DoUseDoorOrButton(GO_COLLISION); + DoUseDoorOrButton(GO_RHINO_KEY, 0, true); + + // The already closed keys cannot be done with DoUseDoorOrButton + if (GameObject* pTrollKey = GetSingleGameObjectFromStorage(GO_TROLL_KEY)) + pTrollKey->SetGoState(GO_STATE_ACTIVE_ALTERNATIVE); + if (GameObject* pMammothKey = GetSingleGameObjectFromStorage(GO_MAMMOTH_KEY)) + pMammothKey->SetGoState(GO_STATE_ACTIVE_ALTERNATIVE); + if (GameObject* pSnakeKey = GetSingleGameObjectFromStorage(GO_SNAKE_KEY)) + pSnakeKey->SetGoState(GO_STATE_ACTIVE_ALTERNATIVE); + + // GO_BRIDGE is type 35 (TRAP_DOOR) and needs to be handled directly + // Real Use of this GO is unknown, but this change of state is expected + DoUseDoorOrButton(GO_BRIDGE); + } + // Remove this timer, as processed + m_mKeyInProgress.erase(itr++); + } + else + { + itr->second -= uiDiff; + ++itr; + } + } + } +} + +InstanceData* GetInstanceData_instance_gundrak(Map* pMap) +{ + return new instance_gundrak(pMap); +} + +void AddSC_instance_gundrak() +{ + Script* pNewScript; + + pNewScript = new Script; + pNewScript->Name = "go_gundrak_altar"; + pNewScript->pGOUse = &GOUse_go_gundrak_altar; + pNewScript->RegisterSelf(); + + pNewScript = new Script; + pNewScript->Name = "instance_gundrak"; + pNewScript->GetInstanceData = &GetInstanceData_instance_gundrak; + pNewScript->RegisterSelf(); +} diff --git a/src/modules/SD2/scripts/northrend/howling_fjord.cpp b/src/modules/SD2/scripts/northrend/howling_fjord.cpp new file mode 100644 index 000000000..42a846ef3 --- /dev/null +++ b/src/modules/SD2/scripts/northrend/howling_fjord.cpp @@ -0,0 +1,781 @@ +/* Copyright (C) 2006 - 2013 ScriptDev2 + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +/* ScriptData +SDName: Howling_Fjord +SD%Complete: ? +SDComment: Quest support: 11300, 11343, 11344, 11464. +SDCategory: Howling Fjord +EndScriptData */ + +/* ContentData +npc_ancient_male_vrykul +at_ancient_male_vrykul +npc_daegarn +npc_silvermoon_harry +npc_lich_king_village +npc_king_ymiron +EndContentData */ + +#include "precompiled.h" + +enum +{ + SPELL_ECHO_OF_YMIRON = 42786, + SPELL_SECRET_OF_WYRMSKULL = 43458, + QUEST_ECHO_OF_YMIRON = 11343, + NPC_MALE_VRYKUL = 24314, + NPC_FEMALE_VRYKUL = 24315, + + SAY_VRYKUL_CURSED = -1000635, + EMOTE_VRYKUL_POINT = -1000636, + EMOTE_VRYKUL_SOB = -1000637, + SAY_VRYKUL_DISPOSE = -1000638, + SAY_VRYKUL_BEG = -1000639, + SAY_VRYKUL_WHAT = -1000640, + SAY_VRYKUL_HIDE = -1000641, +}; + +struct npc_ancient_male_vrykulAI : public ScriptedAI +{ + npc_ancient_male_vrykulAI(Creature* pCreature) : ScriptedAI(pCreature) { Reset(); } + + bool m_bEventInProgress; + uint32 m_uiPhase; + uint32 m_uiPhaseTimer; + + void Reset() override + { + m_bEventInProgress = false; + m_uiPhase = 0; + m_uiPhaseTimer = 0; + } + + void StartEvent() + { + if (m_bEventInProgress) + return; + + m_bEventInProgress = true; + } + + void UpdateAI(const uint32 uiDiff) override + { + if (!m_bEventInProgress) + return; + + if (m_uiPhaseTimer < uiDiff) + m_uiPhaseTimer = 5000; + else + { + m_uiPhaseTimer -= uiDiff; + return; + } + + Creature* pFemale = GetClosestCreatureWithEntry(m_creature, NPC_FEMALE_VRYKUL, 10.0f); + + switch (m_uiPhase) + { + case 0: + DoScriptText(SAY_VRYKUL_CURSED, m_creature); + DoScriptText(EMOTE_VRYKUL_POINT, m_creature); + break; + case 1: + if (pFemale) + DoScriptText(EMOTE_VRYKUL_SOB, pFemale); + DoScriptText(SAY_VRYKUL_DISPOSE, m_creature); + break; + case 2: + if (pFemale) + DoScriptText(SAY_VRYKUL_BEG, pFemale); + break; + case 3: + DoScriptText(SAY_VRYKUL_WHAT, m_creature); + break; + case 4: + if (pFemale) + DoScriptText(SAY_VRYKUL_HIDE, pFemale); + break; + case 5: + DoCastSpellIfCan(m_creature, SPELL_SECRET_OF_WYRMSKULL); + break; + case 6: + Reset(); + return; + } + + ++m_uiPhase; + } +}; + +CreatureAI* GetAI_npc_ancient_male_vrykul(Creature* pCreature) +{ + return new npc_ancient_male_vrykulAI(pCreature); +} + +bool AreaTrigger_at_ancient_male_vrykul(Player* pPlayer, AreaTriggerEntry const* /*pAt*/) +{ + if (pPlayer->IsAlive() && pPlayer->GetQuestStatus(QUEST_ECHO_OF_YMIRON) == QUEST_STATUS_INCOMPLETE && + pPlayer->HasAura(SPELL_ECHO_OF_YMIRON)) + { + if (Creature* pCreature = GetClosestCreatureWithEntry(pPlayer, NPC_MALE_VRYKUL, 20.0f)) + { + if (npc_ancient_male_vrykulAI* pVrykulAI = dynamic_cast(pCreature->AI())) + pVrykulAI->StartEvent(); + } + } + + return true; +} + +/*###### +## npc_daegarn +######*/ + +enum +{ + QUEST_DEFEAT_AT_RING = 11300, + + NPC_FIRJUS = 24213, + NPC_JLARBORN = 24215, + NPC_YOROS = 24214, + NPC_OLUF = 23931, + + NPC_PRISONER_1 = 24253, // looks the same but has different abilities + NPC_PRISONER_2 = 24254, + NPC_PRISONER_3 = 24255, +}; + +static float afSummon[] = {838.81f, -4678.06f, -94.182f}; +static float afCenter[] = {801.88f, -4721.87f, -96.143f}; + +// TODO: make prisoners help (unclear if summoned or using npc's from surrounding cages (summon inside small cages?)) +struct npc_daegarnAI : public ScriptedAI +{ + npc_daegarnAI(Creature* pCreature) : ScriptedAI(pCreature) { Reset(); } + + bool m_bEventInProgress; + ObjectGuid m_playerGuid; + + void Reset() override + { + m_bEventInProgress = false; + m_playerGuid.Clear(); + } + + void StartEvent(Player* pPlayer) + { + if (m_bEventInProgress) + return; + + m_playerGuid = pPlayer->GetObjectGuid(); + + SummonGladiator(NPC_FIRJUS); + } + + void JustSummoned(Creature* pSummon) override + { + if (Player* pPlayer = m_creature->GetMap()->GetPlayer(m_playerGuid)) + { + if (pPlayer->IsAlive()) + { + pSummon->SetWalk(false); + pSummon->GetMotionMaster()->MovePoint(0, afCenter[0], afCenter[1], afCenter[2]); + return; + } + } + + Reset(); + } + + void SummonGladiator(uint32 uiEntry) + { + m_creature->SummonCreature(uiEntry, afSummon[0], afSummon[1], afSummon[2], 0.0f, TEMPSUMMON_TIMED_OOC_DESPAWN, 20 * IN_MILLISECONDS); + } + + void SummonedMovementInform(Creature* pSummoned, uint32 /*uiMotionType*/, uint32 /*uiPointId*/) override + { + Player* pPlayer = m_creature->GetMap()->GetPlayer(m_playerGuid); + + // could be group, so need additional here. + if (!pPlayer || !pPlayer->IsAlive()) + { + Reset(); + return; + } + + if (pSummoned->IsWithinDistInMap(pPlayer, 75.0f)) // ~the radius of the ring + pSummoned->AI()->AttackStart(pPlayer); + } + + void SummonedCreatureDespawn(Creature* pSummoned) override + { + uint32 uiEntry = 0; + + // will eventually reset the event if something goes wrong + switch (pSummoned->GetEntry()) + { + case NPC_FIRJUS: uiEntry = NPC_JLARBORN; break; + case NPC_JLARBORN: uiEntry = NPC_YOROS; break; + case NPC_YOROS: uiEntry = NPC_OLUF; break; + case NPC_OLUF: Reset(); return; + } + + SummonGladiator(uiEntry); + } +}; + +bool QuestAccept_npc_daegarn(Player* pPlayer, Creature* pCreature, const Quest* pQuest) +{ + if (pQuest->GetQuestId() == QUEST_DEFEAT_AT_RING) + { + if (npc_daegarnAI* pDaegarnAI = dynamic_cast(pCreature->AI())) + pDaegarnAI->StartEvent(pPlayer); + } + + return true; +} + +CreatureAI* GetAI_npc_daegarn(Creature* pCreature) +{ + return new npc_daegarnAI(pCreature); +} + +/*###### +## npc_silvermoon_harry +######*/ + +enum +{ + QUEST_GAMBLING_DEBT = 11464, + + SAY_AGGRO = -1000603, + SAY_BEATEN = -1000604, + + GOSSIP_ITEM_GAMBLING_DEBT = -3000101, + GOSSIP_ITEM_PAYING = -3000102, + + SPELL_BLAST_WAVE = 15091, + SPELL_SCORCH = 50183, + + ITEM_HARRY_DEBT = 34115, + FACTION_HOSTILE_SH = 90, // guessed, possibly not correct +}; + +struct npc_silvermoon_harryAI : public ScriptedAI +{ + npc_silvermoon_harryAI(Creature* pCreature) : ScriptedAI(pCreature) { Reset(); } + + bool m_bHarryBeaten; + uint32 m_uiBlastWaveTimer; + uint32 m_uiScorchTimer; + uint32 m_uiResetBeatenTimer; + + void Reset() override + { + m_bHarryBeaten = false; + + // timers guessed + m_uiScorchTimer = 5 * IN_MILLISECONDS; + m_uiBlastWaveTimer = 7 * IN_MILLISECONDS; + + m_uiResetBeatenTimer = MINUTE * IN_MILLISECONDS; + } + + void AttackedBy(Unit* pAttacker) override + { + if (m_creature->getVictim()) + return; + + if (m_creature->IsHostileTo(pAttacker)) + AttackStart(pAttacker); + } + + void DamageTaken(Unit* pDoneBy, uint32& uiDamage) override + { + if (uiDamage > m_creature->GetHealth() || (m_creature->GetHealth() - uiDamage) * 100 / m_creature->GetMaxHealth() < 20) + { + if (Player* pPlayer = pDoneBy->GetCharmerOrOwnerPlayerOrPlayerItself()) + { + if (!m_bHarryBeaten && pPlayer->GetQuestStatus(QUEST_GAMBLING_DEBT) == QUEST_STATUS_INCOMPLETE) + { + uiDamage = 0; // Take 0 damage + + m_creature->RemoveAllAurasOnDeath(); + m_creature->DeleteThreatList(); + m_creature->CombatStop(true); + + DoScriptText(SAY_BEATEN, m_creature); + m_bHarryBeaten = true; + } + } + } + } + + bool IsBeaten() + { + return m_bHarryBeaten; + } + + void UpdateAI(const uint32 uiDiff) override + { + if (m_bHarryBeaten) + { + if (m_uiResetBeatenTimer < uiDiff) + EnterEvadeMode(); + else + m_uiResetBeatenTimer -= uiDiff; + } + + if (!m_creature->SelectHostileTarget() || !m_creature->getVictim()) + return; + + if (m_uiScorchTimer < uiDiff) + { + DoCastSpellIfCan(m_creature->getVictim(), SPELL_SCORCH); + m_uiScorchTimer = 10 * IN_MILLISECONDS; + } + else + m_uiScorchTimer -= uiDiff; + + if (m_uiBlastWaveTimer < uiDiff) + { + DoCastSpellIfCan(m_creature, SPELL_BLAST_WAVE); + m_uiBlastWaveTimer = 50 * IN_MILLISECONDS; + } + else + m_uiBlastWaveTimer -= uiDiff; + + DoMeleeAttackIfReady(); + } +}; + +CreatureAI* GetAI_npc_silvermoon_harry(Creature* pCreature) +{ + return new npc_silvermoon_harryAI(pCreature); +} + +bool GossipHello_npc_silvermoon_harry(Player* pPlayer, Creature* pCreature) +{ + if (pCreature->IsQuestGiver()) + pPlayer->PrepareQuestMenu(pCreature->GetObjectGuid()); + + if (pCreature->IsVendor()) + pPlayer->ADD_GOSSIP_ITEM(GOSSIP_ICON_VENDOR, GOSSIP_TEXT_BROWSE_GOODS, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_TRADE); + + if (pPlayer->GetQuestStatus(QUEST_GAMBLING_DEBT) == QUEST_STATUS_INCOMPLETE) + { + if (npc_silvermoon_harryAI* pHarryAI = dynamic_cast(pCreature->AI())) + { + if (!pHarryAI->IsBeaten()) + pPlayer->ADD_GOSSIP_ITEM_ID(GOSSIP_ICON_CHAT, GOSSIP_ITEM_GAMBLING_DEBT, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 1); + else + pPlayer->ADD_GOSSIP_ITEM_ID(GOSSIP_ICON_CHAT, GOSSIP_ITEM_PAYING, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 2); + } + } + + pPlayer->SEND_GOSSIP_MENU(pPlayer->GetGossipTextId(pCreature), pCreature->GetObjectGuid()); + + return true; +} + +bool GossipSelect_npc_silvermoon_harry(Player* pPlayer, Creature* pCreature, uint32 /*uiSender*/, uint32 uiAction) +{ + switch (uiAction) + { + case GOSSIP_ACTION_TRADE: + pPlayer->SEND_VENDORLIST(pCreature->GetObjectGuid()); + break; + case GOSSIP_ACTION_INFO_DEF+1: + pPlayer->CLOSE_GOSSIP_MENU(); + + DoScriptText(SAY_AGGRO, pCreature, pPlayer); + pCreature->SetFactionTemporary(FACTION_HOSTILE_SH, TEMPFACTION_RESTORE_RESPAWN | TEMPFACTION_RESTORE_COMBAT_STOP); + pCreature->AI()->AttackStart(pPlayer); + break; + case GOSSIP_ACTION_INFO_DEF+2: + if (!pPlayer->HasItemCount(ITEM_HARRY_DEBT, 1)) + { + if (Item* pItem = pPlayer->StoreNewItemInInventorySlot(ITEM_HARRY_DEBT, 1)) + { + pPlayer->SendNewItem(pItem, 1, true, false); + pPlayer->CLOSE_GOSSIP_MENU(); + pCreature->AI()->EnterEvadeMode(); + } + } + break; + } + + return true; +} + +/*###### +## npc_lich_king_village +######*/ + +enum +{ + EMOTE_LICH_KING_FACE = -1000920, + SAY_LICH_KING_1 = -1000921, + SAY_PREPARE = -1000922, + SAY_LICH_KING_2 = -1000923, + SAY_LICH_KING_3 = -1000924, + SAY_LICH_KING_4 = -1000925, + SAY_LICH_KING_5 = -1000926, + SAY_PERSISTANCE = -1000927, + + SPELL_GRASP_OF_THE_LICH_KING = 43489, + SPELL_MAGNETIC_PULL = 29661, + SPELL_WRATH_LICH_KING_FIRST = 43488, + SPELL_WRATH_LICH_KING = 50156, + + NPC_VALKYR_SOULCLAIMER = 24327, + NPC_LICH_KING_WYRMSKULL = 24248, + + QUEST_ID_LK_FLAG = 12485, // Server side dummy quest +}; + +static const DialogueEntry aLichDialogue[] = +{ + // first time dialogue only + {EMOTE_LICH_KING_FACE, NPC_LICH_KING_WYRMSKULL, 4000}, + {QUEST_ID_LK_FLAG, 0, 3000}, + {SAY_LICH_KING_1, NPC_LICH_KING_WYRMSKULL, 20000}, + {NPC_VALKYR_SOULCLAIMER, 0, 4000}, + {SAY_LICH_KING_2, NPC_LICH_KING_WYRMSKULL, 10000}, + {SAY_LICH_KING_3, NPC_LICH_KING_WYRMSKULL, 25000}, + {SAY_LICH_KING_4, NPC_LICH_KING_WYRMSKULL, 25000}, + {SAY_LICH_KING_5, NPC_LICH_KING_WYRMSKULL, 20000}, + {SPELL_WRATH_LICH_KING_FIRST, 0, 10000}, + {NPC_LICH_KING_WYRMSKULL, 0, 0}, + // if the player persists... + {SAY_PERSISTANCE, NPC_LICH_KING_WYRMSKULL, 15000}, + {SPELL_WRATH_LICH_KING, 0, 10000}, + {NPC_LICH_KING_WYRMSKULL, 0, 0}, + {0, 0, 0}, +}; + +struct npc_lich_king_villageAI : public ScriptedAI, private DialogueHelper +{ + npc_lich_king_villageAI(Creature* pCreature) : ScriptedAI(pCreature), + DialogueHelper(aLichDialogue) + { + Reset(); + } + + ObjectGuid m_pHeldPlayer; + bool m_bEventInProgress; + + void Reset() override + { + m_bEventInProgress = false; + } + + void JustDidDialogueStep(int32 iEntry) override + { + switch (iEntry) + { + case QUEST_ID_LK_FLAG: + m_creature->HandleEmote(EMOTE_ONESHOT_LAUGH); + break; + case NPC_VALKYR_SOULCLAIMER: + if (Creature* pCreature = GetClosestCreatureWithEntry(m_creature, NPC_VALKYR_SOULCLAIMER, 20.0f)) + DoScriptText(SAY_PREPARE, pCreature); + break; + case SPELL_WRATH_LICH_KING_FIRST: + if (Player* pPlayer = m_creature->GetMap()->GetPlayer(m_pHeldPlayer)) + { + DoCastSpellIfCan(pPlayer, SPELL_WRATH_LICH_KING_FIRST); + // handle spell scriptEffect in the script + m_creature->DealDamage(pPlayer, pPlayer->GetHealth(), NULL, DIRECT_DAMAGE, SPELL_SCHOOL_MASK_NORMAL, NULL, false); + } + break; + case SPELL_WRATH_LICH_KING: + if (Player* pPlayer = m_creature->GetMap()->GetPlayer(m_pHeldPlayer)) + { + DoCastSpellIfCan(pPlayer, SPELL_WRATH_LICH_KING); + // handle spell scriptEffect in the script + m_creature->DealDamage(pPlayer, pPlayer->GetHealth(), NULL, DIRECT_DAMAGE, SPELL_SCHOOL_MASK_NORMAL, NULL, false); + } + break; + case NPC_LICH_KING_WYRMSKULL: + EnterEvadeMode(); + break; + } + } + + void MoveInLineOfSight(Unit* pWho) override + { + if (!m_bEventInProgress && pWho->GetTypeId() == TYPEID_PLAYER) + { + if (pWho->IsAlive() && m_creature->IsWithinDistInMap(pWho, 15.0) && pWho->HasAura(SPELL_ECHO_OF_YMIRON)) + { + m_pHeldPlayer = pWho->GetObjectGuid(); + m_bEventInProgress = true; + + DoCastSpellIfCan(pWho, SPELL_MAGNETIC_PULL, CAST_TRIGGERED); + DoCastSpellIfCan(pWho, SPELL_GRASP_OF_THE_LICH_KING, CAST_TRIGGERED); + + if (((Player*)pWho)->GetQuestStatus(QUEST_ID_LK_FLAG) == QUEST_STATUS_COMPLETE) + StartNextDialogueText(SAY_PERSISTANCE); + else + StartNextDialogueText(EMOTE_LICH_KING_FACE); + } + } + } + + Creature* GetSpeakerByEntry(uint32 uiEntry) override + { + if (uiEntry == NPC_LICH_KING_WYRMSKULL) + return m_creature; + + return NULL; + } + + void UpdateAI(const uint32 uiDiff) override { DialogueUpdate(uiDiff); } +}; + +CreatureAI* GetAI_npc_lich_king_village(Creature* pCreature) +{ + return new npc_lich_king_villageAI(pCreature); +} + +/*###### +## npc_king_ymiron +######*/ + +enum +{ + EMOTE_KING_SILENCE = -1000928, + SAY_KING_YMIRON_SPEECH_1 = -1000929, + SAY_KING_YMIRON_SPEECH_2 = -1000930, + EMOTE_YMIRON_CROWD_1 = -1000931, + SAY_KING_YMIRON_SPEECH_3 = -1000932, + SAY_KING_YMIRON_SPEECH_4 = -1000933, + SAY_KING_YMIRON_SPEECH_5 = -1000934, + SAY_KING_YMIRON_SPEECH_6 = -1000935, + SAY_KING_YMIRON_SPEECH_7 = -1000936, + EMOTE_YMIRON_CROWD_2 = -1000937, + SAY_KING_YMIRON_SPEECH_8 = -1000938, + EMOTE_YMIRON_CROWD_3 = -1000939, + SAY_KING_YMIRON_SPEECH_9 = -1000940, + + SPELL_ECHO_OF_YMIRON_NIFFLEVAR = 43466, + SPELL_SECRETS_OF_NIFFLEVAR = 43468, + + NPC_CITIZEN_OF_NIFFLEVAR_MALE = 24322, + NPC_CITIZEN_OF_NIFFLEVAR_FEMALE = 24323, + NPC_KING_YMIRON = 24321, + + QUEST_ID_ANGUISH_OF_NIFFLEVAR = 11344, + + MAX_CROWD_TEXT_ENTRIES = 7 +}; + +static const DialogueEntry aNifflevarDialogue[] = +{ + {EMOTE_KING_SILENCE, NPC_KING_YMIRON, 3000}, + {SAY_KING_YMIRON_SPEECH_1, NPC_KING_YMIRON, 5000}, + {SAY_KING_YMIRON_SPEECH_2, NPC_KING_YMIRON, 2000}, + {EMOTE_YMIRON_CROWD_1, NPC_KING_YMIRON, 5000}, + {SAY_KING_YMIRON_SPEECH_3, NPC_KING_YMIRON, 10000}, + {SAY_KING_YMIRON_SPEECH_4, NPC_KING_YMIRON, 9000}, + {SAY_KING_YMIRON_SPEECH_5, NPC_KING_YMIRON, 7000}, + {SAY_KING_YMIRON_SPEECH_6, NPC_KING_YMIRON, 5000}, + {SAY_KING_YMIRON_SPEECH_7, NPC_KING_YMIRON, 9000}, + {EMOTE_YMIRON_CROWD_2, NPC_KING_YMIRON, 5000}, + {SAY_KING_YMIRON_SPEECH_8, NPC_KING_YMIRON, 8000}, + {EMOTE_YMIRON_CROWD_3, NPC_KING_YMIRON, 4000}, + {SAY_KING_YMIRON_SPEECH_9, NPC_KING_YMIRON, 10000}, + {SPELL_SECRETS_OF_NIFFLEVAR, 0, 10000}, + {QUEST_ID_ANGUISH_OF_NIFFLEVAR, 0, 0}, + {0, 0, 0}, +}; + +static const int32 aRandomTextEntries[MAX_CROWD_TEXT_ENTRIES] = { -1000941, -1000942, -1000943, -1000944, -1000945, -1000946, -1000947}; + +struct npc_king_ymironAI : public ScriptedAI, private DialogueHelper +{ + npc_king_ymironAI(Creature* pCreature) : ScriptedAI(pCreature), + DialogueHelper(aNifflevarDialogue) + { + Reset(); + } + + uint32 m_uiCrowdSpeechTimer; + + bool m_bEventInProgress; + bool m_bEventInit; + + GuidList m_lCrowdGuidList; + + void Reset() override + { + m_uiCrowdSpeechTimer = 0; + m_bEventInit = false; + m_bEventInProgress = false; + m_lCrowdGuidList.clear(); + } + + void MoveInLineOfSight(Unit* pWho) override + { + if (!m_bEventInit && pWho->GetTypeId() == TYPEID_PLAYER) + { + // Get all the citizen around the king for future use + if (pWho->IsAlive() && m_creature->IsWithinDistInMap(pWho, 60.0) && ((Player*)pWho)->GetQuestStatus(QUEST_ID_ANGUISH_OF_NIFFLEVAR) == QUEST_STATUS_INCOMPLETE + && pWho->HasAura(SPELL_ECHO_OF_YMIRON_NIFFLEVAR)) + { + std::list lCrowdList; + GetCreatureListWithEntryInGrid(lCrowdList, m_creature, NPC_CITIZEN_OF_NIFFLEVAR_MALE, 60.0f); + GetCreatureListWithEntryInGrid(lCrowdList, m_creature, NPC_CITIZEN_OF_NIFFLEVAR_FEMALE, 60.0f); + + for (std::list::const_iterator itr = lCrowdList.begin(); itr != lCrowdList.end(); ++itr) + m_lCrowdGuidList.push_back( (*itr)->GetObjectGuid() ); + + m_uiCrowdSpeechTimer = 1000; + m_bEventInit = true; + } + } + } + + void JustDidDialogueStep(int32 iEntry) override + { + switch (iEntry) + { + case SPELL_SECRETS_OF_NIFFLEVAR: + DoCastSpellIfCan(m_creature, SPELL_SECRETS_OF_NIFFLEVAR); + break; + case QUEST_ID_ANGUISH_OF_NIFFLEVAR: + EnterEvadeMode(); + break; + } + } + + Creature* GetSpeakerByEntry(uint32 uiEntry) override + { + if (uiEntry == NPC_KING_YMIRON) + return m_creature; + + return NULL; + } + + void ReceiveAIEvent(AIEventType eventType, Creature* /*pSender*/, Unit* pInvoker, uint32 /*uiMiscValue*/) override + { + if (eventType == AI_EVENT_CUSTOM_A && pInvoker->GetTypeId() == TYPEID_PLAYER) + { + if (m_bEventInProgress) + return; + + StartNextDialogueText(EMOTE_KING_SILENCE); + m_uiCrowdSpeechTimer = 0; + m_bEventInProgress = true; + } + } + + ObjectGuid SelectRandomCrowdNpc() + { + if (m_lCrowdGuidList.empty()) + return ObjectGuid(); + + GuidList::iterator iter = m_lCrowdGuidList.begin(); + advance(iter, urand(0, m_lCrowdGuidList.size() - 1)); + + return *iter; + } + + void UpdateAI(const uint32 uiDiff) override + { + DialogueUpdate(uiDiff); + + if (m_uiCrowdSpeechTimer) + { + if (m_uiCrowdSpeechTimer <= uiDiff) + { + // only 15% chance to yell (guessed) + if (roll_chance_i(15)) + { + if (Creature* pCitizen = m_creature->GetMap()->GetCreature(SelectRandomCrowdNpc())) + DoScriptText(aRandomTextEntries[urand(0, MAX_CROWD_TEXT_ENTRIES - 1)], pCitizen); + } + + m_uiCrowdSpeechTimer = 1000; + } + else + m_uiCrowdSpeechTimer -= uiDiff; + } + } +}; + +CreatureAI* GetAI_npc_king_ymiron(Creature* pCreature) +{ + return new npc_king_ymironAI(pCreature); +} + +bool AreaTrigger_at_nifflevar(Player* pPlayer, AreaTriggerEntry const* pAt) +{ + if (pPlayer->IsAlive() && pPlayer->GetQuestStatus(QUEST_ID_ANGUISH_OF_NIFFLEVAR) == QUEST_STATUS_INCOMPLETE && pPlayer->HasAura(SPELL_ECHO_OF_YMIRON_NIFFLEVAR)) + { + if (Creature* pCreature = GetClosestCreatureWithEntry(pPlayer, NPC_KING_YMIRON, 30.0f)) + pCreature->AI()->SendAIEvent(AI_EVENT_CUSTOM_A, pPlayer, pCreature); + + return true; + } + + return false; +} + +void AddSC_howling_fjord() +{ + Script* pNewScript; + + pNewScript = new Script; + pNewScript->Name = "npc_ancient_male_vrykul"; + pNewScript->GetAI = &GetAI_npc_ancient_male_vrykul; + pNewScript->RegisterSelf(); + + pNewScript = new Script; + pNewScript->Name = "at_ancient_male_vrykul"; + pNewScript->pAreaTrigger = &AreaTrigger_at_ancient_male_vrykul; + pNewScript->RegisterSelf(); + + pNewScript = new Script; + pNewScript->Name = "npc_daegarn"; + pNewScript->GetAI = &GetAI_npc_daegarn; + pNewScript->pQuestAcceptNPC = &QuestAccept_npc_daegarn; + pNewScript->RegisterSelf(); + + pNewScript = new Script; + pNewScript->Name = "npc_silvermoon_harry"; + pNewScript->GetAI = &GetAI_npc_silvermoon_harry; + pNewScript->pGossipHello = &GossipHello_npc_silvermoon_harry; + pNewScript->pGossipSelect = &GossipSelect_npc_silvermoon_harry; + pNewScript->RegisterSelf(); + + pNewScript = new Script; + pNewScript->Name = "npc_lich_king_village"; + pNewScript->GetAI = &GetAI_npc_lich_king_village; + pNewScript->RegisterSelf(); + + pNewScript = new Script; + pNewScript->Name = "npc_king_ymiron"; + pNewScript->GetAI = &GetAI_npc_king_ymiron; + pNewScript->RegisterSelf(); + + pNewScript = new Script; + pNewScript->Name = "at_nifflevar"; + pNewScript->pAreaTrigger = &AreaTrigger_at_nifflevar; + pNewScript->RegisterSelf(); +} diff --git a/src/modules/SD2/scripts/northrend/icecrown.cpp b/src/modules/SD2/scripts/northrend/icecrown.cpp new file mode 100644 index 000000000..52987a52f --- /dev/null +++ b/src/modules/SD2/scripts/northrend/icecrown.cpp @@ -0,0 +1,35 @@ +/* Copyright (C) 2006 - 2013 ScriptDev2 + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along 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: 0 +SDComment: Placeholder +SDCategory: Icecrown +EndScriptData */ + +/* ContentData +EndContentData */ + +#include "precompiled.h" + +/*###### +## +######*/ + +void AddSC_icecrown() +{ +} diff --git a/src/modules/SD2/scripts/northrend/icecrown_citadel/frozen_halls/forge_of_souls/boss_bronjahm.cpp b/src/modules/SD2/scripts/northrend/icecrown_citadel/frozen_halls/forge_of_souls/boss_bronjahm.cpp new file mode 100644 index 000000000..74ee68db6 --- /dev/null +++ b/src/modules/SD2/scripts/northrend/icecrown_citadel/frozen_halls/forge_of_souls/boss_bronjahm.cpp @@ -0,0 +1,275 @@ +/* Copyright (C) 2006 - 2013 ScriptDev2 + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +/* ScriptData +SDName: boss_bronjahm +SD%Complete: 90% +SDComment: Small unknown behaviour for his Shadow Bold use in phase 1; Soulstorm needs additional handling in core +SDCategory: The Forge of Souls +EndScriptData */ + +#include "precompiled.h" +#include "forge_of_souls.h" + +enum +{ + SAY_AGGRO_1 = -1632000, // Without sound, really correct? + SAY_AGGRO_2 = -1632001, + SAY_SLAY_1 = -1632002, + SAY_SLAY_2 = -1632003, + SAY_DEATH = -1632004, + SAY_SOULSTORM = -1632005, + SAY_CORRUPT_SOUL = -1632006, + + // Heroic spells are selected by spell difficulty dbc + SPELL_SOULSTORM_VISUAL_OOC = 69008, + SPELL_MAGICS_BANE = 68793, + SPELL_SHADOW_BOLT = 70043, + SPELL_CORRUPT_SOUL = 68839, + SPELL_BANISH_VISUAL = 68862, + SPELL_CONSUME_SOUL_TRIGGER = 68861, + SPELL_TELEPORT = 68988, + SPELL_SOULSTORM_VISUAL = 68870, // Cast before Soulstorm, should trigger some visual spells + SPELL_SOULSTORM = 68872, + SPELL_FEAR = 68950, +}; + +struct boss_bronjahmAI : public ScriptedAI +{ + boss_bronjahmAI(Creature* pCreature) : ScriptedAI(pCreature) + { + m_pInstance = (instance_forge_of_souls*)pCreature->GetInstanceData(); + m_bIsRegularMode = pCreature->GetMap()->IsRegularDifficulty(); + Reset(); + } + + instance_forge_of_souls* m_pInstance; + bool m_bIsRegularMode; + + uint8 m_uiPhase; + + uint32 m_uiMagicsBaneTimer; + uint32 m_uiCorruptSoulTimer; + uint32 m_uiFearTimer; + uint32 m_uiShadowboltTimer; + + void Reset() override + { + m_uiPhase = 0; + m_uiMagicsBaneTimer = urand(8000, 12000); + m_uiCorruptSoulTimer = urand(20000, 30000); + m_uiFearTimer = 1000; + m_uiShadowboltTimer = 5000; + SetCombatMovement(true); + } + + void Aggro(Unit* /*pWho*/) override + { + DoScriptText(urand(0, 1) ? SAY_AGGRO_1 : SAY_AGGRO_2, m_creature); + + if (m_pInstance) + m_pInstance->SetData(TYPE_BRONJAHM, IN_PROGRESS); + + // Remove OOC visual soulstorm effect (added in creature_template_addon + m_creature->RemoveAurasDueToSpell(SPELL_SOULSTORM_VISUAL_OOC); + } + + void KilledUnit(Unit* pVictim) override + { + if (pVictim->GetTypeId() != TYPEID_PLAYER) + return; + + if (urand(0, 1)) + DoScriptText(urand(0, 1) ? SAY_SLAY_1 : SAY_SLAY_2, m_creature); + } + + void JustDied(Unit* /*pKiller*/) override + { + DoScriptText(SAY_DEATH, m_creature); + + if (m_pInstance) + m_pInstance->SetData(TYPE_BRONJAHM, DONE); + } + + void JustReachedHome() override + { + if (m_pInstance) + m_pInstance->SetData(TYPE_BRONJAHM, FAIL); + } + + void SpellHitTarget(Unit* pTarget, SpellEntry const* pSpellEntry) override + { + if (pTarget == m_creature && pSpellEntry->Id == SPELL_TELEPORT) + { + // Say Text and cast Soulstorm + DoScriptText(SAY_SOULSTORM, m_creature); + DoCastSpellIfCan(m_creature, SPELL_SOULSTORM_VISUAL, CAST_TRIGGERED | CAST_INTERRUPT_PREVIOUS); + DoCastSpellIfCan(m_creature, SPELL_SOULSTORM, CAST_INTERRUPT_PREVIOUS); + } + } + + void UpdateAI(const uint32 uiDiff) override + { + if (!m_creature->SelectHostileTarget() || !m_creature->getVictim()) + return; + + if (m_uiPhase == 0) // Phase 1 + { + // Switching Phase, Soulstorm is cast in SpellHitTarget + if (m_creature->GetHealthPercent() < 30.0f) + { + if (DoCastSpellIfCan(m_creature, SPELL_TELEPORT) == CAST_OK) + m_uiPhase = 1; + } + + // Corrupt Soul + if (m_uiCorruptSoulTimer < uiDiff) + { + if (Unit* pTarget = m_creature->SelectAttackingTarget(ATTACKING_TARGET_RANDOM, 0)) + { + if (DoCastSpellIfCan(pTarget, SPELL_CORRUPT_SOUL) == CAST_OK) + { + DoScriptText(SAY_CORRUPT_SOUL, m_creature); + m_uiCorruptSoulTimer = urand(20000, 30000); + } + } + } + else + m_uiCorruptSoulTimer -= uiDiff; + + // Magic's Bane + if (m_uiMagicsBaneTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature, SPELL_MAGICS_BANE) == CAST_OK) + m_uiMagicsBaneTimer = urand(7000, 15000); + } + else + m_uiMagicsBaneTimer -= uiDiff; + + // Used to prevent Shadowbolt-Casting on Aggro for a few seconds + if (m_uiShadowboltTimer <= uiDiff) + m_uiShadowboltTimer = 0; + else + m_uiShadowboltTimer -= uiDiff; + + // Use ShadowBolt as default attack if victim is not in range + // TODO - not entirely clear how this works in case the tank is out of shadow-bolt range + if (!m_uiShadowboltTimer && !m_creature->CanReachWithMeleeAttack(m_creature->getVictim()) && m_creature->GetCombatDistance(m_creature->getVictim(), false) < 20.0f) + { + if (IsCombatMovement()) + { + SetCombatMovement(false); + m_creature->GetMotionMaster()->MoveIdle(); + m_creature->StopMoving(); + } + DoCastSpellIfCan(m_creature->getVictim(), SPELL_SHADOW_BOLT); + } + else + { + if (!IsCombatMovement()) + { + SetCombatMovement(true); + m_creature->GetMotionMaster()->MoveChase(m_creature->getVictim()); + m_uiShadowboltTimer = 2000; // Give some time to chase + } + + DoMeleeAttackIfReady(); + } + } + else // Soulstorm Phase + { + if (m_uiFearTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature, SPELL_FEAR) == CAST_OK) + m_uiFearTimer = urand(10000, 15000); + } + else + m_uiFearTimer -= uiDiff; + + // Default attack + if (Unit* pTarget = m_creature->SelectAttackingTarget(ATTACKING_TARGET_RANDOM, 0)) + DoCastSpellIfCan(pTarget, SPELL_SHADOW_BOLT); + } + } +}; + +CreatureAI* GetAI_boss_bronjahm(Creature* pCreature) +{ + return new boss_bronjahmAI(pCreature); +} + +struct npc_corrupted_soul_fragmentAI : public ScriptedAI +{ + npc_corrupted_soul_fragmentAI(Creature* pCreature) : ScriptedAI(pCreature) + { + Reset(); + DoCastSpellIfCan(m_creature, SPELL_BANISH_VISUAL); + } + + void Reset() override + { + SetCombatMovement(true); + } + + void JustDied(Unit* /*pKiller*/) override + { + if (instance_forge_of_souls* pInstance = (instance_forge_of_souls*)m_creature->GetInstanceData()) + pInstance->SetGuid(DATA_SOULFRAGMENT_REMOVE, m_creature->GetObjectGuid()); + } + + void MoveInLineOfSight(Unit* pWho) override + { + if (pWho->GetEntry() == NPC_BRONJAHM) + { + if (m_creature->IsWithinDistInMap(pWho, INTERACTION_DISTANCE)) + { + DoCastSpellIfCan(pWho, SPELL_CONSUME_SOUL_TRIGGER, CAST_TRIGGERED); + + // Inform the instance about a used soul fragment + if (instance_forge_of_souls* pInstance = (instance_forge_of_souls*)m_creature->GetInstanceData()) + pInstance->SetGuid(DATA_SOULFRAGMENT_REMOVE, m_creature->GetObjectGuid()); + + m_creature->ForcedDespawn(); + return; + } + if (IsCombatMovement()) + { + SetCombatMovement(false); + m_creature->GetMotionMaster()->MoveFollow(pWho, 0.0f, 0.0f); + } + } + } +}; + +CreatureAI* GetAI_npc_corrupted_soul_fragment(Creature* pCreature) +{ + return new npc_corrupted_soul_fragmentAI(pCreature); +} + +void AddSC_boss_bronjahm() +{ + Script* pNewScript; + + pNewScript = new Script; + pNewScript->Name = "boss_bronjahm"; + pNewScript->GetAI = &GetAI_boss_bronjahm; + pNewScript->RegisterSelf(); + + pNewScript = new Script; + pNewScript->Name = "npc_corrupted_soul_fragment"; + pNewScript->GetAI = &GetAI_npc_corrupted_soul_fragment; + pNewScript->RegisterSelf(); +} diff --git a/src/modules/SD2/scripts/northrend/icecrown_citadel/frozen_halls/forge_of_souls/boss_devourer_of_souls.cpp b/src/modules/SD2/scripts/northrend/icecrown_citadel/frozen_halls/forge_of_souls/boss_devourer_of_souls.cpp new file mode 100644 index 000000000..ed34d1bff --- /dev/null +++ b/src/modules/SD2/scripts/northrend/icecrown_citadel/frozen_halls/forge_of_souls/boss_devourer_of_souls.cpp @@ -0,0 +1,334 @@ +/* Copyright (C) 2006 - 2013 ScriptDev2 + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +/* ScriptData +SDName: boss_devourer_of_souls +SD%Complete: 90% +SDComment: Timers need more love, NPC_UNLEASHED_SOUL are not handled proper +SDCategory: The Forge of Souls +EndScriptData */ + +#include "precompiled.h" +#include "forge_of_souls.h" + +enum +{ + SAY_MALE_1_AGGRO = -1632007, + SAY_FEMALE_AGGRO = -1632008, + SAY_MALE_1_SLAY_1 = -1632009, + SAY_FEMALE_SLAY_1 = -1632010, + SAY_MALE_2_SLAY_1 = -1632011, + SAY_MALE_1_SLAY_2 = -1632012, + SAY_FEMALE_SLAY_2 = -1632013, + SAY_MALE_2_SLAY_2 = -1632014, + SAY_MALE_1_DEATH = -1632015, + SAY_FEMALE_DEATH = -1632016, + SAY_MALE_2_DEATH = -1632017, + SAY_MALE_1_SOUL_ATTACK = -1632018, + SAY_FEMALE_SOUL_ATTACK = -1632019, + SAY_MALE_2_SOUL_ATTACK = -1632020, + SAY_MALE_1_DARK_GLARE = -1632021, + SAY_FEMALE_DARK_GLARE = -1632022, + + EMOTE_MIRRORED_SOUL = -1632023, + EMOTE_UNLEASH_SOULS = -1632024, + EMOTE_WAILING_SOULS = -1632025, + + FACE_NORMAL = 0, + FACE_WAILING = 1, + FACE_UNLEASHING = 2, + + SPELL_PHANTOM_BLAST = 68982, + SPELL_PHANTOM_BLAST_H = 70322, + SPELL_WELL_OF_SOULS = 68820, // spawns 36536, this one should cast 68854 (triggers normal dmg spell 68863 ) - 68855(visual) - 72630 (visual) + SPELL_WELL_OF_SOULS_TRIGGER = 68854, + SPELL_WELL_OF_SOULS_VISUAL1 = 68855, + SPELL_WELL_OF_SOULS_VISUAL2 = 72630, + + SPELL_MIRRORED_SOUL = 69048, // selecting target, applying aura 69023 to pass on dmg, dmg triggers 69034 with right amount + SPELL_UNLEASHED_SOULS = 68939, // trigger (68967, select nearby target trigger 68979(summon 36595)), transform, root + SPELL_WAILING_SOULS = 68899, + SPELL_WALIING_SOULS_TARGETS = 68912, + + SPELL_DRUID_MORPH_1_5 = 68931, // delayed visual, 1.5s delay - used before wailing souls + SPELL_DRUID_MORPH_0_5 = 68977, // delayed visual, .5s delay - used before unleash souls + SPELL_SUBMERGE_VISUAL = 68909, // visual used to 'whirl' the heads, used by SPELL_DRUID_MORPH if spell DRUID_MORPH is used to "end" a phase + SPELL_DRUID_MORPH = 68929, // used after wailing and unleashed souls, also triggered by DRUIT_MORPH_* + + NPC_WELL_OF_SOULS = 36536, + NPC_UNLEASHED_SOUL = 36595, +}; + +static const int aTexts[6][3] = +{ + {SAY_MALE_1_AGGRO, SAY_FEMALE_AGGRO, 0}, // 0 - aggro + {SAY_MALE_1_SLAY_1, SAY_FEMALE_SLAY_1, SAY_MALE_2_SLAY_1}, // 1 - slay1 + {SAY_MALE_1_SLAY_2, SAY_FEMALE_SLAY_2, SAY_MALE_2_SLAY_2}, // 2 - slay2 + {SAY_MALE_1_DEATH, SAY_FEMALE_DEATH, SAY_MALE_2_DEATH}, // 3 - death + {SAY_MALE_1_SOUL_ATTACK, SAY_FEMALE_SOUL_ATTACK, SAY_MALE_2_SOUL_ATTACK}, // 4 - unleashing soul + {SAY_MALE_1_DARK_GLARE, SAY_FEMALE_DARK_GLARE, 0} // 5 - glare +}; + +struct boss_devourer_of_soulsAI : public ScriptedAI +{ + boss_devourer_of_soulsAI(Creature* pCreature) : ScriptedAI(pCreature) + { + m_pInstance = (instance_forge_of_souls*)pCreature->GetInstanceData(); + m_bIsRegularMode = pCreature->GetMap()->IsRegularDifficulty(); + Reset(); + } + + instance_forge_of_souls* m_pInstance; + uint8 m_uiFace; + bool m_bIsRegularMode; + + uint32 m_uiPhantomBlastTimer; + uint32 m_uiWellTimer; + uint32 m_uiMirrorTimer; + uint32 m_uiUnleashTimer; + uint32 m_uiWailingTimer; + uint32 m_uiEndPhaseTimer; + + GuidList m_lWellGuids; + + void Reset() override + { + m_uiFace = FACE_NORMAL; + + m_uiPhantomBlastTimer = urand(5000, 10000); + m_uiWellTimer = urand(10000, 15000); + m_uiMirrorTimer = urand(10000, 15000); + m_uiUnleashTimer = urand(15000, 20000); + m_uiWailingTimer = urand(40000, 60000); + m_uiEndPhaseTimer = 0; + } + + void Aggro(Unit* /*pWho*/) override + { + DoScriptText(aTexts[0][m_uiFace], m_creature); + if (m_pInstance) + m_pInstance->SetData(TYPE_DEVOURER_OF_SOULS, IN_PROGRESS); + } + + void KilledUnit(Unit* pVictim) override + { + if (pVictim->GetTypeId() != TYPEID_PLAYER) + return; + + if (urand(0, 1)) + DoScriptText(aTexts[urand(1, 2)][m_uiFace], m_creature); + } + + void JustDied(Unit* /*pKiller*/) override + { + DoScriptText(aTexts[3][m_uiFace], m_creature); + + if (m_pInstance) + m_pInstance->SetData(TYPE_DEVOURER_OF_SOULS, DONE); + + for (GuidList::const_iterator itr = m_lWellGuids.begin(); itr != m_lWellGuids.end(); ++itr) + { + if (Creature* pWell = m_creature->GetMap()->GetCreature(*itr)) + pWell->ForcedDespawn(); + } + m_lWellGuids.clear(); + } + + void JustReachedHome() override + { + if (m_pInstance) + { + m_pInstance->SetData(NPC_DEVOURER_OF_SOULS, FAIL); + // If we previously failed, set such that possible to try again + m_pInstance->SetData(TYPE_ACHIEV_PHANTOM_BLAST, IN_PROGRESS); + } + + for (GuidList::const_iterator itr = m_lWellGuids.begin(); itr != m_lWellGuids.end(); ++itr) + { + if (Creature* pWell = m_creature->GetMap()->GetCreature(*itr)) + pWell->ForcedDespawn(); + } + m_lWellGuids.clear(); + } + + void JustSummoned(Creature* pSummoned) override + { + if (pSummoned->GetEntry() == NPC_WELL_OF_SOULS) + { + m_lWellGuids.push_back(pSummoned->GetObjectGuid()); + pSummoned->CastSpell(pSummoned, SPELL_WELL_OF_SOULS_TRIGGER, true, NULL, NULL, m_creature->GetObjectGuid()); + // Commented as of not stacking auras + // pSummoned->CastSpell(pSummoned, SPELL_WELL_OF_SOULS_VISUAL1, true); + // pSummoned->CastSpell(pSummoned, SPELL_WELL_OF_SOULS_VISUAL2, true); + } + else if (pSummoned->GetEntry() == NPC_UNLEASHED_SOUL) + { + if (Unit* pEnemy = m_creature->SelectAttackingTarget(ATTACKING_TARGET_RANDOM, 0)) + { + // It seems the summoned should rather walk towards the boss, but this results in them attacking the healer + pSummoned->AI()->AttackStart(pEnemy); + pSummoned->AddThreat(pEnemy, 10000.0f); + } + pSummoned->ForcedDespawn(15000); // Note that this is sort of a hack, the more correct fix however would require to toggle the interpretation of summon properties in mangos + } + } + + void SpellHitTarget(Unit* /*pTarget*/, SpellEntry const* pSpellEntry) override + { + switch (pSpellEntry->Id) + { + // If we hit a target with phantom blast, the achievement_criteria is failed + case SPELL_PHANTOM_BLAST: + case SPELL_PHANTOM_BLAST_H: + if (m_pInstance) + m_pInstance->SetData(TYPE_ACHIEV_PHANTOM_BLAST, FAIL); + break; + // Might be placed somewhere else better, important is to note that this text is said after the 3s cast time + case SPELL_WAILING_SOULS: + DoScriptText(aTexts[5][m_uiFace], m_creature); + break; + } + } + + void UpdateAI(const uint32 uiDiff) override + { + if (!m_creature->IsInCombat()) + return; + + // Ending a phase + if (m_uiEndPhaseTimer) + { + if (m_uiEndPhaseTimer <= uiDiff) + { + DoCastSpellIfCan(m_creature, SPELL_DRUID_MORPH, CAST_INTERRUPT_PREVIOUS); + // Sumberge visual might be cast as effect of the above spell. + DoCastSpellIfCan(m_creature, SPELL_SUBMERGE_VISUAL, CAST_TRIGGERED); + m_uiEndPhaseTimer = 0; + + m_uiFace = FACE_NORMAL; + } + else + m_uiEndPhaseTimer -= uiDiff; + } + + // No additional spells, no target selection for wailing souls + if (m_uiFace == FACE_WAILING) + { + // Some special handling in case of starting phase of wailings + if (ObjectGuid targetGuid = m_creature->GetTargetGuid()) + { + if (Unit* pTarget = m_creature->GetMap()->GetUnit(targetGuid)) + m_creature->SetFacingTo(m_creature->GetAngle(pTarget)); + } + + if (m_creature->GetThreatManager().isThreatListEmpty() || !m_creature->GetThreatManager().getHostileTarget()) + m_creature->SelectHostileTarget(); // Most likely must evade, use additional checks in case evading would be prevented + return; + } + + // Update Target and do Combat Spells + if (!m_creature->SelectHostileTarget() || !m_creature->getVictim()) + return; + + // No additional abilities while unleashing + if (m_uiFace == FACE_UNLEASHING) + return; + + // Phantom Blast + if (m_uiPhantomBlastTimer < uiDiff) + { + if (Unit* pTarget = m_creature->SelectAttackingTarget(ATTACKING_TARGET_RANDOM, 0)) + { + if (DoCastSpellIfCan(pTarget, m_bIsRegularMode ? SPELL_PHANTOM_BLAST : SPELL_PHANTOM_BLAST_H) == CAST_OK) + m_uiPhantomBlastTimer = urand(5000, 10000); // TODO + } + } + else + m_uiPhantomBlastTimer -= uiDiff; + + // Jump towards random enemy + if (m_uiWellTimer < uiDiff) + { + if (Unit* pTarget = m_creature->SelectAttackingTarget(ATTACKING_TARGET_RANDOM, 0)) + { + if (DoCastSpellIfCan(pTarget, SPELL_WELL_OF_SOULS) == CAST_OK) + m_uiWellTimer = urand(15000, 25000); // TODO + } + } + else + m_uiWellTimer -= uiDiff; + + // DMG reflection + if (m_uiMirrorTimer < uiDiff) + { + if (!m_creature->IsNonMeleeSpellCasted(true)) + { + DoCastSpellIfCan(m_creature, SPELL_MIRRORED_SOUL, CAST_TRIGGERED); + m_uiMirrorTimer = urand(25000, 35000); // TODO + DoScriptText(EMOTE_MIRRORED_SOUL, m_creature); + } + } + else + m_uiMirrorTimer -= uiDiff; + + // Spawning of Adds + if (m_uiUnleashTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature, SPELL_UNLEASHED_SOULS) == CAST_OK && DoCastSpellIfCan(m_creature, SPELL_DRUID_MORPH_0_5, CAST_TRIGGERED) == CAST_OK) + { + DoScriptText(EMOTE_UNLEASH_SOULS, m_creature); + m_uiUnleashTimer = urand(30000, 60000); // TODO + + m_uiFace = FACE_UNLEASHING; + DoScriptText(aTexts[4][m_uiFace], m_creature); + m_uiEndPhaseTimer = 4500; // 5000 (Duration of Unleasing) + 850(Cast time for unleashing) - 1000(duration of whirl) - a bit air + } + } + else + m_uiUnleashTimer -= uiDiff; + + if (m_uiWailingTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature, SPELL_WALIING_SOULS_TARGETS) == CAST_OK) + { + DoCastSpellIfCan(m_creature, SPELL_DRUID_MORPH_1_5, CAST_TRIGGERED); + DoScriptText(EMOTE_WAILING_SOULS, m_creature); + m_uiFace = FACE_WAILING; + m_uiEndPhaseTimer = 12500; // 100000 (Duration of Wailing) + 3000(casting time) - 1000 (duration of whirl) + 500 (some add. time) + m_uiWailingTimer = urand(25000, 35000); // TODO + } + } + else + m_uiWailingTimer -= uiDiff; + + DoMeleeAttackIfReady(); + } +}; + +CreatureAI* GetAI_boss_devourer_of_souls(Creature* pCreature) +{ + return new boss_devourer_of_soulsAI(pCreature); +} + +void AddSC_boss_devourer_of_souls() +{ + Script* pNewScript; + + pNewScript = new Script; + pNewScript->Name = "boss_devourer_of_souls"; + pNewScript->GetAI = &GetAI_boss_devourer_of_souls; + pNewScript->RegisterSelf(); +} diff --git a/src/modules/SD2/scripts/northrend/icecrown_citadel/frozen_halls/forge_of_souls/forge_of_souls.h b/src/modules/SD2/scripts/northrend/icecrown_citadel/frozen_halls/forge_of_souls/forge_of_souls.h new file mode 100644 index 000000000..376127ec5 --- /dev/null +++ b/src/modules/SD2/scripts/northrend/icecrown_citadel/frozen_halls/forge_of_souls/forge_of_souls.h @@ -0,0 +1,124 @@ +/* Copyright (C) 2006 - 2013 ScriptDev2 + * This program is free software licensed under GPL version 2 + * Please see the included DOCS/LICENSE.TXT for more information */ + +#ifndef DEF_FORGE_OF_SOULS_H +#define DEF_FORGE_OF_SOULS_H + +enum +{ + MAX_ENCOUNTER = 2, + TYPE_BRONJAHM = 1, + TYPE_DEVOURER_OF_SOULS = 2, + TYPE_ACHIEV_PHANTOM_BLAST = 3, + + DATA_SOULFRAGMENT_REMOVE = 4, // on Death and on Use + + NPC_BRONJAHM = 36497, + NPC_DEVOURER_OF_SOULS = 36502, + NPC_CORRUPTED_SOUL_FRAGMENT = 36535, + + // Event NPCs + NPC_SILVANA_BEGIN = 37596, + NPC_SILVANA_END = 38161, + NPC_JAINA_BEGIN = 37597, + NPC_JAINA_END = 38160, + NPC_ARCHMAGE_ELANDRA = 37774, + NPC_ARCHMAGE_KORELN = 37582, + NPC_DARK_RANGER_KALIRA = 37583, + NPC_DARK_RANGER_LORALEN = 37779, + NPC_COLISEUM_CHAMPION_A_P = 37498, // Alliance Paladin + NPC_COLISEUM_CHAMPION_A_F = 37496, // Alliance Footman + NPC_COLISEUM_CHAMPION_A_M = 37497, // Alliance Mage + NPC_COLISEUM_CHAMPION_H_F = 37584, // Horde Footman + NPC_COLISEUM_CHAMPION_H_T = 37587, // Horde Taure + NPC_COLISEUM_CHAMPION_H_M = 37588, // Horde Mage + + ACHIEV_CRIT_SOUL_POWER = 12752, + ACHIEV_CRIT_PHANTOM_BLAST = 12976, +}; + +struct sIntoEventNpcSpawnLocations +{ + uint32 uiEntryHorde, uiEntryAlliance; + float fSpawnX, fSpawnY, fSpawnZ, fSpawnO; +}; + +/* Still TODO +** We have 12 npc-entries to do moving, and often different waypoints for one entry +** Best way to handle the paths of these mobs is still open +*/ + +struct sExtroEventNpcLocations +{ + uint32 uiEntryHorde, uiEntryAlliance; + float fStartO, fEndO; // Orientation for Spawning + float fSpawnX, fSpawnY, fSpawnZ; + float fEndX, fEndY, fEndZ; +}; + +// TODO: verify Horde - Entries +const sIntoEventNpcSpawnLocations aEventBeginLocations[3] = +{ + {NPC_SILVANA_BEGIN, NPC_JAINA_BEGIN, 4901.25439f, 2206.861f, 638.8166f, 5.88175964f}, + {NPC_DARK_RANGER_KALIRA, NPC_ARCHMAGE_ELANDRA, 4899.709961f, 2205.899902f, 638.817017f, 5.864306f}, + {NPC_DARK_RANGER_LORALEN, NPC_ARCHMAGE_KORELN, 4903.160156f, 2213.090088f, 638.817017f, 0.1745329f} +}; + +const sExtroEventNpcLocations aEventEndLocations[18] = +{ + // Horde Entry Ally Entry O_Spawn O_End SpawnPos EndPos + {NPC_SILVANA_END, NPC_JAINA_END, 0.8901179f, 0.890118f, 5606.34033f, 2436.32129f, 705.9351f, 5638.404f, 2477.154f, 708.6932f}, + {NPC_COLISEUM_CHAMPION_H_F, NPC_COLISEUM_CHAMPION_A_F, 0.9773844f, 1.780236f, 5593.632f, 2428.57983f, 705.9351f, 5695.879f, 2522.944f, 714.6915f}, + {NPC_COLISEUM_CHAMPION_H_F, NPC_COLISEUM_CHAMPION_A_F, 1.1519173f, 1.78023f, 5594.079f, 2425.111f, 705.9351f, 5692.123f, 2522.613f, 714.6915f}, + {NPC_COLISEUM_CHAMPION_H_F, NPC_COLISEUM_CHAMPION_A_F, 0.6108652f, 0.296706f, 5597.932f, 2421.78125f, 705.9351f, 5669.314f, 2540.029f, 714.6915f}, + {NPC_COLISEUM_CHAMPION_H_F, NPC_COLISEUM_CHAMPION_A_F, 1.0471975f, 5.358161f, 5598.03564f, 2429.37671f, 705.9351f, 5639.267f, 2520.912f, 708.6959f}, + {NPC_COLISEUM_CHAMPION_H_F, NPC_COLISEUM_CHAMPION_A_F, 0.8901179f, 0.112373f, 5600.836f, 2421.35938f, 705.9351f, 5668.145f, 2543.854f, 714.6915f}, + {NPC_COLISEUM_CHAMPION_H_F, NPC_COLISEUM_CHAMPION_A_F, 0.8901179f, 5.358161f, 5600.848f, 2429.54517f, 705.9351f, 5639.961f, 2522.936f, 708.6959f}, + {NPC_COLISEUM_CHAMPION_H_F, NPC_COLISEUM_CHAMPION_A_F, 0.8901179f, 5.347504f, 5601.46533f, 2426.77075f, 705.9351f, 5643.156f, 2525.342f, 708.6958f}, + {NPC_COLISEUM_CHAMPION_H_F, NPC_COLISEUM_CHAMPION_A_F, 1.1519173f, 0.232039f, 5601.587f, 2418.60425f, 705.9351f, 5670.483f, 2536.204f, 714.6915f}, + {NPC_DARK_RANGER_LORALEN, NPC_ARCHMAGE_KORELN, 0.7853982f, 3.717551f, 5606.35059f, 2432.88013f, 705.9351f, 5688.9f, 2538.981f, 714.6915f}, + {NPC_DARK_RANGER_KALIRA, NPC_ARCHMAGE_ELANDRA, 0.9599311f, 4.694936f, 5602.80371f, 2435.66846f, 705.9351f, 5685.069f, 2541.771f, 714.6915f}, + {NPC_COLISEUM_CHAMPION_H_T, NPC_COLISEUM_CHAMPION_A_P, 0.8552113f, 1.958489f, 5589.8125f, 2421.27075f, 705.9351f, 5669.351f, 2472.626f, 708.6959f}, + {NPC_COLISEUM_CHAMPION_H_T, NPC_COLISEUM_CHAMPION_A_P, 0.8552113f, 2.111848f, 5592.2666f, 2419.37842f, 705.9351f, 5665.927f, 2470.574f, 708.6959f}, + {NPC_COLISEUM_CHAMPION_H_T, NPC_COLISEUM_CHAMPION_A_P, 0.9075712f, 2.196496f, 5594.64746f, 2417.10767f, 705.9351f, 5662.503f, 2468.522f, 708.6958f}, + {NPC_COLISEUM_CHAMPION_H_M, NPC_COLISEUM_CHAMPION_A_M, 1.0646508f, 0.837758f, 5585.49854f, 2418.22925f, 705.9351f, 5624.832f, 2473.713f, 708.6959f}, + {NPC_COLISEUM_CHAMPION_H_M, NPC_COLISEUM_CHAMPION_A_M, 0.9424777f, 0.837758f, 5586.80029f, 2416.97388f, 705.9351f, 5627.443f, 2472.236f, 708.6959f}, + {NPC_COLISEUM_CHAMPION_H_M, NPC_COLISEUM_CHAMPION_A_M, 0.9250245f, 0.977384f, 5591.653f, 2412.89771f, 705.9351f, 5637.912f, 2465.69f, 708.6959f}, + {NPC_COLISEUM_CHAMPION_H_M, NPC_COLISEUM_CHAMPION_A_M, 0.8726646f, 0.977384f, 5593.93652f, 2410.875f, 705.9351f, 5642.629f, 2474.331f, 708.6959f} +}; + +class instance_forge_of_souls : public ScriptedInstance +{ + public: + instance_forge_of_souls(Map* pMap); + ~instance_forge_of_souls() {} + + void Initialize() override; + + void OnCreatureCreate(Creature* pCreature) override; + + void SetData(uint32 uiType, uint32 uiData) override; + uint32 GetData(uint32 uiType) const override; + void SetData64(uint32 uiType, uint64 uiData) override; + + void OnPlayerEnter(Player* pPlayer) override; + void ProcessEventNpcs(Player* pPlayer, bool bChanged); + bool CheckAchievementCriteriaMeet(uint32 uiCriteriaId, Player const* pSource, Unit const* pTarget, uint32 uiMiscValue1 /* = 0*/) const override; + + const char* Save() const override { return m_strInstData.c_str(); } + void Load(const char* chrIn) override; + + protected: + uint32 m_auiEncounter[MAX_ENCOUNTER]; + std::string m_strInstData; + + bool m_bCriteriaPhantomBlastFailed; + + uint32 m_uiTeam; // Team of first entered player, used to set if Jaina or Silvana to spawn + + GuidList m_luiSoulFragmentAliveGUIDs; + GuidList m_lEventMobGUIDs; +}; + +#endif diff --git a/src/modules/SD2/scripts/northrend/icecrown_citadel/frozen_halls/forge_of_souls/instance_forge_of_souls.cpp b/src/modules/SD2/scripts/northrend/icecrown_citadel/frozen_halls/forge_of_souls/instance_forge_of_souls.cpp new file mode 100644 index 000000000..01254d8bf --- /dev/null +++ b/src/modules/SD2/scripts/northrend/icecrown_citadel/frozen_halls/forge_of_souls/instance_forge_of_souls.cpp @@ -0,0 +1,214 @@ +/* Copyright (C) 2006 - 2013 ScriptDev2 + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along 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_forge_of_souls +SD%Complete: 90% +SDComment: TODO: Movement of the extro-event is missing, implementation unclear! +SDCategory: The Forge of Souls +EndScriptData */ + +#include "precompiled.h" +#include "forge_of_souls.h" + +instance_forge_of_souls::instance_forge_of_souls(Map* pMap) : ScriptedInstance(pMap), + m_bCriteriaPhantomBlastFailed(false), + m_uiTeam(0) +{ + Initialize(); +} + +void instance_forge_of_souls::Initialize() +{ + memset(&m_auiEncounter, 0, sizeof(m_auiEncounter)); +} + +void instance_forge_of_souls::OnCreatureCreate(Creature* pCreature) +{ + switch (pCreature->GetEntry()) + { + case NPC_BRONJAHM: + m_mNpcEntryGuidStore[pCreature->GetEntry()] = pCreature->GetObjectGuid(); + break; + + case NPC_CORRUPTED_SOUL_FRAGMENT: + m_luiSoulFragmentAliveGUIDs.push_back(pCreature->GetObjectGuid()); + break; + } +} + +void instance_forge_of_souls::OnPlayerEnter(Player* pPlayer) +{ + if (!m_uiTeam) // very first player to enter + { + m_uiTeam = pPlayer->GetTeam(); + ProcessEventNpcs(pPlayer, false); + } +} + +void instance_forge_of_souls::ProcessEventNpcs(Player* pPlayer, bool bChanged) +{ + if (!pPlayer) + return; + + if (m_auiEncounter[0] != DONE || m_auiEncounter[1] != DONE) + { + // Spawn Begin Mobs + for (uint8 i = 0; i < countof(aEventBeginLocations); ++i) + { + if (Creature* pSummon = pPlayer->SummonCreature(m_uiTeam == HORDE ? aEventBeginLocations[i].uiEntryHorde : aEventBeginLocations[i].uiEntryAlliance, + aEventBeginLocations[i].fSpawnX, aEventBeginLocations[i].fSpawnY, aEventBeginLocations[i].fSpawnZ, aEventBeginLocations[i].fSpawnO, TEMPSUMMON_DEAD_DESPAWN, 24 * HOUR * IN_MILLISECONDS)) + m_lEventMobGUIDs.push_back(pSummon->GetObjectGuid()); + } + } + else + { + // if bChanged, despawn Begin Mobs, spawn End Mobs at Spawn, else spawn EndMobs at End + if (bChanged) + { + for (GuidList::const_iterator itr = m_lEventMobGUIDs.begin(); itr != m_lEventMobGUIDs.end(); ++itr) + { + if (Creature* pSummoned = instance->GetCreature(*itr)) + pSummoned->ForcedDespawn(); + } + + for (uint8 i = 0; i < countof(aEventEndLocations); ++i) + { + pPlayer->SummonCreature(m_uiTeam == HORDE ? aEventEndLocations[i].uiEntryHorde : aEventEndLocations[i].uiEntryAlliance, + aEventEndLocations[i].fSpawnX, aEventEndLocations[i].fSpawnY, aEventEndLocations[i].fSpawnZ, aEventEndLocations[i].fStartO, TEMPSUMMON_DEAD_DESPAWN, 24 * HOUR * IN_MILLISECONDS); + + // TODO: Let the NPCs Move along their paths + } + } + else + { + // Summon at end, without event + for (uint8 i = 0; i < countof(aEventEndLocations); ++i) + { + pPlayer->SummonCreature(m_uiTeam == HORDE ? aEventEndLocations[i].uiEntryHorde : aEventEndLocations[i].uiEntryAlliance, + aEventEndLocations[i].fEndX, aEventEndLocations[i].fEndY, aEventEndLocations[i].fEndZ, aEventEndLocations[i].fEndO, TEMPSUMMON_DEAD_DESPAWN, 24 * HOUR * IN_MILLISECONDS); + } + } + } +} + +bool instance_forge_of_souls::CheckAchievementCriteriaMeet(uint32 uiCriteriaId, Player const* /*pSource*/, Unit const* /*pTarget*/, uint32 /*uiMiscValue1 = 0*/) const +{ + switch (uiCriteriaId) + { + case ACHIEV_CRIT_SOUL_POWER: + return m_luiSoulFragmentAliveGUIDs.size() >= 4; + case ACHIEV_CRIT_PHANTOM_BLAST: + return !m_bCriteriaPhantomBlastFailed; + default: + return 0; + } +} + +void instance_forge_of_souls::SetData(uint32 uiType, uint32 uiData) +{ + switch (uiType) + { + case TYPE_BRONJAHM: + m_auiEncounter[0] = uiData; + + // Despawn remaining adds and clear list + for (GuidList::const_iterator itr = m_luiSoulFragmentAliveGUIDs.begin(); itr != m_luiSoulFragmentAliveGUIDs.end(); ++itr) + { + if (Creature* pFragment = instance->GetCreature(*itr)) + pFragment->ForcedDespawn(); + } + m_luiSoulFragmentAliveGUIDs.clear(); + break; + case TYPE_DEVOURER_OF_SOULS: + m_auiEncounter[1] = uiData; + if (uiData == DONE) + ProcessEventNpcs(GetPlayerInMap(), true); + break; + case TYPE_ACHIEV_PHANTOM_BLAST: + m_bCriteriaPhantomBlastFailed = (uiData == FAIL); + return; + } + + if (uiData == DONE) + { + OUT_SAVE_INST_DATA; + + std::ostringstream saveStream; + saveStream << m_auiEncounter[0] << " " << m_auiEncounter[1]; + + m_strInstData = saveStream.str(); + + SaveToDB(); + OUT_SAVE_INST_DATA_COMPLETE; + } +} + +void instance_forge_of_souls::Load(const char* chrIn) +{ + if (!chrIn) + { + OUT_LOAD_INST_DATA_FAIL; + return; + } + + OUT_LOAD_INST_DATA(chrIn); + + std::istringstream loadStream(chrIn); + loadStream >> m_auiEncounter[0] >> m_auiEncounter[1]; + + for (uint8 i = 0; i < MAX_ENCOUNTER; ++i) + { + if (m_auiEncounter[i] == IN_PROGRESS) + m_auiEncounter[i] = NOT_STARTED; + } + + OUT_LOAD_INST_DATA_COMPLETE; +} + +uint32 instance_forge_of_souls::GetData(uint32 uiType) const +{ + switch (uiType) + { + case TYPE_BRONJAHM: + return m_auiEncounter[0]; + case TYPE_DEVOURER_OF_SOULS: + return m_auiEncounter[1]; + default: + return 0; + } +} + +void instance_forge_of_souls::SetData64(uint32 uiType, uint64 uiData) +{ + if (uiType == DATA_SOULFRAGMENT_REMOVE) + m_luiSoulFragmentAliveGUIDs.remove(ObjectGuid(uiData)); +} + +InstanceData* GetInstanceData_instance_forge_of_souls(Map* pMap) +{ + return new instance_forge_of_souls(pMap); +} + +void AddSC_instance_forge_of_souls() +{ + Script* pNewScript; + + pNewScript = new Script; + pNewScript->Name = "instance_forge_of_souls"; + pNewScript->GetInstanceData = &GetInstanceData_instance_forge_of_souls; + pNewScript->RegisterSelf(); +} diff --git a/src/modules/SD2/scripts/northrend/icecrown_citadel/frozen_halls/halls_of_reflection/boss_falric.cpp b/src/modules/SD2/scripts/northrend/icecrown_citadel/frozen_halls/halls_of_reflection/boss_falric.cpp new file mode 100644 index 000000000..221a0bea1 --- /dev/null +++ b/src/modules/SD2/scripts/northrend/icecrown_citadel/frozen_halls/halls_of_reflection/boss_falric.cpp @@ -0,0 +1,28 @@ +/* Copyright (C) 2006 - 2013 ScriptDev2 + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +/* ScriptData +SDName: boss_falric +SD%Complete: 0% +SDComment: +SDCategory: Halls of Reflection +EndScriptData */ + +#include "precompiled.h" + +void AddSC_boss_falric() +{ +} diff --git a/src/modules/SD2/scripts/northrend/icecrown_citadel/frozen_halls/halls_of_reflection/boss_lich_king.cpp b/src/modules/SD2/scripts/northrend/icecrown_citadel/frozen_halls/halls_of_reflection/boss_lich_king.cpp new file mode 100644 index 000000000..26fd3d6ae --- /dev/null +++ b/src/modules/SD2/scripts/northrend/icecrown_citadel/frozen_halls/halls_of_reflection/boss_lich_king.cpp @@ -0,0 +1,28 @@ +/* Copyright (C) 2006 - 2013 ScriptDev2 + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +/* ScriptData +SDName: boss_lich_king +SD%Complete: 0% +SDComment: +SDCategory: Halls of Reflection +EndScriptData */ + +#include "precompiled.h" + +void AddSC_boss_lich_king() +{ +} diff --git a/src/modules/SD2/scripts/northrend/icecrown_citadel/frozen_halls/halls_of_reflection/boss_marwyn.cpp b/src/modules/SD2/scripts/northrend/icecrown_citadel/frozen_halls/halls_of_reflection/boss_marwyn.cpp new file mode 100644 index 000000000..429639aa0 --- /dev/null +++ b/src/modules/SD2/scripts/northrend/icecrown_citadel/frozen_halls/halls_of_reflection/boss_marwyn.cpp @@ -0,0 +1,28 @@ +/* Copyright (C) 2006 - 2013 ScriptDev2 + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +/* ScriptData +SDName: boss_marwyn +SD%Complete: 0% +SDComment: +SDCategory: Halls of Reflection +EndScriptData */ + +#include "precompiled.h" + +void AddSC_boss_marwyn() +{ +} diff --git a/src/modules/SD2/scripts/northrend/icecrown_citadel/frozen_halls/halls_of_reflection/halls_of_reflection.cpp b/src/modules/SD2/scripts/northrend/icecrown_citadel/frozen_halls/halls_of_reflection/halls_of_reflection.cpp new file mode 100644 index 000000000..9e9bf72b4 --- /dev/null +++ b/src/modules/SD2/scripts/northrend/icecrown_citadel/frozen_halls/halls_of_reflection/halls_of_reflection.cpp @@ -0,0 +1,28 @@ +/* Copyright (C) 2006 - 2013 ScriptDev2 + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +/* ScriptData +SDName: halls_of_reflection.cpp +SD%Complete: 0 +SDComment: Placeholder +SDCategory: Halls of Reflection +EndScriptData */ + +#include "precompiled.h" + +void AddSC_halls_of_reflection() +{ +} diff --git a/src/modules/SD2/scripts/northrend/icecrown_citadel/frozen_halls/halls_of_reflection/halls_of_reflection.h b/src/modules/SD2/scripts/northrend/icecrown_citadel/frozen_halls/halls_of_reflection/halls_of_reflection.h new file mode 100644 index 000000000..6b5f9ef47 --- /dev/null +++ b/src/modules/SD2/scripts/northrend/icecrown_citadel/frozen_halls/halls_of_reflection/halls_of_reflection.h @@ -0,0 +1,3 @@ +/* Copyright (C) 2006 - 2013 ScriptDev2 + * This program is free software licensed under GPL version 2 + * Please see the included DOCS/LICENSE.TXT for more information */ diff --git a/src/modules/SD2/scripts/northrend/icecrown_citadel/frozen_halls/halls_of_reflection/instance_halls_of_reflection.cpp b/src/modules/SD2/scripts/northrend/icecrown_citadel/frozen_halls/halls_of_reflection/instance_halls_of_reflection.cpp new file mode 100644 index 000000000..da5c3bfdb --- /dev/null +++ b/src/modules/SD2/scripts/northrend/icecrown_citadel/frozen_halls/halls_of_reflection/instance_halls_of_reflection.cpp @@ -0,0 +1,28 @@ +/* Copyright (C) 2006 - 2013 ScriptDev2 + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along 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_reflection +SD%Complete: 0 +SDComment: Placeholder +SDCategory: Halls of Reflection +EndScriptData */ + +#include "precompiled.h" + +void AddSC_instance_halls_of_reflection() +{ +} diff --git a/src/modules/SD2/scripts/northrend/icecrown_citadel/frozen_halls/pit_of_saron/boss_forgemaster_garfrost.cpp b/src/modules/SD2/scripts/northrend/icecrown_citadel/frozen_halls/pit_of_saron/boss_forgemaster_garfrost.cpp new file mode 100644 index 000000000..210e0af95 --- /dev/null +++ b/src/modules/SD2/scripts/northrend/icecrown_citadel/frozen_halls/pit_of_saron/boss_forgemaster_garfrost.cpp @@ -0,0 +1,301 @@ +/* Copyright (C) 2006 - 2013 ScriptDev2 + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +/* ScriptData +SDName: boss_forgemaster_garfrost +SD%Complete: 90 +SDComment: Tyrannus outro event NYI. +SDCategory: Pit of Saron +EndScriptData */ + +#include "precompiled.h" +#include "pit_of_saron.h" + +enum +{ + SAY_AGGRO = -1658014, + SAY_SLAY_1 = -1658015, + SAY_BOULDER_HIT = -1658016, // TODO How must this be handled? + SAY_DEATH = -1658017, + SAY_FORGE_1 = -1658018, + SAY_FORGE_2 = -1658019, + + EMOTE_THROW_SARONITE = -1658022, + EMOTE_DEEP_FREEZE = -1658023, + + SPELL_PERMAFROST = 70326, + SPELL_PERMAFROST_AURA_H = 70336, + SPELL_THROW_SARONITE = 68788, + SPELL_THUNDERING_STOMP = 68771, + SPELL_FORGE_FROZEN_BLADE = 68774, + SPELL_CHILLING_WAVE = 68778, + SPELL_FORGE_FROSTBORN_MACE = 68785, + SPELL_DEEP_FREEZE = 70381, + + MAX_PERMAFROST_STACK = 10, // the max allowed stacks for the achiev to pass + + PHASE_NO_ENCHANTMENT = 1, + PHASE_BLADE_ENCHANTMENT = 2, + PHASE_MACE_ENCHANTMENT = 3, + PHASE_MOVEMENT = 4, +}; + +static const float aGarfrostMoveLocs[2][3] = +{ + {657.539f, -203.564f, 526.691f}, + {719.785f, -230.227f, 527.033f}, +}; + +static const float afOutroNpcSpawnLoc[4] = {695.0146f, -123.7532f, 515.3067f, 4.59f}; + +struct boss_forgemaster_garfrostAI : public ScriptedAI +{ + boss_forgemaster_garfrostAI(Creature* pCreature) : ScriptedAI(pCreature) + { + m_pInstance = (instance_pit_of_saron*)pCreature->GetInstanceData(); + m_bIsRegularMode = pCreature->GetMap()->IsRegularDifficulty(); + Reset(); + } + + instance_pit_of_saron* m_pInstance; + bool m_bIsRegularMode; + + uint32 m_uiThrowSaroniteTimer; + uint32 m_uiPhase; + uint32 m_uiChillingWaveTimer; + uint32 m_uiDeepFreezeTimer; + uint32 m_uiCheckPermafrostTimer; + + void Reset() override + { + m_uiCheckPermafrostTimer = 2000; + m_uiThrowSaroniteTimer = 13000; + m_uiChillingWaveTimer = 10000; + m_uiDeepFreezeTimer = 10000; + SetCombatMovement(true); + m_uiPhase = PHASE_NO_ENCHANTMENT; + } + + void Aggro(Unit* pWho) override + { + DoScriptText(SAY_AGGRO, m_creature, pWho); + DoCastSpellIfCan(m_creature, SPELL_PERMAFROST); + + if (m_pInstance) + m_pInstance->SetData(TYPE_GARFROST, IN_PROGRESS); + } + + void JustDied(Unit* pKiller) override + { + DoScriptText(SAY_DEATH, m_creature, pKiller); + + if (m_pInstance) + { + m_pInstance->SetData(TYPE_GARFROST, DONE); + + // Summon Ironskull or Victus for outro + m_creature->SummonCreature(m_pInstance->GetPlayerTeam() == HORDE ? NPC_IRONSKULL_PART1 : NPC_VICTUS_PART1, + afOutroNpcSpawnLoc[0], afOutroNpcSpawnLoc[1], afOutroNpcSpawnLoc[2], afOutroNpcSpawnLoc[3], TEMPSUMMON_TIMED_DESPAWN, 2 * MINUTE * IN_MILLISECONDS); + + // ToDo: handle the other npcs movement + } + } + + void KilledUnit(Unit* /*pVictim*/) override + { + DoScriptText(SAY_SLAY_1, m_creature); + } + + void JustReachedHome() override + { + if (m_pInstance) + m_pInstance->SetData(TYPE_GARFROST, FAIL); + } + + void JustSummoned(Creature* pSummoned) override + { + switch (pSummoned->GetEntry()) + { + case NPC_IRONSKULL_PART1: + case NPC_VICTUS_PART1: + { + float fX, fY, fZ; + pSummoned->SetWalk(false); + m_creature->GetContactPoint(pSummoned, fX, fY, fZ, 4 * INTERACTION_DISTANCE); + pSummoned->GetMotionMaster()->MovePoint(0, fX, fY, fZ); + break; + } + } + } + + void MovementInform(uint32 uiMotionType, uint32 uiPointId) override + { + if (uiMotionType != EFFECT_MOTION_TYPE) + return; + + if (uiPointId != PHASE_BLADE_ENCHANTMENT && uiPointId != PHASE_MACE_ENCHANTMENT) + return; + + // Cast and say expected spell + DoCastSpellIfCan(m_creature, uiPointId == PHASE_BLADE_ENCHANTMENT ? SPELL_FORGE_FROZEN_BLADE : SPELL_FORGE_FROSTBORN_MACE); + DoScriptText(uiPointId == PHASE_BLADE_ENCHANTMENT ? SAY_FORGE_1 : SAY_FORGE_2, m_creature); + + m_uiThrowSaroniteTimer += 5000; // Delay next Saronit + m_uiPhase = uiPointId; + SetCombatMovement(true); + + if (m_creature->getVictim()) + { + m_creature->GetMotionMaster()->Clear(); + m_creature->GetMotionMaster()->MoveChase(m_creature->getVictim()); + } + } + + void UpdateAI(const uint32 uiDiff) override + { + if (!m_creature->SelectHostileTarget() || !m_creature->getVictim()) + return; + + // This needs to be checked only on heroic + if (!m_bIsRegularMode && m_uiCheckPermafrostTimer) + { + if (m_uiCheckPermafrostTimer <= uiDiff) + { + ThreatList playerList = m_creature->GetThreatManager().getThreatList(); + for (ThreatList::const_iterator itr = playerList.begin(); itr != playerList.end(); ++itr) + { + if (Player* pTarget = m_creature->GetMap()->GetPlayer((*itr)->getUnitGuid())) + { + Aura* pAuraIntenseCold = pTarget->GetAura(SPELL_PERMAFROST_AURA_H, EFFECT_INDEX_2); + + if (pAuraIntenseCold) + { + if (pAuraIntenseCold->GetStackAmount() > MAX_PERMAFROST_STACK) + { + if (m_pInstance) + m_pInstance->SetSpecialAchievementCriteria(TYPE_ACHIEV_DOESNT_GO_ELEVEN, false); + + m_uiCheckPermafrostTimer = 0; + return; + } + } + } + } + m_uiCheckPermafrostTimer = 1000; + } + else + m_uiCheckPermafrostTimer -= uiDiff; + } + + // Do nothing more while moving + if (m_uiPhase == PHASE_MOVEMENT) + return; + + // Casted in every phase + if (m_uiThrowSaroniteTimer < uiDiff) + { + // TODO - only target players? + if (Unit* pTarget = m_creature->SelectAttackingTarget(ATTACKING_TARGET_RANDOM, 0)) + { + if (DoCastSpellIfCan(pTarget, SPELL_THROW_SARONITE) == CAST_OK) + { + DoScriptText(EMOTE_THROW_SARONITE, m_creature, pTarget); + m_uiThrowSaroniteTimer = 16000; + } + } + } + else + m_uiThrowSaroniteTimer -= uiDiff; + + switch (m_uiPhase) + { + case PHASE_NO_ENCHANTMENT: + { + if (m_creature->GetHealthPercent() < 66.0f) + { + DoCastSpellIfCan(m_creature, SPELL_THUNDERING_STOMP, CAST_INTERRUPT_PREVIOUS); + SetCombatMovement(false); + + m_creature->GetMotionMaster()->MoveJump(aGarfrostMoveLocs[0][0], aGarfrostMoveLocs[0][1], aGarfrostMoveLocs[0][2], 3 * m_creature->GetSpeed(MOVE_RUN), 10.0f, PHASE_BLADE_ENCHANTMENT); + m_uiPhase = PHASE_MOVEMENT; + + // Stop further action + return; + } + break; + } + case PHASE_BLADE_ENCHANTMENT: + { + if (m_creature->GetHealthPercent() < 33.0f) + { + DoCastSpellIfCan(m_creature, SPELL_THUNDERING_STOMP, CAST_INTERRUPT_PREVIOUS); + SetCombatMovement(false); + + m_creature->GetMotionMaster()->MoveJump(aGarfrostMoveLocs[1][0], aGarfrostMoveLocs[1][1], aGarfrostMoveLocs[1][2], 3 * m_creature->GetSpeed(MOVE_RUN), 10.0f, PHASE_MACE_ENCHANTMENT); + m_uiPhase = PHASE_MOVEMENT; + + // Stop further action + return; + } + + if (m_uiChillingWaveTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature->getVictim(), SPELL_CHILLING_WAVE) == CAST_OK) + m_uiChillingWaveTimer = 14000; + } + else + m_uiChillingWaveTimer -= uiDiff; + + break; + } + case PHASE_MACE_ENCHANTMENT: + { + if (m_uiDeepFreezeTimer < uiDiff) + { + if (Unit* pTarget = m_creature->SelectAttackingTarget(ATTACKING_TARGET_RANDOM, 0)) + { + if (DoCastSpellIfCan(pTarget, SPELL_DEEP_FREEZE) == CAST_OK) + { + DoScriptText(EMOTE_DEEP_FREEZE, m_creature, pTarget); + m_uiDeepFreezeTimer = 20000; + } + } + } + else + m_uiDeepFreezeTimer -= uiDiff; + + break; + } + } + + DoMeleeAttackIfReady(); + } +}; + +CreatureAI* GetAI_boss_forgemaster_garfrost(Creature* pCreature) +{ + return new boss_forgemaster_garfrostAI(pCreature); +} + +void AddSC_boss_garfrost() +{ + Script* pNewScript; + + pNewScript = new Script; + pNewScript->Name = "boss_forgemaster_garfrost"; + pNewScript->GetAI = &GetAI_boss_forgemaster_garfrost; + pNewScript->RegisterSelf(); +} diff --git a/src/modules/SD2/scripts/northrend/icecrown_citadel/frozen_halls/pit_of_saron/boss_krick_and_ick.cpp b/src/modules/SD2/scripts/northrend/icecrown_citadel/frozen_halls/pit_of_saron/boss_krick_and_ick.cpp new file mode 100644 index 000000000..876eea643 --- /dev/null +++ b/src/modules/SD2/scripts/northrend/icecrown_citadel/frozen_halls/pit_of_saron/boss_krick_and_ick.cpp @@ -0,0 +1,420 @@ +/* Copyright (C) 2006 - 2013 ScriptDev2 + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +/* ScriptData +SDName: boss_krick_and_ick +SD%Complete: 95 +SDComment: Timers may need adjustments. +SDCategory: Pit of Saron +EndScriptData */ + +#include "precompiled.h" +#include "pit_of_saron.h" + +enum +{ + SAY_AGGRO = -1658024, + SAY_SLAY_1 = -1658025, + SAY_SLAY_2 = -1658026, + SAY_ORDER_STOP = -1658027, + SAY_ORDER_BLOW = -1658028, + SAY_TARGET_1 = -1658029, + SAY_TARGET_2 = -1658030, + SAY_TARGET_3 = -1658031, + SAY_OUTRO_1 = -1658035, + + EMOTE_KRICK_MINES = -1658032, + EMOTE_ICK_POISON = -1658033, + EMOTE_ICK_CHASING = -1658034, + + // ick spells + SPELL_POISON_NOVA = 68989, + SPELL_MIGHTY_KICK = 69021, + SPELL_PURSUIT = 68987, + SPELL_EXPLOSIVE_BARRAGE_ICK = 69263, + + // krick spells + SPELL_TOXIC_WASTE = 69024, + SPELL_SHADOW_BOLT = 69028, + SPELL_EXPLOSIVE_BARRAGE_KRICK = 69012, // Triggers 69015 every 2 sec + + NPC_EXPLODING_ORB = 36610, + + // exploding orb spells + // SPELL_EXPLOSIVE_BARRAGE_SUMMON = 69015, + SPELL_EXPLODING_ORB_VISUAL = 69017, + SPELL_AUTO_GROW_AND_SPEED_BOOST = 69020, + SPELL_EXPLOSIVE_BARRAGE_DMG = 69019, + SPELL_HASTY_GROW = 44851, // Orb explodes after the 15th stack + + MAX_HASTY_GROW_STACKS = 15, +}; + +static const float afOutroNpcSpawnLoc[4] = {777.2274f, 119.5521f, 510.0363f, 6.05f}; +static const float afTyrannusTeleLoc[4] = {841.01f, 196.245f, 573.964f, 4.46f}; + +/*###### +## boss_ick +######*/ + +struct boss_ickAI : public ScriptedAI +{ + boss_ickAI(Creature* pCreature) : ScriptedAI(pCreature) + { + m_pInstance = (instance_pit_of_saron*)pCreature->GetInstanceData(); + m_uiMountTimer = 1000; + Reset(); + } + + instance_pit_of_saron* m_pInstance; + uint32 m_uiMountTimer; + + uint32 m_uiPoisonNovaTimer; + uint32 m_uiPursueTimer; + uint32 m_uiMightKickTimer; + uint32 m_uiToxicWasteTimer; + uint32 m_uiShadowboltTimer; + uint32 m_uiExplosivBarrageTimer; + uint32 m_uiCooldownTimer; + + void Reset() override + { + m_uiPoisonNovaTimer = urand(20000, 25000); + m_uiPursueTimer = 20000; + m_uiMightKickTimer = 1000; + m_uiToxicWasteTimer = urand(3000, 5000); + m_uiShadowboltTimer = urand(5000, 7000); + m_uiExplosivBarrageTimer = urand(30000, 35000); + m_uiCooldownTimer = 0; + } + + void Aggro(Unit* pWho) override + { + if (m_pInstance) + { + m_pInstance->SetData(TYPE_KRICK, IN_PROGRESS); + + // Say aggro and also put Krick in combat + if (Creature* pKrick = m_pInstance->GetSingleCreatureFromStorage(NPC_KRICK)) + { + DoScriptText(SAY_AGGRO, pKrick); + pKrick->AI()->AttackStart(pWho); + } + } + } + + void KilledUnit(Unit* /*pVictim*/) + { + if (m_pInstance) + { + if (Creature* pKrick = m_pInstance->GetSingleCreatureFromStorage(NPC_KRICK)) + DoScriptText(urand(0, 1) ? SAY_SLAY_1 : SAY_SLAY_2, pKrick); + } + } + + void JustDied(Unit* /*pKiller*/) override + { + if (m_pInstance) + { + m_pInstance->SetData(TYPE_KRICK, DONE); + + if (Creature* pKrick = m_pInstance->GetSingleCreatureFromStorage(NPC_KRICK)) + { + DoScriptText(SAY_OUTRO_1, pKrick); + pKrick->AI()->EnterEvadeMode(); + + // Summon Jaina or Sylvanas for epilogue + pKrick->SummonCreature(m_pInstance->GetPlayerTeam() == HORDE ? NPC_SYLVANAS_PART1 : NPC_JAINA_PART1, + afOutroNpcSpawnLoc[0], afOutroNpcSpawnLoc[1], afOutroNpcSpawnLoc[2], afOutroNpcSpawnLoc[3], TEMPSUMMON_TIMED_DESPAWN, 2 * MINUTE * IN_MILLISECONDS); + } + + if (Creature* pTyrannus = m_pInstance->GetSingleCreatureFromStorage(NPC_TYRANNUS_INTRO)) + pTyrannus->NearTeleportTo(afTyrannusTeleLoc[0], afTyrannusTeleLoc[1], afTyrannusTeleLoc[2], afTyrannusTeleLoc[3]); + } + } + + void JustReachedHome() override + { + if (m_pInstance) + m_pInstance->SetData(TYPE_KRICK, FAIL); + } + + void UpdateAI(const uint32 uiDiff) override + { + if (!m_pInstance) + return; + + // He needs to be mounted manually, not by vehicle_accessories + if (m_uiMountTimer) + { + if (m_uiMountTimer <= uiDiff) + { + if (Creature* pKrick = m_pInstance->GetSingleCreatureFromStorage(NPC_KRICK)) + pKrick->CastSpell(m_creature, SPELL_RIDE_VEHICLE_HARDCODED, true); + + m_uiMountTimer = 0; + } + else + m_uiMountTimer -= uiDiff; + } + + if (!m_creature->SelectHostileTarget() || !m_creature->getVictim()) + return; + + // Cooldown timer - we need to block all Krick spell during some events + if (m_uiCooldownTimer) + { + if (m_uiCooldownTimer <= uiDiff) + m_uiCooldownTimer = 0; + else + m_uiCooldownTimer -= uiDiff; + + return; + } + + if (m_uiPoisonNovaTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature, SPELL_POISON_NOVA) == CAST_OK) + { + if (Creature* pKrick = m_pInstance->GetSingleCreatureFromStorage(NPC_KRICK)) + { + DoScriptText(SAY_ORDER_BLOW, pKrick); + DoScriptText(EMOTE_ICK_POISON, pKrick); + } + + m_uiCooldownTimer = 5000; + m_uiPoisonNovaTimer = urand(20000, 25000); + } + } + else + m_uiPoisonNovaTimer -= uiDiff; + + if (m_uiPursueTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature, SPELL_PURSUIT) == CAST_OK) + { + if (Creature* pKrick = m_pInstance->GetSingleCreatureFromStorage(NPC_KRICK)) + { + switch (urand(0, 2)) + { + case 0: DoScriptText(SAY_TARGET_1, pKrick); break; + case 1: DoScriptText(SAY_TARGET_2, pKrick); break; + case 2: DoScriptText(SAY_TARGET_3, pKrick); break; + } + } + + m_uiCooldownTimer = 17000; + m_uiPursueTimer = urand(50000, 70000); + } + } + else + m_uiPursueTimer -= uiDiff; + + if (m_uiMightKickTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature->getVictim(), SPELL_MIGHTY_KICK) == CAST_OK) + m_uiMightKickTimer = 10000; + } + else + m_uiMightKickTimer -= uiDiff; + + if (m_uiToxicWasteTimer < uiDiff) + { + if (Unit* pTarget = m_creature->SelectAttackingTarget(ATTACKING_TARGET_RANDOM, 0)) + { + if (Creature* pKrick = m_pInstance->GetSingleCreatureFromStorage(NPC_KRICK)) + pKrick->CastSpell(pTarget, SPELL_TOXIC_WASTE, true); + + m_uiToxicWasteTimer = urand(3000, 5000); + } + } + else + m_uiToxicWasteTimer -= uiDiff; + + if (m_uiShadowboltTimer < uiDiff) + { + if (Unit* pTarget = m_creature->SelectAttackingTarget(ATTACKING_TARGET_RANDOM, 0)) + { + if (Creature* pKrick = m_pInstance->GetSingleCreatureFromStorage(NPC_KRICK)) + pKrick->CastSpell(pTarget, SPELL_SHADOW_BOLT, true); + + m_uiShadowboltTimer = urand(4000, 8000); + } + } + else + m_uiShadowboltTimer -= uiDiff; + + if (m_uiExplosivBarrageTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature, SPELL_EXPLOSIVE_BARRAGE_ICK) == CAST_OK) + { + if (Creature* pKrick = m_pInstance->GetSingleCreatureFromStorage(NPC_KRICK)) + { + pKrick->CastSpell(pKrick, SPELL_EXPLOSIVE_BARRAGE_KRICK, true); + + DoScriptText(SAY_ORDER_STOP, pKrick); + DoScriptText(EMOTE_KRICK_MINES, pKrick); + } + + m_uiCooldownTimer = 20000; + m_uiExplosivBarrageTimer = urand(25000, 30000); + } + } + else + m_uiExplosivBarrageTimer -= uiDiff; + + DoMeleeAttackIfReady(); + } +}; + +CreatureAI* GetAI_boss_ick(Creature* pCreature) +{ + return new boss_ickAI(pCreature); +} + +/*###### +## boss_krick +######*/ + +struct boss_krickAI : public ScriptedAI +{ + boss_krickAI(Creature* pCreature) : ScriptedAI(pCreature) + { + m_pInstance = (ScriptedInstance*)pCreature->GetInstanceData(); + Reset(); + } + + ScriptedInstance* m_pInstance; + + void Reset() override { } + + void EnterEvadeMode() override + { + m_creature->RemoveAllAurasOnEvade(); + m_creature->DeleteThreatList(); + m_creature->CombatStop(true); + m_creature->LoadCreatureAddon(true); + + m_creature->SetLootRecipient(NULL); + + Reset(); + + // Don't handle movement. Boss is on vehicle so he doesn't have to go anywhere. On epilogue he needs to stay in place + } + + void JustSummoned(Creature* pSummoned) override + { + switch (pSummoned->GetEntry()) + { + case NPC_SYLVANAS_PART1: + case NPC_JAINA_PART1: + { + float fX, fY, fZ; + pSummoned->SetWalk(false); + m_creature->GetContactPoint(pSummoned, fX, fY, fZ, 2 * INTERACTION_DISTANCE); + pSummoned->GetMotionMaster()->MovePoint(1, fX, fY, fZ); + break; + } + case NPC_EXPLODING_ORB: + pSummoned->CastSpell(pSummoned, SPELL_EXPLODING_ORB_VISUAL, true); + pSummoned->CastSpell(pSummoned, SPELL_AUTO_GROW_AND_SPEED_BOOST, true); + break; + } + } + + void SummonedMovementInform(Creature* /*pSummoned*/, uint32 uiMotionType, uint32 uiPointId) override + { + if (uiMotionType != POINT_MOTION_TYPE || !uiPointId) + return; + + if (m_pInstance) + m_pInstance->SetData(TYPE_KRICK, SPECIAL); + } + + void UpdateAI(const uint32 /*uiDiff*/) override + { + if (!m_creature->SelectHostileTarget() || !m_creature->getVictim()) + return; + } +}; + +CreatureAI* GetAI_boss_krick(Creature* pCreature) +{ + return new boss_krickAI(pCreature); +} + +/*###### +## npc_exploding_orb +######*/ + +struct npc_exploding_orbAI : public Scripted_NoMovementAI +{ + npc_exploding_orbAI(Creature* pCreature) : Scripted_NoMovementAI(pCreature) { Reset(); } + + uint8 m_uiGrowCount; + + void Reset() override + { + m_uiGrowCount = 0; + } + + void AttackStart(Unit* /*pWho*/) override { } + void MoveInLineOfSight(Unit* /*pWho*/) override { } + + void SpellHit(Unit* /*pCaster*/, const SpellEntry* pSpell) override + { + if (pSpell->Id == SPELL_HASTY_GROW) + { + ++m_uiGrowCount; + + if (m_uiGrowCount == MAX_HASTY_GROW_STACKS) + { + if (DoCastSpellIfCan(m_creature, SPELL_EXPLOSIVE_BARRAGE_DMG) == CAST_OK) + { + m_creature->RemoveAllAuras(); + m_creature->ForcedDespawn(1000); + } + } + } + } + + void UpdateAI(const uint32 /*uiDiff*/) override { } +}; + +CreatureAI* GetAI_npc_exploding_orb(Creature* pCreature) +{ + return new npc_exploding_orbAI(pCreature); +} + +void AddSC_boss_krick_and_ick() +{ + Script* pNewScript; + + pNewScript = new Script; + pNewScript->Name = "boss_ick"; + pNewScript->GetAI = &GetAI_boss_ick; + pNewScript->RegisterSelf(); + + pNewScript = new Script; + pNewScript->Name = "boss_krick"; + pNewScript->GetAI = &GetAI_boss_krick; + pNewScript->RegisterSelf(); + + pNewScript = new Script; + pNewScript->Name = "npc_exploding_orb"; + pNewScript->GetAI = &GetAI_npc_exploding_orb; + pNewScript->RegisterSelf(); +} diff --git a/src/modules/SD2/scripts/northrend/icecrown_citadel/frozen_halls/pit_of_saron/boss_scourgelord_tyrannus.cpp b/src/modules/SD2/scripts/northrend/icecrown_citadel/frozen_halls/pit_of_saron/boss_scourgelord_tyrannus.cpp new file mode 100644 index 000000000..706dba6ef --- /dev/null +++ b/src/modules/SD2/scripts/northrend/icecrown_citadel/frozen_halls/pit_of_saron/boss_scourgelord_tyrannus.cpp @@ -0,0 +1,333 @@ +/* Copyright (C) 2006 - 2013 ScriptDev2 + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +/* ScriptData +SDName: boss_scourgelord_tyrannus +SD%Complete: 90 +SDComment: Small adjustments may be required +SDCategory: Pit of Saron +EndScriptData */ + +#include "precompiled.h" +#include "pit_of_saron.h" + +enum +{ + SAY_AGGRO = -1658053, + SAY_SLAY_1 = -1658054, + SAY_SLAY_2 = -1658055, + SAY_DEATH = -1658056, + SAY_MARK = -1658057, + SAY_SMASH = -1658058, + + EMOTE_RIMEFANG_ICEBOLT = -1658059, + EMOTE_SMASH = -1658060, + + // Tyrannus spells + SPELL_FORCEFUL_SMASH = 69155, + SPELL_OVERLORDS_BRAND = 69172, // triggers 69189 and 69190 from target + SPELL_UNHOLY_POWER = 69167, + SPELL_MARK_OF_RIMEFANG = 69275, + + // Rimefang spells + SPELL_HOARFROST = 69246, + SPELL_ICY_BLAST = 69232, + SPELL_KILLING_ICE = 72531, + + // Icy blast + // SPELL_ICY_BLAST_AURA = 69238, + NPC_ICY_BLAST = 36731, // handled in eventAI +}; + +static const float afRimefangExitPos[3] = {1248.29f, 145.924f, 733.914f}; + +/*###### +## boss_tyrannus +######*/ + +struct boss_tyrannusAI : public ScriptedAI +{ + boss_tyrannusAI(Creature* pCreature) : ScriptedAI(pCreature) + { + m_pInstance = (instance_pit_of_saron*)pCreature->GetInstanceData(); + pCreature->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE); + Reset(); + } + + instance_pit_of_saron* m_pInstance; + + uint32 m_uiForcefulSmashTimer; + uint32 m_uiOverlordsBrandTimer; + uint32 m_uiUnholyPowerTimer; + uint32 m_uiMarkOfRimefangTimer; + + void Reset() override + { + m_uiForcefulSmashTimer = 10000; + m_uiOverlordsBrandTimer = 9000; + m_uiUnholyPowerTimer = urand(30000, 35000); + m_uiMarkOfRimefangTimer = 20000; + } + + void Aggro(Unit* pWho) override + { + DoScriptText(SAY_AGGRO, m_creature); + + if (m_pInstance) + { + m_pInstance->SetData(TYPE_TYRANNUS, IN_PROGRESS); + + // Set Rimefang in combat - ToDo: research if it has some wp movement during combat + if (Creature* pRimefang = m_pInstance->GetSingleCreatureFromStorage(NPC_RIMEFANG)) + pRimefang->AI()->AttackStart(pWho); + } + } + + void KilledUnit(Unit* /*pVictim*/) + { + DoScriptText(urand(0, 1) ? SAY_SLAY_1 : SAY_SLAY_2, m_creature); + } + + void JustDied(Unit* /*pKiller*/) override + { + DoScriptText(SAY_DEATH, m_creature); + + if (m_pInstance) + { + m_pInstance->SetData(TYPE_TYRANNUS, DONE); + + // Move Rimefang out of the area + if (Creature* pRimefang = m_pInstance->GetSingleCreatureFromStorage(NPC_RIMEFANG)) + { + pRimefang->AI()->EnterEvadeMode(); + pRimefang->SetWalk(false); + pRimefang->ForcedDespawn(25000); + pRimefang->GetMotionMaster()->MovePoint(0, afRimefangExitPos[0], afRimefangExitPos[1], afRimefangExitPos[2]); + } + + // Move the general near the boss - ToDo: move the other freed slaves as well + if (Creature* pGeneral = m_pInstance->GetSingleCreatureFromStorage(m_pInstance->GetPlayerTeam() == HORDE ? NPC_IRONSKULL_PART2 : NPC_VICTUS_PART2)) + { + float fX, fY, fZ; + pGeneral->SetWalk(false); + m_creature->GetContactPoint(pGeneral, fX, fY, fZ, INTERACTION_DISTANCE); + pGeneral->GetMotionMaster()->MovePoint(0, fX, fY, fZ); + } + } + } + + void JustReachedHome() override + { + if (m_pInstance) + m_pInstance->SetData(TYPE_TYRANNUS, FAIL); + } + + void UpdateAI(const uint32 uiDiff) override + { + if (!m_creature->SelectHostileTarget() || !m_creature->getVictim()) + return; + + if (m_uiForcefulSmashTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature->getVictim(), SPELL_FORCEFUL_SMASH) == CAST_OK) + m_uiForcefulSmashTimer = 50000; + } + else + m_uiForcefulSmashTimer -= uiDiff; + + if (m_uiOverlordsBrandTimer < uiDiff) + { + if (Unit* pTarget = m_creature->SelectAttackingTarget(ATTACKING_TARGET_RANDOM, 1)) + { + if (DoCastSpellIfCan(pTarget, SPELL_OVERLORDS_BRAND) == CAST_OK) + m_uiOverlordsBrandTimer = urand(10000, 13000); + } + } + else + m_uiOverlordsBrandTimer -= uiDiff; + + if (m_uiUnholyPowerTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature, SPELL_UNHOLY_POWER) == CAST_OK) + { + DoScriptText(SAY_SMASH, m_creature); + DoScriptText(EMOTE_SMASH, m_creature); + m_uiUnholyPowerTimer = 60000; + } + } + else + m_uiUnholyPowerTimer -= uiDiff; + + if (m_uiMarkOfRimefangTimer < uiDiff) + { + if (Unit* pTarget = m_creature->SelectAttackingTarget(ATTACKING_TARGET_RANDOM, 0)) + { + if (DoCastSpellIfCan(pTarget, SPELL_MARK_OF_RIMEFANG) == CAST_OK) + { + DoScriptText(SAY_MARK, m_creature); + if (m_pInstance) + { + if (Creature* pRimefang = m_pInstance->GetSingleCreatureFromStorage(NPC_RIMEFANG)) + { + pRimefang->InterruptNonMeleeSpells(true); + pRimefang->CastSpell(pTarget, SPELL_HOARFROST, false); + DoScriptText(EMOTE_RIMEFANG_ICEBOLT, pRimefang, pTarget); + } + } + m_uiMarkOfRimefangTimer = urand(20000, 25000); + } + } + } + else + m_uiMarkOfRimefangTimer -= uiDiff; + + DoMeleeAttackIfReady(); + } +}; + +CreatureAI* GetAI_boss_tyrannus(Creature* pCreature) +{ + return new boss_tyrannusAI(pCreature); +} + +/*###### +## boss_rimefang_pos +######*/ + +struct boss_rimefang_posAI : public ScriptedAI +{ + boss_rimefang_posAI(Creature* pCreature) : ScriptedAI(pCreature) + { + m_pInstance = (instance_pit_of_saron*)pCreature->GetInstanceData(); + SetCombatMovement(false); + m_bHasDoneIntro = false; + m_uiMountTimer = 1000; + Reset(); + } + + instance_pit_of_saron* m_pInstance; + uint32 m_uiMountTimer; + + uint32 m_uiIcyBlastTimer; + bool m_bHasDoneIntro; + + void Reset() override + { + m_uiIcyBlastTimer = 8000; + } + + void EnterEvadeMode() override + { + m_creature->RemoveAllAurasOnEvade(); + m_creature->DeleteThreatList(); + m_creature->CombatStop(true); + m_creature->LoadCreatureAddon(true); + + m_creature->SetLootRecipient(NULL); + + Reset(); + + // Don't handle movement. + } + + void AttackStart(Unit* pWho) override + { + // Don't attack unless Tyrannus is in combat or Ambush is completed + if (m_pInstance && (m_pInstance->GetData(TYPE_AMBUSH) != DONE || m_pInstance->GetData(TYPE_TYRANNUS) != IN_PROGRESS)) + return; + + ScriptedAI::AttackStart(pWho); + } + + void MoveInLineOfSight(Unit* pWho) override + { + if (!m_pInstance) + return; + + // Check if ambush is done + if (m_pInstance->GetData(TYPE_AMBUSH) != DONE) + return; + + // Start the intro when possible + if (!m_bHasDoneIntro && pWho->GetTypeId() == TYPEID_PLAYER && m_creature->IsWithinDistInMap(pWho, 85.0f) && m_creature->IsWithinLOSInMap(pWho)) + { + m_pInstance->SetData(TYPE_TYRANNUS, SPECIAL); + m_bHasDoneIntro = true; + return; + } + + // Check for out of range players - ToDo: confirm the distance + if (m_pInstance->GetData(TYPE_TYRANNUS) == IN_PROGRESS && pWho->GetTypeId() == TYPEID_PLAYER && !m_creature->IsWithinDistInMap(pWho, DEFAULT_VISIBILITY_INSTANCE)) + DoCastSpellIfCan(pWho, SPELL_KILLING_ICE); + } + + void UpdateAI(const uint32 uiDiff) override + { + if (!m_pInstance) + return; + + // He needs to be mounted manually, not by vehicle_accessories + if (m_uiMountTimer) + { + if (m_uiMountTimer <= uiDiff) + { + if (Creature* pTyrannus = m_pInstance->GetSingleCreatureFromStorage(NPC_TYRANNUS)) + pTyrannus->CastSpell(m_creature, SPELL_RIDE_VEHICLE_HARDCODED, true); + + m_uiMountTimer = 0; + } + else + m_uiMountTimer -= uiDiff; + } + + if (!m_creature->SelectHostileTarget() || !m_creature->getVictim()) + return; + + if (m_uiIcyBlastTimer < uiDiff) + { + if (Unit* pTarget = m_creature->SelectAttackingTarget(ATTACKING_TARGET_RANDOM, 0)) + { + if (DoCastSpellIfCan(pTarget, SPELL_ICY_BLAST) == CAST_OK) + { + m_creature->SummonCreature(NPC_ICY_BLAST, pTarget->GetPositionX(), pTarget->GetPositionY(), pTarget->GetPositionZ(), 0, TEMPSUMMON_TIMED_DESPAWN, 90000); + m_uiIcyBlastTimer = 8000; + } + } + } + else + m_uiIcyBlastTimer -= uiDiff; + } +}; + +CreatureAI* GetAI_boss_rimefang_pos(Creature* pCreature) +{ + return new boss_rimefang_posAI(pCreature); +} + +void AddSC_boss_tyrannus() +{ + Script* pNewScript; + + pNewScript = new Script; + pNewScript->Name = "boss_tyrannus"; + pNewScript->GetAI = &GetAI_boss_tyrannus; + pNewScript->RegisterSelf(); + + pNewScript = new Script; + pNewScript->Name = "boss_rimefang_pos"; + pNewScript->GetAI = &GetAI_boss_rimefang_pos; + pNewScript->RegisterSelf(); +} diff --git a/src/modules/SD2/scripts/northrend/icecrown_citadel/frozen_halls/pit_of_saron/instance_pit_of_saron.cpp b/src/modules/SD2/scripts/northrend/icecrown_citadel/frozen_halls/pit_of_saron/instance_pit_of_saron.cpp new file mode 100644 index 000000000..5b22def5f --- /dev/null +++ b/src/modules/SD2/scripts/northrend/icecrown_citadel/frozen_halls/pit_of_saron/instance_pit_of_saron.cpp @@ -0,0 +1,651 @@ +/* Copyright (C) 2006 - 2013 ScriptDev2 +* This program is free software; you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation; either version 2 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along 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_pit_of_saron +SD%Complete: 80% +SDComment: +SDCategory: Pit of Saron +EndScriptData */ + +#include "precompiled.h" +#include "pit_of_saron.h" + +enum +{ + // Intro + SAY_TYRANNUS_INTRO_1 = -1658001, + SAY_JAINA_INTRO_1 = -1658002, + SAY_SYLVANAS_INTRO_1 = -1658003, + SAY_TYRANNUS_INTRO_2 = -1658004, + SAY_TYRANNUS_INTRO_3 = -1658005, + SAY_JAINA_INTRO_2 = -1658006, + SAY_SYLVANAS_INTRO_2 = -1658007, + SAY_TYRANNUS_INTRO_4 = -1658008, + SAY_JAINA_INTRO_3 = -1658009, + SAY_JAINA_INTRO_4 = -1658010, + SAY_SYLVANAS_INTRO_3 = -1658011, + SAY_JAINA_INTRO_5 = -1658012, + SAY_SYLVANAS_INTRO_4 = -1658013, + + // Intro spells + SPELL_NECROMATIC_POWER = 69347, + SPELL_FEIGN_DEATH = 28728, + SPELL_RAISE_DEAD = 69350, + + // Garfrost outro + SAY_TYRANNUS_GARFROST = -1658020, + SAY_GENERAL_GARFROST = -1658021, + + // Ick and Krick outro + SAY_JAINA_KRICK_1 = -1658036, + SAY_SYLVANAS_KRICK_1 = -1658037, + SAY_OUTRO_2 = -1658038, + SAY_JAINA_KRICK_2 = -1658039, + SAY_SYLVANAS_KRICK_2 = -1658040, + SAY_OUTRO_3 = -1658041, + SAY_TYRANNUS_KRICK_1 = -1658042, + SAY_OUTRO_4 = -1658043, + SAY_TYRANNUS_KRICK_2 = -1658044, + SAY_JAINA_KRICK_3 = -1658045, + SAY_SYLVANAS_KRICK_3 = -1658046, + + // Ick and Krick outro spells + SPELL_STRANGULATING = 69413, + SPELL_KRICK_KILL_CREDIT = 71308, + SPELL_SUICIDE = 7, + + // Ambush and Gauntlet + SAY_TYRANNUS_AMBUSH_1 = -1658047, + SAY_TYRANNUS_AMBUSH_2 = -1658048, + SAY_GAUNTLET = -1658049, + + // Gauntlet spells + SPELL_ICICLE_SUMMON = 69424, + SPELL_ACHIEVEMENT_CHECK = 72845, + + // Tyrannus intro + SAY_PREFIGHT_1 = -1658050, + SAY_VICTUS_TRASH = -1658051, + SAY_IRONSKULL_TRASH = -1658068, + SAY_PREFIGHT_2 = -1658052, + + SPELL_EJECT_ALL_PASSENGERS = 50630, + // SPELL_CSA_DUMMY_EFFECT_1 = 56685, // What is this? + + // Sindragosa outro + SAY_VICTUS_OUTRO_1 = -1658061, + SAY_IRONSKULL_OUTRO_2 = -1658069, + SAY_GENERAL_OUTRO_2 = -1658062, + SAY_JAINA_OUTRO_1 = -1658063, + SAY_SYLVANAS_OUTRO_1 = -1658064, + SAY_JAINA_OUTRO_2 = -1658065, + SAY_JAINA_OUTRO_3 = -1658066, + SAY_SYLVANAS_OUTRO_2 = -1658067, + + SPELL_FROST_BOMB = 70521, + SPELL_FROZEN_AFTERMATH = 70518, + SPELL_ARCANE_FORM = 70573, + SPELL_CALL_OF_SYLVANAS_1 = 70636, // triggers 70639 + SPELL_CALL_OF_SYLVANAS_2 = 70638, + // SPELL_CALL_OF_SYLVANAS_3 = 70642, + SPELL_JAINAS_CALL_1 = 70527, // triggers 70525 + SPELL_JAINAS_CALL_2 = 70623, +}; + +static const DialogueEntryTwoSide aPoSDialogues[] = +{ + // Instance intro + {NPC_TYRANNUS_INTRO, 0, 0, 0, 4000}, + {SAY_TYRANNUS_INTRO_1, NPC_TYRANNUS_INTRO, 0, 0, 6000}, + {SAY_TYRANNUS_INTRO_2, NPC_TYRANNUS_INTRO, 0, 0, 12000}, + {SAY_JAINA_INTRO_1, NPC_JAINA_PART1, SAY_SYLVANAS_INTRO_1, NPC_SYLVANAS_PART1, 5000}, // ToDo: move the soldiers to attack position + {SAY_TYRANNUS_INTRO_3, NPC_TYRANNUS_INTRO, 0, 0, 5000}, + {SPELL_NECROMATIC_POWER, 0, 0, 0, 3000}, + {SAY_JAINA_INTRO_2, NPC_JAINA_PART1, SAY_SYLVANAS_INTRO_2, NPC_SYLVANAS_PART1, 4000}, + {SAY_TYRANNUS_INTRO_4, NPC_TYRANNUS_INTRO, 0, 0, 4000}, // ToDo: send the solderis back to fight as zombies + {SAY_JAINA_INTRO_3, NPC_JAINA_PART1, 0, 0, 6000}, + {SAY_JAINA_INTRO_4, NPC_JAINA_PART1, SAY_SYLVANAS_INTRO_3, NPC_SYLVANAS_PART1, 5000}, + {SAY_JAINA_INTRO_5, NPC_JAINA_PART1, SAY_SYLVANAS_INTRO_4, NPC_SYLVANAS_PART1, 0}, + + // Garfrost outro + {NPC_GARFROST, 0, 0, 0, 4000}, // ToDo: move the freed slaves to position + {SAY_GENERAL_GARFROST, NPC_VICTUS_PART1, SAY_GENERAL_GARFROST, NPC_IRONSKULL_PART1, 2000}, + {SAY_TYRANNUS_GARFROST, NPC_TYRANNUS_INTRO, 0, 0, 0}, + + // Ick and Krick outro + {SAY_JAINA_KRICK_1, NPC_JAINA_PART1, SAY_SYLVANAS_KRICK_1, NPC_SYLVANAS_PART1, 6000}, + {SAY_OUTRO_2, NPC_KRICK, 0, 0, 16000}, + {SAY_JAINA_KRICK_2, NPC_JAINA_PART1, SAY_SYLVANAS_KRICK_2, NPC_SYLVANAS_PART1, 7000}, + {SAY_OUTRO_3, NPC_KRICK, 0, 0, 7000}, + {SAY_TYRANNUS_KRICK_1, NPC_TYRANNUS_INTRO, 0, 0, 3000}, + {SPELL_STRANGULATING, 0, 0, 0, 3000}, + {SAY_OUTRO_4, NPC_KRICK, 0, 0, 3000}, + {SAY_TYRANNUS_KRICK_2, NPC_TYRANNUS_INTRO, 0, 0, 11000}, + {SAY_JAINA_KRICK_3, NPC_JAINA_PART1, SAY_SYLVANAS_KRICK_3, NPC_SYLVANAS_PART1, 0}, + + // Tyrannus intro + {NPC_TYRANNUS, 0, 0, 0, 10000}, // ToDo: move the freed slaves to position + {SAY_PREFIGHT_1, NPC_TYRANNUS, 0, 0, 13000}, + {SAY_VICTUS_TRASH, NPC_VICTUS_PART2, SAY_IRONSKULL_TRASH, NPC_IRONSKULL_PART2, 9000}, + {SAY_PREFIGHT_2, NPC_TYRANNUS, 0, 0, 10000}, + {NPC_RIMEFANG, 0, 0, 0, 0}, + + // Tyrannus outro + {NPC_SINDRAGOSA, 0, 0, 0, 30000}, + {SAY_VICTUS_OUTRO_1, NPC_VICTUS_PART2, SAY_IRONSKULL_OUTRO_2, NPC_IRONSKULL_PART2, 17000}, + {SAY_GENERAL_OUTRO_2, NPC_VICTUS_PART2, SAY_GENERAL_OUTRO_2, NPC_IRONSKULL_PART2, 14000}, + {SAY_JAINA_OUTRO_1, NPC_JAINA_PART2, SAY_SYLVANAS_OUTRO_1, NPC_SYLVANAS_PART2, 1000}, + {SPELL_FROST_BOMB, 0, 0, 0, 7000}, + {NPC_JAINA_PART2, 0, 0, 0, 8000}, + {SAY_JAINA_OUTRO_2, NPC_JAINA_PART2, SAY_SYLVANAS_OUTRO_2, NPC_SYLVANAS_PART2, 15000}, + {SAY_JAINA_OUTRO_3, NPC_JAINA_PART2, 0, 0, 0}, + {0, 0, 0}, +}; + +instance_pit_of_saron::instance_pit_of_saron(Map* pMap) : ScriptedInstance(pMap), DialogueHelper(aPoSDialogues), + m_uiAmbushAggroCount(0), + m_uiTeam(TEAM_NONE), + m_uiIciclesTimer(0) +{ + Initialize(); +} + +void instance_pit_of_saron::Initialize() +{ + memset(&m_auiEncounter, 0, sizeof(m_auiEncounter)); + InitializeDialogueHelper(this); + + for (uint8 i = 0; i < MAX_SPECIAL_ACHIEV_CRITS; ++i) + m_abAchievCriteria[i] = false; +} + +void instance_pit_of_saron::OnPlayerEnter(Player* pPlayer) +{ + if (!m_uiTeam) // very first player to enter + { + m_uiTeam = pPlayer->GetTeam(); + SetDialogueSide(m_uiTeam == ALLIANCE); + ProcessIntroEventNpcs(pPlayer); + } +} + +void instance_pit_of_saron::ProcessIntroEventNpcs(Player* pPlayer) +{ + if (!pPlayer) + return; + + // Not if the bosses are already killed + if (GetData(TYPE_GARFROST) == DONE || GetData(TYPE_KRICK) == DONE) + return; + + StartNextDialogueText(NPC_TYRANNUS_INTRO); + + // Spawn Begin Mobs + for (uint8 i = 0; i < countof(aEventBeginLocations); ++i) + { + // ToDo: maybe despawn the intro npcs when the other events occur + if (Creature* pSummon = pPlayer->SummonCreature(m_uiTeam == HORDE ? aEventBeginLocations[i].uiEntryHorde : aEventBeginLocations[i].uiEntryAlliance, + aEventBeginLocations[i].fX, aEventBeginLocations[i].fY, aEventBeginLocations[i].fZ, aEventBeginLocations[i].fO, TEMPSUMMON_TIMED_DESPAWN, 24 * HOUR * IN_MILLISECONDS)) + { + pSummon->SetWalk(false); + pSummon->GetMotionMaster()->MovePoint(0, aEventBeginLocations[i].fMoveX, aEventBeginLocations[i].fMoveY, aEventBeginLocations[i].fMoveZ); + } + } +} + +void instance_pit_of_saron::OnCreatureCreate(Creature* pCreature) +{ + switch (pCreature->GetEntry()) + { + case NPC_TYRANNUS_INTRO: + case NPC_JAINA_PART1: + case NPC_SYLVANAS_PART1: + case NPC_GARFROST: + case NPC_KRICK: + case NPC_ICK: + case NPC_TYRANNUS: + case NPC_RIMEFANG: + case NPC_IRONSKULL_PART1: + case NPC_VICTUS_PART1: + case NPC_IRONSKULL_PART2: + case NPC_VICTUS_PART2: + case NPC_JAINA_PART2: + case NPC_SYLVANAS_PART2: + case NPC_SINDRAGOSA: + m_mNpcEntryGuidStore[pCreature->GetEntry()] = pCreature->GetObjectGuid(); + break; + case NPC_STALKER: + m_lTunnelStalkersGuidList.push_back(pCreature->GetObjectGuid()); + break; + case NPC_YMIRJAR_DEATHBRINGER: + case NPC_YMIRJAR_WRATHBRINGER: + case NPC_YMIRJAR_FLAMEBEARER: + case NPC_FALLEN_WARRIOR: + case NPC_COLDWRAITH: + // Sort only the temporary summons + if (pCreature->IsTemporarySummon()) + m_lAmbushNpcsGuidList.push_back(pCreature->GetObjectGuid()); + break; + case NPC_GENERAL_BUNNY: + if (pCreature->GetPositionY() < 130.0f) + { + if (pCreature->GetOrientation() != 0) + m_lArcaneShieldBunniesGuidList.push_back(pCreature->GetObjectGuid()); + else + m_lFrozenAftermathBunniesGuidList.push_back(pCreature->GetObjectGuid()); + } + break; + } +} + +void instance_pit_of_saron::OnObjectCreate(GameObject* pGo) +{ + switch (pGo->GetEntry()) + { + case GO_ICEWALL: + if (m_auiEncounter[TYPE_GARFROST] == DONE && m_auiEncounter[TYPE_KRICK] == DONE) + pGo->SetGoState(GO_STATE_ACTIVE); + break; + case GO_HALLS_OF_REFLECT_PORT: + break; + + default: + return; + } + m_mGoEntryGuidStore[pGo->GetEntry()] = pGo->GetObjectGuid(); +} + +void instance_pit_of_saron::SetData(uint32 uiType, uint32 uiData) +{ + switch (uiType) + { + case TYPE_GARFROST: + if (uiData == DONE && m_auiEncounter[TYPE_KRICK] == DONE) + DoUseDoorOrButton(GO_ICEWALL); + if (uiData == IN_PROGRESS) + SetSpecialAchievementCriteria(TYPE_ACHIEV_DOESNT_GO_ELEVEN, true); + else if (uiData == DONE) + StartNextDialogueText(NPC_GARFROST); + m_auiEncounter[uiType] = uiData; + break; + case TYPE_KRICK: + if (uiData == DONE && m_auiEncounter[TYPE_GARFROST] == DONE) + DoUseDoorOrButton(GO_ICEWALL); + if (uiData == SPECIAL) + { + // Used just to start the epilogue + StartNextDialogueText(SAY_JAINA_KRICK_1); + return; + } + m_auiEncounter[uiType] = uiData; + break; + case TYPE_TYRANNUS: + if (uiData == DONE) + StartNextDialogueText(NPC_SINDRAGOSA); + else if (uiData == SPECIAL) + { + // Used just to start the intro + StartNextDialogueText(NPC_TYRANNUS); + return; + } + m_auiEncounter[uiType] = uiData; + break; + case TYPE_AMBUSH: + if (uiData == DONE) + { + // Complete tunnel achievement + if (Creature* pTyrannus = GetSingleCreatureFromStorage(NPC_TYRANNUS)) + pTyrannus->CastSpell(pTyrannus, SPELL_ACHIEVEMENT_CHECK, true); + + m_uiIciclesTimer = 0; + } + m_auiEncounter[uiType] = uiData; + break; + default: + return; + } + + if (uiData == DONE) + { + OUT_SAVE_INST_DATA; + + std::ostringstream saveStream; + saveStream << m_auiEncounter[0] << " " << m_auiEncounter[1] << " " << m_auiEncounter[2] << " " << m_auiEncounter[3]; + + m_strInstData = saveStream.str(); + + SaveToDB(); + OUT_SAVE_INST_DATA_COMPLETE; + } +} + +void instance_pit_of_saron::Load(const char* chrIn) +{ + if (!chrIn) + { + 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 < MAX_ENCOUNTER; ++i) + { + if (m_auiEncounter[i] == IN_PROGRESS) + m_auiEncounter[i] = NOT_STARTED; + } + + OUT_LOAD_INST_DATA_COMPLETE; +} + +uint32 instance_pit_of_saron::GetData(uint32 uiType) const +{ + if (uiType < MAX_ENCOUNTER) + return m_auiEncounter[uiType]; + + return 0; +} + +void instance_pit_of_saron::OnCreatureEnterCombat(Creature* pCreature) +{ + if (pCreature->GetEntry() == NPC_YMIRJAR_DEATHBRINGER) + { + ++m_uiAmbushAggroCount; + + // Summon the rest of the mobs at the 2nd ambush + if (m_uiAmbushAggroCount == 2) + { + Creature* pTyrannus = GetSingleCreatureFromStorage(NPC_TYRANNUS_INTRO); + if (!pTyrannus) + return; + + DoScriptText(SAY_TYRANNUS_AMBUSH_2, pTyrannus); + pTyrannus->SetWalk(false); + pTyrannus->GetMotionMaster()->MovePoint(0, afTyrannusMovePos[2][0], afTyrannusMovePos[2][1], afTyrannusMovePos[2][2]); + + // Spawn Mobs + for (uint8 i = 0; i < countof(aEventSecondAmbushLocations); ++i) + { + if (Creature* pSummon = pTyrannus->SummonCreature(aEventSecondAmbushLocations[i].uiEntryHorde, aEventSecondAmbushLocations[i].fX, aEventSecondAmbushLocations[i].fY, + aEventSecondAmbushLocations[i].fZ, aEventSecondAmbushLocations[i].fO, TEMPSUMMON_DEAD_DESPAWN, 0)) + { + pSummon->SetWalk(false); + pSummon->GetMotionMaster()->MovePoint(1, aEventSecondAmbushLocations[i].fMoveX, aEventSecondAmbushLocations[i].fMoveY, aEventSecondAmbushLocations[i].fMoveZ); + } + } + } + } +} + +void instance_pit_of_saron::OnCreatureDeath(Creature* pCreature) +{ + switch (pCreature->GetEntry()) + { + case NPC_YMIRJAR_DEATHBRINGER: + case NPC_YMIRJAR_WRATHBRINGER: + case NPC_YMIRJAR_FLAMEBEARER: + case NPC_FALLEN_WARRIOR: + case NPC_COLDWRAITH: + // Check for tunnel event end - these mobs are not summoned + if (pCreature->IsTemporarySummon()) + { + m_lAmbushNpcsGuidList.remove(pCreature->GetObjectGuid()); + + // If empty start tunnel event + if (m_lAmbushNpcsGuidList.empty()) + { + Creature* pTyrannus = GetSingleCreatureFromStorage(NPC_TYRANNUS_INTRO); + if (!pTyrannus) + return; + + DoScriptText(SAY_GAUNTLET, pTyrannus); + pTyrannus->SetWalk(false); + pTyrannus->GetMotionMaster()->MovePoint(0, afTyrannusMovePos[0][0], afTyrannusMovePos[0][1], afTyrannusMovePos[0][2]); + pTyrannus->ForcedDespawn(20000); + + m_uiIciclesTimer = urand(3000, 5000); + SetSpecialAchievementCriteria(TYPE_ACHIEV_DONT_LOOK_UP, true); + } + } + break; + } +} + +void instance_pit_of_saron::SetSpecialAchievementCriteria(uint32 uiType, bool bIsMet) +{ + if (uiType < MAX_SPECIAL_ACHIEV_CRITS) + m_abAchievCriteria[uiType] = bIsMet; +} + +bool instance_pit_of_saron::CheckAchievementCriteriaMeet(uint32 uiCriteriaId, Player const* /*pSource*/, Unit const* /*pTarget*/, uint32 /*uiMiscValue1 = 0*/) const +{ + switch (uiCriteriaId) + { + case ACHIEV_CRIT_DOESNT_GO_ELEVEN: + return m_abAchievCriteria[TYPE_ACHIEV_DOESNT_GO_ELEVEN]; + case ACHIEV_CRIT_DONT_LOOK_UP: + return m_abAchievCriteria[TYPE_ACHIEV_DONT_LOOK_UP]; + + default: + return false; + } +} + +void instance_pit_of_saron::JustDidDialogueStep(int32 iEntry) +{ + switch (iEntry) + { + case SPELL_NECROMATIC_POWER: + // Transfor all soldiers into undead + if (Creature* pTyrannus = GetSingleCreatureFromStorage(NPC_TYRANNUS_INTRO)) + pTyrannus->CastSpell(pTyrannus, SPELL_NECROMATIC_POWER, true); + break; + case SAY_OUTRO_3: + // Move Tyrannus into position + if (Creature* pTyrannus = GetSingleCreatureFromStorage(NPC_TYRANNUS_INTRO)) + { + pTyrannus->SetWalk(false); + pTyrannus->GetMotionMaster()->MovePoint(0, afTyrannusMovePos[1][0], afTyrannusMovePos[1][1], afTyrannusMovePos[1][2]); + } + break; + case SPELL_STRANGULATING: + // Strangulate Krick + if (Creature* pKrick = GetSingleCreatureFromStorage(NPC_KRICK)) + { + pKrick->CastSpell(pKrick, SPELL_STRANGULATING, true); + pKrick->SetLevitate(true); + pKrick->GetMotionMaster()->MovePoint(0, pKrick->GetPositionX(), pKrick->GetPositionY(), pKrick->GetPositionZ() + 5.0f); + } + break; + case SAY_TYRANNUS_KRICK_2: + // Kill Krick + if (Creature* pKrick = GetSingleCreatureFromStorage(NPC_KRICK)) + { + pKrick->CastSpell(pKrick, SPELL_KRICK_KILL_CREDIT, true); + pKrick->CastSpell(pKrick, SPELL_SUICIDE, true); + } + break; + case SAY_JAINA_INTRO_3: + case SAY_JAINA_KRICK_3: + // Move Tyrannus to a safe position + if (Creature* pTyrannus = GetSingleCreatureFromStorage(NPC_TYRANNUS_INTRO)) + pTyrannus->GetMotionMaster()->MovePoint(0, afTyrannusMovePos[0][0], afTyrannusMovePos[0][1], afTyrannusMovePos[0][2]); + break; + case NPC_TYRANNUS: + { + Creature* pTyrannus = GetSingleCreatureFromStorage(NPC_TYRANNUS); + if (!pTyrannus) + return; + + // Spawn tunnel end event mobs + for (uint8 i = 0; i < countof(aEventTunnelEndLocations); ++i) + { + if (Creature* pSummon = pTyrannus->SummonCreature(m_uiTeam == HORDE ? aEventTunnelEndLocations[i].uiEntryHorde : aEventTunnelEndLocations[i].uiEntryAlliance, + aEventTunnelEndLocations[i].fX, aEventTunnelEndLocations[i].fY, aEventTunnelEndLocations[i].fZ, aEventTunnelEndLocations[i].fO, TEMPSUMMON_DEAD_DESPAWN, 0)) + { + pSummon->SetWalk(false); + pSummon->GetMotionMaster()->MovePoint(0, aEventTunnelEndLocations[i].fMoveX, aEventTunnelEndLocations[i].fMoveY, aEventTunnelEndLocations[i].fMoveZ); + } + } + break; + } + case NPC_RIMEFANG: + // Eject Tyrannus and prepare for combat + if (Creature* pRimefang = GetSingleCreatureFromStorage(NPC_RIMEFANG)) + { + pRimefang->CastSpell(pRimefang, SPELL_EJECT_ALL_PASSENGERS, true); + pRimefang->SetWalk(false); + pRimefang->GetMotionMaster()->MovePoint(0, afTyrannusMovePos[3][0], afTyrannusMovePos[3][1], afTyrannusMovePos[3][2]); + } + if (Creature* pTyrannus = GetSingleCreatureFromStorage(NPC_TYRANNUS)) + pTyrannus->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE); + break; + case SAY_VICTUS_OUTRO_1: + { + Player* pPlayer = GetPlayerInMap(); + if (!pPlayer) + return; + + // Spawn Sindragosa + if (Creature* pSummon = pPlayer->SummonCreature(aEventOutroLocations[0].uiEntryHorde, aEventOutroLocations[0].fX, aEventOutroLocations[0].fY, + aEventOutroLocations[0].fZ, aEventOutroLocations[0].fO, TEMPSUMMON_TIMED_DESPAWN, 2 * MINUTE * IN_MILLISECONDS)) + { + pSummon->SetWalk(false); + pSummon->GetMotionMaster()->MovePoint(0, aEventOutroLocations[0].fMoveX, aEventOutroLocations[0].fMoveY, aEventOutroLocations[0].fMoveZ); + } + // Spawn Jaina or Sylvanas + if (Creature* pSummon = pPlayer->SummonCreature(m_uiTeam == HORDE ? aEventOutroLocations[1].uiEntryHorde : aEventOutroLocations[1].uiEntryAlliance, + aEventOutroLocations[1].fX, aEventOutroLocations[1].fY, aEventOutroLocations[1].fZ, aEventOutroLocations[1].fO, TEMPSUMMON_TIMED_DESPAWN, 24 * HOUR * IN_MILLISECONDS)) + { + pSummon->SetWalk(false); + pSummon->GetMotionMaster()->MovePoint(0, aEventOutroLocations[1].fMoveX, aEventOutroLocations[1].fMoveY, aEventOutroLocations[1].fMoveZ); + } + break; + } + case SAY_JAINA_OUTRO_1: + // Visual effect + for (GuidList::const_iterator itr = m_lArcaneShieldBunniesGuidList.begin(); itr != m_lArcaneShieldBunniesGuidList.end(); ++itr) + { + if (Creature* pBunny = instance->GetCreature(*itr)) + pBunny->CastSpell(pBunny, SPELL_ARCANE_FORM, true); + } + // Teleport players + if (Creature* pTemp = GetSingleCreatureFromStorage(m_uiTeam == HORDE ? NPC_SYLVANAS_PART2 : NPC_JAINA_PART2)) + { + pTemp->CastSpell(pTemp, m_uiTeam == HORDE ? SPELL_CALL_OF_SYLVANAS_2 : SPELL_JAINAS_CALL_2, true); + pTemp->CastSpell(pTemp, m_uiTeam == HORDE ? SPELL_CALL_OF_SYLVANAS_2 : SPELL_JAINAS_CALL_2, true); + } + break; + case SPELL_FROST_BOMB: + // Frost bomb on the platform + if (Creature* pSindragosa = GetSingleCreatureFromStorage(NPC_SINDRAGOSA)) + pSindragosa->CastSpell(pSindragosa, SPELL_FROST_BOMB, true); + // Visual effect + for (GuidList::const_iterator itr = m_lFrozenAftermathBunniesGuidList.begin(); itr != m_lFrozenAftermathBunniesGuidList.end(); ++itr) + { + if (Creature* pBunny = instance->GetCreature(*itr)) + pBunny->CastSpell(pBunny, SPELL_FROZEN_AFTERMATH, true); + } + break; + case NPC_JAINA_PART2: + // Visual effect remove + for (GuidList::const_iterator itr = m_lArcaneShieldBunniesGuidList.begin(); itr != m_lArcaneShieldBunniesGuidList.end(); ++itr) + { + if (Creature* pBunny = instance->GetCreature(*itr)) + pBunny->RemoveAurasDueToSpell(SPELL_ARCANE_FORM); + } + // Sindragosa exit + if (Creature* pSindragosa = GetSingleCreatureFromStorage(NPC_SINDRAGOSA)) + pSindragosa->GetMotionMaster()->MovePoint(0, 759.148f, 199.955f, 720.857f); + // Jaina / Sylvanas starts moving (should use wp) + if (Creature* pTemp = GetSingleCreatureFromStorage(m_uiTeam == HORDE ? NPC_SYLVANAS_PART2 : NPC_JAINA_PART2)) + { + pTemp->SetWalk(true); + pTemp->GetMotionMaster()->MovePoint(0, 1057.76f, 111.927f, 628.4123f); + } + break; + case SAY_JAINA_OUTRO_2: + if (Creature* pTemp = GetSingleCreatureFromStorage(m_uiTeam == HORDE ? NPC_SYLVANAS_PART2 : NPC_JAINA_PART2)) + pTemp->SetFlag(UNIT_NPC_FLAGS, UNIT_NPC_FLAG_QUESTGIVER); + + // ToDo: Jaina / Sylvanas should have some waypoint movement here and the door should be opened only when they get in front of it. + DoUseDoorOrButton(GO_HALLS_OF_REFLECT_PORT); + break; + } +} + +void instance_pit_of_saron::DoStartAmbushEvent() +{ + Creature* pTyrannus = GetSingleCreatureFromStorage(NPC_TYRANNUS_INTRO); + if (!pTyrannus) + return; + + DoScriptText(SAY_TYRANNUS_AMBUSH_1, pTyrannus); + + // Spawn Mobs + for (uint8 i = 0; i < countof(aEventFirstAmbushLocations); ++i) + { + if (Creature* pSummon = pTyrannus->SummonCreature(aEventFirstAmbushLocations[i].uiEntryHorde, aEventFirstAmbushLocations[i].fX, aEventFirstAmbushLocations[i].fY, + aEventFirstAmbushLocations[i].fZ, aEventFirstAmbushLocations[i].fO, TEMPSUMMON_DEAD_DESPAWN, 0)) + { + pSummon->SetWalk(false); + pSummon->GetMotionMaster()->MovePoint(1, aEventFirstAmbushLocations[i].fMoveX, aEventFirstAmbushLocations[i].fMoveY, aEventFirstAmbushLocations[i].fMoveZ); + } + } +} + +void instance_pit_of_saron::Update(uint32 uiDiff) +{ + DialogueUpdate(uiDiff); + + if (m_uiIciclesTimer) + { + if (m_uiIciclesTimer <= uiDiff) + { + for (GuidList::const_iterator itr = m_lTunnelStalkersGuidList.begin(); itr != m_lTunnelStalkersGuidList.end(); ++itr) + { + // Only 5% of the stalkers will actually spawn an icicle + if (roll_chance_i(95)) + continue; + + if (Creature* pStalker = instance->GetCreature(*itr)) + pStalker->CastSpell(pStalker, SPELL_ICICLE_SUMMON, true); + } + m_uiIciclesTimer = urand(3000, 5000); + } + else + m_uiIciclesTimer -= uiDiff; + } +} + +InstanceData* GetInstanceData_instance_pit_of_saron(Map* pMap) +{ + return new instance_pit_of_saron(pMap); +} + +void AddSC_instance_pit_of_saron() +{ + Script* pNewScript; + + pNewScript = new Script; + pNewScript->Name = "instance_pit_of_saron"; + pNewScript->GetInstanceData = &GetInstanceData_instance_pit_of_saron; + pNewScript->RegisterSelf(); +} diff --git a/src/modules/SD2/scripts/northrend/icecrown_citadel/frozen_halls/pit_of_saron/pit_of_saron.cpp b/src/modules/SD2/scripts/northrend/icecrown_citadel/frozen_halls/pit_of_saron/pit_of_saron.cpp new file mode 100644 index 000000000..5ee6a4c99 --- /dev/null +++ b/src/modules/SD2/scripts/northrend/icecrown_citadel/frozen_halls/pit_of_saron/pit_of_saron.cpp @@ -0,0 +1,203 @@ +/* Copyright (C) 2006 - 2013 ScriptDev2 + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +/* ScriptData +SDName: pit_of_saron +SD%Complete: 100 +SDComment: +SDCategory: Pit of Saron +EndScriptData */ + +/* ContentData +EndContentData */ + +#include "precompiled.h" +#include "pit_of_saron.h" + +enum +{ + // Ambush event + SPELL_EMPOWERED_SHADOW_BOLT = 69528, + SPELL_SUMMON_UNDEAD = 69516, + + // Icicles + SPELL_ICICLE = 69426, + SPELL_ICICLE_DUMMY = 69428, + SPELL_ICE_SHARDS_H = 70827, // used to check the tunnel achievement +}; + +/*###### +## npc_ymirjar_deathbringer +######*/ + +struct npc_ymirjar_deathbringerAI : public ScriptedAI +{ + npc_ymirjar_deathbringerAI(Creature* pCreature) : ScriptedAI(pCreature) { Reset(); } + + uint32 m_uiShadowBoltTimer; + + void Reset() override + { + m_uiShadowBoltTimer = urand(1000, 3000); + } + + void MovementInform(uint32 uiMotionType, uint32 uiPointId) override + { + if (uiMotionType != POINT_MOTION_TYPE || !uiPointId) + return; + + DoCastSpellIfCan(m_creature, SPELL_SUMMON_UNDEAD); + } + + void UpdateAI(const uint32 uiDiff) override + { + if (!m_creature->SelectHostileTarget() || !m_creature->getVictim()) + return; + + if (m_uiShadowBoltTimer < uiDiff) + { + if (Unit* pTarget = m_creature->SelectAttackingTarget(ATTACKING_TARGET_RANDOM, 0)) + { + if (DoCastSpellIfCan(pTarget, SPELL_EMPOWERED_SHADOW_BOLT) == CAST_OK) + m_uiShadowBoltTimer = urand(2000, 3000); + } + } + else + m_uiShadowBoltTimer -= uiDiff; + + DoMeleeAttackIfReady(); + } +}; + +CreatureAI* GetAI_npc_ymirjar_deathbringer(Creature* pCreature) +{ + return new npc_ymirjar_deathbringerAI(pCreature); +} + +bool EffectDummyCreature_spell_summon_undead(Unit* /*pCaster*/, uint32 uiSpellId, SpellEffectIndex uiEffIndex, Creature* pCreatureTarget, ObjectGuid /*originalCasterGuid*/) +{ + // always check spellid and effectindex + if (uiSpellId == SPELL_SUMMON_UNDEAD && uiEffIndex == EFFECT_INDEX_0) + { + if (pCreatureTarget->GetEntry() != NPC_YMIRJAR_DEATHBRINGER) + return true; + + float fX, fY, fZ; + for (uint8 i = 0; i < 4; ++i) + { + pCreatureTarget->GetNearPoint(pCreatureTarget, fX, fY, fZ, 0, frand(8.0f, 12.0f), M_PI_F * 0.5f * i); + pCreatureTarget->SummonCreature(i % 2 ? NPC_YMIRJAR_WRATHBRINGER : NPC_YMIRJAR_FLAMEBEARER, fX, fY, fZ, 3.75f, TEMPSUMMON_DEAD_DESPAWN, 0); + } + + // always return true when we are handling this spell and effect + return true; + } + + return false; +} + +/*###### +## npc_collapsing_icicle +######*/ + +struct npc_collapsing_icicleAI : public ScriptedAI +{ + npc_collapsing_icicleAI(Creature* pCreature) : ScriptedAI(pCreature) + { + m_pInstance = (instance_pit_of_saron*)pCreature->GetInstanceData(); + Reset(); + } + + instance_pit_of_saron* m_pInstance; + + void Reset() override + { + DoCastSpellIfCan(m_creature, SPELL_ICICLE_DUMMY, CAST_TRIGGERED); + DoCastSpellIfCan(m_creature, SPELL_ICICLE, CAST_TRIGGERED); + } + + void SpellHitTarget(Unit* pTarget, const SpellEntry* pSpell) override + { + // Mark the achiev failed + if (pSpell->Id == SPELL_ICE_SHARDS_H && pTarget->GetTypeId() == TYPEID_PLAYER && m_pInstance) + m_pInstance->SetSpecialAchievementCriteria(TYPE_ACHIEV_DONT_LOOK_UP, false); + } + + void AttackStart(Unit* /*pWho*/) override { } + void MoveInLineOfSight(Unit* /*pWho*/) override { } + void UpdateAI(const uint32 /*uiDiff*/) override { } +}; + +CreatureAI* GetAI_npc_collapsing_icicle(Creature* pCreature) +{ + return new npc_collapsing_icicleAI(pCreature); +} + +/*###### +## at_pit_of_saron +######*/ + +bool AreaTrigger_at_pit_of_saron(Player* pPlayer, AreaTriggerEntry const* pAt) +{ + if (pPlayer->isGameMaster() || !pPlayer->IsAlive()) + return false; + + instance_pit_of_saron* pInstance = (instance_pit_of_saron*)pPlayer->GetInstanceData(); + if (!pInstance) + return false; + + if (pAt->id == AREATRIGGER_ID_TUNNEL_START) + { + if (pInstance->GetData(TYPE_GARFROST) != DONE || pInstance->GetData(TYPE_KRICK) != DONE || + pInstance->GetData(TYPE_AMBUSH) != NOT_STARTED) + return false; + + pInstance->DoStartAmbushEvent(); + pInstance->SetData(TYPE_AMBUSH, IN_PROGRESS); + return true; + } + else if (pAt->id == AREATRIGGER_ID_TUNNEL_END) + { + if (pInstance->GetData(TYPE_AMBUSH) != IN_PROGRESS) + return false; + + pInstance->SetData(TYPE_AMBUSH, DONE); + return true; + } + + return false; +} + +void AddSC_pit_of_saron() +{ + Script* pNewScript; + + pNewScript = new Script; + pNewScript->Name = "npc_ymirjar_deathbringer"; + pNewScript->GetAI = &GetAI_npc_ymirjar_deathbringer; + pNewScript->pEffectDummyNPC = &EffectDummyCreature_spell_summon_undead; + pNewScript->RegisterSelf(); + + pNewScript = new Script; + pNewScript->Name = "npc_collapsing_icicle"; + pNewScript->GetAI = &GetAI_npc_collapsing_icicle; + pNewScript->RegisterSelf(); + + pNewScript = new Script; + pNewScript->Name = "at_pit_of_saron"; + pNewScript->pAreaTrigger = &AreaTrigger_at_pit_of_saron; + pNewScript->RegisterSelf(); +} diff --git a/src/modules/SD2/scripts/northrend/icecrown_citadel/frozen_halls/pit_of_saron/pit_of_saron.h b/src/modules/SD2/scripts/northrend/icecrown_citadel/frozen_halls/pit_of_saron/pit_of_saron.h new file mode 100644 index 000000000..39a2bedd7 --- /dev/null +++ b/src/modules/SD2/scripts/northrend/icecrown_citadel/frozen_halls/pit_of_saron/pit_of_saron.h @@ -0,0 +1,182 @@ +/* Copyright (C) 2006 - 2013 ScriptDev2 + * This program is free software licensed under GPL version 2 + * Please see the included DOCS/LICENSE.TXT for more information */ + +#ifndef DEF_ICECROWN_PIT_H +#define DEF_ICECROWN_PIT_H + +enum +{ + MAX_ENCOUNTER = 4, + MAX_SPECIAL_ACHIEV_CRITS = 2, + + TYPE_GARFROST = 0, + TYPE_KRICK = 1, + TYPE_TYRANNUS = 2, + TYPE_AMBUSH = 3, + + TYPE_ACHIEV_DOESNT_GO_ELEVEN = 0, + TYPE_ACHIEV_DONT_LOOK_UP = 1, + + // Bosses + NPC_TYRANNUS_INTRO = 36794, + NPC_GARFROST = 36494, + NPC_KRICK = 36477, + NPC_ICK = 36476, + NPC_TYRANNUS = 36658, + NPC_RIMEFANG = 36661, + NPC_SINDRAGOSA = 37755, + + // Intro part npcs + NPC_SYLVANAS_PART1 = 36990, + NPC_SYLVANAS_PART2 = 38189, + NPC_JAINA_PART1 = 36993, + NPC_JAINA_PART2 = 38188, + NPC_KILARA = 37583, + NPC_ELANDRA = 37774, + NPC_LORALEN = 37779, + NPC_KORELN = 37582, + NPC_CHAMPION_1_HORDE = 37584, + NPC_CHAMPION_2_HORDE = 37587, + NPC_CHAMPION_3_HORDE = 37588, + NPC_CHAMPION_1_ALLIANCE = 37496, + NPC_CHAMPION_2_ALLIANCE = 37497, + NPC_CHAMPION_3_ALLIANCE = 37498, + NPC_CORRUPTED_CHAMPION = 36796, + + // Enslaved npcs + NPC_IRONSKULL_PART1 = 37592, + NPC_IRONSKULL_PART2 = 37581, + NPC_VICTUS_PART1 = 37591, + NPC_VICTUS_PART2 = 37580, + NPC_FREE_HORDE_SLAVE_1 = 37577, + NPC_FREE_HORDE_SLAVE_2 = 37578, + NPC_FREE_HORDE_SLAVE_3 = 37579, + NPC_FREE_ALLIANCE_SLAVE_1 = 37572, + NPC_FREE_ALLIANCE_SLAVE_2 = 37575, + NPC_FREE_ALLIANCE_SLAVE_3 = 37576, + + // Ambush npcs + NPC_YMIRJAR_DEATHBRINGER = 36892, + NPC_YMIRJAR_WRATHBRINGER = 36840, + NPC_YMIRJAR_FLAMEBEARER = 36893, + NPC_FALLEN_WARRIOR = 36841, + NPC_COLDWRAITH = 36842, + NPC_STALKER = 32780, + NPC_GENERAL_BUNNY = 24110, + + GO_ICEWALL = 201885, // open after gafrost/krick + GO_HALLS_OF_REFLECT_PORT = 201848, // unlocked by jaina/sylvanas at last outro + + AREATRIGGER_ID_TUNNEL_START = 5578, + AREATRIGGER_ID_TUNNEL_END = 5581, + + ACHIEV_CRIT_DOESNT_GO_ELEVEN = 12993, // Garfrost, achiev 4524 + ACHIEV_CRIT_DONT_LOOK_UP = 12994, // Gauntlet, achiev 4525 +}; + +static const float afTyrannusMovePos[4][3] = +{ + {922.6365f, 145.877f, 643.2216f}, // Hide position + {835.5887f, 139.4345f, 530.9526f}, // Ick position + {906.9048f, -49.03813f, 618.8016f}, // Tunnel position + {966.3345f, 159.2058f, 665.0453f}, // Rimefang position +}; + +struct EventNpcLocations +{ + uint32 uiEntryHorde, uiEntryAlliance; + float fX, fY, fZ, fO; + float fMoveX, fMoveY, fMoveZ; +}; + +const EventNpcLocations aEventBeginLocations[3] = +{ + {NPC_SYLVANAS_PART1, NPC_JAINA_PART1, 430.3012f, 212.204f, 530.1146f, 0.042f, 440.7882f, 213.7587f, 528.7103f}, + {NPC_KILARA, NPC_ELANDRA, 429.7142f, 212.3021f, 530.2822f, 0.14f, 438.9462f, 215.4271f, 528.7087f}, + {NPC_LORALEN, NPC_KORELN, 429.5675f, 211.7748f, 530.3246f, 5.972f, 438.5052f, 211.5399f, 528.7085f}, + // ToDo: add the soldiers here when proper waypoint movement is supported +}; + +const EventNpcLocations aEventFirstAmbushLocations[2] = +{ + {NPC_YMIRJAR_DEATHBRINGER, 0, 951.6696f, 53.06405f, 567.5153f, 1.51f, 914.7256f, 76.66406f, 553.8029f}, + {NPC_YMIRJAR_DEATHBRINGER, 0, 950.9911f, 60.26712f, 566.7658f, 1.79f, 883.1805f, 52.69792f, 527.6385f}, +}; + +const EventNpcLocations aEventSecondAmbushLocations[] = +{ + {NPC_FALLEN_WARRIOR, 0, 916.658f, -55.94097f, 591.6827f, 1.85f, 950.5694f, 31.85649f, 572.2693f}, + {NPC_FALLEN_WARRIOR, 0, 923.8055f, -55.63195f, 591.8663f, 1.85f, 941.3954f, 35.83769f, 571.4308f}, + {NPC_FALLEN_WARRIOR, 0, 936.0625f, -53.52778f, 592.0226f, 1.85f, 934.8011f, 8.024931f, 577.3419f}, + {NPC_FALLEN_WARRIOR, 0, 919.7518f, -68.39236f, 592.2916f, 1.85f, 932.5734f, -22.54153f, 587.403f}, + {NPC_FALLEN_WARRIOR, 0, 926.8993f, -68.08334f, 592.0798f, 1.85f, 922.6043f, -22.07627f, 585.6684f}, + {NPC_FALLEN_WARRIOR, 0, 939.1563f, -65.97916f, 592.2205f, 1.85f, 927.0928f, -32.97949f, 589.3028f}, + {NPC_COLDWRAITH, 0, 924.0261f, -62.3316f, 592.0191f, 2.01f, 929.4673f, 9.722589f, 577.4904f}, + {NPC_COLDWRAITH, 0, 936.4531f, -60.45486f, 592.1215f, 1.63f, 936.1395f, -4.003471f, 581.3139f}, + {NPC_COLDWRAITH, 0, 935.8055f, -72.76736f, 592.077f, 1.66f, 933.8441f, -47.83234f, 591.7538f}, + {NPC_COLDWRAITH, 0, 923.3785f, -74.6441f, 592.368f, 2.37f, 920.726f, -42.32272f, 589.9808f} +}; + +const EventNpcLocations aEventTunnelEndLocations[] = +{ + {NPC_IRONSKULL_PART2, NPC_VICTUS_PART2, 1071.45f, 48.23907f, 630.4871f, 1.68f, 1046.361f, 124.7031f, 628.2811f}, + // ToDo: add the freed slaves here when proper waypoint movement is supported +}; +const EventNpcLocations aEventOutroLocations[] = +{ + {NPC_SINDRAGOSA, 0, 842.8611f, 194.5556f, 531.6536f, 6.108f, 900.106f, 181.677f, 659.374f}, + {NPC_SYLVANAS_PART2, NPC_JAINA_PART2, 1062.85f, 100.075f, 631.0021f, 1.77f, 1062.85f, 100.075f, 631.0021f}, +}; + + +class instance_pit_of_saron : public ScriptedInstance, private DialogueHelper +{ + public: + instance_pit_of_saron(Map* pMap); + ~instance_pit_of_saron() {} + + void Initialize() override; + + void OnCreatureCreate(Creature* pCreature) override; + void OnObjectCreate(GameObject* pGo) override; + + void OnPlayerEnter(Player* pPlayer) override; + void OnCreatureEnterCombat(Creature* pCreature) override; + void OnCreatureDeath(Creature* pCreature) override; + + void SetData(uint32 uiType, uint32 uiData) override; + uint32 GetData(uint32 uiType) const override; + + uint32 GetPlayerTeam() { return m_uiTeam; } + + void DoStartAmbushEvent(); + + void SetSpecialAchievementCriteria(uint32 uiType, bool bIsMet); + bool CheckAchievementCriteriaMeet(uint32 uiCriteriaId, Player const* pSource, Unit const* pTarget, uint32 uiMiscValue1 /* = 0*/) const override; + + const char* Save() const override { return m_strInstData.c_str(); } + void Load(const char* chrIn) override; + + void Update(uint32 uiDiff); + + protected: + void JustDidDialogueStep(int32 iEntry) override; + void ProcessIntroEventNpcs(Player* pPlayer); + + uint32 m_auiEncounter[MAX_ENCOUNTER]; + std::string m_strInstData; + + bool m_abAchievCriteria[MAX_SPECIAL_ACHIEV_CRITS]; + + uint8 m_uiAmbushAggroCount; + uint32 m_uiTeam; // Team of first entered player, used to set if Jaina or Silvana to spawn + uint32 m_uiIciclesTimer; + + GuidList m_lTunnelStalkersGuidList; + GuidList m_lAmbushNpcsGuidList; + GuidList m_lArcaneShieldBunniesGuidList; + GuidList m_lFrozenAftermathBunniesGuidList; +}; + +#endif diff --git a/src/modules/SD2/scripts/northrend/icecrown_citadel/icecrown_citadel/blood_prince_council.cpp b/src/modules/SD2/scripts/northrend/icecrown_citadel/icecrown_citadel/blood_prince_council.cpp new file mode 100644 index 000000000..94d44a207 --- /dev/null +++ b/src/modules/SD2/scripts/northrend/icecrown_citadel/icecrown_citadel/blood_prince_council.cpp @@ -0,0 +1,912 @@ +/* Copyright (C) 2006 - 2013 ScriptDev2 + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +/* ScriptData +SDName: blood_prince_council +SD%Complete: 80% +SDComment: Timers; Some details are not very clear about this encounter: spells 72087 and 73001 require additional research. +SDCategory: Icecrown Citadel +EndScriptData */ + +#include "precompiled.h" +#include "icecrown_citadel.h" + +enum +{ + // Yells + SAY_COUNCIL_INTRO_1 = -1631101, // Intro by Bloodqueen + SAY_COUNCIL_INTRO_2 = -1631102, + + SAY_KELESETH_INVOCATION = -1631103, + SAY_KELESETH_SPECIAL = -1631104, + SAY_KELESETH_SLAY_1 = -1631105, + SAY_KELESETH_SLAY_2 = -1631106, + SAY_KELESETH_BERSERK = -1631107, + SAY_KELESETH_DEATH = -1631108, + + SAY_TALDARAM_INVOCATION = -1631109, + SAY_TALDARAM_SPECIAL = -1631110, + SAY_TALDARAM_SLAY_1 = -1631111, + SAY_TALDARAM_SLAY_2 = -1631112, + SAY_TALDARAM_BERSERK = -1631113, + SAY_TALDARAM_DEATH = -1631114, + + SAY_VALANAR_INVOCATION = -1631115, + SAY_VALANAR_SPECIAL = -1631116, + SAY_VALANAR_SLAY_1 = -1631117, + SAY_VALANAR_SLAY_2 = -1631118, + SAY_VALANAR_BERSERK = -1631119, + SAY_VALANAR_DEATH = -1631120, + + EMOTE_INVOCATION = -1631197, + EMOTE_SHOCK_VORTEX = -1631198, + EMOTE_FLAMES = -1631199, + + // Generic spells + SPELL_BERSERK = 26662, + SPELL_FEIGN_DEATH = 71598, + + SPELL_INVOCATION_BLOOD = 70934, + SPELL_INVOCATION_BLOOD_2 = 71596, + SPELL_INVOCATION_V_MOVE = 71075, + SPELL_INVOCATION_K_MOVE = 71079, + SPELL_INVOCATION_T_MOVE = 71082, + + // Valanar spells + SPELL_INVOCATION_VALANAR = 70952, + SPELL_KINETIC_BOMB_TARGET = 72053, // summons 38458 - the target of the bomb + SPELL_KINETIC_BOMB = 72080, // summons 38454 + SPELL_SHOCK_VORTEX = 72037, // summons 38422 + SPELL_EMP_SHOCK_VORTEX = 72039, + + NPC_KINETIC_BOMB = 38454, + NPC_KINETIC_BOMB_TARGET = 38458, + + // shock vortex spells - in eventAI + // SPELL_SHOCK_VORTEX_AURA = 71945, + // SPELL_SHOCK_VORTEX_VISUAL = 72633, + + // kinetic bomb spells + SPELL_KINETIC_BOMB_DMG = 72052, + SPELL_KINETIC_BOMB_VISUAL = 72054, + SPELL_UNSTABLE = 72059, // procs 72087 + SPELL_KINETIC_KNOCKBACK = 72087, + + // Keleseth spells + SPELL_INVOCATION_KELESETH = 70981, + SPELL_SHADOW_LANCE = 71405, + SPELL_EMP_SHADOW_LANCE = 71815, + SPELL_SHADOW_RESONANCE = 71943, // summons 38369 + // SPELL_SHADOW_PRISON = 73001, // on heroic - not sure how to use + + // dark nucleus spells + SPELL_SHADOW_RESONANCE_AURA = 71911, // purpose unk - maybe range check + SPELL_SHADOW_RESONANCE_BUFF = 71822, // channeled from the dark nucleus + SPELL_SHADOW_RESONANCE_DMG = 72980, // self destruction spell + + // Taldaram spells + SPELL_INVOCATION_TALDARAM = 70982, + SPELL_GLITTERING_SPARKS = 71806, // triggers 71807 + SPELL_CONJURE_FLAME = 71718, // triggers 71719 which summons 38332 + SPELL_CONJURE_EMP_FLAME = 72040, // triggers 72041 which summons 38451 + + NPC_BALL_OF_FLAME = 38332, + NPC_BALL_OF_INFERNO_FLAME = 38451, + + // ball of flame spells + SPELL_BALL_FLAMES_VISUAL = 71706, + SPELL_FLAMES = 71393, // cast on the impact + SPELL_BALL_FLAMES_PERIODIC = 71709, // triggers 71708 + SPELL_FLAMES_PROC = 71756, + + MAX_PRINCES = 3, +}; + +static const DialogueEntry aIntroDialogue[] = +{ + {SAY_COUNCIL_INTRO_1, NPC_LANATHEL_INTRO, 15000}, + {SAY_COUNCIL_INTRO_2, NPC_LANATHEL_INTRO, 10000}, + {NPC_BLOOD_ORB_CONTROL, 0, 0}, + {0, 0, 0}, +}; + +static const float aLanathelFlyPos[3] = {4660.49f, 2769.2f, 430.0f}; + +/*###### +## npc_queen_lanathel_intro +######*/ + +struct npc_queen_lanathel_introAI : public ScriptedAI, private DialogueHelper +{ + npc_queen_lanathel_introAI(Creature* pCreature) : ScriptedAI(pCreature), + DialogueHelper(aIntroDialogue) + { + m_pInstance = (ScriptedInstance*)pCreature->GetInstanceData(); + InitializeDialogueHelper(m_pInstance); + m_bEventStarted = false; + Reset(); + } + + ScriptedInstance* m_pInstance; + + bool m_bEventStarted; + + void Reset() override + { + // Flying animation + m_creature->SetByteFlag(UNIT_FIELD_BYTES_1, 3, UNIT_BYTE1_FLAG_ALWAYS_STAND | UNIT_BYTE1_FLAG_UNK_2); + } + + void MoveInLineOfSight(Unit* pWho) override + { + // The range distance is not sure + if (!m_bEventStarted && pWho->GetTypeId() == TYPEID_PLAYER && !((Player*)pWho)->isGameMaster() && + pWho->IsWithinDistInMap(m_creature, 100.0f) && pWho->IsWithinLOSInMap(m_creature)) + { + StartNextDialogueText(SAY_COUNCIL_INTRO_1); + m_bEventStarted = true; + } + } + + void JustDidDialogueStep(int32 iEntry) override + { + switch (iEntry) + { + case SAY_COUNCIL_INTRO_2: + m_creature->GetMotionMaster()->MovePoint(1, aLanathelFlyPos[0], aLanathelFlyPos[1], aLanathelFlyPos[2]); + break; + case NPC_BLOOD_ORB_CONTROL: + if (m_pInstance) + { + if (Creature* pTaldaram = m_pInstance->GetSingleCreatureFromStorage(NPC_TALDARAM)) + { + pTaldaram->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_OOC_NOT_ATTACKABLE); + pTaldaram->RemoveAurasDueToSpell(SPELL_FEIGN_DEATH); + pTaldaram->SetHealth(1); + } + if (Creature* pKeleseth = m_pInstance->GetSingleCreatureFromStorage(NPC_KELESETH)) + { + pKeleseth->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_OOC_NOT_ATTACKABLE); + pKeleseth->RemoveAurasDueToSpell(SPELL_FEIGN_DEATH); + pKeleseth->SetHealth(1); + } + if (Creature* pValanar = m_pInstance->GetSingleCreatureFromStorage(NPC_VALANAR)) + { + pValanar->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_OOC_NOT_ATTACKABLE); + pValanar->RemoveAurasDueToSpell(SPELL_FEIGN_DEATH); + pValanar->SetHealth(1); + } + } + break; + } + } + + void MovementInform(uint32 uiMoveType, uint32 uiPointId) override + { + if (uiMoveType != POINT_MOTION_TYPE || !uiPointId) + return; + + // Emote here, and force them to stand up - this needs to be done as a workaround for some core issues + if (m_pInstance) + { + // This should be casted when they stand up - but because of the workaround, it will be casted here + if (Creature* pOrb = m_pInstance->GetSingleCreatureFromStorage(NPC_BLOOD_ORB_CONTROL)) + pOrb->CastSpell(pOrb, SPELL_INVOCATION_VALANAR, false); + if (Creature* pTaldaram = m_pInstance->GetSingleCreatureFromStorage(NPC_TALDARAM)) + pTaldaram->HandleEmote(EMOTE_ONESHOT_ROAR); + if (Creature* pKeleseth = m_pInstance->GetSingleCreatureFromStorage(NPC_KELESETH)) + pKeleseth->HandleEmote(EMOTE_ONESHOT_ROAR); + if (Creature* pValanar = m_pInstance->GetSingleCreatureFromStorage(NPC_VALANAR)) + pValanar->HandleEmote(EMOTE_ONESHOT_ROAR); + } + + // Despawn when reached point + m_creature->ForcedDespawn(); + } + + void UpdateAI(const uint32 uiDiff) { DialogueUpdate(uiDiff); } +}; + +CreatureAI* GetAI_npc_queen_lanathel_intro(Creature* pCreature) +{ + return new npc_queen_lanathel_introAI(pCreature); +} + +/*###### +## npc_ball_of_flame +######*/ + +struct npc_ball_of_flameAI : public ScriptedAI +{ + npc_ball_of_flameAI(Creature* pCreature) : ScriptedAI(pCreature) { Reset(); } + + bool m_bHasFlamesCasted; + + uint32 m_uiTargetGuidLow; + + void Reset() override + { + m_bHasFlamesCasted = false; + + DoCastSpellIfCan(m_creature, SPELL_BALL_FLAMES_VISUAL, CAST_TRIGGERED); + + // Empowered flame + if (m_creature->GetEntry() == NPC_BALL_OF_INFERNO_FLAME) + { + DoCastSpellIfCan(m_creature, SPELL_BALL_FLAMES_PERIODIC, CAST_TRIGGERED); + DoCastSpellIfCan(m_creature, SPELL_FLAMES_PROC, CAST_TRIGGERED); + } + } + + void DoInitializeTarget(uint32 uiGuid) { m_uiTargetGuidLow = uiGuid; } + + void MoveInLineOfSight(Unit* pWho) override + { + if (!m_bHasFlamesCasted && pWho->GetTypeId() == TYPEID_PLAYER && pWho->GetGUIDLow() == m_uiTargetGuidLow && + pWho->IsWithinDist(m_creature, ATTACK_DISTANCE)) + { + if (DoCastSpellIfCan(m_creature, SPELL_FLAMES) == CAST_OK) + { + m_bHasFlamesCasted = true; + m_creature->GetMotionMaster()->MoveIdle(); + m_creature->ForcedDespawn(1000); + } + } + } + + void AttackStart(Unit* /*pWho*/) override { } + + void UpdateAI(const uint32 /*uiDiff*/) override { } +}; + +CreatureAI* GetAI_npc_ball_of_flame(Creature* pCreature) +{ + return new npc_ball_of_flameAI(pCreature); +}; + +/*###### +## npc_kinetic_bomb +######*/ + +struct npc_kinetic_bombAI : public ScriptedAI +{ + npc_kinetic_bombAI(Creature* pCreature) : ScriptedAI(pCreature) { Reset(); } + + void Reset() override + { + DoCastSpellIfCan(m_creature, SPELL_UNSTABLE, CAST_TRIGGERED); + DoCastSpellIfCan(m_creature, SPELL_KINETIC_BOMB_VISUAL, CAST_TRIGGERED); + } + + void DamageTaken(Unit* /*pDealer*/, uint32& uiDamage) override + { + // Note: this npc shouldn't take any damage - however this has an issue in the core, because the Unstanble spell doesn't proc on 0 damage + uiDamage = 0; + } + + void MovementInform(uint32 uiMoveType, uint32 uiPointId) override + { + if (uiMoveType != POINT_MOTION_TYPE || !uiPointId) + return; + + DoCastSpellIfCan(m_creature, SPELL_KINETIC_BOMB_DMG); + m_creature->ForcedDespawn(1000); + } + + void AttackStart(Unit* /*pWho*/) override { } + + void MoveInLineOfSight(Unit* /*pWho*/) override { } + + void UpdateAI(const uint32 /*uiDiff*/) override { } +}; + +CreatureAI* GetAI_npc_kinetic_bomb(Creature* pCreature) +{ + return new npc_kinetic_bombAI(pCreature); +}; + +/*###### +## npc_dark_nucleus +######*/ + +struct npc_dark_nucleusAI : public ScriptedAI +{ + npc_dark_nucleusAI(Creature* pCreature) : ScriptedAI(pCreature) { Reset(); } + + uint32 m_uiDistanceCheck; + + void Reset() override + { + DoCastSpellIfCan(m_creature, SPELL_SHADOW_RESONANCE_AURA, CAST_TRIGGERED); + DoCastSpellIfCan(m_creature, SPELL_SHADOW_RESONANCE_DMG, CAST_TRIGGERED); + + m_uiDistanceCheck = 1000; + } + + void AttackStart(Unit* pWho) override + { + if (m_creature->Attack(pWho, true)) + { + m_creature->AddThreat(pWho); + m_creature->SetInCombatWith(pWho); + pWho->SetInCombatWith(m_creature); + DoStartMovement(pWho, 10.0f); + } + } + + void DamageTaken(Unit* pDealer, uint32& /*uiDamage*/) override + { + if (m_creature->getVictim() && pDealer != m_creature->getVictim()) + { + DoResetThreat(); + m_creature->AddThreat(pDealer, 100000.0f); + m_creature->InterruptNonMeleeSpells(true); + } + } + + void UpdateAI(const uint32 uiDiff) override + { + if (!m_creature->SelectHostileTarget() || !m_creature->getVictim()) + return; + + if (m_uiDistanceCheck < uiDiff) + { + if (m_creature->GetDistance(m_creature->getVictim()) < 15.0f) + DoCastSpellIfCan(m_creature->getVictim(), SPELL_SHADOW_RESONANCE_BUFF); + + m_uiDistanceCheck = 1000; + } + else + m_uiDistanceCheck -= uiDiff; + } +}; + +CreatureAI* GetAI_npc_dark_nucleus(Creature* pCreature) +{ + return new npc_dark_nucleusAI(pCreature); +}; + +/*###### +## npc_blood_orb_control +######*/ + +struct npc_blood_orb_controlAI : public Scripted_NoMovementAI +{ + npc_blood_orb_controlAI(Creature* pCreature) : Scripted_NoMovementAI(pCreature) + { + m_pInstance = (ScriptedInstance*)pCreature->GetInstanceData(); + Reset(); + } + + ScriptedInstance* m_pInstance; + + uint8 m_uiLastResult; + uint32 m_uiInvocationTimer; + + void Reset() override + { + m_uiInvocationTimer = 30000; + } + + void Aggro(Unit* /*pWho*/) override + { + if (m_pInstance) + m_pInstance->SetData(TYPE_BLOOD_PRINCE_COUNCIL, IN_PROGRESS); + } + + void JustDied(Unit* /*pKiller*/) override + { + if (m_pInstance) + { + m_pInstance->SetData(TYPE_BLOOD_PRINCE_COUNCIL, DONE); + + // Kill the 3 princes + if (Creature* pTmp = m_pInstance->GetSingleCreatureFromStorage(NPC_VALANAR)) + m_creature->DealDamage(pTmp, pTmp->GetHealth(), NULL, DIRECT_DAMAGE, SPELL_SCHOOL_MASK_NONE, NULL, false); + + if (Creature* pTmp = m_pInstance->GetSingleCreatureFromStorage(NPC_KELESETH)) + m_creature->DealDamage(pTmp, pTmp->GetHealth(), NULL, DIRECT_DAMAGE, SPELL_SCHOOL_MASK_NONE, NULL, false); + + if (Creature* pTmp = m_pInstance->GetSingleCreatureFromStorage(NPC_TALDARAM)) + m_creature->DealDamage(pTmp, pTmp->GetHealth(), NULL, DIRECT_DAMAGE, SPELL_SCHOOL_MASK_NONE, NULL, false); + } + } + + void JustReachedHome() override + { + if (m_pInstance) + m_pInstance->SetData(TYPE_BLOOD_PRINCE_COUNCIL, FAIL); + } + + void UpdateAI(const uint32 uiDiff) override + { + if (!m_creature->SelectHostileTarget() || !m_creature->getVictim()) + return; + + // every 30 seconds cast Invocation of Blood on random prince + if (m_uiInvocationTimer < uiDiff) + { + uint8 uiResult = urand(0, MAX_PRINCES - 1); + uiResult = uiResult == m_uiLastResult ? (uiResult + 1) % MAX_PRINCES : uiResult; + m_uiLastResult = uiResult; + + switch (uiResult) + { + case 0: + DoCastSpellIfCan(m_creature, SPELL_INVOCATION_V_MOVE, CAST_TRIGGERED); + DoCastSpellIfCan(m_creature, SPELL_INVOCATION_VALANAR, CAST_TRIGGERED); + break; + case 1: + DoCastSpellIfCan(m_creature, SPELL_INVOCATION_K_MOVE, CAST_TRIGGERED); + DoCastSpellIfCan(m_creature, SPELL_INVOCATION_KELESETH, CAST_TRIGGERED); + break; + case 2: + DoCastSpellIfCan(m_creature, SPELL_INVOCATION_T_MOVE, CAST_TRIGGERED); + DoCastSpellIfCan(m_creature, SPELL_INVOCATION_TALDARAM, CAST_TRIGGERED); + break; + } + + m_uiInvocationTimer = 47000; + } + else + m_uiInvocationTimer -= uiDiff; + } +}; + +CreatureAI* GetAI_npc_blood_orb_control(Creature* pCreature) +{ + return new npc_blood_orb_controlAI(pCreature); +} + +/*###### +## blood_prince_council_base +######*/ + +struct blood_prince_council_baseAI : public ScriptedAI +{ + blood_prince_council_baseAI(Creature* pCreature) : ScriptedAI(pCreature) + { + m_pInstance = (instance_icecrown_citadel*)pCreature->GetInstanceData(); + DoCastSpellIfCan(m_creature, SPELL_FEIGN_DEATH); + m_uiResetTimer = 0; + Reset(); + } + + instance_icecrown_citadel* m_pInstance; + + uint32 m_uiInvocationSpellEntry; + int32 m_iSayInvocationEntry; + int32 m_iSayBerserkEntry; + + uint32 m_uiEmpowermentTimer; + uint32 m_uiResetTimer; + uint32 m_uiBerserkTimer; + uint32 m_uiSphereTimer; + + bool m_bIsSaidSpecial; // 1st spell cast after being empowered is followed by special say + + void Reset() override + { + m_bIsSaidSpecial = false; + m_uiEmpowermentTimer = 0; + m_uiSphereTimer = urand(5000, 15000); + m_uiBerserkTimer = 10 * MINUTE * IN_MILLISECONDS; + } + + void EnterEvadeMode() override + { + // Reset the health to 1 + m_creature->SetHealth(1); + + // Reset blood orb + if (m_creature->GetEntry() == NPC_VALANAR) + m_uiResetTimer = 5000; + + ScriptedAI::EnterEvadeMode(); + } + + void DamageTaken(Unit* /*pDealer*/, uint32& uiDamage) override + { + // Damage is shared by the Blood Orb Control npc + if (!m_uiEmpowermentTimer) + uiDamage = 0; + + // ##### Workaround for missing aura 300 - Remove when this is implemented in core ##### + if (!m_pInstance || !uiDamage) + return; + + if (Creature* pOrb = m_pInstance->GetSingleCreatureFromStorage(NPC_BLOOD_ORB_CONTROL)) + pOrb->DealDamage(pOrb, uiDamage, NULL, DIRECT_DAMAGE, SPELL_SCHOOL_MASK_NORMAL, NULL, false); + // ##### End of workaround ##### + } + + void SpellHit(Unit* pCaster, const SpellEntry* pSpell) override + { + // When hit by the Invocation spell, then set the health of the blood control npc + if (pSpell->Id == m_uiInvocationSpellEntry) + { + m_creature->SetHealth(pCaster->GetHealth()); + DoScriptText(EMOTE_INVOCATION, m_creature); + DoScriptText(m_iSayInvocationEntry, m_creature); + m_uiEmpowermentTimer = 30000; + } + } + + void UpdateAI(const uint32 uiDiff) override + { + // On evade, reset the blood orb on Valanar + if (m_uiResetTimer) + { + if (m_uiResetTimer <= uiDiff) + { + if (m_pInstance) + { + if (Creature* pOrb = m_pInstance->GetSingleCreatureFromStorage(NPC_BLOOD_ORB_CONTROL)) + pOrb->CastSpell(pOrb, SPELL_INVOCATION_VALANAR, false); + + m_uiResetTimer = 0; + } + } + else + m_uiResetTimer -= uiDiff; + } + + if (!m_creature->SelectHostileTarget() || !m_creature->getVictim()) + return; + + // Invocation of Blood + if (m_uiEmpowermentTimer) + { + if (m_uiEmpowermentTimer <= uiDiff) + { + m_creature->RemoveAurasDueToSpell(m_uiInvocationSpellEntry); + m_creature->SetHealth(1); + m_bIsSaidSpecial = false; + m_uiEmpowermentTimer = 00; + } + else + m_uiEmpowermentTimer -= uiDiff; + } + + // Berserk + if (m_uiBerserkTimer) + { + if (m_uiBerserkTimer <= uiDiff) + { + if (DoCastSpellIfCan(m_creature, SPELL_BERSERK) == CAST_OK) + { + DoScriptText(m_iSayBerserkEntry, m_creature); + m_uiBerserkTimer = 0; + } + } + else + m_uiBerserkTimer -= uiDiff; + } + } +}; + +/*###### +## boss_valanar_icc +######*/ + +struct boss_valanar_iccAI : public blood_prince_council_baseAI +{ + boss_valanar_iccAI(Creature* pCreature) : blood_prince_council_baseAI(pCreature) + { + m_uiInvocationSpellEntry = SPELL_INVOCATION_VALANAR; + m_iSayInvocationEntry = SAY_VALANAR_INVOCATION; + m_iSayBerserkEntry = SAY_VALANAR_BERSERK; + Reset(); + } + + uint32 m_uiVortexTimer; + + void Reset() override + { + blood_prince_council_baseAI::Reset(); + + m_uiVortexTimer = urand(5000, 10000); + } + + void KilledUnit(Unit* pVictim) override + { + if (pVictim->GetTypeId() == TYPEID_PLAYER) + DoScriptText(urand(0, 1) ? SAY_VALANAR_SLAY_1 : SAY_VALANAR_SLAY_2, m_creature); + } + + void JustDied(Unit* /*pKiller*/) override + { + DoScriptText(SAY_VALANAR_DEATH, m_creature); + } + + void JustSummoned(Creature* pSummoned) override + { + if (pSummoned->GetEntry() == NPC_KINETIC_BOMB_TARGET) + pSummoned->CastSpell(pSummoned->GetPositionX(), pSummoned->GetPositionY(), pSummoned->GetPositionZ() + 20.0f, SPELL_KINETIC_BOMB, true, NULL, NULL, m_creature->GetObjectGuid()); + else if (pSummoned->GetEntry() == NPC_KINETIC_BOMB) + { + // Handle Kinetic bomb movement + pSummoned->SetLevitate(true); + pSummoned->GetMotionMaster()->MovePoint(1, pSummoned->GetPositionX(), pSummoned->GetPositionY(), pSummoned->GetPositionZ() - 20.0f, false); + } + } + + void UpdateAI(const uint32 uiDiff) override + { + blood_prince_council_baseAI::UpdateAI(uiDiff); + + if (!m_creature->SelectHostileTarget() || !m_creature->getVictim()) + return; + + if (m_uiSphereTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature, SPELL_KINETIC_BOMB_TARGET) == CAST_OK) + m_uiSphereTimer = 27000; + } + else + m_uiSphereTimer -= uiDiff; + + if (m_uiVortexTimer < uiDiff) + { + if (Unit* pTarget = m_creature->SelectAttackingTarget(ATTACKING_TARGET_RANDOM, 0)) + { + if (DoCastSpellIfCan(pTarget, m_uiEmpowermentTimer ? SPELL_EMP_SHOCK_VORTEX : SPELL_SHOCK_VORTEX) == CAST_OK) + { + if (m_uiEmpowermentTimer) + DoScriptText(EMOTE_SHOCK_VORTEX, m_creature); + + if (m_uiEmpowermentTimer && !m_bIsSaidSpecial) + { + DoScriptText(SAY_VALANAR_SPECIAL, m_creature); + m_bIsSaidSpecial = false; + } + + m_uiVortexTimer = 17000; + } + } + } + else + m_uiVortexTimer -= uiDiff; + + DoMeleeAttackIfReady(); + } +}; + +CreatureAI* GetAI_boss_valanar_icc(Creature* pCreature) +{ + return new boss_valanar_iccAI(pCreature); +} + +/*###### +## boss_keleseth_icc +######*/ + +struct boss_keleseth_iccAI : public blood_prince_council_baseAI +{ + boss_keleseth_iccAI(Creature* pCreature) : blood_prince_council_baseAI(pCreature) + { + m_uiInvocationSpellEntry = SPELL_INVOCATION_KELESETH; + m_iSayInvocationEntry = SAY_KELESETH_INVOCATION; + m_iSayBerserkEntry = SAY_KELESETH_BERSERK; + Reset(); + } + + uint32 m_uiShadowLanceTimer; + + void Reset() override + { + blood_prince_council_baseAI::Reset(); + + m_uiShadowLanceTimer = urand(2000, 3000); + m_uiSphereTimer = 4000; + } + + void AttackStart(Unit* pWho) override + { + if (m_creature->Attack(pWho, true)) + { + m_creature->AddThreat(pWho); + m_creature->SetInCombatWith(pWho); + pWho->SetInCombatWith(m_creature); + DoStartMovement(pWho, 20.0f); + } + } + + void KilledUnit(Unit* pVictim) override + { + if (pVictim->GetTypeId() == TYPEID_PLAYER) + DoScriptText(urand(0, 1) ? SAY_KELESETH_SLAY_1 : SAY_KELESETH_SLAY_2, m_creature); + } + + void JustDied(Unit* /*pKiller*/) override + { + DoScriptText(SAY_KELESETH_DEATH, m_creature); + } + + void JustSummoned(Creature* pSummoned) override + { + pSummoned->SetInCombatWithZone(); + } + + void UpdateAI(const uint32 uiDiff) override + { + blood_prince_council_baseAI::UpdateAI(uiDiff); + + if (!m_creature->SelectHostileTarget() || !m_creature->getVictim()) + return; + + if (m_uiSphereTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature, SPELL_SHADOW_RESONANCE) == CAST_OK) + m_uiSphereTimer = 25000; + } + else + m_uiSphereTimer -= uiDiff; + + if (m_uiShadowLanceTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature->getVictim(), m_uiEmpowermentTimer ? SPELL_EMP_SHADOW_LANCE : SPELL_SHADOW_LANCE) == CAST_OK) + { + if (m_uiEmpowermentTimer && !m_bIsSaidSpecial) + { + DoScriptText(SAY_KELESETH_SPECIAL, m_creature); + m_bIsSaidSpecial = true; + } + + m_uiShadowLanceTimer = urand(2000, 3000); + } + } + else + m_uiShadowLanceTimer -= uiDiff; + } +}; + +CreatureAI* GetAI_boss_keleseth_icc(Creature* pCreature) +{ + return new boss_keleseth_iccAI(pCreature); +} + +/*###### +## boss_taldaram_icc +######*/ + +struct boss_taldaram_iccAI : public blood_prince_council_baseAI +{ + boss_taldaram_iccAI(Creature* pCreature) : blood_prince_council_baseAI(pCreature) + { + m_uiInvocationSpellEntry = SPELL_INVOCATION_TALDARAM; + m_iSayInvocationEntry = SAY_TALDARAM_INVOCATION; + m_iSayBerserkEntry = SAY_TALDARAM_BERSERK; + Reset(); + } + + uint32 m_uiSparksTimer; + + void Reset() override + { + blood_prince_council_baseAI::Reset(); + + m_uiSparksTimer = urand(8000, 15000); + } + + void KilledUnit(Unit* pVictim) override + { + if (pVictim->GetTypeId() == TYPEID_PLAYER) + DoScriptText(urand(0, 1) ? SAY_TALDARAM_SLAY_1 : SAY_TALDARAM_SLAY_2, m_creature); + } + + void JustDied(Unit* /*pKiller*/) override + { + DoScriptText(SAY_TALDARAM_DEATH, m_creature); + } + + void JustSummoned(Creature* pSummoned) override + { + // We need to initialize the target which is the ball of flame should follow + if (Unit* pTarget = m_creature->SelectAttackingTarget(ATTACKING_TARGET_RANDOM, 0, uint32(0), SELECT_FLAG_NOT_IN_MELEE_RANGE | SELECT_FLAG_PLAYER)) + { + if (npc_ball_of_flameAI* pBallAI = dynamic_cast(pSummoned->AI())) + pBallAI->DoInitializeTarget(pTarget->GetGUIDLow()); + + DoScriptText(EMOTE_FLAMES, pSummoned, pTarget); + pSummoned->GetMotionMaster()->MoveFollow(pTarget, 0, 0); + } + } + + void UpdateAI(const uint32 uiDiff) override + { + blood_prince_council_baseAI::UpdateAI(uiDiff); + + if (!m_creature->SelectHostileTarget() || !m_creature->getVictim()) + return; + + if (m_uiSphereTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature, m_uiEmpowermentTimer ? SPELL_CONJURE_EMP_FLAME : SPELL_CONJURE_FLAME) == CAST_OK) + { + if (m_uiEmpowermentTimer && !m_bIsSaidSpecial) + { + DoScriptText(SAY_TALDARAM_SPECIAL, m_creature); + m_bIsSaidSpecial = true; + } + + m_uiSphereTimer = 20000; + } + } + else + m_uiSphereTimer -= uiDiff; + + if (m_uiSparksTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature, SPELL_GLITTERING_SPARKS) == CAST_OK) + m_uiSparksTimer = 30000; + } + else + m_uiSparksTimer -= uiDiff; + + DoMeleeAttackIfReady(); + } +}; + +CreatureAI* GetAI_boss_taldaram_icc(Creature* pCreature) +{ + return new boss_taldaram_iccAI(pCreature); +} + +void AddSC_blood_prince_council() +{ + Script* pNewScript; + + pNewScript = new Script; + pNewScript->Name = "npc_queen_lanathel_intro"; + pNewScript->GetAI = &GetAI_npc_queen_lanathel_intro; + pNewScript->RegisterSelf(); + + pNewScript = new Script; + pNewScript->Name = "npc_ball_of_flame"; + pNewScript->GetAI = &GetAI_npc_ball_of_flame; + pNewScript->RegisterSelf(); + + pNewScript = new Script; + pNewScript->Name = "npc_kinetic_bomb"; + pNewScript->GetAI = &GetAI_npc_kinetic_bomb; + pNewScript->RegisterSelf(); + + pNewScript = new Script; + pNewScript->Name = "npc_dark_nucleus"; + pNewScript->GetAI = &GetAI_npc_dark_nucleus; + pNewScript->RegisterSelf(); + + pNewScript = new Script; + pNewScript->Name = "npc_blood_orb_control"; + pNewScript->GetAI = &GetAI_npc_blood_orb_control; + pNewScript->RegisterSelf(); + + pNewScript = new Script; + pNewScript->Name = "boss_taldaram_icc"; + pNewScript->GetAI = &GetAI_boss_taldaram_icc; + pNewScript->RegisterSelf(); + + pNewScript = new Script; + pNewScript->Name = "boss_keleseth_icc"; + pNewScript->GetAI = &GetAI_boss_keleseth_icc; + pNewScript->RegisterSelf(); + + pNewScript = new Script; + pNewScript->Name = "boss_valanar_icc"; + pNewScript->GetAI = &GetAI_boss_valanar_icc; + pNewScript->RegisterSelf(); +} diff --git a/src/modules/SD2/scripts/northrend/icecrown_citadel/icecrown_citadel/boss_blood_queen_lanathel.cpp b/src/modules/SD2/scripts/northrend/icecrown_citadel/icecrown_citadel/boss_blood_queen_lanathel.cpp new file mode 100644 index 000000000..cddf4ab79 --- /dev/null +++ b/src/modules/SD2/scripts/northrend/icecrown_citadel/icecrown_citadel/boss_blood_queen_lanathel.cpp @@ -0,0 +1,337 @@ +/* Copyright (C) 2006 - 2013 ScriptDev2 + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +/* ScriptData +SDName: boss_blood_queen_lanathel +SD%Complete: 60% +SDComment: Timers; Most spells are dummy targeting spells and need core support; Quest 24756 event NYI. +SDCategory: Icecrown Citadel +EndScriptData */ + +#include "precompiled.h" +#include "icecrown_citadel.h" + +enum +{ + SAY_AGGRO = -1631121, + SAY_BITE_1 = -1631122, + SAY_BITE_2 = -1631123, + SAY_SHADOWS = -1631124, + SAY_PACT = -1631125, + SAY_MC = -1631126, + SAY_AIR_PHASE = -1631127, + SAY_BERSERK = -1631128, + SAY_DEATH = -1631129, + SAY_SLAY_1 = -1631195, + SAY_SLAY_2 = -1631196, + + // all phases + SPELL_BERSERK = 26662, + SPELL_SHROUD_OF_SORROW = 70986, + + // ground phase + SPELL_BLOOD_MIRROR = 70837, // triggers 70445 and other similar spells + SPELL_SWARMING_SHADOWS = 71861, // triggers 71264 and 71267 + SPELL_PACT_OF_THE_DARKFALLEN = 71336, // triggers 71340 + SPELL_VAMPIRIC_BITE = 71837, // triggers 71726 and 70946 + SPELL_TWILIGHT_BLOODBOLT = 71445, // triggers 72313, 71446 and 71818 + SPELL_DELIRIOUS_SLASH = 72261, // heroic only - triggers 71623 and 72264 + SPELL_PRESENCE_OF_DARKFALLEN = 70994, // heroic only - triggers 71958, 71959 and 71952 + SPELL_THIRST_QUENCHED = 72154, // related to quest 24756 + + // air phase + SPELL_INCITE_TERROR = 73070, + SPELL_BLOODBOLT_WHIRL = 71772, + + // others + // NPC_SWARMING_SHADOWS = 38163, // has aura 71267 (or 71277?) + + // encounter phases + PHASE_GROUND = 1, + PHASE_RUNNING = 2, + PHASE_AIR = 3, + PHASE_FLYING = 4, + + // movement points + POINT_CENTER_GROUND = 1, + POINT_CENTER_AIR = 2 +}; + +static const float aQueenPosition[2][3] = +{ + {4595.64f, 2769.19f, 400.13f}, + {4595.90f, 2769.31f, 421.83f}, +}; + +struct boss_blood_queen_lanathelAI : public ScriptedAI +{ + boss_blood_queen_lanathelAI(Creature* pCreature) : ScriptedAI(pCreature) + { + m_pInstance = (instance_icecrown_citadel*)pCreature->GetInstanceData(); + Reset(); + } + + instance_icecrown_citadel* m_pInstance; + + uint8 m_uiPhase; + uint32 m_uiPhaseTimer; + + uint32 m_uiBloodMirrorTimer; + uint32 m_uiEnrageTimer; + uint32 m_uiVampiricBiteTimer; + uint32 m_uiBloodboltTimer; + uint32 m_uiPactDarkfallenTimer; + uint32 m_uiSwarmingShadowsTimer; + uint32 m_uiDeliriousSlashTimer; + + void Reset() override + { + m_uiPhase = PHASE_GROUND; + m_uiPhaseTimer = 120000; // 2 min + + m_uiEnrageTimer = 330000; // 5 min and 30 secs + m_uiBloodMirrorTimer = 0; + m_uiDeliriousSlashTimer = 20000; + m_uiVampiricBiteTimer = 15000; + m_uiBloodboltTimer = urand(15000, 20000); + m_uiPactDarkfallenTimer = 15000; + m_uiSwarmingShadowsTimer = 30000; + + m_creature->RemoveByteFlag(UNIT_FIELD_BYTES_1, 3, UNIT_BYTE1_FLAG_ALWAYS_STAND | UNIT_BYTE1_FLAG_UNK_2); + } + + void JustReachedHome() override + { + if (m_pInstance) + m_pInstance->SetData(TYPE_QUEEN_LANATHEL, FAIL); + } + + void KilledUnit(Unit* /*pVictim*/) override + { + DoScriptText(urand(0, 1) ? SAY_SLAY_1 : SAY_SLAY_2, m_creature); + } + + void Aggro(Unit* /*pWho*/) override + { + DoScriptText(SAY_AGGRO, m_creature); + DoCastSpellIfCan(m_creature, SPELL_SHROUD_OF_SORROW, CAST_TRIGGERED); + + if (m_pInstance) + { + m_pInstance->SetData(TYPE_QUEEN_LANATHEL, IN_PROGRESS); + + if (m_pInstance->IsHeroicDifficulty()) + DoCastSpellIfCan(m_creature, SPELL_PRESENCE_OF_DARKFALLEN, CAST_TRIGGERED); + } + } + + void JustDied(Unit* /*pKiller*/) override + { + DoScriptText(SAY_DEATH, m_creature); + + if (m_pInstance) + m_pInstance->SetData(TYPE_QUEEN_LANATHEL, DONE); + } + + void MovementInform(uint32 uiMovementType, uint32 uiPointId) override + { + if (uiMovementType != POINT_MOTION_TYPE) + return; + + if (uiPointId == POINT_CENTER_GROUND) + { + if (m_uiPhase == PHASE_RUNNING) + { + if (DoCastSpellIfCan(m_creature, SPELL_INCITE_TERROR) == CAST_OK) + { + m_uiPhase = PHASE_FLYING; + + m_creature->SetLevitate(true); + m_creature->SetByteFlag(UNIT_FIELD_BYTES_1, 3, UNIT_BYTE1_FLAG_ALWAYS_STAND | UNIT_BYTE1_FLAG_UNK_2); + + m_creature->GetMotionMaster()->Clear(); + m_creature->GetMotionMaster()->MovePoint(POINT_CENTER_AIR, aQueenPosition[1][0], aQueenPosition[1][1], aQueenPosition[1][2], false); + } + } + else if (m_uiPhase == PHASE_FLYING) + { + m_uiPhase = PHASE_GROUND; + m_uiPhaseTimer = 120000; + SetCombatMovement(true); + + m_creature->SetLevitate(false); + m_creature->RemoveByteFlag(UNIT_FIELD_BYTES_1, 3, UNIT_BYTE1_FLAG_ALWAYS_STAND | UNIT_BYTE1_FLAG_UNK_2); + + m_creature->GetMotionMaster()->Clear(); + if (m_creature->getVictim()) + m_creature->GetMotionMaster()->MoveChase(m_creature->getVictim()); + } + } + else if (uiPointId == POINT_CENTER_AIR) + { + if (DoCastSpellIfCan(m_creature, SPELL_BLOODBOLT_WHIRL) == CAST_OK) + { + DoScriptText(SAY_AIR_PHASE, m_creature); + m_uiPhase = PHASE_AIR; + m_uiPhaseTimer = 7000; + } + } + } + + void UpdateAI(const uint32 uiDiff) override + { + if (!m_creature->SelectHostileTarget() || !m_creature->getVictim()) + return; + + if (m_uiEnrageTimer) + { + if (m_uiEnrageTimer <= uiDiff) + { + if (DoCastSpellIfCan(m_creature, SPELL_BERSERK) == CAST_OK) + { + DoScriptText(SAY_BERSERK, m_creature); + m_uiEnrageTimer = 0; + } + } + else + m_uiEnrageTimer -= uiDiff; + } + + switch (m_uiPhase) + { + case PHASE_GROUND: + { + // Air phase change timer + if (m_uiPhaseTimer < uiDiff) + { + SetCombatMovement(false); + m_creature->GetMotionMaster()->Clear(); + m_creature->GetMotionMaster()->MovePoint(POINT_CENTER_GROUND, aQueenPosition[0][0], aQueenPosition[0][1], aQueenPosition[0][2]); + + m_uiPhase = PHASE_RUNNING; + m_uiPhaseTimer = 0; + } + else + m_uiPhaseTimer -= uiDiff; + + // Only one bite per fight + if (m_uiVampiricBiteTimer) + { + if (m_uiVampiricBiteTimer <= uiDiff) + { + if (DoCastSpellIfCan(m_creature, SPELL_VAMPIRIC_BITE) == CAST_OK) + { + DoScriptText(urand(0, 1) ? SAY_BITE_1 : SAY_BITE_2, m_creature); + m_uiVampiricBiteTimer = 0; + } + } + else + m_uiVampiricBiteTimer -= uiDiff; + } + + if (m_uiBloodMirrorTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature, SPELL_BLOOD_MIRROR) == CAST_OK) + m_uiBloodMirrorTimer = 5000; + } + else + m_uiBloodMirrorTimer -= uiDiff; + + if (m_uiBloodboltTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature, SPELL_TWILIGHT_BLOODBOLT) == CAST_OK) + m_uiBloodboltTimer = urand(15000, 20000); + } + else + m_uiBloodboltTimer -= uiDiff; + + if (m_uiPactDarkfallenTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature, SPELL_PACT_OF_THE_DARKFALLEN) == CAST_OK) + { + DoScriptText(SAY_PACT, m_creature); + m_uiPactDarkfallenTimer = urand(20000, 25000); + } + } + else + m_uiPactDarkfallenTimer -= uiDiff; + + if (m_uiSwarmingShadowsTimer <= uiDiff) + { + if (DoCastSpellIfCan(m_creature, SPELL_SWARMING_SHADOWS) == CAST_OK) + { + DoScriptText(SAY_SHADOWS, m_creature); + m_uiSwarmingShadowsTimer = urand(30000, 35000); + } + } + else + m_uiSwarmingShadowsTimer -= uiDiff; + + // Heroic spells + if (m_pInstance && m_pInstance->IsHeroicDifficulty()) + { + if (m_uiDeliriousSlashTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature, SPELL_DELIRIOUS_SLASH) == CAST_OK) + m_uiDeliriousSlashTimer = 15000; + } + else + m_uiDeliriousSlashTimer -= uiDiff; + } + + DoMeleeAttackIfReady(); + + break; + } + case PHASE_RUNNING: + case PHASE_FLYING: + { + // Nothing here. Wait for arriving at the point + break; + } + case PHASE_AIR: + { + if (m_uiPhaseTimer < uiDiff) + { + m_uiPhase = PHASE_FLYING; + m_uiPhaseTimer = 0; + + m_creature->GetMotionMaster()->Clear(); + m_creature->GetMotionMaster()->MovePoint(POINT_CENTER_GROUND, aQueenPosition[0][0], aQueenPosition[0][1], aQueenPosition[0][2]); + } + else + m_uiPhaseTimer -= uiDiff; + + break; + } + } + } +}; + +CreatureAI* GetAI_boss_blood_queen_lanathel(Creature* pCreature) +{ + return new boss_blood_queen_lanathelAI(pCreature); +} + +void AddSC_boss_blood_queen_lanathel() +{ + Script* pNewScript; + + pNewScript = new Script; + pNewScript->Name = "boss_blood_queen_lanathel"; + pNewScript->GetAI = &GetAI_boss_blood_queen_lanathel; + pNewScript->RegisterSelf(); +} diff --git a/src/modules/SD2/scripts/northrend/icecrown_citadel/icecrown_citadel/boss_deathbringer_saurfang.cpp b/src/modules/SD2/scripts/northrend/icecrown_citadel/icecrown_citadel/boss_deathbringer_saurfang.cpp new file mode 100644 index 000000000..3b6c19327 --- /dev/null +++ b/src/modules/SD2/scripts/northrend/icecrown_citadel/icecrown_citadel/boss_deathbringer_saurfang.cpp @@ -0,0 +1,403 @@ +/* Copyright (C) 2006 - 2013 ScriptDev2 + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +/* ScriptData +SDName: boss_deathbringer_saurfang +SD%Complete: 60% +SDComment: Blood Power and all the related spells require core support; Intro and Outro event NYI. +SDCategory: Icecrown Citadel +EndScriptData */ + +#include "precompiled.h" +#include "icecrown_citadel.h" + +enum +{ + SAY_AGGRO = -1631028, + SAY_FALLENCHAMPION = -1631029, + SAY_BLOODBEASTS = -1631030, + SAY_SLAY_1 = -1631031, + SAY_SLAY_2 = -1631032, + SAY_BERSERK = -1631033, + SAY_DEATH = -1631034, + EMOTE_FRENZY = -1631193, + EMOTE_SCENT = -1631194, + + SAY_INTRO_ALLY_0 = -1631035, + SAY_INTRO_ALLY_1 = -1631036, + SAY_INTRO_ALLY_2 = -1631037, + SAY_INTRO_ALLY_3 = -1631038, + SAY_INTRO_ALLY_4 = -1631039, + SAY_INTRO_ALLY_5 = -1631040, + SAY_INTRO_HORDE_1 = -1631041, + SAY_INTRO_HORDE_2 = -1631042, + SAY_INTRO_HORDE_3 = -1631043, + SAY_INTRO_HORDE_4 = -1631044, + SAY_INTRO_HORDE_5 = -1631045, + SAY_INTRO_HORDE_6 = -1631046, + SAY_INTRO_HORDE_7 = -1631047, + SAY_INTRO_HORDE_8 = -1631048, + SAY_INTRO_HORDE_9 = -1631049, + SAY_OUTRO_ALLY_1 = -1631050, + SAY_OUTRO_ALLY_2 = -1631051, + SAY_OUTRO_ALLY_3 = -1631052, + SAY_OUTRO_ALLY_4 = -1631053, + SAY_OUTRO_ALLY_5 = -1631054, + SAY_OUTRO_ALLY_6 = -1631055, + SAY_OUTRO_ALLY_7 = -1631056, + SAY_OUTRO_ALLY_8 = -1631057, + SAY_OUTRO_ALLY_9 = -1631058, + SAY_OUTRO_ALLY_10 = -1631059, + SAY_OUTRO_ALLY_11 = -1631060, + SAY_OUTRO_ALLY_12 = -1631061, + SAY_OUTRO_ALLY_13 = -1631062, + SAY_OUTRO_ALLY_14 = -1631063, + SAY_OUTRO_ALLY_15 = -1631064, + SAY_OUTRO_ALLY_16 = -1631065, + SAY_OUTRO_HORDE_1 = -1631066, + SAY_OUTRO_HORDE_2 = -1631067, + SAY_OUTRO_HORDE_3 = -1631068, + SAY_OUTRO_HORDE_4 = -1631069, + + // intro event related + // SPELL_GRIP_OF_AGONY = 70572, + // SPELL_VEHICLE_HARDCODED = 46598, + + // combat spells + SPELL_BLOOD_POWER = 72371, + // SPELL_BLOOD_POWER_SCALE = 72370, // included in creature_template_addon + // SPELL_ZERO_POWER = 72242, // included in creature_template_addon + + // SPELL_MARK_FALLEN_DAMAGE = 72256, // procs on Saurfang melee attack - 72255 - included in creature_template_addon + SPELL_MARK_FALLEN_CHAMPION = 72293, // procs on target death - 72260 + SPELL_REMOVE_MARKS = 72257, + + // SPELL_RUNE_OF_BLOOD_PROC = 72408, // procs on Saurfang mele attack - 72409 - included in creature_template_addon + SPELL_RUNE_OF_BLOOD = 72410, + + SPELL_BLOOD_NOVA = 72378, + SPELL_BOILING_BLOOD = 72385, + + SPELL_CALL_BLOOD_BEAST_1 = 72172, // summons 38508 + SPELL_CALL_BLOOD_BEAST_2 = 72173, + SPELL_CALL_BLOOD_BEAST_3 = 72356, + SPELL_CALL_BLOOD_BEAST_4 = 72357, + SPELL_CALL_BLOOD_BEAST_5 = 72358, + SPELL_SCENT_OF_BLOOD = 72769, // triggers 72771 on the blood beasts + + SPELL_BERSERK = 26662, + SPELL_FRENZY = 72737, + + // Summoned spells + SPELL_RESISTANT_SKIN = 72723, + SPELL_BLOOD_LINK_BEAST = 72176, + + FACTION_ID_UNDEAD = 974, + + POINT_ID_INTRO = 1, + POINT_ID_EVADE = 2, +}; + +static const float fIntroPosition[4] = { -491.30f, 2211.35f, 541.11f, 3.16f}; + +struct boss_deathbringer_saurfangAI : public ScriptedAI +{ + boss_deathbringer_saurfangAI(Creature* pCreature) : ScriptedAI(pCreature) + { + m_pInstance = (instance_icecrown_citadel*)pCreature->GetInstanceData(); + m_powerBloodPower = m_creature->GetPowerType(); + m_bIsIntroDone = false; + Reset(); + } + + instance_icecrown_citadel* m_pInstance; + + uint32 m_uiRuneOfBloodTimer; + uint32 m_uiBoilingBloodTimer; + uint32 m_uiBloodNovaTimer; + uint32 m_uiBloodBeastsTimer; + uint32 m_uiScentOfBloodTimer; + uint32 m_uiBerserkTimer; + + bool m_bIsFrenzied; + bool m_bIsIntroDone; + + Powers m_powerBloodPower; + + void Reset() override + { + m_uiRuneOfBloodTimer = 25000; + m_uiBoilingBloodTimer = 19000; + m_uiBloodNovaTimer = 20000; + m_uiBloodBeastsTimer = 40000; + m_uiScentOfBloodTimer = 47000; + m_uiBerserkTimer = 8 * MINUTE * IN_MILLISECONDS; + + if (m_pInstance && m_pInstance->IsHeroicDifficulty()) + m_uiBerserkTimer = 6 * MINUTE * IN_MILLISECONDS; + + m_bIsFrenzied = false; + + m_creature->SetPower(m_powerBloodPower, 0); + } + + void Aggro(Unit* /*pWho*/) override + { + DoScriptText(SAY_AGGRO, m_creature); + DoCastSpellIfCan(m_creature, SPELL_BLOOD_POWER, CAST_TRIGGERED); + + if (m_pInstance) + m_pInstance->SetData(TYPE_DEATHBRINGER_SAURFANG, IN_PROGRESS); + } + + void MoveInLineOfSight(Unit* pWho) override + { + if (!m_bIsIntroDone && pWho->GetTypeId() == TYPEID_PLAYER && !((Player*)pWho)->isGameMaster() && m_creature->GetDistance2d(pWho) < 50.0f) + { + m_creature->GetMotionMaster()->MovePoint(POINT_ID_INTRO, fIntroPosition[0], fIntroPosition[1], fIntroPosition[2]); + if (m_pInstance) + m_pInstance->DoUseDoorOrButton(GO_SAURFANG_DOOR); + m_bIsIntroDone = true; + } + + ScriptedAI::MoveInLineOfSight(pWho); + } + + void KilledUnit(Unit* pVictim) override + { + if (pVictim->GetTypeId() != TYPEID_PLAYER) + return; + + if (urand(0, 1)) + DoScriptText(urand(0, 1) ? SAY_SLAY_1 : SAY_SLAY_2, m_creature); + } + + void JustDied(Unit* /*pKiller*/) override + { + DoScriptText(SAY_DEATH, m_creature); + DoCastSpellIfCan(m_creature, SPELL_REMOVE_MARKS, CAST_TRIGGERED); + + if (m_pInstance) + m_pInstance->SetData(TYPE_DEATHBRINGER_SAURFANG, DONE); + } + + void JustReachedHome() override + { + if (m_pInstance) + m_pInstance->SetData(TYPE_DEATHBRINGER_SAURFANG, FAIL); + } + + void EnterEvadeMode() override + { + m_creature->RemoveAllAurasOnEvade(); + m_creature->DeleteThreatList(); + m_creature->CombatStop(true); + + // Boss needs to evade to the point in front of the door + if (m_creature->IsAlive()) + m_creature->GetMotionMaster()->MovePoint(POINT_ID_EVADE, fIntroPosition[0], fIntroPosition[1], fIntroPosition[2]); + + m_creature->SetLootRecipient(NULL); + + Reset(); + } + + void MovementInform(uint32 uiMoveType, uint32 uiPointId) override + { + if (uiMoveType != POINT_MOTION_TYPE) + return; + + if (uiPointId == POINT_ID_EVADE) + { + m_creature->SetFacingTo(fIntroPosition[3]); + + if (m_pInstance) + m_pInstance->SetData(TYPE_DEATHBRINGER_SAURFANG, FAIL); + } + else if (uiPointId == POINT_ID_INTRO) + { + if (m_pInstance) + m_pInstance->DoUseDoorOrButton(GO_SAURFANG_DOOR); + + // Note: this should be done only after the intro event is finished + // ToDo: move this to the proper place after the intro will be implemented + // Also the faction needs to be checked if it should be handled in database + m_creature->setFaction(FACTION_ID_UNDEAD); + m_creature->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE | UNIT_FLAG_PASSIVE); + m_creature->SetInCombatWithZone(); + } + } + + void JustSummoned(Creature* pSummoned) override + { + pSummoned->CastSpell(pSummoned, SPELL_RESISTANT_SKIN, true); + pSummoned->CastSpell(pSummoned, SPELL_BLOOD_LINK_BEAST, true); + + // Note: the summoned should be activated only after 2-3 seconds after summon - can be done in eventAI + if (Unit* pTarget = m_creature->SelectAttackingTarget(ATTACKING_TARGET_RANDOM, 0)) + pSummoned->AI()->AttackStart(pTarget); + } + + // Wrapper to help get a random player for the Mark of the Fallen Champion + Unit* SelectRandomPlayerForMark() + { + // Search only for players which are not within 18 yards of the boss + std::vector suitableTargets; + ThreatList const& threatList = m_creature->GetThreatManager().getThreatList(); + + for (ThreatList::const_iterator itr = threatList.begin(); itr != threatList.end(); ++itr) + { + if (Unit* pTarget = m_creature->GetMap()->GetUnit((*itr)->getUnitGuid())) + { + if (pTarget->GetTypeId() == TYPEID_PLAYER && pTarget != m_creature->getVictim() && !pTarget->HasAura(SPELL_MARK_FALLEN_CHAMPION)) + suitableTargets.push_back(pTarget); + } + } + + if (suitableTargets.empty()) + return m_creature->getVictim(); + else + return suitableTargets[urand(0, suitableTargets.size() - 1)]; + } + + void UpdateAI(const uint32 uiDiff) override + { + if (!m_creature->SelectHostileTarget() || !m_creature->getVictim()) + return; + + // Mark of the Fallen Champion + // ToDo: enable this when blood power is fully supported by the core + /*if (m_creature->GetPower(m_powerBloodPower) >= 100) + { + if (Unit* pTarget = SelectRandomPlayerForMark()) + { + if (DoCastSpellIfCan(pTarget, SPELL_MARK_FALLEN_CHAMPION) == CAST_OK) + { + DoScriptText(SAY_FALLENCHAMPION, m_creature); + m_creature->SetPower(m_powerBloodPower, 0); + } + } + }*/ + + // Frenzy (soft enrage) + if (!m_bIsFrenzied) + { + if (m_creature->GetHealthPercent() <= 30.0f) + { + if (DoCastSpellIfCan(m_creature, SPELL_FRENZY) == CAST_OK) + { + DoScriptText(EMOTE_FRENZY, m_creature); + m_bIsFrenzied = true; + } + } + } + + // Berserk (hard enrage) + if (m_uiBerserkTimer) + { + if (m_uiBerserkTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature, SPELL_BERSERK) == CAST_OK) + { + DoScriptText(SAY_BERSERK, m_creature); + m_uiBerserkTimer = 0; + } + } + else + m_uiBerserkTimer -= uiDiff; + } + + // Rune of Blood + if (m_uiRuneOfBloodTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature->getVictim(), SPELL_RUNE_OF_BLOOD) == CAST_OK) + m_uiRuneOfBloodTimer = 25000; + } + else + m_uiRuneOfBloodTimer -= uiDiff; + + // Boiling Blood + if (m_uiBoilingBloodTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature, SPELL_BOILING_BLOOD) == CAST_OK) + m_uiBoilingBloodTimer = 15000; + } + else + m_uiBoilingBloodTimer -= uiDiff; + + // Blood Nova + if (m_uiBloodNovaTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature, SPELL_BLOOD_NOVA) == CAST_OK) + m_uiBloodNovaTimer = 20000; + } + else + m_uiBloodNovaTimer -= uiDiff; + + // Call Blood Beasts + if (m_uiBloodBeastsTimer < uiDiff) + { + DoScriptText(SAY_BLOODBEASTS, m_creature); + + DoCastSpellIfCan(m_creature, SPELL_CALL_BLOOD_BEAST_1, CAST_TRIGGERED); + DoCastSpellIfCan(m_creature, SPELL_CALL_BLOOD_BEAST_2, CAST_TRIGGERED); + + if (m_pInstance && m_pInstance->Is25ManDifficulty()) + { + DoCastSpellIfCan(m_creature, SPELL_CALL_BLOOD_BEAST_3, CAST_TRIGGERED); + DoCastSpellIfCan(m_creature, SPELL_CALL_BLOOD_BEAST_4, CAST_TRIGGERED); + DoCastSpellIfCan(m_creature, SPELL_CALL_BLOOD_BEAST_5, CAST_TRIGGERED); + } + + m_uiBloodBeastsTimer = 40000; + m_uiScentOfBloodTimer = 7000; + } + else + m_uiBloodBeastsTimer -= uiDiff; + + // Scent of Blood + if (m_pInstance && m_pInstance->IsHeroicDifficulty()) + { + if (m_uiScentOfBloodTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature, SPELL_SCENT_OF_BLOOD) == CAST_OK) + { + DoScriptText(EMOTE_SCENT, m_creature); + m_uiScentOfBloodTimer = 40000; + } + } + else + m_uiScentOfBloodTimer -= uiDiff; + } + + DoMeleeAttackIfReady(); + } +}; + +CreatureAI* GetAI_boss_deathbringer_saurfang(Creature* pCreature) +{ + return new boss_deathbringer_saurfangAI(pCreature); +} + +void AddSC_boss_deathbringer_saurfang() +{ + Script* pNewScript; + + pNewScript = new Script; + pNewScript->Name = "boss_deathbringer_saurfang"; + pNewScript->GetAI = &GetAI_boss_deathbringer_saurfang; + pNewScript->RegisterSelf(); +} diff --git a/src/modules/SD2/scripts/northrend/icecrown_citadel/icecrown_citadel/boss_festergut.cpp b/src/modules/SD2/scripts/northrend/icecrown_citadel/icecrown_citadel/boss_festergut.cpp new file mode 100644 index 000000000..b5745a0b1 --- /dev/null +++ b/src/modules/SD2/scripts/northrend/icecrown_citadel/icecrown_citadel/boss_festergut.cpp @@ -0,0 +1,227 @@ +/* Copyright (C) 2006 - 2013 ScriptDev2 + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +/* ScriptData +SDName: boss_festergut +SD%Complete: 90% +SDComment: +SDCategory: Icecrown Citadel +EndScriptData */ + +#include "precompiled.h" +#include "icecrown_citadel.h" + +enum +{ + SPELL_BERSERK = 47008, + + // Gastric Bloat + SPELL_GASTRIC_BLOAT = 72214, // proc aura, ~8 sec cooldown, cooldown for Creature requires implementation in core + + // Inhale Blight + SPELL_INHALE_BLIGHT = 69165, + SPELL_INHALED_BLIGHT_10 = 69166, + SPELL_INHALED_BLIGHT_25 = 71912, + + // Pungent Blight + SPELL_PUNGENT_BLIGHT = 69195, + + // Gaseous Blight + SPELL_GASEUS_BLIGHT_DUMMY = 69125, // gas is spread into the room on aggro + // periodic auras spells + SPELL_GASEOUS_BLIGHT_1 = 69157, + SPELL_GASEOUS_BLIGHT_2 = 69162, + SPELL_GASEOUS_BLIGHT_3 = 69164, + + // visual gas dummy auras + SPELL_GASEOUS_BLIGHT_DUMMY1 = 69126, + SPELL_GASEOUS_BLIGHT_DUMMY2 = 69152, + SPELL_GASEOUS_BLIGHT_DUMMY3 = 69154, + + // Inoculent + SPELL_REMOVE_INOCULENT = 69298, + + // Gas Spore + SPELL_GAS_SPORE = 69278, + + // Vile Gas + SPELL_VILE_GAS_SUMMON = 72288, + SPELL_VILE_GAS = 71307 +}; + +enum +{ + SAY_AGGRO = -1631082, + SAY_BLIGHT = -1631083, + SAY_SPORE = -1631084, + SAY_PUNGUENT_BLIGHT = -1631085, + SAY_PUNGUENT_BLIGHT_EMOTE = -1631086, + SAY_SLAY_1 = -1631087, + SAY_SLAY_2 = -1631088, + SAY_BERSERK = -1631089, + SAY_DEATH = -1631090, + SAY_FESTERGUT_DEATH = -1631091, +}; + +struct boss_festergutAI : public ScriptedAI +{ + boss_festergutAI(Creature* pCreature) : ScriptedAI(pCreature) + { + m_pInstance = (instance_icecrown_citadel*)pCreature->GetMap()->GetInstanceData(); + Reset(); + } + + instance_icecrown_citadel* m_pInstance; + + uint32 m_uiBerserkTimer; + uint32 m_uiGastricBloatTimer; + uint32 m_uiInhaleBlightTimer; + uint32 m_uiGasSporeTimer; + uint32 m_uiVileGasTimer; + + void Reset() override + { + m_uiBerserkTimer = 5 * MINUTE * IN_MILLISECONDS; + m_uiGastricBloatTimer = 10000; + m_uiInhaleBlightTimer = 30000; + m_uiGasSporeTimer = 20000; + m_uiVileGasTimer = 10000; + } + + void Aggro(Unit* /*pWho*/) override + { + DoScriptText(SAY_AGGRO, m_creature); + + DoCastSpellIfCan(m_creature, SPELL_GASTRIC_BLOAT, CAST_TRIGGERED); // not working as intended currently + DoCastSpellIfCan(m_creature, SPELL_GASEOUS_BLIGHT_1, CAST_TRIGGERED); // DoT aura + DoCastSpellIfCan(m_creature, SPELL_GASEUS_BLIGHT_DUMMY, CAST_TRIGGERED); // visual cast on dummy npc + + if (m_pInstance) + m_pInstance->SetData(TYPE_FESTERGUT, IN_PROGRESS); + } + + void KilledUnit(Unit* /*pVictim*/) override + { + DoScriptText(urand(0, 1) ? SAY_SLAY_1 : SAY_SLAY_2, m_creature); + } + + void JustReachedHome() override + { + if (m_pInstance) + m_pInstance->SetData(TYPE_FESTERGUT, FAIL); + + DoCastSpellIfCan(m_creature, SPELL_REMOVE_INOCULENT, CAST_TRIGGERED); + } + + void JustDied(Unit* /*pKiller*/) override + { + if (m_pInstance) + m_pInstance->SetData(TYPE_FESTERGUT, DONE); + + DoScriptText(SAY_DEATH, m_creature); + DoCastSpellIfCan(m_creature, SPELL_REMOVE_INOCULENT, CAST_TRIGGERED); + } + + void UpdateAI(const uint32 uiDiff) override + { + if (!m_creature->SelectHostileTarget() || !m_creature->getVictim()) + return; + + // Berserk + if (m_uiBerserkTimer <= uiDiff) + { + if (DoCastSpellIfCan(m_creature, SPELL_BERSERK) == CAST_OK) + { + DoScriptText(SAY_BERSERK, m_creature); + m_uiBerserkTimer = 5 * MINUTE * IN_MILLISECONDS; + } + } + else + m_uiBerserkTimer -= uiDiff; + + // Inhale Blight and Pungent Blight + if (m_uiInhaleBlightTimer <= uiDiff) + { + SpellAuraHolder* holder = m_creature->GetSpellAuraHolder(SPELL_INHALED_BLIGHT_10); + + if (!holder) + holder = m_creature->GetSpellAuraHolder(SPELL_INHALED_BLIGHT_25); + + // inhale the gas or if already have 3 stacks - release it + if (holder && holder->GetStackAmount() >= 3) + { + if (DoCastSpellIfCan(m_creature, SPELL_PUNGENT_BLIGHT) == CAST_OK) + { + DoScriptText(SAY_PUNGUENT_BLIGHT_EMOTE, m_creature); + DoScriptText(SAY_PUNGUENT_BLIGHT, m_creature); + m_uiInhaleBlightTimer = 35000; + } + } + else if (DoCastSpellIfCan(m_creature, SPELL_INHALE_BLIGHT) == CAST_OK) + { + if (m_pInstance) + { + if (Creature* pProfessor = m_pInstance->GetSingleCreatureFromStorage(NPC_PROFESSOR_PUTRICIDE)) + DoScriptText(SAY_BLIGHT, pProfessor); + } + m_uiInhaleBlightTimer = 30000; + } + } + else + m_uiInhaleBlightTimer -= uiDiff; + + // Gas Spore + if (m_uiGasSporeTimer <= uiDiff) + { + if (DoCastSpellIfCan(m_creature, SPELL_GAS_SPORE) == CAST_OK) + { + DoScriptText(SAY_SPORE, m_creature); + m_uiGasSporeTimer = 40000; + } + } + else + m_uiGasSporeTimer -= uiDiff; + + // Vile Gas + if (m_uiVileGasTimer <= uiDiff) + { + if (DoCastSpellIfCan(m_creature, SPELL_VILE_GAS_SUMMON, CAST_TRIGGERED) == CAST_OK) + { + if (DoCastSpellIfCan(m_creature, SPELL_VILE_GAS) == CAST_OK) + m_uiVileGasTimer = 30000; + } + } + else + m_uiVileGasTimer -= uiDiff; + + DoMeleeAttackIfReady(); + } +}; + +CreatureAI* GetAI_boss_festergut(Creature* pCreature) +{ + return new boss_festergutAI(pCreature); +} + +void AddSC_boss_festergut() +{ + Script* pNewScript; + + pNewScript = new Script; + pNewScript->Name = "boss_festergut"; + pNewScript->GetAI = &GetAI_boss_festergut; + pNewScript->RegisterSelf(); +} diff --git a/src/modules/SD2/scripts/northrend/icecrown_citadel/icecrown_citadel/boss_lady_deathwhisper.cpp b/src/modules/SD2/scripts/northrend/icecrown_citadel/icecrown_citadel/boss_lady_deathwhisper.cpp new file mode 100644 index 000000000..bb40b6f90 --- /dev/null +++ b/src/modules/SD2/scripts/northrend/icecrown_citadel/icecrown_citadel/boss_lady_deathwhisper.cpp @@ -0,0 +1,509 @@ +/* Copyright (C) 2006 - 2013 ScriptDev2 + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +/* ScriptData +SDName: boss_lady_deathwhisper +SD%Complete: 95% +SDComment: Minor adjustments may be required +SDCategory: Icecrown Citadel +EndScriptData */ + +#include "precompiled.h" +#include "icecrown_citadel.h" + +enum +{ + // yells + SAY_AGGRO = -1631018, + SAY_PHASE_TWO = -1631019, + SAY_DARK_EMPOWERMENT = -1631020, + SAY_DARK_TRANSFORMATION = -1631021, + SAY_ANIMATE_DEAD = -1631022, + SAY_DOMINATE_MIND = -1631023, + SAY_BERSERK = -1631024, + SAY_DEATH = -1631025, + SAY_SLAY_1 = -1631026, + SAY_SLAY_2 = -1631027, + + // spells - phase 1 + SPELL_SHADOW_CHANNELING = 43897, + SPELL_MANA_BARRIER = 70842, + SPELL_SHADOW_BOLT = 71254, + + // phase 2 + SPELL_INSIGNIFICANCE = 71204, + SPELL_FROSTBOLT = 71420, + SPELL_FROSTBOLT_VOLLEY = 72905, + SPELL_SUMMON_SPIRIT = 71363, // triggers 71426 + + // common + SPELL_BERSERK = 26662, + SPELL_DOMINATE_MIND = 71289, + SPELL_DEATH_AND_DECAY = 71001, + SPELL_DARK_EMPOWERMENT = 70896, // dummy - triggers 70901 - only on Adherents - transforms target into 38136 + SPELL_DARK_TRANSFORMATION = 70895, // dummy - triggers 70900 - only on Fanatics - transforms target into 38135 + SPELL_DARK_MARTYRDOM = 70897, // dummy - triggers 70903 on Adherents or 71236 on Fanatics + // SPELL_SUMMON_ADHERENT = 70820, // cast by the stalkers - only server side + // SPELL_SUMMON_FANATIC = 70819, // cast by the stalkers - only server side + + // npcs + NPC_CULT_ADHERENT = 37949, + NPC_CULT_FANATIC = 37890, + NPC_VENGEFUL_SHADE = 38222, // has aura 71494 +}; + +static const uint32 aLeftSummonedCultists[3] = {NPC_CULT_ADHERENT, NPC_CULT_FANATIC, NPC_CULT_ADHERENT}; +static const uint32 aRightSummonedCultists[3] = {NPC_CULT_FANATIC, NPC_CULT_ADHERENT, NPC_CULT_FANATIC}; + +struct boss_lady_deathwhisperAI : public ScriptedAI +{ + boss_lady_deathwhisperAI(Creature* pCreature) : ScriptedAI(pCreature) + { + m_pInstance = (instance_icecrown_citadel*)pCreature->GetInstanceData(); + Reset(); + } + + instance_icecrown_citadel* m_pInstance; + + bool m_bIsPhaseOne; + bool m_bIsLeftSideSummon; + + uint32 m_uiBerserkTimer; + uint32 m_uiSummonWaveTimer; + uint32 m_uiCultistBuffTimer; + uint32 m_uiDarkMartyrdomTimer; + uint32 m_uiTouchOfInsignificanceTimer; + uint32 m_uiShadowBoltTimer; + uint32 m_uiDeathAndDecayTimer; + uint32 m_uiFrostboltTimer; + uint32 m_uiFrostboltVolleyTimer; + uint32 m_uiDominateMindTimer; + uint32 m_uiVengefulShadeTimer; + + uint8 m_uiMindControlCount; + + GuidList m_lCultistSpawnedGuidList; + GuidVector m_vRightStalkersGuidVector; + GuidVector m_vLeftStalkersGuidVector; + ObjectGuid m_middleStalkerGuid; + + void Reset() override + { + m_bIsPhaseOne = true; + m_bIsLeftSideSummon = roll_chance_i(50) ? true : false; + m_uiBerserkTimer = 10 * MINUTE * IN_MILLISECONDS; + m_uiSummonWaveTimer = 10000; + m_uiCultistBuffTimer = 0; + m_uiDarkMartyrdomTimer = 30000; + m_uiTouchOfInsignificanceTimer = 7000; + m_uiShadowBoltTimer = 2000; + m_uiDeathAndDecayTimer = urand(10000, 15000); + m_uiFrostboltTimer = urand(5000, 10000); + m_uiFrostboltVolleyTimer = 5000; + m_uiDominateMindTimer = urand(30000, 45000); + m_uiVengefulShadeTimer = 10000; + m_uiMindControlCount = 0; + + SetCombatMovement(false); + DoCastSpellIfCan(m_creature, SPELL_SHADOW_CHANNELING); + + // Set the max allowed mind control targets + if (m_pInstance) + { + if (m_pInstance->Is25ManDifficulty()) + m_uiMindControlCount = m_pInstance->IsHeroicDifficulty() ? 3 : 1; + else + m_uiMindControlCount = m_pInstance->IsHeroicDifficulty() ? 1 : 0; + } + } + + void Aggro(Unit* /*pWho*/) override + { + DoScriptText(SAY_AGGRO, m_creature); + DoCastSpellIfCan(m_creature, SPELL_MANA_BARRIER, CAST_TRIGGERED); + + if (m_pInstance) + { + m_pInstance->SetData(TYPE_LADY_DEATHWHISPER, IN_PROGRESS); + + // Sort the summoning stalkers + GuidList lStalkersGuidList; + m_pInstance->GetDeathwhisperStalkersList(lStalkersGuidList); + DoSortSummoningStalkers(lStalkersGuidList); + } + } + + void KilledUnit(Unit* pVictim) override + { + if (pVictim->GetTypeId() != TYPEID_PLAYER) + return; + + if (urand(0, 1)) + DoScriptText(urand(0, 1) ? SAY_SLAY_1 : SAY_SLAY_2, m_creature); + } + + void JustDied(Unit* /*pKiller*/) override + { + DoScriptText(SAY_DEATH, m_creature); + + if (m_pInstance) + m_pInstance->SetData(TYPE_LADY_DEATHWHISPER, DONE); + } + + void JustReachedHome() override + { + if (m_pInstance) + m_pInstance->SetData(TYPE_LADY_DEATHWHISPER, FAIL); + } + + void JustSummoned(Creature* pSummoned) override + { + switch (pSummoned->GetEntry()) + { + case NPC_CULT_ADHERENT: + case NPC_CULT_FANATIC: + m_lCultistSpawnedGuidList.push_back(pSummoned->GetObjectGuid()); + break; + } + + if (Unit* pTarget = m_creature->SelectAttackingTarget(ATTACKING_TARGET_RANDOM, 0)) + pSummoned->AI()->AttackStart(pTarget); + } + + void SummonedCreatureJustDied(Creature* pSummoned) override + { + if (pSummoned->GetEntry() != NPC_VENGEFUL_SHADE) + m_lCultistSpawnedGuidList.remove(pSummoned->GetObjectGuid()); + } + + // Wrapper to help sort the summoning stalkers + void DoSortSummoningStalkers(GuidList& lDeathwhisperStalkers) + { + std::list lRightStalkers; + std::list lLeftStalkers; + + if (!lDeathwhisperStalkers.empty()) + { + for (GuidList::const_iterator itr = lDeathwhisperStalkers.begin(); itr != lDeathwhisperStalkers.end(); ++itr) + { + if (Creature* pStalker = m_creature->GetMap()->GetCreature(*itr)) + { + if (pStalker->GetPositionZ() > 60.0f) + m_middleStalkerGuid = pStalker->GetObjectGuid(); + else if (pStalker->GetPositionY() < 2215.0f) + lLeftStalkers.push_back(pStalker); + else + lRightStalkers.push_back(pStalker); + } + } + + lLeftStalkers.sort(sortFromNorthToSouth); + lRightStalkers.sort(sortFromNorthToSouth); + + // Store the sorted stalkers in a vector for each side + for (std::list::const_iterator itr = lLeftStalkers.begin(); itr != lLeftStalkers.end(); ++itr) + m_vLeftStalkersGuidVector.push_back((*itr)->GetObjectGuid()); + for (std::list::const_iterator itr = lRightStalkers.begin(); itr != lRightStalkers.end(); ++itr) + m_vRightStalkersGuidVector.push_back((*itr)->GetObjectGuid()); + } + } + + static bool sortFromNorthToSouth(Creature* pFirst, Creature* pSecond) + { + return pFirst && pSecond && pFirst->GetPositionX() < pSecond->GetPositionX(); + } + + // Wrapper to select a random cultist + Creature* DoSelectRandomCultist(uint32 uiEntry = 0) + { + std::vector vCultists; + vCultists.reserve(m_lCultistSpawnedGuidList.size()); + + for (GuidList::const_iterator itr = m_lCultistSpawnedGuidList.begin(); itr != m_lCultistSpawnedGuidList.end(); ++itr) + { + if (Creature* pCultist = m_creature->GetMap()->GetCreature(*itr)) + { + // Allow to be sorted them by entry + if (!uiEntry) + vCultists.push_back(pCultist); + else if (pCultist->GetEntry() == uiEntry) + vCultists.push_back(pCultist); + } + } + + if (vCultists.empty()) + return NULL; + + return vCultists[urand(0, vCultists.size() - 1)]; + } + + // Wrapper to handle the adds summmoning + void DoSummonCultistWave() + { + if (!m_pInstance) + return; + + // On 25 man mode we need to summon on all points + if (m_pInstance->Is25ManDifficulty()) + { + for (uint8 i = 0; i < 3; ++i) + { + if (Creature* pStalker = m_creature->GetMap()->GetCreature(m_vLeftStalkersGuidVector[i])) + m_creature->SummonCreature(aLeftSummonedCultists[i], pStalker->GetPositionX(), pStalker->GetPositionY(), pStalker->GetPositionZ(), 0, TEMPSUMMON_DEAD_DESPAWN, 0); + if (Creature* pStalker = m_creature->GetMap()->GetCreature(m_vRightStalkersGuidVector[i])) + m_creature->SummonCreature(aRightSummonedCultists[i], pStalker->GetPositionX(), pStalker->GetPositionY(), pStalker->GetPositionZ(), 0, TEMPSUMMON_DEAD_DESPAWN, 0); + } + + if (Creature* pStalker = m_creature->GetMap()->GetCreature(m_middleStalkerGuid)) + m_creature->SummonCreature(roll_chance_i(50) ? NPC_CULT_FANATIC : NPC_CULT_ADHERENT, pStalker->GetPositionX(), pStalker->GetPositionY(), pStalker->GetPositionZ(), 0.0f, TEMPSUMMON_CORPSE_DESPAWN, 0); + } + // On 10 man mode we summon on the left or on the right + else + { + // Summon just 1 add in phase 2 heroic + if (m_pInstance->IsHeroicDifficulty() && !m_bIsPhaseOne) + { + if (Creature* pStalker = m_creature->GetMap()->GetCreature(m_middleStalkerGuid)) + m_creature->SummonCreature(roll_chance_i(50) ? NPC_CULT_FANATIC : NPC_CULT_ADHERENT, pStalker->GetPositionX(), pStalker->GetPositionY(), pStalker->GetPositionZ(), 0.0f, TEMPSUMMON_CORPSE_DESPAWN, 0); + } + else + { + GuidVector vTempVector = m_bIsLeftSideSummon ? m_vLeftStalkersGuidVector : m_vRightStalkersGuidVector; + for (uint8 i = 0; i < 3; ++i) + { + if (Creature* pStalker = m_creature->GetMap()->GetCreature(vTempVector[i])) + m_creature->SummonCreature(m_bIsLeftSideSummon ? aLeftSummonedCultists[i] : aRightSummonedCultists[i], pStalker->GetPositionX(), pStalker->GetPositionY(), pStalker->GetPositionZ(), 0, TEMPSUMMON_DEAD_DESPAWN, 0); + } + + // change sides for next summoning + m_bIsLeftSideSummon = !m_bIsLeftSideSummon; + } + } + } + + // Wrapper to handle the second phase start + void DoStartSecondPhase() + { + DoScriptText(SAY_PHASE_TWO, m_creature); + SetCombatMovement(true); + m_creature->GetMotionMaster()->MoveChase(m_creature->getVictim()); + m_bIsPhaseOne = false; + } + + void UpdateAI(const uint32 uiDiff) override + { + if (!m_creature->SelectHostileTarget() || !m_creature->getVictim()) + return; + + if (m_uiBerserkTimer) + { + if (m_uiBerserkTimer <= uiDiff) + { + if (DoCastSpellIfCan(m_creature, SPELL_BERSERK) == CAST_OK) + { + DoScriptText(SAY_BERSERK, m_creature); + m_uiBerserkTimer = 0; + } + } + else + m_uiBerserkTimer -= uiDiff; + } + + if (m_uiDeathAndDecayTimer < uiDiff) + { + if (Unit* pTarget = m_creature->SelectAttackingTarget(ATTACKING_TARGET_RANDOM, 1)) + { + if (DoCastSpellIfCan(pTarget, SPELL_DEATH_AND_DECAY) == CAST_OK) + m_uiDeathAndDecayTimer = 20000; + } + } + else + m_uiDeathAndDecayTimer -= uiDiff; + + if (m_uiMindControlCount) + { + if (m_uiDominateMindTimer < uiDiff) + { + for (uint8 i = 0; i < m_uiMindControlCount; ++i) + { + if (Unit* pTarget = m_creature->SelectAttackingTarget(ATTACKING_TARGET_RANDOM, 1, SPELL_DOMINATE_MIND, SELECT_FLAG_PLAYER)) + DoCastSpellIfCan(pTarget, SPELL_DOMINATE_MIND, CAST_TRIGGERED); + } + + DoScriptText(SAY_DOMINATE_MIND, m_creature); + m_uiDominateMindTimer = 40000; + } + else + m_uiDominateMindTimer -= uiDiff; + } + + // Summon waves - in phase 1 or on heroic + if (m_pInstance && (m_pInstance->IsHeroicDifficulty() || m_bIsPhaseOne)) + { + if (m_uiSummonWaveTimer < uiDiff) + { + DoSummonCultistWave(); + m_uiCultistBuffTimer = 10000; + m_uiDarkMartyrdomTimer = 40000; + m_uiSummonWaveTimer = m_pInstance->IsHeroicDifficulty() ? 45000 : 60000; + } + else + m_uiSummonWaveTimer -= uiDiff; + + if (m_uiCultistBuffTimer) + { + if (m_uiCultistBuffTimer <= uiDiff) + { + // Choose a random of Fanatic or Adherent + bool bIsFanatic = roll_chance_i(50) ? true : false; + uint32 uiNpcEntry = bIsFanatic ? NPC_CULT_FANATIC : NPC_CULT_ADHERENT; + uint32 uiSpellEntry = bIsFanatic ? SPELL_DARK_TRANSFORMATION : SPELL_DARK_EMPOWERMENT; + int32 iTextEntry = bIsFanatic ? SAY_DARK_TRANSFORMATION : SAY_DARK_EMPOWERMENT; + + Creature* pTarget = DoSelectRandomCultist(uiNpcEntry); + if (pTarget && DoCastSpellIfCan(pTarget, uiSpellEntry) == CAST_OK) + { + // Remove the selected cultist from the list because we don't want it selected twice + m_lCultistSpawnedGuidList.remove(pTarget->GetObjectGuid()); + DoScriptText(iTextEntry, m_creature); + m_uiCultistBuffTimer = 0; + } + } + else + m_uiCultistBuffTimer -= uiDiff; + } + + if (m_uiDarkMartyrdomTimer) + { + if (m_uiDarkMartyrdomTimer <= uiDiff) + { + // Try to get a target on which to cast Martyrdom + if (Creature* pTarget = DoSelectRandomCultist()) + { + if (DoCastSpellIfCan(pTarget, SPELL_DARK_MARTYRDOM) == CAST_OK) + { + DoScriptText(SAY_ANIMATE_DEAD, m_creature); + m_uiDarkMartyrdomTimer = 0; + } + } + else + m_uiDarkMartyrdomTimer = 0; + } + else + m_uiDarkMartyrdomTimer -= uiDiff; + } + } + + // Phase 1 specific spells + if (m_bIsPhaseOne) + { + if (m_uiShadowBoltTimer < uiDiff) + { + if (Unit* pTarget = m_creature->SelectAttackingTarget(ATTACKING_TARGET_RANDOM, 0)) + { + if (DoCastSpellIfCan(pTarget, SPELL_SHADOW_BOLT) == CAST_OK) + m_uiShadowBoltTimer = 2000; + } + } + else + m_uiShadowBoltTimer -= uiDiff; + } + // Phase 2 specific spells + else + { + if (m_uiTouchOfInsignificanceTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature->getVictim(), SPELL_INSIGNIFICANCE) == CAST_OK) + m_uiTouchOfInsignificanceTimer = 7000; + } + else + m_uiTouchOfInsignificanceTimer -= uiDiff; + + if (m_uiFrostboltTimer < uiDiff) + { + if (Unit* pTarget = m_creature->SelectAttackingTarget(ATTACKING_TARGET_RANDOM, 0)) + { + if (DoCastSpellIfCan(pTarget, SPELL_FROSTBOLT) == CAST_OK) + m_uiFrostboltTimer = urand(2000, 4000); + } + } + else + m_uiFrostboltTimer -= uiDiff; + + if (m_uiFrostboltVolleyTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature, SPELL_FROSTBOLT_VOLLEY) == CAST_OK) + m_uiFrostboltVolleyTimer = urand(15000, 20000); + } + else + m_uiFrostboltVolleyTimer -= uiDiff; + + if (m_uiVengefulShadeTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature, SPELL_SUMMON_SPIRIT) == CAST_OK) + m_uiVengefulShadeTimer = 10000; + } + else + m_uiVengefulShadeTimer -= uiDiff; + + DoMeleeAttackIfReady(); + } + } +}; + +CreatureAI* GetAI_boss_lady_deathwhisper(Creature* pCreature) +{ + return new boss_lady_deathwhisperAI(pCreature); +} + +bool EffectDummyCreature_spell_mana_barrier(Unit* /*pCaster*/, uint32 uiSpellId, SpellEffectIndex uiEffIndex, Creature* pCreatureTarget, ObjectGuid /*originalCasterGuid*/) +{ + // always check spellid and effectindex + if (uiSpellId == SPELL_MANA_BARRIER && uiEffIndex == EFFECT_INDEX_0) + { + uint32 uiDamage = pCreatureTarget->GetMaxHealth() - pCreatureTarget->GetHealth(); + if (!uiDamage) + return true; + + if (pCreatureTarget->GetPower(POWER_MANA) < uiDamage) + { + uiDamage = pCreatureTarget->GetPower(POWER_MANA); + pCreatureTarget->RemoveAurasDueToSpell(SPELL_MANA_BARRIER); + + if (boss_lady_deathwhisperAI* pBossAI = dynamic_cast(pCreatureTarget->AI())) + pBossAI->DoStartSecondPhase(); + } + + pCreatureTarget->DealHeal(pCreatureTarget, uiDamage, GetSpellStore()->LookupEntry(SPELL_MANA_BARRIER)); + pCreatureTarget->ModifyPower(POWER_MANA, -int32(uiDamage)); + + // always return true when we are handling this spell and effect + return true; + } + + return false; +} + +void AddSC_boss_lady_deathwhisper() +{ + Script* pNewScript; + + pNewScript = new Script; + pNewScript->Name = "boss_lady_deathwhisper"; + pNewScript->GetAI = &GetAI_boss_lady_deathwhisper; + pNewScript->pEffectDummyNPC = &EffectDummyCreature_spell_mana_barrier; + pNewScript->RegisterSelf(); +} diff --git a/src/modules/SD2/scripts/northrend/icecrown_citadel/icecrown_citadel/boss_lord_marrowgar.cpp b/src/modules/SD2/scripts/northrend/icecrown_citadel/icecrown_citadel/boss_lord_marrowgar.cpp new file mode 100644 index 000000000..ad8269fa3 --- /dev/null +++ b/src/modules/SD2/scripts/northrend/icecrown_citadel/icecrown_citadel/boss_lord_marrowgar.cpp @@ -0,0 +1,309 @@ +/* Copyright (C) 2006 - 2013 ScriptDev2 + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with 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_marrowgar +SD%Complete: 75% +SDComment: Bone Spike Spell requires vehicle support +SDCategory: Icecrown Citadel +EndScriptData */ + +#include "precompiled.h" +#include "icecrown_citadel.h" + +enum +{ + SAY_AGGRO = -1631002, + SAY_BONE_STORM = -1631003, + SAY_BONE_SPIKE_1 = -1631004, + SAY_BONE_SPIKE_2 = -1631005, + SAY_BONE_SPIKE_3 = -1631006, + SAY_SLAY_1 = -1631007, + SAY_SLAY_2 = -1631008, + SAY_DEATH = -1631009, + SAY_BERSERK = -1631010, + + // spells + SPELL_BERSERK = 47008, + SPELL_BONE_SLICE = 69055, + SPELL_BONE_STORM = 69076, + SPELL_COLDFLAME = 69140, + SPELL_COLDFLAME_STORM = 72705, + SPELL_BONE_SPIKE = 69057, + SPELL_BONE_SPIKE_STORM = 73142, + + // summoned spells + SPELL_COLDFLAME_AURA = 69145, + SPELL_IMPALED = 69065, + + // npcs + NPC_BONE_SPIKE = 38711, + NPC_COLDFLAME = 36672, + + // phases and max cold flame charges + PHASE_NORMAL = 1, + PHASE_BONE_STORM_CHARGE = 2, + PHASE_BONE_STORM_CHARGING = 3, + PHASE_BONE_STORM_COLDFLAME = 4, + + MAX_CHARGES_NORMAL = 4, + MAX_CHARGES_HEROIC = 5, +}; + +struct boss_lord_marrowgarAI : public ScriptedAI +{ + boss_lord_marrowgarAI(Creature* pCreature) : ScriptedAI(pCreature) + { + m_pInstance = (instance_icecrown_citadel*)pCreature->GetInstanceData(); + // on heroic, there is 1 more Bone Storm charge + m_uiMaxCharges = m_pInstance && m_pInstance->IsHeroicDifficulty() ? MAX_CHARGES_HEROIC : MAX_CHARGES_NORMAL; + Reset(); + } + + instance_icecrown_citadel* m_pInstance; + + uint8 m_uiPhase; + uint8 m_uiChargesCount; + uint8 m_uiMaxCharges; + uint32 m_uiBerserkTimer; + uint32 m_uiBoneSliceTimer; + uint32 m_uiColdflameTimer; + uint32 m_uiBoneSpikeTimer; + uint32 m_uiBoneStormTimer; + uint32 m_uiBoneStormChargeTimer; + uint32 m_uiBoneStormColdflameTimer; + + void Reset() override + { + SetCombatMovement(true); + + m_uiPhase = PHASE_NORMAL; + m_uiChargesCount = 0; + m_uiBerserkTimer = 10 * MINUTE * IN_MILLISECONDS; + m_uiBoneSliceTimer = 1000; + m_uiColdflameTimer = 5000; + m_uiBoneSpikeTimer = 15000; + m_uiBoneStormTimer = 45000; + m_uiBoneStormChargeTimer = 3000; + m_uiBoneStormColdflameTimer = 1000; + } + + void Aggro(Unit* /*pWho*/) override + { + DoScriptText(SAY_AGGRO, m_creature); + + if (m_pInstance) + m_pInstance->SetData(TYPE_MARROWGAR, IN_PROGRESS); + } + + void KilledUnit(Unit* pVictim) override + { + if (pVictim->GetTypeId() != TYPEID_PLAYER) + return; + + if (urand(0, 1)) + DoScriptText(urand(0, 1) ? SAY_SLAY_1 : SAY_SLAY_2, m_creature); + } + + void JustDied(Unit* /*pKiller*/) override + { + DoScriptText(SAY_DEATH, m_creature); + + if (m_pInstance) + m_pInstance->SetData(TYPE_MARROWGAR, DONE); + } + + void JustReachedHome() override + { + if (m_pInstance) + m_pInstance->SetData(TYPE_MARROWGAR, FAIL); + } + + void MovementInform(uint32 uiType, uint32 uiPointId) override + { + if (uiType != POINT_MOTION_TYPE) + return; + + if (uiPointId) + { + m_uiPhase = PHASE_BONE_STORM_COLDFLAME; + ++m_uiChargesCount; + } + } + + void JustSummoned(Creature* pSummoned) override + { + if (pSummoned->GetEntry() == NPC_COLDFLAME) + { + pSummoned->CastSpell(pSummoned, SPELL_COLDFLAME_AURA, true); + + float fX, fY; + float fZ = pSummoned->GetPositionZ(); + // Note: the NearPoint2D function may not be correct here, because we may use a wrong Z value + m_creature->GetNearPoint2D(fX, fY, 80.0f, m_creature->GetAngle(pSummoned)); + pSummoned->GetMotionMaster()->MovePoint(0, fX, fY, fZ, false); + } + } + + void UpdateAI(const uint32 uiDiff) override + { + if (!m_creature->SelectHostileTarget() || !m_creature->getVictim()) + return; + + switch (m_uiPhase) + { + case PHASE_NORMAL: + + // Coldflame + if (m_uiColdflameTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature, SPELL_COLDFLAME) == CAST_OK) + m_uiColdflameTimer = 5000; + } + else + m_uiColdflameTimer -= uiDiff; + + // Bone Storm + if (m_uiBoneStormTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature, SPELL_BONE_STORM) == CAST_OK) + { + // ToDo: research if we need to increase the speed here + DoScriptText(SAY_BONE_STORM, m_creature); + m_uiPhase = PHASE_BONE_STORM_CHARGE; + SetCombatMovement(false); + m_creature->GetMotionMaster()->MoveIdle(); + m_uiBoneStormTimer = 90000; + } + } + else + m_uiBoneStormTimer -= uiDiff; + + // Bone Slice + if (m_uiBoneSliceTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature->getVictim(), SPELL_BONE_SLICE) == CAST_OK) + m_uiBoneSliceTimer = 1000; + } + else + m_uiBoneSliceTimer -= uiDiff; + + DoMeleeAttackIfReady(); + + break; + case PHASE_BONE_STORM_CHARGE: + + // next charge to random enemy + if (m_uiBoneStormChargeTimer < uiDiff) + { + if (Unit* pTarget = m_creature->SelectAttackingTarget(ATTACKING_TARGET_RANDOM, 0, uint32(0), SELECT_FLAG_PLAYER)) + { + float fX, fY, fZ; + pTarget->GetPosition(fX, fY, fZ); + m_creature->GetMotionMaster()->Clear(); + m_creature->GetMotionMaster()->MovePoint(1, fX, fY, fZ); + m_uiBoneStormChargeTimer = 3000; + m_uiPhase = PHASE_BONE_STORM_CHARGING; + } + } + else + m_uiBoneStormChargeTimer -= uiDiff; + + break; + case PHASE_BONE_STORM_CHARGING: + // waiting to arrive at target position + break; + case PHASE_BONE_STORM_COLDFLAME: + + if (m_uiBoneStormColdflameTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature, SPELL_COLDFLAME_STORM) == CAST_OK) + { + // When the max cold flame charges are reached, remove Bone storm aura + if (m_uiChargesCount == m_uiMaxCharges) + { + m_creature->RemoveAurasDueToSpell(SPELL_BONE_STORM); + m_uiBoneStormTimer = 60000; + m_uiBoneSliceTimer = 10000; + SetCombatMovement(true); + m_creature->GetMotionMaster()->MoveChase(m_creature->getVictim()); + m_uiChargesCount = 0; + m_uiPhase = PHASE_NORMAL; + } + else + m_uiPhase = PHASE_BONE_STORM_CHARGE; + + m_uiBoneStormColdflameTimer = 1000; + } + } + else + m_uiBoneStormColdflameTimer -= uiDiff; + + break; + } + + // Bone spike - different spells for the normal phase or storm phase + // ToDo: uncommnet this when vehicles and the Bone spike spells are properly supported by core + /*if (m_pInstance && (m_pInstance->IsHeroicDifficulty() || m_uiPhase == PHASE_NORMAL)) + { + if (m_uiBoneSpikeTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature, m_uiPhase == PHASE_NORMAL ? SPELL_BONE_SPIKE : SPELL_BONE_SPIKE_STORM) == CAST_OK) + { + switch (urand(0, 2)) + { + case 0: DoScriptText(SAY_BONE_SPIKE_1, m_creature); break; + case 1: DoScriptText(SAY_BONE_SPIKE_2, m_creature); break; + case 2: DoScriptText(SAY_BONE_SPIKE_3, m_creature); break; + } + m_uiBoneSpikeTimer = 18000; + } + } + else + m_uiBoneSpikeTimer -= uiDiff; + }*/ + + // Berserk + if (m_uiBerserkTimer) + { + if (m_uiBerserkTimer <= uiDiff) + { + if (DoCastSpellIfCan(m_creature, SPELL_BERSERK)) + { + DoScriptText(SAY_BERSERK, m_creature); + m_uiBerserkTimer = 0; + } + } + else + m_uiBerserkTimer -= uiDiff; + } + } +}; + +CreatureAI* GetAI_boss_lord_marrowgar(Creature* pCreature) +{ + return new boss_lord_marrowgarAI(pCreature); +} + +void AddSC_boss_lord_marrowgar() +{ + Script* pNewScript; + + pNewScript = new Script; + pNewScript->Name = "boss_lord_marrowgar"; + pNewScript->GetAI = &GetAI_boss_lord_marrowgar; + pNewScript->RegisterSelf(); +} diff --git a/src/modules/SD2/scripts/northrend/icecrown_citadel/icecrown_citadel/boss_professor_putricide.cpp b/src/modules/SD2/scripts/northrend/icecrown_citadel/icecrown_citadel/boss_professor_putricide.cpp new file mode 100644 index 000000000..f2ea143de --- /dev/null +++ b/src/modules/SD2/scripts/northrend/icecrown_citadel/icecrown_citadel/boss_professor_putricide.cpp @@ -0,0 +1,500 @@ +/* Copyright (C) 2006 - 2013 ScriptDev2 + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +/* ScriptData +SDName: boss_professor_putricide +SD%Complete: 70% +SDComment: NYI: Abomination and table handling, Malleable Goo, + possibly Green Ooze and Orange Gas scripts require handling in sd2, but need further research on their spells +SDCategory: Icecrown Citadel +EndScriptData */ + +#include "precompiled.h" +#include "icecrown_citadel.h" + +enum +{ + SAY_AGGRO = -1631092, + SAY_AIRLOCK = -1631093, + SAY_PHASE_CHANGE = -1631094, + SAY_TRANSFORM_1 = -1631095, + SAY_TRANSFORM_2 = -1631096, + SAY_SLAY_1 = -1631097, + SAY_SLAY_2 = -1631098, + SAY_BERSERK = -1631099, + SAY_DEATH = -1631100, +}; + +enum +{ + SPELL_BERSERK = 47008, + + // controlled abomination + SPELL_MUTATED_TRANSFORMATION = 70308, + SPELL_EAT_OOZE = 72527, + SPELL_REGURGITATED_OOZE = 70539, + SPELL_MUTATED_SLASH = 70542, + SPELL_MUTATED_AURA = 70405, + SPELL_ABOMINATION_POWER_DRAIN = 70385, // prevents normal regen of abomination's power + + SPELL_UNSTABLE_EXPERIMENT = 70351, // ooze and gas summoning spells in basepoints of effects of this spell suggest that they should be handled in core + + // Volatile Experiment on heroic difficulties + SPELL_VOLATILE_EXPERIMENT = 72840, // single target dummy effect + SPELL_VOLATILE_EXPERIMENT_2 = 72841, // single target dummy effect + SPELL_VOLATILE_EXPERIMENT_3 = 72842, // radius target dummy effect + SPELL_VOLATILE_EXPERIMENT_4 = 72843, // radius target dummy effect + + SPELL_GREEN_OOZE_SUMMON = 71412, + SPELL_ORANGE_OOZE_SUMMON = 71415, + + SPELL_OOZE_ADHESIVE = 70447, + SPELL_OOZE_ERUPTION = 70492, + + SPELL_GASEOUS_BLOAT = 70672, + SPELL_EXPUNGED_GAS = 70701, + SPELL_GASEOUS_BLOAT_VISUAL = 70215, + + SPELL_SLIME_PUDDLE = 70341, + SPELL_SLIME_PUDDLE_SUMMON = 70342, + SPELL_SLIME_PUDDLE_AURA = 70343, +// SPELL_SLIME_PUDDLE_TRIGGER = 71424, // trigger summon spell from target? +// SPELL_SLIME_PUDDLE_SUMMON_TRIG = 71425, + SPELL_GROW_STACKER = 70345, + SPELL_GROW_STACKER_GROW_AURA = 70347, + + SPELL_MALLEABLE_GOO_MISSILE = 70852, + + SPELL_CHOKING_GAS_BOMB = 71255, + SPELL_CHOKING_GAS_BOMB_AURA = 71259, + SPELL_CHOKING_GAS_BOMB_EXPL_AUR = 71280, + SPELL_CHOKING_GAS_EXPLOSION = 71279, + + // phase transitions + SPELL_TEAR_GAS = 71617, // stuns players + SPELL_TEAR_GAS_PERIODIC_AURA = 73170, // stuns summoned creatures? + SPELL_TEAR_GAS_CANCEL = 71620, + + SPELL_CREATE_CONCOCTION = 71621, + SPELL_GUZZLE_POTIONS = 71893, + + SPELL_MUTATED_PLAGUE = 72451, + + // heroic + SPELL_UNBOUND_PLAGUE = 70911, + SPELL_OOZE_VARIABLE = 70352, // aura 303 - dont allow taking damage from attacker with linked aura303? + SPELL_OOZE_VARIABLE_OOZE = 74118, // anyway, implemented as hardcoded in script + SPELL_GAS_VARIABLE = 70353, + SPELL_GAS_VARIABLE_GAS = 74119, + + SPELL_OOZE_TANK_PROTECTION = 71770 +}; + +enum Phase +{ + PHASE_ONE = 1, + PHASE_RUNNING_ONE = 2, + PHASE_TRANSITION_ONE = 3, + PHASE_TWO = 4, + PHASE_RUNNING_TWO = 5, + PHASE_TRANSITION_TWO = 6, + PHASE_THREE = 7 +}; + +enum Waypoint +{ + POINT_PUTRICIDE_SPAWN = 1 +}; + +static const float fPutricidePosition[1][3] = +{ + {4356.78f, 3263.51f, 389.40f} // 0 Putricide spawn point +}; + +struct boss_professor_putricideAI : public ScriptedAI +{ + boss_professor_putricideAI(Creature* pCreature) : ScriptedAI(pCreature) + { + m_pInstance = (instance_icecrown_citadel*)pCreature->GetInstanceData(); + Reset(); + } + + instance_icecrown_citadel* m_pInstance; + + uint32 m_uiPhase; + + uint32 m_uiHealthCheckTimer; + uint32 m_uiTransitionTimer; + uint32 m_uiEnrageTimer; + uint32 m_uiPuddleTimer; + uint32 m_uiUnstableExperimentTimer; + uint32 m_uiUnboundPlagueTimer; + uint32 m_uiChokingGasBombTimer; + + void Reset() override + { + m_uiPhase = PHASE_ONE; + m_uiHealthCheckTimer = 1000; + m_uiEnrageTimer = 10 * MINUTE * IN_MILLISECONDS; + m_uiPuddleTimer = 10000; + m_uiUnstableExperimentTimer = 20000; + m_uiUnboundPlagueTimer = 10000; + m_uiChokingGasBombTimer = urand(10000, 15000); + } + + void KilledUnit(Unit* /*pVictim*/) override + { + DoScriptText(urand(0, 1) ? SAY_SLAY_1 : SAY_SLAY_2, m_creature); + } + + void Aggro(Unit* /*pWho*/) override + { + if (m_pInstance) + m_pInstance->SetData(TYPE_PROFESSOR_PUTRICIDE, IN_PROGRESS); + + DoScriptText(SAY_AGGRO, m_creature); + } + + void JustDied(Unit* /*pKiller*/) override + { + if (m_pInstance) + m_pInstance->SetData(TYPE_PROFESSOR_PUTRICIDE, DONE); + + DoScriptText(SAY_DEATH, m_creature); + } + + void JustReachedHome() override + { + if (m_pInstance) + m_pInstance->SetData(TYPE_PROFESSOR_PUTRICIDE, FAIL); + } + + void MovementInform(uint32 uiMovementType, uint32 uiData) override + { + if (uiMovementType != POINT_MOTION_TYPE) + return; + + if (uiData == POINT_PUTRICIDE_SPAWN) + { + if (m_uiPhase == PHASE_RUNNING_ONE) + { + if (m_pInstance && m_pInstance->IsHeroicDifficulty()) + { + DoScriptText(SAY_PHASE_CHANGE, m_creature); + m_uiTransitionTimer = 30000; + } + else + { + DoCastSpellIfCan(m_creature, SPELL_CREATE_CONCOCTION); + DoScriptText(SAY_TRANSFORM_1, m_creature); + m_uiTransitionTimer = 15000; + } + + m_uiPhase = PHASE_TRANSITION_ONE; // waiting for entering phase 2 + } + else if (m_uiPhase == PHASE_RUNNING_TWO) + { + if (m_pInstance && m_pInstance->IsHeroicDifficulty()) + { + DoScriptText(SAY_PHASE_CHANGE, m_creature); + m_uiTransitionTimer = 30000; + } + else + { + DoCastSpellIfCan(m_creature, SPELL_GUZZLE_POTIONS); + DoScriptText(SAY_TRANSFORM_2, m_creature); + m_uiTransitionTimer = 15000; + } + + m_uiPhase = PHASE_TRANSITION_TWO; // waiting for entering phase 3 + } + } + } + + void UpdateAI(const uint32 uiDiff) override + { + if (!m_creature->SelectHostileTarget() || !m_creature->getVictim()) + return; + + // Enrage + if (m_uiEnrageTimer) + { + if (m_uiEnrageTimer <= uiDiff) + { + if (DoCastSpellIfCan(m_creature, SPELL_BERSERK) == CAST_OK) + { + DoScriptText(SAY_BERSERK, m_creature); + m_uiEnrageTimer = 0; + } + } + else + m_uiEnrageTimer -= uiDiff; + } + + switch (m_uiPhase) + { + case PHASE_ONE: + { + // health check + if (m_uiHealthCheckTimer <= uiDiff) + { + if (m_creature->GetHealthPercent() <= 80.0f) + { + uint32 spellId = (m_pInstance && m_pInstance->IsHeroicDifficulty() ? SPELL_VOLATILE_EXPERIMENT : SPELL_TEAR_GAS); + + if (DoCastSpellIfCan(m_creature, spellId) == CAST_OK) + { + m_creature->GetMotionMaster()->Clear(); + SetCombatMovement(false); + m_creature->GetMotionMaster()->MovePoint(POINT_PUTRICIDE_SPAWN, fPutricidePosition[0][0], fPutricidePosition[0][1], fPutricidePosition[0][2]); + m_uiPhase = PHASE_RUNNING_ONE; + return; + } + } + m_uiHealthCheckTimer = 1000; + } + else + m_uiHealthCheckTimer -= uiDiff; + + // Unbound Plague + if (m_pInstance && m_pInstance->IsHeroicDifficulty()) + { + if (m_uiUnboundPlagueTimer <= uiDiff) + { + if (Unit* pTarget = m_creature->SelectAttackingTarget(ATTACKING_TARGET_RANDOM, 1, SPELL_UNBOUND_PLAGUE, SELECT_FLAG_PLAYER)) + { + if (DoCastSpellIfCan(pTarget, SPELL_UNBOUND_PLAGUE) == CAST_OK) + m_uiUnboundPlagueTimer = 70000; + } + } + else + m_uiUnboundPlagueTimer -= uiDiff; + } + + // Slime Puddle + if (m_uiPuddleTimer <= uiDiff) + { + for (int i = 0; i < 2; ++i) + { + if (Unit* pTarget = m_creature->SelectAttackingTarget(ATTACKING_TARGET_RANDOM, 0, SPELL_SLIME_PUDDLE_SUMMON, SELECT_FLAG_PLAYER)) + DoCastSpellIfCan(pTarget, SPELL_SLIME_PUDDLE, CAST_TRIGGERED); + } + m_uiPuddleTimer = 30000; + } + else + m_uiPuddleTimer -= uiDiff; + + // Unstable Experiment + if (m_uiUnstableExperimentTimer <= uiDiff) + { + if (DoCastSpellIfCan(m_creature, SPELL_UNSTABLE_EXPERIMENT) == CAST_OK) + m_uiUnstableExperimentTimer = 30000; + } + else + m_uiUnstableExperimentTimer -= uiDiff; + + break; + } + case PHASE_TRANSITION_ONE: + { + if (m_uiTransitionTimer <= uiDiff) + { + m_creature->GetMotionMaster()->Clear(); + SetCombatMovement(true); + m_creature->GetMotionMaster()->MoveChase(m_creature->getVictim()); + m_uiPhase = PHASE_TWO; + + if (m_pInstance && m_pInstance->IsHeroicDifficulty()) + { + DoCastSpellIfCan(m_creature, SPELL_CREATE_CONCOCTION); + DoScriptText(SAY_TRANSFORM_1, m_creature); + } + else + DoCastSpellIfCan(m_creature, SPELL_TEAR_GAS_CANCEL, CAST_INTERRUPT_PREVIOUS); + } + else + m_uiTransitionTimer -= uiDiff; + + return; + } + case PHASE_TWO: + { + // health check + if (m_uiHealthCheckTimer <= uiDiff) + { + if (m_creature->GetHealthPercent() <= 35.0f) + { + uint32 spellId = (m_pInstance && m_pInstance->IsHeroicDifficulty() ? SPELL_VOLATILE_EXPERIMENT : SPELL_TEAR_GAS); + + if (DoCastSpellIfCan(m_creature, spellId) == CAST_OK) + { + m_creature->GetMotionMaster()->Clear(); + SetCombatMovement(false); + m_creature->GetMotionMaster()->MovePoint(POINT_PUTRICIDE_SPAWN, fPutricidePosition[0][0], fPutricidePosition[0][1], fPutricidePosition[0][2]); + m_uiPhase = PHASE_RUNNING_TWO; + + // TODO: remove Mutated Abomination + + return; + } + } + m_uiHealthCheckTimer = 1000; + } + else + m_uiHealthCheckTimer -= uiDiff; + + // Unbound Plague + if (m_pInstance && m_pInstance->IsHeroicDifficulty()) + { + if (m_uiUnboundPlagueTimer <= uiDiff) + { + if (Unit* pTarget = m_creature->SelectAttackingTarget(ATTACKING_TARGET_RANDOM, 1, SPELL_UNBOUND_PLAGUE, SELECT_FLAG_PLAYER)) + { + if (DoCastSpellIfCan(pTarget, SPELL_UNBOUND_PLAGUE) == CAST_OK) + m_uiUnboundPlagueTimer = 70000; + } + } + else + m_uiUnboundPlagueTimer -= uiDiff; + } + + // Slime Puddle + if (m_uiPuddleTimer <= uiDiff) + { + for (int i = 0; i < 2; ++i) + { + if (Unit* pTarget = m_creature->SelectAttackingTarget(ATTACKING_TARGET_RANDOM, 0, SPELL_SLIME_PUDDLE_SUMMON, SELECT_FLAG_PLAYER)) + DoCastSpellIfCan(pTarget, SPELL_SLIME_PUDDLE, CAST_TRIGGERED); + } + + m_uiPuddleTimer = 30000; + } + else + m_uiPuddleTimer -= uiDiff; + + // Unstable Experiment + if (m_uiUnstableExperimentTimer <= uiDiff) + { + if (DoCastSpellIfCan(m_creature, SPELL_UNSTABLE_EXPERIMENT) == CAST_OK) + m_uiUnstableExperimentTimer = 30000; + } + else + m_uiUnstableExperimentTimer -= uiDiff; + + // Choking Gas + if (m_uiChokingGasBombTimer <= uiDiff) + { + if (DoCastSpellIfCan(m_creature, SPELL_CHOKING_GAS_BOMB) == CAST_OK) + m_uiChokingGasBombTimer = urand(25000, 30000); + } + else + m_uiChokingGasBombTimer -= uiDiff; + + // TODO: Malleable Goo + + break; + } + case PHASE_TRANSITION_TWO: + { + if (m_uiTransitionTimer <= uiDiff) + { + m_creature->GetMotionMaster()->Clear(); + SetCombatMovement(true); + m_creature->GetMotionMaster()->MoveChase(m_creature->getVictim()); + m_uiPhase = PHASE_THREE; + + if (m_pInstance && m_pInstance->IsHeroicDifficulty()) + { + DoCastSpellIfCan(m_creature, SPELL_GUZZLE_POTIONS); + DoScriptText(SAY_TRANSFORM_2, m_creature); + } + else + DoCastSpellIfCan(m_creature, SPELL_TEAR_GAS_CANCEL, CAST_INTERRUPT_PREVIOUS); + } + else + m_uiTransitionTimer -= uiDiff; + + return; + } + case PHASE_THREE: + { + // Unbound Plague + if (m_pInstance && m_pInstance->IsHeroicDifficulty()) + { + if (m_uiUnboundPlagueTimer <= uiDiff) + { + if (Unit* pTarget = m_creature->SelectAttackingTarget(ATTACKING_TARGET_RANDOM, 1, SPELL_UNBOUND_PLAGUE, SELECT_FLAG_PLAYER)) + { + if (DoCastSpellIfCan(pTarget, SPELL_UNBOUND_PLAGUE) == CAST_OK) + m_uiUnboundPlagueTimer = 70000; + } + } + else + m_uiUnboundPlagueTimer -= uiDiff; + } + + // Slime Puddle + if (m_uiPuddleTimer <= uiDiff) + { + for (int i = 0; i < 2; ++i) + { + if (Unit* pTarget = m_creature->SelectAttackingTarget(ATTACKING_TARGET_RANDOM, 1, SPELL_SLIME_PUDDLE_SUMMON, SELECT_FLAG_PLAYER)) + DoCastSpellIfCan(pTarget, SPELL_SLIME_PUDDLE, CAST_TRIGGERED); + } + m_uiPuddleTimer = 30000; + } + else + m_uiPuddleTimer -= uiDiff; + + // Choking Gas + if (m_uiChokingGasBombTimer <= uiDiff) + { + if (DoCastSpellIfCan(m_creature, SPELL_CHOKING_GAS_BOMB) == CAST_OK) + m_uiChokingGasBombTimer = urand(25000, 30000); + } + else + m_uiChokingGasBombTimer -= uiDiff; + + // TODO: Malleable Goo + + break; + } + case PHASE_RUNNING_ONE: + case PHASE_RUNNING_TWO: + { + // wait for arriving at the table (during phase transition) + break; + } + } + + DoMeleeAttackIfReady(); + } +}; + +CreatureAI* GetAI_boss_professor_putricide(Creature* pCreature) +{ + return new boss_professor_putricideAI(pCreature); +} + +void AddSC_boss_professor_putricide() +{ + Script* pNewScript; + + pNewScript = new Script; + pNewScript->Name = "boss_professor_putricide"; + pNewScript->GetAI = &GetAI_boss_professor_putricide; + pNewScript->RegisterSelf(); +} diff --git a/src/modules/SD2/scripts/northrend/icecrown_citadel/icecrown_citadel/boss_rotface.cpp b/src/modules/SD2/scripts/northrend/icecrown_citadel/icecrown_citadel/boss_rotface.cpp new file mode 100644 index 000000000..0f0a50445 --- /dev/null +++ b/src/modules/SD2/scripts/northrend/icecrown_citadel/icecrown_citadel/boss_rotface.cpp @@ -0,0 +1,341 @@ +/* Copyright (C) 2006 - 2013 ScriptDev2 + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +/* ScriptData +SDName: boss_rotface +SD%Complete: 70% +SDComment: +SDCategory: Icecrown Citadel +EndScriptData */ + +#include "precompiled.h" +#include "icecrown_citadel.h" + +enum +{ + SAY_AGGRO = -1631071, + SAY_SLIME_SPRAY = -1631072, + SAY_OOZE_EXPLODE = -1631073, + SAY_SLIME_FLOW_1 = -1631074, + SAY_SLIME_FLOW_2 = -1631075, + SAY_SLAY_1 = -1631076, + SAY_SLAY_2 = -1631077, + SAY_BERSERK = -1631078, + SAY_DEATH = -1631079, + SAY_ROTFACE_DEATH = -1631080, +}; + +enum +{ + // Mutated Infection + SPELL_MUTATED_INFECTION_1 = 70090, // periodic trigger auras + SPELL_MUTATED_INFECTION_2 = 70003, + SPELL_MUTATED_INFECTION_3 = 70004, + SPELL_MUTATED_INFECTION_4 = 70005, + SPELL_MUTATED_INFECTION_5 = 70006, + + // Slime Spray +// SPELL_SLIME_SPRAY_SUMMON = 70882, // precast of Slime Spray dmg spell + SPELL_SLIME_SPRAY = 69508, + + // Ooze Flood + SPELL_OOZE_FLOOD_PERIODIC = 70069, // periodically trigger ooze flood + SPELL_OOZE_FLOOD_REMOVE = 70079, + + // Little Ooze + SPELL_STICKY_OOZE = 69774, + SPELL_STICKY_AURA = 69776, // aura on dummy Sticky Ooze NPC + SPELL_WEAK_RADIATING_OOZE = 69750, + SPELL_LITTLE_OOZE_COMBINE = 69537, // periodic check +// SPELL_MERGE = 69889, + + // Big Ooze + SPELL_UNSTABLE_OOZE = 69558, // stacking buff + SPELL_RADIATING_OOZE = 69760, + SPELL_BIG_OOZE_COMBINE = 69552, // periodic check + SPELL_BIG_OOZE_BUFF_COMB = 69611, // periodic check + SPELL_UNSTABLE_EXPLOSION = 69839, + + MAX_MUTATE_INFACTION_STEPS = 5, +}; + +static const uint32 uiMutatedInfections[MAX_MUTATE_INFACTION_STEPS] = +{ + SPELL_MUTATED_INFECTION_1, + SPELL_MUTATED_INFECTION_2, + SPELL_MUTATED_INFECTION_3, + SPELL_MUTATED_INFECTION_4, + SPELL_MUTATED_INFECTION_5 +}; + +struct boss_rotfaceAI : public ScriptedAI +{ + boss_rotfaceAI(Creature* pCreature) : ScriptedAI(pCreature) + { + m_pInstance = (instance_icecrown_citadel*)pCreature->GetInstanceData(); + Reset(); + } + + instance_icecrown_citadel* m_pInstance; + + uint32 m_uiSlimeSprayTimer; + uint32 m_uiSlimeFlowTimer; + uint32 m_uiMutatedInfectionTimer; + uint32 m_uiInfectionsRate; + + void Reset() override + { + m_uiSlimeSprayTimer = urand(17000, 23000); + m_uiSlimeFlowTimer = 20000; + m_uiMutatedInfectionTimer = 60000; + m_uiInfectionsRate = 1; + } + + void Aggro(Unit* /*pWho*/) override + { + if (m_pInstance) + m_pInstance->SetData(TYPE_ROTFACE, IN_PROGRESS); + + DoScriptText(SAY_AGGRO, m_creature); + + DoCastSpellIfCan(m_creature, SPELL_MUTATED_INFECTION_1, CAST_TRIGGERED); + DoCastSpellIfCan(m_creature, SPELL_OOZE_FLOOD_PERIODIC, CAST_TRIGGERED); + } + + void JustReachedHome() override + { + if (m_pInstance) + m_pInstance->SetData(TYPE_ROTFACE, FAIL); + + DoCastSpellIfCan(m_creature, SPELL_OOZE_FLOOD_REMOVE, CAST_TRIGGERED); + } + + void KilledUnit(Unit* pVictim) override + { + if (pVictim->GetTypeId() == TYPEID_PLAYER) + DoScriptText(urand(0, 1) ? SAY_SLAY_1 : SAY_SLAY_2, m_creature, pVictim); + } + + void JustDied(Unit* /*pKiller*/) override + { + if (m_pInstance) + m_pInstance->SetData(TYPE_ROTFACE, DONE); + + DoScriptText(SAY_DEATH, m_creature); + } + + void UpdateAI(const uint32 uiDiff) override + { + if (!m_creature->SelectHostileTarget() || !m_creature->getVictim()) + return; + + // Slime Spray + if (m_uiSlimeSprayTimer <= uiDiff) + { + if (DoCastSpellIfCan(m_creature, SPELL_SLIME_SPRAY) == CAST_OK) + { + DoScriptText(SAY_SLIME_SPRAY, m_creature); + m_uiSlimeSprayTimer = urand(17000, 23000); + } + } + else + m_uiSlimeSprayTimer -= uiDiff; + + // Mutated Infection - faster with time + // implemented this instead of phases + if (m_uiInfectionsRate < MAX_MUTATE_INFACTION_STEPS) + { + if (m_uiMutatedInfectionTimer <= uiDiff) + { + if (DoCastSpellIfCan(m_creature, uiMutatedInfections[m_uiInfectionsRate], CAST_TRIGGERED) == CAST_OK) + { + m_creature->RemoveAurasDueToSpell(uiMutatedInfections[m_uiInfectionsRate - 1]); + // every next 15 seconds faster + m_uiMutatedInfectionTimer = 60000 - m_uiInfectionsRate * 15000; + ++m_uiInfectionsRate; + } + } + else + m_uiMutatedInfectionTimer -= uiDiff; + } + + // Slime Flow + if (m_uiSlimeFlowTimer <= uiDiff) + { + if (Creature* pProfessor = m_pInstance->GetSingleCreatureFromStorage(NPC_PROFESSOR_PUTRICIDE)) + DoScriptText(urand(0, 1) ? SAY_SLIME_FLOW_1 : SAY_SLIME_FLOW_2, pProfessor); + + m_uiSlimeFlowTimer = 20000; + } + else + m_uiSlimeFlowTimer -= uiDiff; + + DoMeleeAttackIfReady(); + } +}; + +CreatureAI* GetAI_boss_rotface(Creature* pCreature) +{ + return new boss_rotfaceAI(pCreature); +} + +struct mob_little_oozeAI : public ScriptedAI +{ + mob_little_oozeAI(Creature* pCreature) : ScriptedAI(pCreature) + { + m_pInstance = (instance_icecrown_citadel*)pCreature->GetInstanceData(); + Reset(); + } + + instance_icecrown_citadel* m_pInstance; + uint32 m_uiStickyOozeTimer; + + void Reset() override + { + m_uiStickyOozeTimer = 5000; + } + + void EnterEvadeMode() override + { + m_creature->ForcedDespawn(); + } + + void Aggro(Unit* pWho) override + { + m_creature->AddThreat(pWho, 500000.0f); // not sure about the threat amount but should be very high + DoCastSpellIfCan(m_creature, SPELL_WEAK_RADIATING_OOZE, CAST_TRIGGERED); + DoCastSpellIfCan(m_creature, SPELL_LITTLE_OOZE_COMBINE, CAST_TRIGGERED); + } + + void UpdateAI(const uint32 uiDiff) override + { + if (!m_creature->SelectHostileTarget() || !m_creature->getVictim()) + return; + + if (m_uiStickyOozeTimer <= uiDiff) + { + if (DoCastSpellIfCan(m_creature->getVictim(), SPELL_STICKY_OOZE) == CAST_OK) + m_uiStickyOozeTimer = urand(10000, 15000); + } + else + m_uiStickyOozeTimer -= uiDiff; + + DoMeleeAttackIfReady(); + } +}; + +CreatureAI* GetAI_mob_little_ooze(Creature* pCreature) +{ + return new mob_little_oozeAI(pCreature); +} + +struct mob_big_oozeAI : public ScriptedAI +{ + mob_big_oozeAI(Creature* pCreature) : ScriptedAI(pCreature) + { + m_pInstance = (instance_icecrown_citadel*)pCreature->GetInstanceData(); + Reset(); + } + + instance_icecrown_citadel* m_pInstance; + uint32 m_uiStickyOozeTimer; + uint32 m_uiUnstableExplosionCheckTimer; + + void Reset() override + { + m_uiStickyOozeTimer = 5000; + m_uiUnstableExplosionCheckTimer = 1000; + } + + void EnterEvadeMode() override + { + m_creature->ForcedDespawn(); + } + + void Aggro(Unit* pWho) override + { + m_creature->AddThreat(pWho, 500000.0f); + DoCastSpellIfCan(m_creature, SPELL_RADIATING_OOZE, CAST_TRIGGERED); + DoCastSpellIfCan(m_creature, SPELL_BIG_OOZE_COMBINE, CAST_TRIGGERED); + DoCastSpellIfCan(m_creature, SPELL_BIG_OOZE_BUFF_COMB, CAST_TRIGGERED); + } + + void UpdateAI(const uint32 uiDiff) override + { + if (!m_creature->SelectHostileTarget() || !m_creature->getVictim()) + return; + + // Unstable Ooze + if (m_uiUnstableExplosionCheckTimer) + { + if (m_uiUnstableExplosionCheckTimer <= uiDiff) + { + m_uiUnstableExplosionCheckTimer = 1000; + + SpellAuraHolder* holder = m_creature->GetSpellAuraHolder(SPELL_UNSTABLE_OOZE); + if (holder && holder->GetStackAmount() >= 5) + { + if (DoCastSpellIfCan(m_creature, SPELL_UNSTABLE_EXPLOSION) == CAST_OK) + { + if (m_pInstance) + { + if (Creature* pRotface = m_pInstance->GetSingleCreatureFromStorage(NPC_ROTFACE)) + DoScriptText(SAY_OOZE_EXPLODE, pRotface); + } + } + } + } + else + m_uiUnstableExplosionCheckTimer -= uiDiff; + } + + // Sticky Ooze + if (m_uiStickyOozeTimer <= uiDiff) + { + if (DoCastSpellIfCan(m_creature->getVictim(), SPELL_STICKY_OOZE) == CAST_OK) + m_uiStickyOozeTimer = urand(10000, 15000); + } + else + m_uiStickyOozeTimer -= uiDiff; + + DoMeleeAttackIfReady(); + } +}; + +CreatureAI* GetAI_mob_big_ooze(Creature* pCreature) +{ + return new mob_big_oozeAI(pCreature); +} + +void AddSC_boss_rotface() +{ + Script* pNewscript; + + pNewscript = new Script; + pNewscript->Name = "boss_rotface"; + pNewscript->GetAI = &GetAI_boss_rotface; + pNewscript->RegisterSelf(); + + pNewscript = new Script; + pNewscript->Name = "mob_little_ooze"; + pNewscript->GetAI = &GetAI_mob_little_ooze; + pNewscript->RegisterSelf(); + + pNewscript = new Script; + pNewscript->Name = "mob_big_ooze"; + pNewscript->GetAI = &GetAI_mob_big_ooze; + pNewscript->RegisterSelf(); +} diff --git a/src/modules/SD2/scripts/northrend/icecrown_citadel/icecrown_citadel/boss_sindragosa.cpp b/src/modules/SD2/scripts/northrend/icecrown_citadel/icecrown_citadel/boss_sindragosa.cpp new file mode 100644 index 000000000..e66e490c0 --- /dev/null +++ b/src/modules/SD2/scripts/northrend/icecrown_citadel/icecrown_citadel/boss_sindragosa.cpp @@ -0,0 +1,875 @@ +/* Copyright (C) 2006 - 2013 ScriptDev2 + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +/* ScriptData +SDName: boss_sindragosa +SD%Complete: 80% +SDComment: requires core support for ice blocks (spells and GO in LoS checking) +SDCategory: Icecrown Citadel +EndScriptData */ + +#include "precompiled.h" +#include "icecrown_citadel.h" + +enum +{ + SAY_AGGRO = -1631148, + SAY_UNCHAINED_MAGIC = -1631149, + SAY_BLISTERING_COLD = -1631150, + SAY_RESPIRE = -1631151, + SAY_TAKEOFF = -1631152, + SAY_PHASE_3 = -1631153, + SAY_SLAY_1 = -1631154, + SAY_SLAY_2 = -1631155, + SAY_BERSERK = -1631156, + SAY_DEATH = -1631157, + + // Spells + + // Sindragosa + + // all phases + SPELL_BERSERK = 26662, + + // Phase 1 and 3 + SPELL_TAIL_SMASH = 71077, + SPELL_CLEAVE = 19983, + SPELL_FROST_AURA = 70084, + SPELL_FROST_BREATH = 69649, + SPELL_ICY_GRIP = 70117, + SPELL_PERMEATING_CHILL = 70109, + SPELL_UNCHAINED_MAGIC = 69762, + + // Phase 2 + SPELL_ICE_TOMB = 69712, // triggers Frost Beacon on random targets, which triggers actual Ice Tomb after 7 sec. + SPELL_ICE_TOMB_PROTECTION = 69700, // protects from taking dmg while in Ice Tomb, should be triggered by Ice Tomb stunning spell + // Frost Bomb related + SPELL_FROST_BOMB = 69846, // summons dummy target npc + SPELL_FROST_BOMB_DMG = 69845, + SPELL_FROST_BOMB_VISUAL = 70022, // circle mark +// SPELL_FROST_BOMB_OTHER = 70521, // no idea where it is used, wowhead says it is used by some other Sindragosa (37755) + + // Phase 3 + SPELL_MYSTIC_BUFFET = 70128, + SPELL_ICE_TOMB_SINGLE = 69675, + + // Rimefang + SPELL_RIMEFANG_FROST_AURA = 71387, + SPELL_RIMEFANG_FROST_BREATH = 71386, + SPELL_RIMEFANG_ICY_BLAST = 71376, + + // Spinestalker + SPELL_SPINESTALKER_BELLOWING_ROAR = 36922, + SPELL_SPINESTALKER_CLEAVE = 40505, + SPELL_SPINESTALKER_TAIL_SWEEP = 71369 +}; + +enum SindragosaPhase +{ + SINDRAGOSA_PHASE_OOC = 0, + SINDRAGOSA_PHASE_AGGRO = 1, + SINDRAGOSA_PHASE_GROUND = 2, + SINDRAGOSA_PHASE_FLYING_TO_AIR = 3, + SINDRAGOSA_PHASE_AIR = 4, + SINDRAGOSA_PHASE_FLYING_TO_GROUND = 5, + SINDRAGOSA_PHASE_THREE = 6 +}; + +enum SindragosaPoint +{ + SINDRAGOSA_POINT_GROUND_CENTER = 0, + SINDRAGOSA_POINT_AIR_CENTER = 1, + SINDRAGOSA_POINT_AIR_PHASE_2 = 2, + SINDRAGOSA_POINT_AIR_EAST = 3, + SINDRAGOSA_POINT_AIR_WEST = 4 +}; + +enum RimefangPhase +{ + RIMEFANG_PHASE_GROUND = 0, + RIMEFANG_PHASE_FLYING = 1, + RIMEFANG_PHASE_AIR = 2 +}; + +enum RimefangPoint +{ + RIMEFANG_POINT_GROUND = 0, + RIMEFANG_POINT_AIR = 1, + RIMEFANG_POINT_INITIAL_LAND_AIR = 2, + RIMEFANG_POINT_INITIAL_LAND = 3 +}; + +enum SpinestalkerPoint +{ + SPINESTALKER_POINT_INITIAL_LAND_AIR = 0, + SPINESTALKER_POINT_INITIAL_LAND = 1 +}; + +#define FROST_BOMB_MIN_X 4367.0f +#define FROST_BOMB_MAX_X 4424.0f +#define FROST_BOMB_MIN_Y 2437.0f +#define FROST_BOMB_MAX_Y 2527.0f + +static const float SindragosaPosition[10][3] = +{ + {4407.44f, 2484.37f, 203.37f}, // 0 center, ground + {4407.44f, 2484.37f, 235.37f}, // 1 center, air + {4470.00f, 2484.37f, 235.37f}, // 2 Sindragosa air phase point + {4414.32f, 2456.94f, 203.37f}, // 3 Rimefang landing point + {4414.32f, 2456.94f, 228.37f}, // 4 Rimefang above landing point + {4414.32f, 2512.73f, 203.37f}, // 5 Spinestalker landing point + {4414.32f, 2512.73f, 228.37f}, // 6 Spinestalker above landing point + {4505.00f, 2484.37f, 235.37f}, // 7 Sindragosa spawn point + {4505.00f, 2444.37f, 235.37f}, // 8 Sindragosa east flying point + {4505.00f, 2524.37f, 235.37f}, // 9 Sindragosa west flying point +}; + +struct boss_sindragosaAI : public ScriptedAI +{ + boss_sindragosaAI(Creature* pCreature) : ScriptedAI(pCreature) + { + m_pInstance = (instance_icecrown_citadel*)pCreature->GetInstanceData(); + Reset(); + } + + instance_icecrown_citadel* m_pInstance; + + uint32 m_uiPhase; + uint32 m_uiPhaseTimer; + uint32 m_uiBerserkTimer; + uint32 m_uiCleaveTimer; + uint32 m_uiFrostBreathTimer; + uint32 m_uiTailSmashTimer; + uint32 m_uiIcyGripTimer; + uint32 m_uiUnchainedMagicTimer; + uint32 m_uiFrostBombTimer; + uint32 m_uiIceTombSingleTimer; + + void Reset() override + { + m_uiPhase = SINDRAGOSA_PHASE_OOC; + m_uiPhaseTimer = 45000; + m_uiBerserkTimer = 10 * MINUTE * IN_MILLISECONDS; + m_uiCleaveTimer = urand(5000, 15000); + m_uiTailSmashTimer = 20000; + m_uiFrostBreathTimer = 5000; + m_uiIcyGripTimer = 35000; + m_uiIceTombSingleTimer = 15000; + m_uiUnchainedMagicTimer = urand(15000, 30000); + } + + void SetFlying(bool bIsFlying) + { + if (bIsFlying) + m_creature->SetByteFlag(UNIT_FIELD_BYTES_1, 3, UNIT_BYTE1_FLAG_UNK_2); + else + m_creature->RemoveByteFlag(UNIT_FIELD_BYTES_1, 3, UNIT_BYTE1_FLAG_UNK_2); + + m_creature->SetLevitate(bIsFlying); + m_creature->SetWalk(bIsFlying); + } + + void EnterEvadeMode() override + { + SetFlying(true); + ScriptedAI::EnterEvadeMode(); + } + + void JustReachedHome() override + { + if (m_pInstance) + m_pInstance->SetData(TYPE_SINDRAGOSA, FAIL); + + m_creature->GetMotionMaster()->MovePoint(SINDRAGOSA_POINT_AIR_EAST, SindragosaPosition[8][0], SindragosaPosition[8][1], SindragosaPosition[8][2], false); + } + + void KilledUnit(Unit* /*pVictim*/) override + { + DoScriptText(urand(0, 1) ? SAY_SLAY_1 : SAY_SLAY_2, m_creature); + } + + void AttackStart(Unit* pWho) override + { + ScriptedAI::AttackStart(pWho); + + // on aggro: land first, then start the encounter + if (m_uiPhase == SINDRAGOSA_PHASE_OOC) + { + m_uiPhase = SINDRAGOSA_PHASE_AGGRO; + SetCombatMovement(false); + m_creature->SetWalk(true); + m_creature->GetMotionMaster()->Clear(); + m_creature->GetMotionMaster()->MovePoint(SINDRAGOSA_POINT_AIR_CENTER, SindragosaPosition[1][0], SindragosaPosition[1][1], SindragosaPosition[1][2], false); + } + } + + void Aggro(Unit* /*pWho*/) override + { + DoScriptText(SAY_AGGRO, m_creature); + // instance data set when sindragosa lands + } + + void JustDied(Unit* /*pKiller*/) override + { + DoScriptText(SAY_DEATH, m_creature); + + if (m_pInstance) + m_pInstance->SetData(TYPE_SINDRAGOSA, DONE); + } + + void MovementInform(uint32 uiMovementType, uint32 uiPointId) override + { + if (uiMovementType != POINT_MOTION_TYPE) + return; + + if (uiPointId == SINDRAGOSA_POINT_AIR_EAST) + { + m_creature->GetMotionMaster()->MovePoint(SINDRAGOSA_POINT_AIR_WEST, SindragosaPosition[9][0], SindragosaPosition[9][1], SindragosaPosition[9][2], false); + } + else if (uiPointId == SINDRAGOSA_POINT_AIR_WEST) + { + m_creature->GetMotionMaster()->MovePoint(SINDRAGOSA_POINT_AIR_EAST, SindragosaPosition[8][0], SindragosaPosition[8][1], SindragosaPosition[8][2], false); + } + else if (uiPointId == SINDRAGOSA_POINT_GROUND_CENTER) + { + // fly up + if (m_uiPhase == SINDRAGOSA_PHASE_GROUND) + { + m_uiPhase = SINDRAGOSA_PHASE_FLYING_TO_AIR; + SetFlying(true); + m_creature->GetMotionMaster()->MovePoint(SINDRAGOSA_POINT_AIR_CENTER, SindragosaPosition[1][0], SindragosaPosition[1][1], SindragosaPosition[1][2], false); + } + else // land and attack + { + // on aggro, after landing: set instance data and cast initial spells + if (m_uiPhase == SINDRAGOSA_PHASE_AGGRO) + { + DoCastSpellIfCan(m_creature, SPELL_FROST_AURA, CAST_TRIGGERED); + DoCastSpellIfCan(m_creature, SPELL_PERMEATING_CHILL, CAST_TRIGGERED); + + if (m_pInstance) + m_pInstance->SetData(TYPE_SINDRAGOSA, IN_PROGRESS); + } + + m_uiPhase = SINDRAGOSA_PHASE_GROUND; + SetFlying(false); + SetCombatMovement(true); + + if (Unit* pVictim = m_creature->getVictim()) + m_creature->GetMotionMaster()->MoveChase(pVictim); + } + } + else if (uiPointId == SINDRAGOSA_POINT_AIR_CENTER) + { + if (m_uiPhase == SINDRAGOSA_PHASE_AGGRO || m_uiPhase == SINDRAGOSA_PHASE_FLYING_TO_GROUND) + { + // land + m_creature->GetMotionMaster()->MovePoint(SINDRAGOSA_POINT_GROUND_CENTER, SindragosaPosition[0][0], SindragosaPosition[0][1], SindragosaPosition[0][2], false); + } + else if (m_uiPhase == SINDRAGOSA_PHASE_FLYING_TO_AIR) + { + // fly up (air phase) + m_creature->GetMotionMaster()->MovePoint(SINDRAGOSA_POINT_AIR_PHASE_2, SindragosaPosition[2][0], SindragosaPosition[2][1], SindragosaPosition[2][2], false); + } + } + else if (uiPointId == SINDRAGOSA_POINT_AIR_PHASE_2) + { + m_creature->SetOrientation(M_PI_F); // face the platform + m_uiFrostBombTimer = 10000; // set initial Frost Bomb timer + DoCastSpellIfCan(m_creature, SPELL_ICE_TOMB); + m_uiPhase = SINDRAGOSA_PHASE_AIR; + } + } + + void DoFrostBomb() + { + float x, y, z; + x = frand(FROST_BOMB_MIN_X, FROST_BOMB_MAX_X); + y = frand(FROST_BOMB_MIN_Y, FROST_BOMB_MAX_Y); + z = SindragosaPosition[0][2]; // platform height + + m_creature->CastSpell(x, y, z, SPELL_FROST_BOMB, false); + } + + void UpdateAI(const uint32 uiDiff) override + { + if (!m_creature->SelectHostileTarget() || !m_creature->getVictim()) + return; + + // Berserk + if (m_uiBerserkTimer) + { + if (m_uiBerserkTimer <= uiDiff) + { + if (DoCastSpellIfCan(m_creature, SPELL_BERSERK) == CAST_OK) + { + DoScriptText(SAY_BERSERK, m_creature); + m_uiBerserkTimer = 0; + } + } + else + m_uiBerserkTimer -= uiDiff; + } + + switch (m_uiPhase) + { + case SINDRAGOSA_PHASE_THREE: + { + // Ice Tomb + if (m_uiIceTombSingleTimer <= uiDiff) + { + if (Unit* pTarget = m_creature->SelectAttackingTarget(ATTACKING_TARGET_RANDOM, 1, SPELL_ICE_TOMB_SINGLE, SELECT_FLAG_PLAYER)) + { + if (DoCastSpellIfCan(pTarget, SPELL_ICE_TOMB) == CAST_OK) + m_uiIceTombSingleTimer = 15000; + } + } + else + m_uiIceTombSingleTimer -= uiDiff; + + // no break + } + case SINDRAGOSA_PHASE_GROUND: + { + // Phase 1 only + if (m_uiPhase == SINDRAGOSA_PHASE_GROUND) + { + // Health Check + if (m_creature->GetHealthPercent() <= 30.0f) + { + if (DoCastSpellIfCan(m_creature, SPELL_MYSTIC_BUFFET) == CAST_OK) + { + m_uiPhase = SINDRAGOSA_PHASE_THREE; + DoScriptText(SAY_PHASE_3, m_creature); + } + } + + // Phase 2 (air) + if (m_uiPhaseTimer <= uiDiff) + { + m_uiPhaseTimer = 33000; + DoScriptText(SAY_TAKEOFF, m_creature); + SetCombatMovement(false); + m_creature->GetMotionMaster()->MovePoint(SINDRAGOSA_POINT_GROUND_CENTER, SindragosaPosition[0][0], SindragosaPosition[0][1], SindragosaPosition[0][2], false); + } + else + m_uiPhaseTimer -= uiDiff; + } + + // Cleave + if (m_uiCleaveTimer <= uiDiff) + { + if (DoCastSpellIfCan(m_creature->getVictim(), SPELL_CLEAVE) == CAST_OK) + m_uiCleaveTimer = urand(5000, 15000); + } + else + m_uiCleaveTimer -= uiDiff; + + // Tail Smash + if (m_uiTailSmashTimer <= uiDiff) + { + if (DoCastSpellIfCan(m_creature, SPELL_TAIL_SMASH) == CAST_OK) + m_uiTailSmashTimer = urand(10000, 20000); + } + else + m_uiTailSmashTimer -= uiDiff; + + // Frost Breath + if (m_uiFrostBreathTimer <= uiDiff) + { + if (DoCastSpellIfCan(m_creature->getVictim(), SPELL_FROST_BREATH) == CAST_OK) + m_uiFrostBreathTimer = urand(15000, 20000); + } + else + m_uiFrostBreathTimer -= uiDiff; + + // Unchained Magic + if (m_uiUnchainedMagicTimer <= uiDiff) + { + if (DoCastSpellIfCan(m_creature, SPELL_UNCHAINED_MAGIC) == CAST_OK) + { + m_uiUnchainedMagicTimer = urand(40000, 60000); + DoScriptText(SAY_UNCHAINED_MAGIC, m_creature); + } + } + else + m_uiUnchainedMagicTimer -= uiDiff; + + // Icy Grip and Blistering Cold + if (m_uiIcyGripTimer <= uiDiff) + { + if (DoCastSpellIfCan(m_creature, SPELL_ICY_GRIP) == CAST_OK) + { + m_uiIcyGripTimer = 70000; + DoScriptText(SAY_BLISTERING_COLD, m_creature); + } + } + else + m_uiIcyGripTimer -= uiDiff; + + DoMeleeAttackIfReady(); + break; + } + case SINDRAGOSA_PHASE_FLYING_TO_GROUND: + case SINDRAGOSA_PHASE_FLYING_TO_AIR: + break; + case SINDRAGOSA_PHASE_AIR: + { + // Phase One (ground) + if (m_uiPhaseTimer <= uiDiff) + { + m_uiPhase = SINDRAGOSA_PHASE_FLYING_TO_GROUND; + m_uiPhaseTimer = 42000; + m_creature->GetMotionMaster()->MovePoint(SINDRAGOSA_POINT_AIR_CENTER, SindragosaPosition[1][0], SindragosaPosition[1][1], SindragosaPosition[1][2], false); + } + else + m_uiPhaseTimer -= uiDiff; + + // Frost Bomb + if (m_uiFrostBombTimer <= uiDiff) + { + DoFrostBomb(); + m_uiFrostBombTimer = 6000; + } + else + m_uiFrostBombTimer -= uiDiff; + + break; + } + } + + // evade on top of the stairs + EnterEvadeIfOutOfCombatArea(uiDiff); + } +}; + +CreatureAI* GetAI_boss_sindragosa(Creature* pCreature) +{ + return new boss_sindragosaAI(pCreature); +} + +struct npc_rimefang_iccAI : public ScriptedAI +{ + npc_rimefang_iccAI(Creature* pCreature) : ScriptedAI(pCreature) + { + m_pInstance = (instance_icecrown_citadel*)pCreature->GetInstanceData(); + + // Icy Blast - 3 casts on 10man, 6 on 25man + m_uiIcyBlastMaxCount = 3; + if (m_pInstance && m_pInstance->Is25ManDifficulty()) + m_uiIcyBlastMaxCount = 6; + + m_bHasLanded = false; + m_bIsReady = false; + + Reset(); + } + + instance_icecrown_citadel* m_pInstance; + + uint32 m_uiPhase; + uint32 m_uiPhaseTimer; + uint32 m_uiFrostBreathTimer; + uint32 m_uiIcyBlastCounter; + uint32 m_uiIcyBlastMaxCount; + uint32 m_uiIcyBlastTimer; + bool m_bHasLanded; // landed after player entered areatrigger + bool m_bIsReady; + + void Reset() override + { + m_uiPhase = RIMEFANG_PHASE_GROUND; + m_uiPhaseTimer = 25000; + m_uiFrostBreathTimer = urand(5000, 8000); + m_uiIcyBlastTimer = 0; + m_uiIcyBlastCounter = 0; + + SetCombatMovement(true); + } + + void SetFlying(bool bIsFlying) + { + if (bIsFlying) + m_creature->SetByteFlag(UNIT_FIELD_BYTES_1, 3, UNIT_BYTE1_FLAG_UNK_2); + else + m_creature->RemoveByteFlag(UNIT_FIELD_BYTES_1, 3, UNIT_BYTE1_FLAG_UNK_2); + + m_creature->SetLevitate(bIsFlying); + m_creature->SetWalk(bIsFlying); + } + + void Aggro(Unit* /*pWho*/) override + { + DoCastSpellIfCan(m_creature, SPELL_RIMEFANG_FROST_AURA, CAST_TRIGGERED); + } + + void AttackStart(Unit* pWho) override + { + if (!m_bIsReady) + { + if (!m_bHasLanded) + { + m_bHasLanded = true; + m_creature->GetMotionMaster()->MovePoint(RIMEFANG_POINT_INITIAL_LAND_AIR, SindragosaPosition[4][0], SindragosaPosition[4][1], SindragosaPosition[4][2], false); + } + + return; + } + + ScriptedAI::AttackStart(pWho); + } + + void JustDied(Unit* /*pKiller*/) override + { + if (!m_pInstance) + return; + + Creature* pSpinestalker = m_pInstance->GetSingleCreatureFromStorage(NPC_SPINESTALKER); + if (!pSpinestalker || !pSpinestalker->IsAlive()) + { + if (Creature* pSindragosa = m_creature->SummonCreature(NPC_SINDRAGOSA, SindragosaPosition[7][0], SindragosaPosition[7][1], SindragosaPosition[7][2], 0.0f, TEMPSUMMON_MANUAL_DESPAWN, 0)) + pSindragosa->SetInCombatWithZone(); + } + } + + // evade to point on platform + void EnterEvadeMode() override + { + m_creature->RemoveAllAurasOnEvade(); + m_creature->DeleteThreatList(); + m_creature->CombatStop(true); + + if (m_creature->IsAlive()) + m_creature->GetMotionMaster()->MovePoint(RIMEFANG_POINT_INITIAL_LAND, SindragosaPosition[3][0], SindragosaPosition[3][1], SindragosaPosition[3][2], false); + + m_creature->SetLootRecipient(NULL); + + Reset(); + } + + void MovementInform(uint32 uiMovementType, uint32 uiPointId) override + { + if (uiMovementType != POINT_MOTION_TYPE) + return; + + if (uiPointId == RIMEFANG_POINT_INITIAL_LAND_AIR) + { + m_creature->GetMotionMaster()->MovePoint(RIMEFANG_POINT_INITIAL_LAND, SindragosaPosition[3][0], SindragosaPosition[3][1], SindragosaPosition[3][2], false); + } + else if (uiPointId == RIMEFANG_POINT_INITIAL_LAND) + { + m_creature->GetMotionMaster()->MoveIdle(); + m_creature->SetFacingTo(M_PI_F); + m_bIsReady = true; + SetFlying(false); + } + else if (uiPointId == RIMEFANG_POINT_GROUND) + { + m_uiPhase = RIMEFANG_PHASE_GROUND; + SetFlying(false); + SetCombatMovement(true); + + if (Unit* pVictim = m_creature->getVictim()) + m_creature->GetMotionMaster()->MoveChase(pVictim); + } + else if (uiPointId == RIMEFANG_POINT_AIR) + { + m_uiPhase = RIMEFANG_PHASE_AIR; + } + } + + void UpdateAI(const uint32 uiDiff) override + { + if (!m_creature->SelectHostileTarget() || !m_creature->getVictim()) + return; + + if (m_uiPhase == RIMEFANG_PHASE_GROUND) + { + // Frost Breath + if (m_uiFrostBreathTimer <= uiDiff) + { + if (DoCastSpellIfCan(m_creature->getVictim(), SPELL_RIMEFANG_FROST_BREATH) == CAST_OK) + m_uiFrostBreathTimer = urand(5000, 8000); + } + else + m_uiFrostBreathTimer -= uiDiff; + + // Icy Blast - air phase + if (m_uiPhaseTimer <= uiDiff) + { + m_uiPhaseTimer = 40000; + m_uiPhase = RIMEFANG_PHASE_FLYING; + SetFlying(true); + SetCombatMovement(false); + m_creature->GetMotionMaster()->MovePoint(RIMEFANG_POINT_AIR, m_creature->GetPositionX(), m_creature->GetPositionY(), m_creature->GetPositionZ() + 20.0f, false); + return; + } + else + m_uiPhaseTimer -= uiDiff; + + DoMeleeAttackIfReady(); + } + else if (m_uiPhase == RIMEFANG_PHASE_AIR) + { + // Icy Blast + if (m_uiIcyBlastTimer <= uiDiff) + { + if (Unit* pTarget = m_creature->SelectAttackingTarget(ATTACKING_TARGET_RANDOM, 0, SPELL_RIMEFANG_ICY_BLAST, SELECT_FLAG_PLAYER)) + { + if (DoCastSpellIfCan(pTarget, SPELL_RIMEFANG_ICY_BLAST) == CAST_OK) + { + m_uiIcyBlastTimer = 3000; + ++m_uiIcyBlastCounter; + + // phase end + if (m_uiIcyBlastCounter >= m_uiIcyBlastMaxCount) + { + m_uiIcyBlastCounter = 0; + m_uiIcyBlastTimer = 0; + m_uiPhase = RIMEFANG_PHASE_FLYING; + m_creature->GetMotionMaster()->MovePoint(RIMEFANG_POINT_GROUND, m_creature->GetPositionX(), m_creature->GetPositionY(), m_creature->GetPositionZ() - 20.0f, false); + } + } + } + } + else + m_uiIcyBlastTimer -= uiDiff; + } + } +}; + +CreatureAI* GetAI_npc_rimefang_icc(Creature* pCreature) +{ + return new npc_rimefang_iccAI(pCreature); +} + + +struct npc_spinestalker_iccAI : public ScriptedAI +{ + npc_spinestalker_iccAI(Creature* pCreature) : ScriptedAI(pCreature) + { + m_pInstance = (instance_icecrown_citadel*)pCreature->GetInstanceData(); + m_bHasLanded = false; + m_bIsReady = false; + Reset(); + } + + instance_icecrown_citadel* m_pInstance; + + uint32 m_uiBellowingRoarTimer; + uint32 m_uiTailSweepTimer; + uint32 m_uiCleaveTimer; + bool m_bHasLanded; + bool m_bIsReady; + + void Reset() override + { + m_uiBellowingRoarTimer = urand(8000, 24000); + m_uiTailSweepTimer = urand(4000, 8000); + m_uiCleaveTimer = urand(5000, 8000); + } + + void SetFlying(bool bIsFlying) + { + if (bIsFlying) + m_creature->SetByteFlag(UNIT_FIELD_BYTES_1, 3, UNIT_BYTE1_FLAG_UNK_2); + else + m_creature->RemoveByteFlag(UNIT_FIELD_BYTES_1, 3, UNIT_BYTE1_FLAG_UNK_2); + + m_creature->SetLevitate(bIsFlying); + m_creature->SetWalk(bIsFlying); + } + + void JustDied(Unit* /*pKiller*/) override + { + if (!m_pInstance) + return; + + Creature* pRimefang = m_pInstance->GetSingleCreatureFromStorage(NPC_RIMEFANG); + if (!pRimefang || !pRimefang->IsAlive()) + { + if (Creature* pSindragosa = m_creature->SummonCreature(NPC_SINDRAGOSA, SindragosaPosition[7][0], SindragosaPosition[7][1], SindragosaPosition[7][2], 0.0f, TEMPSUMMON_MANUAL_DESPAWN, 0)) + pSindragosa->SetInCombatWithZone(); + } + } + + void AttackStart(Unit* pWho) override + { + if (!m_bIsReady) + { + if (!m_bHasLanded) + { + m_bHasLanded = true; + m_creature->GetMotionMaster()->MovePoint(SPINESTALKER_POINT_INITIAL_LAND_AIR, SindragosaPosition[6][0], SindragosaPosition[6][1], SindragosaPosition[6][2], false); + } + + return; + } + + ScriptedAI::AttackStart(pWho); + } + + void EnterEvadeMode() override + { + m_creature->RemoveAllAurasOnEvade(); + m_creature->DeleteThreatList(); + m_creature->CombatStop(true); + + if (m_creature->IsAlive()) + m_creature->GetMotionMaster()->MovePoint(SPINESTALKER_POINT_INITIAL_LAND, SindragosaPosition[5][0], SindragosaPosition[5][1], SindragosaPosition[5][2]); + + m_creature->SetLootRecipient(NULL); + + Reset(); + } + + void MovementInform(uint32 uiMovementType, uint32 uiPointId) override + { + if (uiMovementType != POINT_MOTION_TYPE) + return; + + if (uiPointId == SPINESTALKER_POINT_INITIAL_LAND_AIR) + { + m_creature->GetMotionMaster()->MovePoint(SPINESTALKER_POINT_INITIAL_LAND, SindragosaPosition[5][0], SindragosaPosition[5][1], SindragosaPosition[5][2], false); + } + else if (uiPointId == SPINESTALKER_POINT_INITIAL_LAND) + { + m_creature->GetMotionMaster()->MoveIdle(); + m_creature->SetFacingTo(M_PI_F); + m_bIsReady = true; + SetFlying(false); + } + } + + void UpdateAI(const uint32 uiDiff) override + { + if (!m_creature->SelectHostileTarget() || !m_creature->getVictim()) + return; + + // Cleave + if (m_uiCleaveTimer <= uiDiff) + { + if (DoCastSpellIfCan(m_creature->getVictim(), SPELL_SPINESTALKER_CLEAVE) == CAST_OK) + m_uiCleaveTimer = urand(5000, 8000); + } + else + m_uiCleaveTimer -= uiDiff; + + // Tail Sweep + if (m_uiTailSweepTimer <= uiDiff) + { + if (DoCastSpellIfCan(m_creature, SPELL_SPINESTALKER_TAIL_SWEEP) == CAST_OK) + m_uiTailSweepTimer = urand(4000, 8000); + } + else + m_uiTailSweepTimer -= uiDiff; + + // Bellowing Roar + if (m_uiBellowingRoarTimer <= uiDiff) + { + if (DoCastSpellIfCan(m_creature, SPELL_SPINESTALKER_BELLOWING_ROAR) == CAST_OK) + m_uiBellowingRoarTimer = urand(8000, 24000); + } + else + m_uiBellowingRoarTimer -= uiDiff; + + DoMeleeAttackIfReady(); + } +}; + +CreatureAI* GetAI_npc_spinestalker_icc(Creature* pCreature) +{ + return new npc_spinestalker_iccAI(pCreature); +} + +/** + * Frost Bomb - npc marking the target of Frost Bomb + */ +struct mob_frost_bombAI : public ScriptedAI +{ + mob_frost_bombAI(Creature* pCreature) : ScriptedAI(pCreature) + { + m_pInstance = (instance_icecrown_citadel*)pCreature->GetInstanceData(); + Reset(); + } + + instance_icecrown_citadel* m_pInstance; + uint32 m_uiFrostBombTimer; + + void Reset() override + { + SetCombatMovement(false); + DoCastSpellIfCan(m_creature, SPELL_FROST_BOMB_VISUAL, CAST_TRIGGERED); + m_uiFrostBombTimer = 6000; + } + + void AttackStart(Unit* /*pWho*/) override {} + + void UpdateAI(const uint32 uiDiff) override + { + // Frost Bomb (dmg) + if (m_uiFrostBombTimer) + { + if (m_uiFrostBombTimer <= uiDiff) + { + if (m_pInstance) + { + if (Creature* pSindragosa = m_pInstance->GetSingleCreatureFromStorage(NPC_SINDRAGOSA)) + { + if (pSindragosa->AI()->DoCastSpellIfCan(m_creature, SPELL_FROST_BOMB_DMG) == CAST_OK) + { + m_creature->RemoveAurasDueToSpell(SPELL_FROST_BOMB_VISUAL); + m_creature->ForcedDespawn(2000); + m_uiFrostBombTimer = 0; + } + } + } + } + else + m_uiFrostBombTimer -= uiDiff; + } + } +}; + +CreatureAI* GetAI_mob_frost_bomb(Creature* pCreature) +{ + return new mob_frost_bombAI(pCreature); +} + +void AddSC_boss_sindragosa() +{ + Script* pNewScript; + + pNewScript = new Script; + pNewScript->Name = "boss_sindragosa"; + pNewScript->GetAI = &GetAI_boss_sindragosa; + pNewScript->RegisterSelf(); + + pNewScript = new Script; + pNewScript->Name = "npc_rimefang_icc"; + pNewScript->GetAI = &GetAI_npc_rimefang_icc; + pNewScript->RegisterSelf(); + + pNewScript = new Script; + pNewScript->Name = "npc_spinestalker_icc"; + pNewScript->GetAI = &GetAI_npc_spinestalker_icc; + pNewScript->RegisterSelf(); + + pNewScript = new Script; + pNewScript->Name = "mob_frost_bomb"; + pNewScript->GetAI = &GetAI_mob_frost_bomb; + pNewScript->RegisterSelf(); +} diff --git a/src/modules/SD2/scripts/northrend/icecrown_citadel/icecrown_citadel/boss_the_lich_king.cpp b/src/modules/SD2/scripts/northrend/icecrown_citadel/icecrown_citadel/boss_the_lich_king.cpp new file mode 100644 index 000000000..b6dc2b5c1 --- /dev/null +++ b/src/modules/SD2/scripts/northrend/icecrown_citadel/icecrown_citadel/boss_the_lich_king.cpp @@ -0,0 +1,720 @@ +/* Copyright (C) 2006 - 2013 ScriptDev2 + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with 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_lich_king +SD%Complete: 5% +SDComment: +SDCategory: Icecrown Citadel +EndScriptData */ + +#include "precompiled.h" +#include "icecrown_citadel.h" + +enum +{ + SAY_INTRO_1 = -1631158, + SAY_INTRO_2 = -1631159, + SAY_INTRO_3 = -1631160, + SAY_INTRO_4 = -1631161, + SAY_INTRO_5 = -1631162, + SAY_AGGRO = -1631163, + SAY_REMORSELESS_WINTER = -1631164, + SAY_SHATTER_ARENA = -1631165, + SAY_SUMMON_VALKYR = -1631166, + SAY_HARVEST_SOUL = -1631167, + SAY_FM_TERENAS_AID_1 = -1631168, + SAY_FM_TERENAS_AID_2 = -1631169, + SAY_FM_TERENAS_AID_3 = -1631170, + SAY_FM_PLAYER_ESCAPE = -1631171, + SAY_FM_PLAYER_DEATH = -1631172, + SAY_SPECIAL_1 = -1631173, + SAY_SPECIAL_2 = -1631174, + SAY_LAST_PHASE = -1631175, + SAY_SLAY_1 = -1631176, + SAY_SLAY_2 = -1631177, + SAY_ENRAGE = -1631178, + SAY_OUTRO_1 = -1631179, + SAY_OUTRO_2 = -1631180, + SAY_OUTRO_3 = -1631181, + SAY_OUTRO_4 = -1631182, + SAY_OUTRO_5 = -1631183, + SAY_OUTRO_6 = -1631184, + SAY_OUTRO_7 = -1631185, + SAY_OUTRO_8 = -1631186, + SAY_OUTRO_9 = -1631187, + SAY_OUTRO_10 = -1631188, + SAY_OUTRO_11 = -1631189, + SAY_OUTRO_12 = -1631190, + SAY_OUTRO_13 = -1631191, + SAY_OUTRO_14 = -1631192, +}; + +enum +{ + SPELL_BERSERK = 47008, + SPELL_SIT_EMOTE_NO_SHEATH = 73220, + SPELL_PLAGUE_AVOIDANCE = 72846, + + // Intro + SPELL_ICE_LOCK = 71614, + + // Outro + SPELL_FURY_OF_FROSTMOURNE = 72350, + SPELL_FURY_OF_FROSTMOURNE2 = 72351, // cannot resurect aura + SPELL_RAISE_DEAD = 71769, + SPELL_THROW_FROSTMOURNE = 73017, // 1 + SPELL_BROKEN_FROSTMOURNE = 72398, // 2 + SPELL_SUMMON_FROSTMOURNE = 72407, // 3 summon npc which casts 4 and 5 and LK enters this npc as vehicle + SPELL_FROSTMOURNE_DESPAWN = 72726, // 4 + SPELL_FROSTMOURNE_SPIRITS = 72405, // 5 + SPELL_SOUL_BARRAGE = 72305, // strangulation and sounds + SPELL_LK_CINEMATIC = 73159, + + // Tirion + SPELL_LIGHTS_BLESSING = 71797, // after 5secs smashes Ice Lock + + // Terenas Menethil + SPELL_MASS_RESURRECTION = 72429, // dummy + SPELL_MASS_RESURRECTION2 = 72423, // actual res + + // Phase One + SPELL_NECROTIC_PLAGUE = 70337, + SPELL_NECROTIC_PLAGUE_STACK = 70338, + SPELL_INFEST = 70541, + SPELL_SUMMON_GHOULS = 70358, + SPELL_SUMMON_HORROR = 70372, + SPELL_SHADOW_TRAP = 73539, + + // Phase transition + SPELL_REMORSELESS_WINTER_1 = 68981, // rooting caster and with Activate Object effect + SPELL_REMORSELESS_WINTER_2 = 72259, // rooting caster and with Send Script Event (23507) effect + SPELL_QUAKE = 72262, + SPELL_PAIN_AND_SUFFERING = 72133, + SPELL_RAGING_SPIRIT = 69200, + SPELL_SUMMON_RAGING_SPIRIT = 69201, + SPELL_SUMMON_ICE_SPHERE = 69103, + SPELL_ICE_SPHERE = 69104, // missile and summon effect + + // Phase Two + SPELL_SUMMON_VALKYR = 69037, + SPELL_SUMMON_VALKYRS = 74361, // 25man + SPELL_SOUL_REAPER = 69409, + SPELL_DEFILE = 72762, + + // Phase Three + SPELL_VILE_SPIRITS = 70498, + SPELL_HARVEST_SOUL = 68980, // stun aura and periodic damage, triggers summoning of vehicle + SPELL_HARVEST_SOUL_TP_FM_N = 72546, // teleports to Frostmourne Room and applies 60sec dummy aura (normal) + SPELL_HARVEST_SOUL_TP_FM_H = 73655, // teleports to Frostmourne Room and applies 60sec DoT aura (heroic) + SPELL_HARVEST_SOUL_CLONE = 71372, + SPELL_HARVEST_SOULS = 74296, + SPELL_HARVESTED_SOUL_1 = 73028, + SPELL_HARVESTED_SOUL_2 = 74321, + SPELL_HARVESTED_SOUL_3 = 74322, + SPELL_HARVESTED_SOUL_4 = 74323, + + SPELL_FROSTMOURNE_TP_VISUAL = 73078, + + // Shambling Horror + SPELL_FRENZY = 28747, + SPELL_ENRAGE = 72143, + SPELL_SHOCKWAVE = 72149, + + // Shadow Trap + SPELL_SHADOW_TRAP_VISUAL = 73530, + SPELL_SHADOW_TRAP_AURA = 73525, + + // Raging Spirit + SPELL_SOUL_SHRIEK = 69242, + SPELL_RAGING_SPIRIT_VISUAL = 69198, // clone effect (clone of player) + + // Ice Sphere + SPELL_ICE_SPHERE_VISUAL = 69090, + SPELL_ICE_BURST_AURA = 69109, + SPELL_ICE_BURST = 69108, + SPELL_ICE_PULSE = 69091, + + // Val'kyr Shadowguard + SPELL_LIFE_SIPHON = 73783, + SPELL_VALKYR_CHARGE = 74399, + SPELL_HARVEST_SOUL_VEHICLE = 68985, + SPELL_EJECT_PASSENGERS = 68576, + SPELL_WINGS_OF_THE_DAMNED = 74352, + + // Defile + SPELL_DEFILE_AURA = 72743, + SPELL_DEFILE_GROW = 72756, + + // Vile Spirit and Wicked Spirit + SPELL_SPIRIT_BURST_AURA = 70502, + + // Spirit Warden + SPELL_DARK_HUNGER = 69383, + SPELL_DESTROY_SOUL = 74086, + SPELL_SOUL_RIP = 69397, // 3500, each next one x2 (maybe based on HP of target?) + + // Terenas in Frostmourne + SPELL_RESTORE_SOUL = 72595, + SPELL_RESTORE_SOUL_HEROIC = 73650, + SPELL_LIGHTS_FAVOR = 69382, + SPELL_SUMMON_SPIRIT_BOMBS_1 = 73581, // heroic only, summons Spirit Bomb every 1 sec + SPELL_SUMMON_SPIRIT_BOMBS_2 = 74299, // 2 secs interval + SPELL_SUMMON_SPIRIT_BOMB = 74300, // aura doesnt work somehow, so we will use manual summoning + + // Spirit Bomb + SPELL_SPIRIT_BOMB_VISUAL = 73572, + SPELL_EXPLOSION = 73804, + + // NPCs + NPC_SHADOW_TRAP = 39137, + NPC_FROSTMOURNE = 38584, + NPC_ICE_SPHERE = 36633, + NPC_RAGING_SPIRIT = 36701, + NPC_DEFILE = 38757, + NPC_SPIRIT_WARDEN = 36824, + NPC_TERENAS_FM_NORMAL = 36823, + NPC_TERENAS_FM_HEROIC = 39217, + NPC_WICKED_SPIRIT = 39190, + NPC_SPIRIT_BOMB = 39189, +}; + +enum Phase +{ + PHASE_INTRO = 0, // intro + PHASE_ONE = 1, // phase one + PHASE_RUNNING_WINTER_ONE = 2, // running to center of platform to cast Remorseless Winter + PHASE_TRANSITION_ONE = 3, // Remorseless Winter aura and casting spells, summoning orbs and spirits + PHASE_QUAKE_ONE = 4, // casting Quake + PHASE_TWO = 5, // phase two with val'kyrs and some more spells + PHASE_RUNNING_WINTER_TWO = 6, // running to center of platform to cast Remorseless Winter again + PHASE_TRANSITION_TWO = 7, // second Remorseless Winter phase + PHASE_QUAKE_TWO = 8, // second Quake casting + PHASE_THREE = 9, // phase three, casting Soul Harvest (Frostmourne phase) + PHASE_IN_FROSTMOURNE = 10, // phase three, waiting untill whole raid leaves Frostmourne + PHASE_CUTSCENE = 11, // phase when LK kills raid, Terenas comes etc. + PHASE_DEATH_AWAITS = 12, // strangulating Lich King, raid group finishing him +}; + +enum Point +{ + POINT_CENTER_LAND = 1, + POINT_CENTER_LAND_TIRION = 2, + POINT_TELEPORTER_TIRION = 3, + POINT_VALKYR_THROW = 4, + POINT_VALKYR_CENTER = 5, + POINT_TP_TO_FM = 6, // point where strangulate vehicle moves, after reaching player is teleported into frostmourne + POINT_SPIRIT_BOMB = 7, // Spirit Bomb moving down +}; + +static const float fLichKingPosition[11][3] = +{ + {458.59f, -2122.71f, 1040.86f}, // 0 Lich King Intro + {503.16f, -2124.52f, 1040.86f}, // 1 Center of the platform + {500.16f, -2124.52f, 1040.86f}, // 2 Tirion strikes Lich King + {481.70f, -2124.64f, 1040.86f}, // 3 Tirion 2 + {498.00f, -2201.57f, 1046.09f}, // 4 Valkyrs? + {517.48f, -2124.91f, 1040.86f}, // 5 Tirion? + {529.85f, -2124.71f, 1040.86f}, // 6 Lich king final, o=3.1146 + {520.31f, -2124.71f, 1040.86f}, // 7 Frostmourne + {453.80f, -2088.20f, 1040.86f}, // 8 Val'kyr drop point right to Frozen Throne + {457.03f, -2155.08f, 1040.86f}, // 9 Val'kyr drop point left to Frozen Throne + {494.31f, -2523.08f, 1249.87f}, // 10 center of platform inside Frostmourne +}; + +struct boss_the_lich_king_iccAI : public ScriptedAI +{ + boss_the_lich_king_iccAI(Creature* pCreature) : ScriptedAI(pCreature) + { + m_pInstance = (instance_icecrown_citadel*)pCreature->GetInstanceData(); + Reset(); + } + + instance_icecrown_citadel* m_pInstance; + + uint32 m_uiPhase; + uint32 m_uiPhaseTimer; + uint32 m_uiBerserkTimer; + uint32 m_uiGhoulsTimer; + uint32 m_uiHorrorTimer; + uint32 m_uiNecroticPlagueTimer; + uint32 m_uiInfestTimer; + uint32 m_uiShadowTrapTimer; + uint32 m_uiPainSufferingTimer; + uint32 m_uiRagingSpiritTimer; + uint32 m_uiIceSphereTimer; + uint32 m_uiValkyrTimer; + uint32 m_uiDefileTimer; + uint32 m_uiSoulReaperTimer; + uint32 m_uiHarvestSoulTimer; + uint32 m_uiFrostmournePhaseTimer; + uint32 m_uiVileSpiritsTimer; + + void Reset() override + { + // TODO: handling phases "intro" and "one" and aggroing depending on resetting encounter + m_uiPhase = PHASE_INTRO; + + m_uiBerserkTimer = 15 * MINUTE * IN_MILLISECONDS; + m_uiGhoulsTimer = 13000; + m_uiHorrorTimer = 21000; + m_uiInfestTimer = 20000; + m_uiNecroticPlagueTimer = 23000; + m_uiShadowTrapTimer = 15000; + m_uiPainSufferingTimer = 6000; + m_uiRagingSpiritTimer = 20000; + m_uiIceSphereTimer = 6000; + m_uiValkyrTimer = 10000; + m_uiDefileTimer = 30000; + m_uiSoulReaperTimer = 25000; + m_uiHarvestSoulTimer = 5000; + m_uiVileSpiritsTimer = 20000; + } + + void Aggro(Unit* /*pWho*/) override + { + if (m_pInstance) + m_pInstance->SetData(TYPE_LICH_KING, IN_PROGRESS); + + DoScriptText(SAY_AGGRO, m_creature); + m_uiPhase = PHASE_ONE; + } + + void KilledUnit(Unit* pWho) override + { + if (pWho->GetTypeId() == TYPEID_PLAYER) + DoScriptText(urand(0, 1) ? SAY_SLAY_1 : SAY_SLAY_2, m_creature); + } + + void JustDied(Unit* /*pKiller*/) override + { + if (m_pInstance) + m_pInstance->SetData(TYPE_LICH_KING, DONE); + + DoScriptText(SAY_OUTRO_14, m_creature); + + // TODO: finish event, after around 8 seconds play cinematic + } + + void JustReachedHome() override + { + if (m_pInstance) + m_pInstance->SetData(TYPE_LICH_KING, FAIL); + } + + void MovementInform(uint32 uiMovementType, uint32 uiData) override + { + if (uiMovementType != POINT_MOTION_TYPE) + return; + + switch (uiData) + { + case POINT_CENTER_LAND: + { + if (m_uiPhase == PHASE_RUNNING_WINTER_ONE) + { + DoScriptText(SAY_REMORSELESS_WINTER, m_creature); + + // TODO: not sure which spell in which phase + // DoCastSpellIfCan(m_creature, SPELL_REMORSELESS_WINTER_1); + + m_uiPhase = PHASE_TRANSITION_ONE; + m_uiPhaseTimer = 62000; + + // TODO: set phase initial timers + // TODO: on heroic despawn Shadow Traps + } + else if (m_uiPhase == PHASE_RUNNING_WINTER_TWO) + { + DoScriptText(SAY_REMORSELESS_WINTER, m_creature); + + // TODO: not sure which spell in which phase + // DoCastSpellIfCan(m_creature, SPELL_REMORSELESS_WINTER_2); + + m_uiPhase = PHASE_TRANSITION_TWO; + m_uiPhaseTimer = 62000; + + // TODO: set phase initial timers + } + else if (m_uiPhase == PHASE_DEATH_AWAITS) + { + DoCastSpellIfCan(m_creature, SPELL_RAISE_DEAD); + } + + break; + } + } + } + + void UpdateAI(const uint32 uiDiff) override + { + if (m_uiPhase != PHASE_INTRO && m_uiPhase != PHASE_DEATH_AWAITS) + { + // check evade + if (!m_creature->SelectHostileTarget() || !m_creature->getVictim()) + return; + + // Berserk + if (m_uiBerserkTimer) + { + if (m_uiBerserkTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature, SPELL_BERSERK) == CAST_OK) + { + DoScriptText(SAY_ENRAGE, m_creature); + m_uiBerserkTimer = 0; + } + } + else + m_uiBerserkTimer -= uiDiff; + } + } + + switch (m_uiPhase) + { + case PHASE_INTRO: + { + // wait until set in combat + return; + } + case PHASE_ONE: + { + // check HP + if (m_creature->GetHealthPercent() <= 70.0f) + { + // phase transition + m_creature->GetMotionMaster()->Clear(); + m_creature->GetMotionMaster()->MovePoint(POINT_CENTER_LAND, fLichKingPosition[1][0], fLichKingPosition[1][1], fLichKingPosition[1][2]); + m_uiPhase = PHASE_RUNNING_WINTER_ONE; + return; + } + + // Necrotic Plague + if (m_uiNecroticPlagueTimer < uiDiff) + { + // shouldn't be targeting players who already have Necrotic Plague on them + if (Unit* pTarget = m_creature->SelectAttackingTarget(ATTACKING_TARGET_RANDOM, 1, SPELL_NECROTIC_PLAGUE, SELECT_FLAG_PLAYER)) + { + if (DoCastSpellIfCan(pTarget, SPELL_NECROTIC_PLAGUE) == CAST_OK) + m_uiNecroticPlagueTimer = 30000; + } + } + else + m_uiNecroticPlagueTimer -= uiDiff; + + // Infest + if (m_uiInfestTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature, SPELL_INFEST) == CAST_OK) + m_uiInfestTimer = urand(20000, 25000); + } + else + m_uiInfestTimer -= uiDiff; + + // Summon Ghouls + if (m_uiGhoulsTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature, SPELL_SUMMON_GHOULS) == CAST_OK) + m_uiGhoulsTimer = 32000; + } + else + m_uiGhoulsTimer -= uiDiff; + + // Summon Shambling Horror + if (m_uiHorrorTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature, SPELL_SUMMON_HORROR) == CAST_OK) + m_uiHorrorTimer = 60000; + } + else + m_uiHorrorTimer -= uiDiff; + + // Shadow Trap (heroic) + if (m_pInstance && m_pInstance->IsHeroicDifficulty()) + { + if (m_uiShadowTrapTimer < uiDiff) + { + if (Unit* pTarget = m_creature->SelectAttackingTarget(ATTACKING_TARGET_RANDOM, 1, SPELL_SHADOW_TRAP, SELECT_FLAG_PLAYER)) + { + if (DoCastSpellIfCan(pTarget, SPELL_SHADOW_TRAP) == CAST_OK) + m_uiShadowTrapTimer = 15000; + } + } + else + m_uiShadowTrapTimer -= uiDiff; + } + + DoMeleeAttackIfReady(); + + break; + } + case PHASE_RUNNING_WINTER_ONE: + case PHASE_RUNNING_WINTER_TWO: + { + // wait for waypoint arrival + break; + } + case PHASE_TRANSITION_ONE: + case PHASE_TRANSITION_TWO: + { + // phase end timer + if (m_uiPhaseTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature, SPELL_QUAKE) == CAST_OK) + { + DoScriptText(SAY_SHATTER_ARENA, m_creature); + m_uiPhase = (m_uiPhase == PHASE_TRANSITION_ONE ? PHASE_QUAKE_ONE : PHASE_QUAKE_TWO); + m_uiPhaseTimer = 6500; + } + } + else + m_uiPhaseTimer -= uiDiff; + + // Pain and Suffering + if (m_uiPainSufferingTimer < uiDiff) + { + if (Unit* pTarget = m_creature->SelectAttackingTarget(ATTACKING_TARGET_RANDOM, 0, SPELL_PAIN_AND_SUFFERING, SELECT_FLAG_PLAYER)) + { + if (DoCastSpellIfCan(pTarget, SPELL_PAIN_AND_SUFFERING) == CAST_OK) + m_uiPainSufferingTimer = urand(1500, 3000); + } + } + else + m_uiPainSufferingTimer -= uiDiff; + + // Summon Ice Sphere + if (m_uiIceSphereTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature, SPELL_ICE_SPHERE) == CAST_OK) + m_uiIceSphereTimer = 6000; + } + else + m_uiIceSphereTimer -= uiDiff; + + // Summon Raging Spirit + if (m_uiRagingSpiritTimer < uiDiff) + { + if (Unit* pTarget = m_creature->SelectAttackingTarget(ATTACKING_TARGET_RANDOM, 0, SPELL_RAGING_SPIRIT, SELECT_FLAG_PLAYER)) + { + if (DoCastSpellIfCan(pTarget, SPELL_RAGING_SPIRIT, CAST_TRIGGERED) == CAST_OK) + m_uiRagingSpiritTimer = (m_uiPhase == PHASE_TRANSITION_ONE ? 20000 : 15000); + } + } + else + m_uiRagingSpiritTimer -= uiDiff; + + break; + } + case PHASE_QUAKE_ONE: + case PHASE_QUAKE_TWO: + { + // Casting Quake spell - phase end timer + if (m_uiPhaseTimer < uiDiff) + { + // TODO: destroy platform + + m_uiPhase = (m_uiPhase == PHASE_QUAKE_ONE ? PHASE_TWO : PHASE_THREE); + m_creature->GetMotionMaster()->Clear(); + m_creature->GetMotionMaster()->MoveChase(m_creature->getVictim()); + return; + } + else + m_uiPhaseTimer -= uiDiff; + + break; + } + case PHASE_TWO: + { + // check HP + if (m_creature->GetHealthPercent() <= 40.0f) + { + // phase transition + m_creature->GetMotionMaster()->Clear(); + m_creature->GetMotionMaster()->MovePoint(POINT_CENTER_LAND, fLichKingPosition[1][0], fLichKingPosition[1][1], fLichKingPosition[1][2]); + m_uiPhaseTimer = 60000; + m_uiPhase = PHASE_RUNNING_WINTER_TWO; + } + + // Soul Reaper + if (m_uiSoulReaperTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature->getVictim(), SPELL_SOUL_REAPER) == CAST_OK) + m_uiSoulReaperTimer = 30000; + } + else + m_uiSoulReaperTimer -= uiDiff; + + // Infest + if (m_uiInfestTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature, SPELL_INFEST) == CAST_OK) + m_uiInfestTimer = urand(20000, 25000); + } + else + m_uiInfestTimer -= uiDiff; + + // Defile + if (m_uiDefileTimer < uiDiff) + { + // shouldn't be targeting players in vehicles + if (Unit* pTarget = m_creature->SelectAttackingTarget(ATTACKING_TARGET_RANDOM, 1, SPELL_DEFILE, SELECT_FLAG_PLAYER)) + { + if (DoCastSpellIfCan(pTarget, SPELL_DEFILE) == CAST_OK) + m_uiDefileTimer = 30000; + } + } + else + m_uiDefileTimer -= uiDiff; + + // Summon Val'kyr + if (m_uiValkyrTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature, SPELL_SUMMON_VALKYR) == CAST_OK) + { + DoScriptText(SAY_SUMMON_VALKYR, m_creature); + m_uiValkyrTimer = 50000; + } + } + else + m_uiValkyrTimer -= uiDiff; + + DoMeleeAttackIfReady(); + + break; + } + case PHASE_THREE: + { + // check HP + if (m_creature->GetHealthPercent() <= 10.0f) + { + if (DoCastSpellIfCan(m_creature, SPELL_FURY_OF_FROSTMOURNE) == CAST_OK) + { + DoScriptText(SAY_LAST_PHASE, m_creature); + m_uiPhase = PHASE_DEATH_AWAITS; + + // TODO: start ending event + + return; + } + } + + // Soul Reaper + if (m_uiSoulReaperTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature->getVictim(), SPELL_SOUL_REAPER) == CAST_OK) + m_uiSoulReaperTimer = 30000; + } + else + m_uiSoulReaperTimer -= uiDiff; + + // Defile + if (m_uiDefileTimer < uiDiff) + { + // shouldn't be targeting players in vehicles + if (Unit* pTarget = m_creature->SelectAttackingTarget(ATTACKING_TARGET_RANDOM, 1, SPELL_DEFILE, SELECT_FLAG_PLAYER)) + { + if (DoCastSpellIfCan(pTarget, SPELL_DEFILE) == CAST_OK) + m_uiDefileTimer = 30000; + } + } + else + m_uiDefileTimer -= uiDiff; + + // Harvest Soul + if (m_uiHarvestSoulTimer < uiDiff) + { + Unit* pTarget = NULL; + bool m_bIsHeroic = m_pInstance && m_pInstance->IsHeroicDifficulty(); + if (m_bIsHeroic) + pTarget = m_creature; + else + pTarget = m_creature->SelectAttackingTarget(ATTACKING_TARGET_RANDOM, 1, SPELL_HARVEST_SOUL, SELECT_FLAG_PLAYER); + + if (pTarget) + { + if (DoCastSpellIfCan(pTarget, m_bIsHeroic ? SPELL_HARVEST_SOULS : SPELL_HARVEST_SOUL) == CAST_OK) + { + DoScriptText(SAY_HARVEST_SOUL, m_creature); + m_uiHarvestSoulTimer = m_bIsHeroic ? 120000 : 70000; + + // TODO: prepare Frostmourne room - summon bombs and Tirion, or Tirion and the "bad spirit-guy" + + if (m_bIsHeroic) + { + m_uiPhase = PHASE_IN_FROSTMOURNE; + SetCombatMovement(false); + m_creature->StopMoving(); + m_uiFrostmournePhaseTimer = 47000; + m_uiDefileTimer = 1000; + } + } + } + } + else + m_uiHarvestSoulTimer -= uiDiff; + + // Vile Spirits + if (m_uiVileSpiritsTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature, SPELL_VILE_SPIRITS) == CAST_OK) + m_uiVileSpiritsTimer = 30000; + } + else + m_uiVileSpiritsTimer -= uiDiff; + + DoMeleeAttackIfReady(); + + break; + } + case PHASE_IN_FROSTMOURNE: + { + // check if players are alive before entering evade mode? + // wait until they leave Frostmourne + if (m_uiFrostmournePhaseTimer < uiDiff) + { + m_uiPhase = PHASE_THREE; + if (m_creature->getVictim()) + m_creature->GetMotionMaster()->MoveChase(m_creature->getVictim()); + return; + } + else + m_uiFrostmournePhaseTimer -= uiDiff; + + break; + } + case PHASE_DEATH_AWAITS: + { + // wait for swift death + break; + } + } + } +}; + +CreatureAI* GetAI_boss_the_lich_king_icc(Creature* pCreature) +{ + return new boss_the_lich_king_iccAI(pCreature); +} + +void AddSC_boss_the_lich_king() +{ + Script* pNewScript; + + pNewScript = new Script; + pNewScript->Name = "boss_the_lich_king_icc"; + pNewScript->GetAI = &GetAI_boss_the_lich_king_icc; + pNewScript->RegisterSelf(); +} diff --git a/src/modules/SD2/scripts/northrend/icecrown_citadel/icecrown_citadel/boss_valithria_dreamwalker.cpp b/src/modules/SD2/scripts/northrend/icecrown_citadel/icecrown_citadel/boss_valithria_dreamwalker.cpp new file mode 100644 index 000000000..d8dcec691 --- /dev/null +++ b/src/modules/SD2/scripts/northrend/icecrown_citadel/icecrown_citadel/boss_valithria_dreamwalker.cpp @@ -0,0 +1,158 @@ +/* Copyright (C) 2006 - 2013 ScriptDev2 + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +/* ScriptData +SDName: boss_valithria +SD%Complete: 5% +SDComment: +SDCategory: Icecrown Citadel +EndScriptData */ + +#include "precompiled.h" +#include "icecrown_citadel.h" + +enum SvalnaTexts // TODO Maybe need own file? +{ + SAY_SVALNA_EVENT_1 = -1631130, + SAY_SVALNA_EVENT_2 = -1631131, + SAY_SVALNA_EVENT_3 = -1631132, + SAY_SVALNA_EVENT_4 = -1631133, + SAY_KILLING_CRUSADERS = -1631134, + SAY_RESSURECT = -1631135, + SAY_SVALNA_AGGRO = -1631136, + SAY_KILL_CAPTAIN = -1631137, + SAY_KILL_PLAYER = -1631138, + SAY_DEATH = -1631139, +}; + +enum +{ + SAY_AGGRO = -1631140, + SAY_PORTAL = -1631141, + SAY_75_HEALTH = -1631142, + SAY_25_HEALTH = -1631143, + SAY_0_HEALTH = -1631144, + SAY_PLAYER_DIES = -1631145, + SAY_BERSERK = -1631146, + SAY_VICTORY = -1631147, +}; + +// Valithria +enum +{ + SPELL_NIGHTMARE_PORTAL_PRE = 71977, + SPELL_NIGHTMARE_PORTAL = 71987, + SPELL_TWISTED_NIGHTMARES = 71941, + SPELL_TWISTED_NIGHTMARES_DOT = 71940, + SPELL_NIGHTMARE_CLOUD = 71970, // Nightmare Clouds cast this on dreaming Valithria - then she deals this dmg to Real Valithria(?) + SPELL_NIGHTMARE_CLOUD_VISUAL = 71939, + + SPELL_DREAM_PORTAL_PRE = 71301, + SPELL_DREAM_PORTAL = 71305, + SPELL_EMERALD_VIGOR = 70873, + SPELL_DREAM_CLOUD_VISUAL = 70876, + + SPELL_DREAM_STATE = 70766, + + SPELL_DREAMWALKER_RAGE = 71189, + SPELL_IMMUNITY = 72724, + SPELL_CORRUPTION = 70904, + SPELL_DREAM_SLIP = 71196, + SPELL_ICE_SPIKE = 70702, + + SPELL_CLEAR_DREAMS_NIGHTMARES = 75863, // script effect removes Emerald Vigor and Nightmare auras + + // summons + // TODO: + // these spells should be applied to dummy npcs in gates + // they should apply these auras once the encounter has started + // but how to apply this, which spell on which npc and when? + // how to handle summon timers speedup? + SPELL_SUMMON_TIMER_SUPPRESSER = 70912, + SPELL_SUMMON_TIMER_SKELETON = 70913, + SPELL_SUMMON_TIMER_ZOMBIE = 70914, + SPELL_SUMMON_TIMER_ABOMIN = 70915, + SPELL_SUMMON_TIMER_ARCHMAGE = 70916, + + // entries + NPC_NIGHTMARE_PORTAL_PRE = 38429, + NPC_NIGHTMARE_PORTAL = 38430, + NPC_NIGHTMARE_CLOUD = 38421, + NPC_DREAM_PORTAL_PRE = 38186, + NPC_DREAM_PORTAL = 37945, + NPC_DREAM_CLOUD = 37985, + + // Achievements + SPELL_ACHIEVEMENT_CREDIT = 72706, + + // other + SUMMON_TYPES_NUMBER = 4 +}; + +struct boss_valithria_dreamwalkerAI : public ScriptedAI +{ + boss_valithria_dreamwalkerAI(Creature* pCreature) : ScriptedAI(pCreature) + { + m_pInstance = (instance_icecrown_citadel*)pCreature->GetInstanceData(); + Reset(); + } + + instance_icecrown_citadel* m_pInstance; + + void Reset() override + { + } + + void JustReachedHome() override + { + if (m_pInstance) + m_pInstance->SetData(TYPE_VALITHRIA, FAIL); + } + + // actually, when summoned creature kills a player + void KilledUnit(Unit* pVictim) override + { + if (pVictim->GetTypeId() == TYPEID_PLAYER) + DoScriptText(SAY_PLAYER_DIES, m_creature, pVictim); + } + + void JustDied(Unit* /*pKiller*/) override + { + DoScriptText(SAY_0_HEALTH, m_creature); + + if (m_pInstance) + m_pInstance->SetData(TYPE_VALITHRIA, FAIL); + } + + void UpdateAI(const uint32 /*uiDiff*/) override + { + } +}; + +CreatureAI* GetAI_boss_valithria_dreamwalker(Creature* pCreature) +{ + return new boss_valithria_dreamwalkerAI(pCreature); +}; + +void AddSC_boss_valithria_dreamwalker() +{ + Script* pNewscript; + + pNewscript = new Script; + pNewscript->Name = "boss_valithria_dreamwalker"; + pNewscript->GetAI = &GetAI_boss_valithria_dreamwalker; + pNewscript->RegisterSelf(); +} diff --git a/src/modules/SD2/scripts/northrend/icecrown_citadel/icecrown_citadel/gunship_battle.cpp b/src/modules/SD2/scripts/northrend/icecrown_citadel/icecrown_citadel/gunship_battle.cpp new file mode 100644 index 000000000..499996995 --- /dev/null +++ b/src/modules/SD2/scripts/northrend/icecrown_citadel/icecrown_citadel/gunship_battle.cpp @@ -0,0 +1,28 @@ +/* Copyright (C) 2006 - 2013 ScriptDev2 + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +/* ScriptData +SDName: gunship_battle +SD%Complete: 0% +SDComment: +SDCategory: Icecrown Citadel +EndScriptData */ + +#include "precompiled.h" + +void AddSC_gunship_battle() +{ +} diff --git a/src/modules/SD2/scripts/northrend/icecrown_citadel/icecrown_citadel/icecrown_citadel.h b/src/modules/SD2/scripts/northrend/icecrown_citadel/icecrown_citadel/icecrown_citadel.h new file mode 100644 index 000000000..33924c706 --- /dev/null +++ b/src/modules/SD2/scripts/northrend/icecrown_citadel/icecrown_citadel/icecrown_citadel.h @@ -0,0 +1,255 @@ +/* Copyright (C) 2006 - 2013 ScriptDev2 + * This program is free software licensed under GPL version 2 + * Please see the included DOCS/LICENSE.TXT for more information */ + +#ifndef DEF_ICECROWN_CITADEL_H +#define DEF_ICECROWN_CITADEL_H + +enum +{ + MAX_ENCOUNTER = 12, + + TYPE_MARROWGAR = 0, + TYPE_LADY_DEATHWHISPER = 1, + TYPE_GUNSHIP_BATTLE = 2, + TYPE_DEATHBRINGER_SAURFANG = 3, + TYPE_FESTERGUT = 4, + TYPE_ROTFACE = 5, + TYPE_PROFESSOR_PUTRICIDE = 6, + TYPE_BLOOD_PRINCE_COUNCIL = 7, + TYPE_QUEEN_LANATHEL = 8, + TYPE_VALITHRIA = 9, + TYPE_SINDRAGOSA = 10, + TYPE_LICH_KING = 11, + + // NPC entries + NPC_LORD_MARROWGAR = 36612, + NPC_LADY_DEATHWHISPER = 36855, + NPC_DEATHBRINGER_SAURFANG = 37813, + NPC_FESTERGUT = 36626, + NPC_ROTFACE = 36627, + NPC_PROFESSOR_PUTRICIDE = 36678, + NPC_TALDARAM = 37973, + NPC_VALANAR = 37970, + NPC_KELESETH = 37972, + NPC_QUEEN_LANATHEL = 37955, + NPC_VALITHRIA = 36789, + NPC_SINDRAGOSA = 36853, + NPC_LICH_KING = 36597, + + // boss-related and other NPCs + NPC_DEATHWHISPER_SPAWN_STALKER = 37947, + NPC_DEATHWHISPER_CONTROLLER = 37948, + NPC_OVERLORD_SAURFANG = 37187, + NPC_KORKRON_REAVER = 37920, + NPC_MURADIN_BRONZEBEARD = 37200, // Saurfang's encounter and at the instance entrance + NPC_SKYBREAKER_MARINE = 37380, + NPC_ALLIANCE_MARINE = 37830, + NPC_BLOOD_ORB_CONTROL = 38008, + NPC_LANATHEL_INTRO = 38004, + NPC_VALITHRIA_QUEST = 38589, + NPC_VALITHRIA_COMBAT_TRIGGER = 38752, + NPC_MURADIN = 36948, // Gunship Battle's encounter(?) + NPC_TIRION = 38995, + NPC_MENETHIL = 38579, + NPC_FROSTMOURNE_TRIGGER = 38584, + NPC_FROSTMOURNE_HOLDER = 27880, + NPC_STINKY = 37025, + NPC_PRECIOUS = 37217, + NPC_PUDDLE_STALKER = 37013, // related to Festergut and Rotface + NPC_RIMEFANG = 37533, + NPC_SPINESTALKER = 37534, + + // GameObjects entries + GO_ICEWALL_1 = 201911, + GO_ICEWALL_2 = 201910, + GO_MARROWGAR_DOOR = 201857, // Marrowgar combat door + + GO_ORATORY_DOOR = 201563, + GO_DEATHWHISPER_ELEVATOR = 202220, + + GO_SAURFANG_DOOR = 201825, + + GO_GREEN_PLAGUE = 201370, // Rotface combat door + GO_ORANGE_PLAGUE = 201371, // Festergut combat door + GO_SCIENTIST_DOOR = 201372, // Putricide combat door + GO_SCIENTIST_DOOR_COLLISION = 201612, // Putricide pathway doors + GO_SCIENTIST_DOOR_ORANGE = 201613, + GO_SCIENTIST_DOOR_GREEN = 201614, + GO_GREEN_VALVE = 201615, // Valves used to release the Gas / Oozes in order to open the pathway to Putricide - triggers event 23426 + GO_ORANGE_VALVE = 201616, // triggers event 23438 + GO_ORANGE_TUBE = 201617, + GO_GREEN_TUBE = 201618, + + // GO_BLOODWING_DOOR = 201920, // not used + GO_CRIMSON_HALL_DOOR = 201376, // Council combat door + GO_COUNCIL_DOOR_1 = 201377, + GO_COUNCIL_DOOR_2 = 201378, + GO_BLOODPRINCE_DOOR = 201746, // Lanathel combat door + GO_ICECROWN_GRATE = 201755, // Lanathel trap door + + // GO_FROSTWING_DOOR = 201919, // not used + GO_GREEN_DRAGON_ENTRANCE = 201375, // Valithria combat door + GO_GREEN_DRAGON_EXIT = 201374, + GO_VALITHRIA_DOOR_1 = 201381, // Valithria event doors + GO_VALITHRIA_DOOR_2 = 201382, + GO_VALITHRIA_DOOR_3 = 201383, + GO_VALITHRIA_DOOR_4 = 201380, + GO_SINDRAGOSA_SHORTCUT_ENTRANCE = 201369, // Shortcut doors are opened only after the trash before Sindragosa is cleared + GO_SINDRAGOSA_SHORTCUT_EXIT = 201379, + GO_SINDRAGOSA_ENTRANCE = 201373, + + GO_FROZENTRONE_TRANSPORTER = 202223, + GO_ICESHARD_1 = 202142, + GO_ICESHARD_2 = 202141, + GO_ICESHARD_3 = 202143, + GO_ICESHARD_4 = 202144, + GO_FROSTY_WIND = 202188, + GO_FROSTY_EDGE = 202189, + GO_SNOW_EDGE = 202190, + GO_ARTHAS_PLATFORM = 202161, + GO_ARTHAS_PRECIPICE = 202078, + + GO_PLAGUE_SIGIL = 202182, // Possible used after each wing is cleared + GO_FROSTWING_SIGIL = 202181, + GO_BLOODWING_SIGIL = 202183, + + // Loot chests + GO_SAURFANG_CACHE = 202239, + GO_SAURFANG_CACHE_25 = 202240, + GO_SAURFANG_CACHE_10_H = 202238, + GO_SAURFANG_CACHE_25_H = 202241, + + GO_GUNSHIP_ARMORY_A = 201872, + GO_GUNSHIP_ARMORY_A_25 = 201873, + GO_GUNSHIP_ARMORY_A_10H = 201874, + GO_GUNSHIP_ARMORY_A_25H = 201875, + + GO_GUNSHIP_ARMORY_H = 202177, + GO_GUNSHIP_ARMORY_H_25 = 202178, + GO_GUNSHIP_ARMORY_H_10H = 202179, + GO_GUNSHIP_ARMORY_H_25H = 202180, + + GO_DREAMWALKER_CACHE = 201959, + GO_DREAMWALKER_CACHE_25 = 202339, + GO_DREAMWALKER_CACHE_10_H = 202338, + GO_DREAMWALKER_CACHE_25_H = 202340, + + // Area triggers + AREATRIGGER_MARROWGAR_INTRO = 5732, + AREATRIGGER_DEATHWHISPER_INTRO = 5709, + AREATRIGGER_SINDRAGOSA_PLATFORM = 5604, + + // Achievement criterias + ACHIEV_CRIT_BONED_10N = 12775, // Lord Marrowgar, achievs 4534, 4610 + ACHIEV_CRIT_BONED_25N = 12962, + ACHIEV_CRIT_BONED_10H = 13393, + ACHIEV_CRIT_BONED_25H = 13394, + + ACHIEV_CRIT_HOUSE_10N = 12776, // Lady Deathwhisper, achievs 4535, 4611 + ACHIEV_CRIT_HOUSE_25N = 12997, + ACHIEV_CRIT_HOUSE_10H = 12995, + ACHIEV_CRIT_HOUSE_25H = 12998, + + ACHIEV_CRIT_IM_ON_A_BOAT_10N = 12777, // Gunship Battle, achievs 4536, 4612 + ACHIEV_CRIT_IM_ON_A_BOAT_25N = 13080, + ACHIEV_CRIT_IM_ON_A_BOAT_10H = 13079, + ACHIEV_CRIT_IM_ON_A_BOAT_25H = 13081, + + ACHIEV_CRIT_MADE_A_MESS_10N = 12778, // Deathbringer Saurfang, achievs 4537, 4613 + ACHIEV_CRIT_MADE_A_MESS_25N = 13036, + ACHIEV_CRIT_MADE_A_MESS_10H = 13035, + ACHIEV_CRIT_MADE_A_MESS_25H = 13037, + + ACHIEV_CRIT_FLU_SHOT_SHORTAGE_10N = 12977, // Festergut, achievs 4615, 4577 + ACHIEV_CRIT_FLU_SHOT_SHORTAGE_25N = 12982, + ACHIEV_CRIT_FLU_SHOT_SHORTAGE_10H = 12986, + ACHIEV_CRIT_FLU_SHOT_SHORTAGE_25H = 12967, + + ACHIEV_CRIT_DANCES_WITH_OOZES_10N = 12984, // Rotface, achievs 4538, 4614 + ACHIEV_CRIT_DANCES_WITH_OOZES_25N = 12966, + ACHIEV_CRIT_DANCES_WITH_OOZES_10H = 12985, + ACHIEV_CRIT_DANCES_WITH_OOZES_25H = 12983, + + ACHIEV_CRIT_NAUSEA_10N = 12987, // Professor Putricide, achievs 4578, 4616 + ACHIEV_CRIT_NAUSEA_25N = 12968, + ACHIEV_CRIT_NAUSEA_10H = 12988, + ACHIEV_CRIT_NAUSEA_25H = 12981, + + ACHIEV_CRIT_ORB_WHISPERER_10N = 13033, // Blood Prince Council, achievs 4582, 4617 + ACHIEV_CRIT_ORB_WHISPERER_25N = 12969, + ACHIEV_CRIT_ORB_WHISPERER_10H = 13034, + ACHIEV_CRIT_ORB_WHISPERER_25H = 13032, + + ACHIEV_CRIT_ONCE_BITTEN_TWICE_SHY_10N = 12780, // Blood-Queen Lana'thel, achievs 4539, 4618 + ACHIEV_CRIT_ONCE_BITTEN_TWICE_SHY_25N = 13012, + ACHIEV_CRIT_ONCE_BITTEN_TWICE_SHY_10V = 13011, + ACHIEV_CRIT_ONCE_BITTEN_TWICE_SHY_25V = 13013, + + ACHIEV_CRIT_PORTAL_JOCKEY_10N = 12978, // Valithria, achievs 4579, 4619 + ACHIEV_CRIT_PORTAL_JOCKEY_25N = 12971, + ACHIEV_CRIT_PORTAL_JOCKEY_10H = 12979, + ACHIEV_CRIT_PORTAL_JOCKEY_25H = 12980, + + ACHIEV_CRIT_ALL_YOU_CAN_EAT_10N = 12822, // Sindragosa, achievs 4580, 4620 + ACHIEV_CRIT_ALL_YOU_CAN_EAT_25N = 12972, + ACHIEV_CRIT_ALL_YOU_CAN_EAT_10V = 12996, + ACHIEV_CRIT_ALL_YOU_CAN_EAT_25V = 12989, + + ACHIEV_CRIT_WAITING_A_LONG_TIME_10N = 13246, // Lich King, achievs 4601, 4621 + ACHIEV_CRIT_WAITING_A_LONG_TIME_25N = 13244, + ACHIEV_CRIT_WAITING_A_LONG_TIME_10H = 13247, + ACHIEV_CRIT_WAITING_A_LONG_TIME_25H = 13245, +}; + +class instance_icecrown_citadel : public ScriptedInstance, private DialogueHelper +{ + public: + instance_icecrown_citadel(Map* pMap); + + void Initialize() override; + bool IsEncounterInProgress() const override; + + void OnPlayerEnter(Player* pPlayer) override; + void OnCreatureCreate(Creature* pCreature) override; + void OnObjectCreate(GameObject* pGo) override; + + void OnCreatureDeath(Creature* pCreature) override; + + void SetData(uint32 uiType, uint32 uiData) override; + uint32 GetData(uint32 uiType) const override; + + const char* Save() const override { return m_strInstData.c_str(); } + void Load(const char* strIn) override; + + void DoHandleCitadelAreaTrigger(uint32 uiTriggerId, Player* pPlayer); + + // Difficulty wrappers + bool IsHeroicDifficulty() { return instance->GetDifficulty() == RAID_DIFFICULTY_10MAN_HEROIC || instance->GetDifficulty() == RAID_DIFFICULTY_25MAN_HEROIC; } + bool Is25ManDifficulty() { return instance->GetDifficulty() == RAID_DIFFICULTY_25MAN_NORMAL || instance->GetDifficulty() == RAID_DIFFICULTY_25MAN_HEROIC; } + + void GetDeathwhisperStalkersList(GuidList& lList) { lList = m_lDeathwhisperStalkersGuids; } + + // Open Putricide door in a few seconds + void DoPreparePutricideDoor() { m_uiPutricideValveTimer = 15000; } + + bool CheckAchievementCriteriaMeet(uint32 uiCriteriaId, Player const* pSource, Unit const* pTarget = NULL, uint32 uiMiscvalue1 = 0) const override; + + void Update(uint32 uiDiff) override; + + private: + std::string m_strInstData; + uint32 m_auiEncounter[MAX_ENCOUNTER]; + + uint32 m_uiTeam; // Team of first entered player, used on the Gunship event + uint32 m_uiPutricideValveTimer; + + bool m_bHasMarrowgarIntroYelled; + bool m_bHasDeathwhisperIntroYelled; + bool m_bHasRimefangLanded; + bool m_bHasSpinestalkerLanded; + + GuidList m_lDeathwhisperStalkersGuids; +}; + +#endif diff --git a/src/modules/SD2/scripts/northrend/icecrown_citadel/icecrown_citadel/instance_icecrown_citadel.cpp b/src/modules/SD2/scripts/northrend/icecrown_citadel/icecrown_citadel/instance_icecrown_citadel.cpp new file mode 100644 index 000000000..c246f747c --- /dev/null +++ b/src/modules/SD2/scripts/northrend/icecrown_citadel/icecrown_citadel/instance_icecrown_citadel.cpp @@ -0,0 +1,529 @@ +/* Copyright (C) 2006 - 2013 ScriptDev2 + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along 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_icecrown_citadel +SD%Complete: 20% +SDComment: Just basic stuff +SDCategory: Icecrown Citadel +EndScriptData */ + +#include "precompiled.h" +#include "icecrown_citadel.h" + +enum +{ + // Marrowgar + SAY_MARROWGAR_INTRO = -1631001, + + // Deathwhisper + SAY_DEATHWHISPER_SPEECH_1 = -1631011, + SAY_DEATHWHISPER_SPEECH_2 = -1631012, + SAY_DEATHWHISPER_SPEECH_3 = -1631013, + SAY_DEATHWHISPER_SPEECH_4 = -1631014, + SAY_DEATHWHISPER_SPEECH_5 = -1631015, + SAY_DEATHWHISPER_SPEECH_6 = -1631016, + SAY_DEATHWHISPER_SPEECH_7 = -1631017, + + // Festergut + SAY_STINKY_DIES = -1631081, + // Rotface + SAY_PRECIOUS_DIES = -1631070, +}; + +static const DialogueEntry aCitadelDialogue[] = +{ + {SAY_DEATHWHISPER_SPEECH_1, NPC_LADY_DEATHWHISPER, 12000}, + {SAY_DEATHWHISPER_SPEECH_2, NPC_LADY_DEATHWHISPER, 11000}, + {SAY_DEATHWHISPER_SPEECH_3, NPC_LADY_DEATHWHISPER, 10000}, + {SAY_DEATHWHISPER_SPEECH_4, NPC_LADY_DEATHWHISPER, 9000}, + {SAY_DEATHWHISPER_SPEECH_5, NPC_LADY_DEATHWHISPER, 10000}, + {SAY_DEATHWHISPER_SPEECH_6, NPC_LADY_DEATHWHISPER, 10000}, + {SAY_DEATHWHISPER_SPEECH_7, NPC_LADY_DEATHWHISPER, 0}, + {0, 0, 0}, +}; + +instance_icecrown_citadel::instance_icecrown_citadel(Map* pMap) : ScriptedInstance(pMap), DialogueHelper(aCitadelDialogue), + m_uiTeam(0), + m_uiPutricideValveTimer(0), + m_bHasMarrowgarIntroYelled(false), + m_bHasDeathwhisperIntroYelled(false), + m_bHasRimefangLanded(false), + m_bHasSpinestalkerLanded(false) +{ + Initialize(); +} + +void instance_icecrown_citadel::Initialize() +{ + InitializeDialogueHelper(this); + memset(&m_auiEncounter, 0, sizeof(m_auiEncounter)); +} + +bool instance_icecrown_citadel::IsEncounterInProgress() const +{ + for (uint8 i = 0; i < MAX_ENCOUNTER; ++i) + { + if (m_auiEncounter[i] == IN_PROGRESS) + return true; + } + + return false; +} + +void instance_icecrown_citadel::DoHandleCitadelAreaTrigger(uint32 uiTriggerId, Player* pPlayer) +{ + if (uiTriggerId == AREATRIGGER_MARROWGAR_INTRO && !m_bHasMarrowgarIntroYelled) + { + if (Creature* pMarrowgar = GetSingleCreatureFromStorage(NPC_LORD_MARROWGAR)) + { + DoScriptText(SAY_MARROWGAR_INTRO, pMarrowgar); + m_bHasMarrowgarIntroYelled = true; + } + } + else if (uiTriggerId == AREATRIGGER_DEATHWHISPER_INTRO && !m_bHasDeathwhisperIntroYelled) + { + StartNextDialogueText(SAY_DEATHWHISPER_SPEECH_1); + m_bHasDeathwhisperIntroYelled = true; + } + else if (uiTriggerId == AREATRIGGER_SINDRAGOSA_PLATFORM) + { + if (Creature* pSindragosa = GetSingleCreatureFromStorage(NPC_SINDRAGOSA)) + { + if (pSindragosa->IsAlive() && !pSindragosa->IsInCombat()) + pSindragosa->SetInCombatWithZone(); + } + else + { + if (!m_bHasRimefangLanded) + { + if (Creature* pRimefang = GetSingleCreatureFromStorage(NPC_RIMEFANG)) + { + pRimefang->AI()->AttackStart(pPlayer); + m_bHasRimefangLanded = true; + } + } + + if (!m_bHasSpinestalkerLanded) + { + if (Creature* pSpinestalker = GetSingleCreatureFromStorage(NPC_SPINESTALKER)) + { + pSpinestalker->AI()->AttackStart(pPlayer); + m_bHasSpinestalkerLanded = true; + } + } + } + } +} + +void instance_icecrown_citadel::OnPlayerEnter(Player* pPlayer) +{ + if (!m_uiTeam) // very first player to enter + m_uiTeam = pPlayer->GetTeam(); +} + +void instance_icecrown_citadel::OnCreatureCreate(Creature* pCreature) +{ + switch (pCreature->GetEntry()) + { + case NPC_LORD_MARROWGAR: + case NPC_LADY_DEATHWHISPER: + case NPC_DEATHBRINGER_SAURFANG: + case NPC_FESTERGUT: + case NPC_ROTFACE: + case NPC_PROFESSOR_PUTRICIDE: + case NPC_TALDARAM: + case NPC_VALANAR: + case NPC_KELESETH: + case NPC_LANATHEL_INTRO: + case NPC_VALITHRIA: + case NPC_SINDRAGOSA: + case NPC_LICH_KING: + case NPC_TIRION: + case NPC_RIMEFANG: + case NPC_SPINESTALKER: + case NPC_VALITHRIA_COMBAT_TRIGGER: + case NPC_BLOOD_ORB_CONTROL: + m_mNpcEntryGuidStore[pCreature->GetEntry()] = pCreature->GetObjectGuid(); + break; + case NPC_DEATHWHISPER_SPAWN_STALKER: + m_lDeathwhisperStalkersGuids.push_back(pCreature->GetObjectGuid()); + return; + } +} + +void instance_icecrown_citadel::OnObjectCreate(GameObject* pGo) +{ + switch (pGo->GetEntry()) + { + case GO_ICEWALL_1: + case GO_ICEWALL_2: + case GO_ORATORY_DOOR: + if (m_auiEncounter[TYPE_MARROWGAR] == DONE) + pGo->SetGoState(GO_STATE_ACTIVE); + break; + case GO_DEATHWHISPER_ELEVATOR: + // ToDo: set in motion when TYPE_LADY_DEATHWHISPER == DONE + break; + case GO_SAURFANG_DOOR: + case GO_SCIENTIST_DOOR: + case GO_CRIMSON_HALL_DOOR: + case GO_GREEN_DRAGON_ENTRANCE: + if (m_auiEncounter[TYPE_DEATHBRINGER_SAURFANG] == DONE) + pGo->SetGoState(GO_STATE_ACTIVE); + break; + case GO_ORANGE_TUBE: + if (m_auiEncounter[TYPE_FESTERGUT] == DONE) + pGo->SetGoState(GO_STATE_ACTIVE); + break; + case GO_GREEN_TUBE: + if (m_auiEncounter[TYPE_ROTFACE] == DONE) + pGo->SetGoState(GO_STATE_ACTIVE); + break; + case GO_SCIENTIST_DOOR_GREEN: + // If both Festergut and Rotface are DONE, set as ACTIVE_ALTERNATIVE + if (m_auiEncounter[TYPE_FESTERGUT] == DONE && m_auiEncounter[TYPE_ROTFACE] == DONE) + pGo->SetGoState(GO_STATE_ACTIVE_ALTERNATIVE); + else if (m_auiEncounter[TYPE_ROTFACE] == DONE) + pGo->SetGoState(GO_STATE_READY); + break; + case GO_SCIENTIST_DOOR_ORANGE: + // If both Festergut and Rotface are DONE, set as ACTIVE_ALTERNATIVE + if (m_auiEncounter[TYPE_FESTERGUT] == DONE && m_auiEncounter[TYPE_ROTFACE] == DONE) + pGo->SetGoState(GO_STATE_ACTIVE_ALTERNATIVE); + else if (m_auiEncounter[TYPE_FESTERGUT] == DONE) + pGo->SetGoState(GO_STATE_READY); + break; + case GO_SCIENTIST_DOOR_COLLISION: + if (m_auiEncounter[TYPE_FESTERGUT] == DONE && m_auiEncounter[TYPE_ROTFACE] == DONE) + pGo->SetGoState(GO_STATE_ACTIVE); + break; + case GO_COUNCIL_DOOR_1: + case GO_COUNCIL_DOOR_2: + if (m_auiEncounter[TYPE_BLOOD_PRINCE_COUNCIL] == DONE) + pGo->SetGoState(GO_STATE_ACTIVE); + break; + case GO_GREEN_DRAGON_EXIT: + if (m_auiEncounter[TYPE_VALITHRIA] == DONE) + pGo->SetGoState(GO_STATE_ACTIVE); + break; + case GO_SAURFANG_CACHE: + case GO_SAURFANG_CACHE_25: + case GO_SAURFANG_CACHE_10_H: + case GO_SAURFANG_CACHE_25_H: + m_mGoEntryGuidStore[GO_SAURFANG_CACHE] = pGo->GetObjectGuid(); + return; + case GO_GUNSHIP_ARMORY_A: + case GO_GUNSHIP_ARMORY_A_25: + case GO_GUNSHIP_ARMORY_A_10H: + case GO_GUNSHIP_ARMORY_A_25H: + m_mGoEntryGuidStore[GO_GUNSHIP_ARMORY_A] = pGo->GetObjectGuid(); + return; + case GO_GUNSHIP_ARMORY_H: + case GO_GUNSHIP_ARMORY_H_25: + case GO_GUNSHIP_ARMORY_H_10H: + case GO_GUNSHIP_ARMORY_H_25H: + m_mGoEntryGuidStore[GO_GUNSHIP_ARMORY_H] = pGo->GetObjectGuid(); + return; + case GO_DREAMWALKER_CACHE: + case GO_DREAMWALKER_CACHE_25: + case GO_DREAMWALKER_CACHE_10_H: + case GO_DREAMWALKER_CACHE_25_H: + m_mGoEntryGuidStore[GO_DREAMWALKER_CACHE] = pGo->GetObjectGuid(); + return; + case GO_ICESHARD_1: + case GO_ICESHARD_2: + case GO_ICESHARD_3: + case GO_ICESHARD_4: + case GO_FROSTY_WIND: + case GO_FROSTY_EDGE: + case GO_SNOW_EDGE: + case GO_ARTHAS_PLATFORM: + case GO_ARTHAS_PRECIPICE: + case GO_MARROWGAR_DOOR: + case GO_BLOODPRINCE_DOOR: + case GO_SINDRAGOSA_ENTRANCE: + case GO_VALITHRIA_DOOR_1: + case GO_VALITHRIA_DOOR_2: + case GO_VALITHRIA_DOOR_3: + case GO_VALITHRIA_DOOR_4: + case GO_ICECROWN_GRATE: + case GO_SINDRAGOSA_SHORTCUT_ENTRANCE: + case GO_SINDRAGOSA_SHORTCUT_EXIT: + case GO_ORANGE_PLAGUE: + case GO_GREEN_PLAGUE: + case GO_ORANGE_VALVE: + case GO_GREEN_VALVE: + break; + } + m_mGoEntryGuidStore[pGo->GetEntry()] = pGo->GetObjectGuid(); +} + +void instance_icecrown_citadel::OnCreatureDeath(Creature* pCreature) +{ + switch (pCreature->GetEntry()) + { + case NPC_STINKY: + if (Creature* pFestergut = GetSingleCreatureFromStorage(NPC_FESTERGUT)) + { + if (pFestergut->IsAlive()) + DoScriptText(SAY_STINKY_DIES, pFestergut); + } + break; + case NPC_PRECIOUS: + if (Creature* pRotface = GetSingleCreatureFromStorage(NPC_ROTFACE)) + { + if (pRotface->IsAlive()) + DoScriptText(SAY_PRECIOUS_DIES, pRotface); + } + break; + } +} + +void instance_icecrown_citadel::SetData(uint32 uiType, uint32 uiData) +{ + switch (uiType) + { + case TYPE_MARROWGAR: + m_auiEncounter[uiType] = uiData; + DoUseDoorOrButton(GO_MARROWGAR_DOOR); + if (uiData == DONE) + { + DoUseDoorOrButton(GO_ICEWALL_1); + DoUseDoorOrButton(GO_ICEWALL_2); + + // Note: this door use may not be correct. In theory the door should be already opened + DoUseDoorOrButton(GO_ORATORY_DOOR); + } + break; + case TYPE_LADY_DEATHWHISPER: + m_auiEncounter[uiType] = uiData; + DoUseDoorOrButton(GO_ORATORY_DOOR); + // ToDo: set the elevateor in motion when TYPE_LADY_DEATHWHISPER == DONE + break; + case TYPE_GUNSHIP_BATTLE: + m_auiEncounter[uiType] = uiData; + if (uiData == DONE) + DoRespawnGameObject(m_uiTeam == ALLIANCE ? GO_GUNSHIP_ARMORY_A : GO_GUNSHIP_ARMORY_H, 60 * MINUTE); + break; + case TYPE_DEATHBRINGER_SAURFANG: + m_auiEncounter[uiType] = uiData; + if (uiData == DONE) + { + DoUseDoorOrButton(GO_SAURFANG_DOOR); + DoRespawnGameObject(GO_SAURFANG_CACHE, 60 * MINUTE); + + // Note: these doors may not be correct. In theory the doors should be already opened + DoUseDoorOrButton(GO_SCIENTIST_DOOR); + DoUseDoorOrButton(GO_CRIMSON_HALL_DOOR); + DoUseDoorOrButton(GO_GREEN_DRAGON_ENTRANCE); + } + break; + case TYPE_FESTERGUT: + m_auiEncounter[uiType] = uiData; + DoUseDoorOrButton(GO_ORANGE_PLAGUE); + if (uiData == DONE) + DoToggleGameObjectFlags(GO_ORANGE_VALVE, GO_FLAG_NO_INTERACT, false); + break; + case TYPE_ROTFACE: + m_auiEncounter[uiType] = uiData; + DoUseDoorOrButton(GO_GREEN_PLAGUE); + if (uiData == DONE) + DoToggleGameObjectFlags(GO_GREEN_VALVE, GO_FLAG_NO_INTERACT, false); + break; + case TYPE_PROFESSOR_PUTRICIDE: + m_auiEncounter[uiType] = uiData; + DoUseDoorOrButton(GO_SCIENTIST_DOOR); + break; + case TYPE_BLOOD_PRINCE_COUNCIL: + m_auiEncounter[uiType] = uiData; + DoUseDoorOrButton(GO_CRIMSON_HALL_DOOR); + if (uiData == DONE) + { + DoUseDoorOrButton(GO_COUNCIL_DOOR_1); + DoUseDoorOrButton(GO_COUNCIL_DOOR_2); + } + break; + case TYPE_QUEEN_LANATHEL: + m_auiEncounter[uiType] = uiData; + DoUseDoorOrButton(GO_BLOODPRINCE_DOOR); + if (uiData == DONE) + DoUseDoorOrButton(GO_ICECROWN_GRATE); + break; + case TYPE_VALITHRIA: + m_auiEncounter[uiType] = uiData; + DoUseDoorOrButton(GO_GREEN_DRAGON_ENTRANCE); + // Side doors + DoUseDoorOrButton(GO_VALITHRIA_DOOR_1); + DoUseDoorOrButton(GO_VALITHRIA_DOOR_2); + // Some doors are used only in 25 man mode + if (instance->GetDifficulty() == RAID_DIFFICULTY_25MAN_NORMAL || instance->GetDifficulty() == RAID_DIFFICULTY_25MAN_HEROIC) + { + DoUseDoorOrButton(GO_VALITHRIA_DOOR_3); + DoUseDoorOrButton(GO_VALITHRIA_DOOR_4); + } + if (uiData == DONE) + { + DoUseDoorOrButton(GO_GREEN_DRAGON_EXIT); + DoUseDoorOrButton(GO_SINDRAGOSA_ENTRANCE); + DoRespawnGameObject(GO_DREAMWALKER_CACHE, 60 * MINUTE); + } + break; + case TYPE_SINDRAGOSA: + m_auiEncounter[uiType] = uiData; + DoUseDoorOrButton(GO_SINDRAGOSA_ENTRANCE); + break; + case TYPE_LICH_KING: + m_auiEncounter[uiType] = uiData; + break; + default: + script_error_log("Instance Icecrown Citadel: ERROR SetData = %u for type %u does not exist/not implemented.", uiType, uiData); + return; + } + + 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]; + + m_strInstData = saveStream.str(); + + SaveToDB(); + OUT_SAVE_INST_DATA_COMPLETE; + } +} + +uint32 instance_icecrown_citadel::GetData(uint32 uiType) const +{ + if (uiType < MAX_ENCOUNTER) + return m_auiEncounter[uiType]; + + return 0; +} + +bool instance_icecrown_citadel::CheckAchievementCriteriaMeet(uint32 /*uiCriteriaId*/, Player const* /*pSource*/, Unit const* /*pTarget*/, uint32 /*uiMiscvalue1*/) const +{ + // ToDo: + return false; +} + +void instance_icecrown_citadel::Load(const char* strIn) +{ + if (!strIn) + { + OUT_LOAD_INST_DATA_FAIL; + return; + } + + OUT_LOAD_INST_DATA(strIn); + + std::istringstream loadStream(strIn); + 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) + { + if (m_auiEncounter[i] == IN_PROGRESS) + m_auiEncounter[i] = NOT_STARTED; + } + + OUT_LOAD_INST_DATA_COMPLETE; +} + +void instance_icecrown_citadel::Update(uint32 uiDiff) +{ + DialogueUpdate(uiDiff); + + if (m_uiPutricideValveTimer) + { + if (m_uiPutricideValveTimer <= uiDiff) + { + // Open the pathway to Putricide when the timer expires + DoUseDoorOrButton(GO_SCIENTIST_DOOR_COLLISION); + if (GameObject* pDoor = GetSingleGameObjectFromStorage(GO_SCIENTIST_DOOR_GREEN)) + pDoor->SetGoState(GO_STATE_ACTIVE_ALTERNATIVE); + if (GameObject* pDoor = GetSingleGameObjectFromStorage(GO_SCIENTIST_DOOR_ORANGE)) + pDoor->SetGoState(GO_STATE_ACTIVE_ALTERNATIVE); + + m_uiPutricideValveTimer = 0; + } + else + m_uiPutricideValveTimer -= uiDiff; + } +} + +InstanceData* GetInstanceData_instance_icecrown_citadel(Map* pMap) +{ + return new instance_icecrown_citadel(pMap); +} + +bool AreaTrigger_at_icecrown_citadel(Player* pPlayer, AreaTriggerEntry const* pAt) +{ + if (pAt->id == AREATRIGGER_MARROWGAR_INTRO || pAt->id == AREATRIGGER_DEATHWHISPER_INTRO || + pAt->id == AREATRIGGER_SINDRAGOSA_PLATFORM) + { + if (pPlayer->isGameMaster() || pPlayer->IsDead()) + return false; + + if (instance_icecrown_citadel* pInstance = (instance_icecrown_citadel*)pPlayer->GetInstanceData()) + pInstance->DoHandleCitadelAreaTrigger(pAt->id, pPlayer); + } + + return false; +} + +bool ProcessEventId_event_gameobject_citadel_valve(uint32 /*uiEventId*/, Object* pSource, Object* /*pTarget*/, bool bIsStart) +{ + if (bIsStart && pSource->GetTypeId() == TYPEID_PLAYER) + { + if (instance_icecrown_citadel* pInstance = (instance_icecrown_citadel*)((Player*)pSource)->GetInstanceData()) + { + // Note: the Tubes and doors are activated by DB script + if (pInstance->GetData(TYPE_FESTERGUT) == DONE && pInstance->GetData(TYPE_ROTFACE) == DONE) + pInstance->DoPreparePutricideDoor(); + + return false; + } + } + return false; +} + +void AddSC_instance_icecrown_citadel() +{ + Script* pNewScript; + + pNewScript = new Script; + pNewScript->Name = "instance_icecrown_citadel"; + pNewScript->GetInstanceData = &GetInstanceData_instance_icecrown_citadel; + pNewScript->RegisterSelf(); + + pNewScript = new Script; + pNewScript->Name = "at_icecrown_citadel"; + pNewScript->pAreaTrigger = &AreaTrigger_at_icecrown_citadel; + pNewScript->RegisterSelf(); + + pNewScript = new Script; + pNewScript->Name = "event_gameobject_citadel_valve"; + pNewScript->pProcessEventId = &ProcessEventId_event_gameobject_citadel_valve; + pNewScript->RegisterSelf(); +} diff --git a/src/modules/SD2/scripts/northrend/naxxramas/boss_anubrekhan.cpp b/src/modules/SD2/scripts/northrend/naxxramas/boss_anubrekhan.cpp new file mode 100644 index 000000000..97b031f09 --- /dev/null +++ b/src/modules/SD2/scripts/northrend/naxxramas/boss_anubrekhan.cpp @@ -0,0 +1,235 @@ +/* Copyright (C) 2006 - 2013 ScriptDev2 + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +/* ScriptData +SDName: Boss_Anubrekhan +SD%Complete: 95 +SDComment: Intro text usage is not very clear. Requires additional research. +SDCategory: Naxxramas +EndScriptData */ + +#include "precompiled.h" +#include "naxxramas.h" + +enum +{ + SAY_GREET = -1533000, + SAY_AGGRO1 = -1533001, + SAY_AGGRO2 = -1533002, + SAY_AGGRO3 = -1533003, + SAY_TAUNT1 = -1533004, + SAY_TAUNT2 = -1533005, + SAY_TAUNT3 = -1533006, + SAY_TAUNT4 = -1533007, + SAY_SLAY = -1533008, + + EMOTE_CRYPT_GUARD = -1533153, + EMOTE_INSECT_SWARM = -1533154, + EMOTE_CORPSE_SCARABS = -1533155, + + SPELL_IMPALE = 28783, // May be wrong spell id. Causes more dmg than I expect + SPELL_IMPALE_H = 56090, + SPELL_LOCUSTSWARM = 28785, // This is a self buff that triggers the dmg debuff + SPELL_LOCUSTSWARM_H = 54021, + + // spellId invalid + // SPELL_SUMMONGUARD = 29508, // Summons 1 crypt guard at targeted location - spell doesn't exist in 3.x.x + + SPELL_SELF_SPAWN_5 = 29105, // This spawns 5 corpse scarabs ontop of us (most likely the pPlayer casts this on death) + SPELL_SELF_SPAWN_10 = 28864, // This is used by the crypt guards when they die + + NPC_CRYPT_GUARD = 16573 +}; + +static const DialogueEntry aIntroDialogue[] = +{ + {SAY_GREET, NPC_ANUB_REKHAN, 7000}, + {SAY_TAUNT1, NPC_ANUB_REKHAN, 13000}, + {SAY_TAUNT2, NPC_ANUB_REKHAN, 11000}, + {SAY_TAUNT3, NPC_ANUB_REKHAN, 10000}, + {SAY_TAUNT4, NPC_ANUB_REKHAN, 0}, + {0, 0, 0} +}; + +static const float aCryptGuardLoc[4] = {3333.5f, -3475.9f, 287.1f, 3.17f}; + +struct boss_anubrekhanAI : public ScriptedAI +{ + boss_anubrekhanAI(Creature* pCreature) : ScriptedAI(pCreature), + m_introDialogue(aIntroDialogue) + { + m_pInstance = (instance_naxxramas*)pCreature->GetInstanceData(); + m_bIsRegularMode = pCreature->GetMap()->IsRegularDifficulty(); + m_introDialogue.InitializeDialogueHelper(m_pInstance); + m_bHasTaunted = false; + Reset(); + } + + instance_naxxramas* m_pInstance; + DialogueHelper m_introDialogue; + bool m_bIsRegularMode; + + uint32 m_uiImpaleTimer; + uint32 m_uiLocustSwarmTimer; + uint32 m_uiSummonTimer; + bool m_bHasTaunted; + + void Reset() override + { + m_uiImpaleTimer = 15000; + m_uiLocustSwarmTimer = 90000; + m_uiSummonTimer = m_bIsRegularMode ? 20000 : 0;// spawn a guardian only after 20 seconds in normal mode; in heroic there are already 2 Guards spawned + } + + void KilledUnit(Unit* pVictim) override + { + // Force the player to spawn corpse scarabs via spell + if (pVictim->GetTypeId() == TYPEID_PLAYER) + pVictim->CastSpell(pVictim, SPELL_SELF_SPAWN_5, true, NULL, NULL, m_creature->GetObjectGuid()); + + if (urand(0, 4)) + return; + + DoScriptText(SAY_SLAY, m_creature); + } + + void Aggro(Unit* /*pWho*/) override + { + switch (urand(0, 2)) + { + case 0: DoScriptText(SAY_AGGRO1, m_creature); break; + case 1: DoScriptText(SAY_AGGRO2, m_creature); break; + case 2: DoScriptText(SAY_AGGRO3, m_creature); break; + } + + if (m_pInstance) + m_pInstance->SetData(TYPE_ANUB_REKHAN, IN_PROGRESS); + } + + void JustDied(Unit* /*pKiller*/) override + { + if (m_pInstance) + m_pInstance->SetData(TYPE_ANUB_REKHAN, DONE); + } + + void JustReachedHome() override + { + if (m_pInstance) + m_pInstance->SetData(TYPE_ANUB_REKHAN, FAIL); + } + + void MoveInLineOfSight(Unit* pWho) override + { + if (!m_bHasTaunted && pWho->GetTypeId() == TYPEID_PLAYER && m_creature->IsWithinDistInMap(pWho, 110.0f) && m_creature->IsWithinLOSInMap(pWho)) + { + m_introDialogue.StartNextDialogueText(SAY_GREET); + m_bHasTaunted = true; + } + + ScriptedAI::MoveInLineOfSight(pWho); + } + + void JustSummoned(Creature* pSummoned) override + { + if (pSummoned->GetEntry() == NPC_CRYPT_GUARD) + DoScriptText(EMOTE_CRYPT_GUARD, pSummoned); + + if (Unit* pTarget = m_creature->SelectAttackingTarget(ATTACKING_TARGET_RANDOM, 0)) + pSummoned->AI()->AttackStart(pTarget); + } + + void SummonedCreatureDespawn(Creature* pSummoned) override + { + // If creature despawns on out of combat, skip this + if (!m_creature->SelectHostileTarget() || !m_creature->getVictim()) + return; + + if (pSummoned->GetEntry() == NPC_CRYPT_GUARD) + { + pSummoned->CastSpell(pSummoned, SPELL_SELF_SPAWN_10, true, NULL, NULL, m_creature->GetObjectGuid()); + DoScriptText(EMOTE_CORPSE_SCARABS, pSummoned); + } + } + + void UpdateAI(const uint32 uiDiff) override + { + m_introDialogue.DialogueUpdate(uiDiff); + + if (!m_creature->SelectHostileTarget() || !m_creature->getVictim()) + return; + + // Impale + if (m_uiImpaleTimer < uiDiff) + { + // Cast Impale on a random target + // Do NOT cast it when we are afflicted by locust swarm + if (!m_creature->HasAura(SPELL_LOCUSTSWARM) && !m_creature->HasAura(SPELL_LOCUSTSWARM_H)) + { + if (Unit* pTarget = m_creature->SelectAttackingTarget(ATTACKING_TARGET_RANDOM, 0)) + DoCastSpellIfCan(pTarget, m_bIsRegularMode ? SPELL_IMPALE : SPELL_IMPALE_H); + } + + m_uiImpaleTimer = 15000; + } + else + m_uiImpaleTimer -= uiDiff; + + // Locust Swarm + if (m_uiLocustSwarmTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature, m_bIsRegularMode ? SPELL_LOCUSTSWARM : SPELL_LOCUSTSWARM_H) == CAST_OK) + { + DoScriptText(EMOTE_INSECT_SWARM, m_creature); + + // Summon a crypt guard + m_uiSummonTimer = 3000; + m_uiLocustSwarmTimer = 100000; + } + } + else + m_uiLocustSwarmTimer -= uiDiff; + + // Summon + if (m_uiSummonTimer) + { + if (m_uiSummonTimer <= uiDiff) + { + // Workaround for the not existing spell + m_creature->SummonCreature(NPC_CRYPT_GUARD, aCryptGuardLoc[0], aCryptGuardLoc[1], aCryptGuardLoc[2], aCryptGuardLoc[3], TEMPSUMMON_TIMED_OOC_OR_DEAD_DESPAWN, 30000); + m_uiSummonTimer = 0; + } + else + m_uiSummonTimer -= uiDiff; + } + + DoMeleeAttackIfReady(); + } +}; + +CreatureAI* GetAI_boss_anubrekhan(Creature* pCreature) +{ + return new boss_anubrekhanAI(pCreature); +} + +void AddSC_boss_anubrekhan() +{ + Script* pNewScript; + + pNewScript = new Script; + pNewScript->Name = "boss_anubrekhan"; + pNewScript->GetAI = &GetAI_boss_anubrekhan; + pNewScript->RegisterSelf(); +} diff --git a/src/modules/SD2/scripts/northrend/naxxramas/boss_faerlina.cpp b/src/modules/SD2/scripts/northrend/naxxramas/boss_faerlina.cpp new file mode 100644 index 000000000..b61017f24 --- /dev/null +++ b/src/modules/SD2/scripts/northrend/naxxramas/boss_faerlina.cpp @@ -0,0 +1,205 @@ +/* Copyright (C) 2006 - 2013 ScriptDev2 + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +/* ScriptData +SDName: Boss_Faerlina +SD%Complete: 100 +SDComment: +SDCategory: Naxxramas +EndScriptData */ + +#include "precompiled.h" +#include "naxxramas.h" + +enum +{ + SAY_GREET = -1533009, + SAY_AGGRO_1 = -1533010, + SAY_AGGRO_2 = -1533011, + SAY_AGGRO_3 = -1533012, + SAY_AGGRO_4 = -1533013, + SAY_SLAY_1 = -1533014, + SAY_SLAY_2 = -1533015, + SAY_DEATH = -1533016, + + EMOTE_BOSS_GENERIC_FRENZY = -1000005, + + // SOUND_RANDOM_AGGRO = 8955, // soundId containing the 4 aggro sounds, we not using this + + SPELL_POSIONBOLT_VOLLEY = 28796, + SPELL_POSIONBOLT_VOLLEY_H = 54098, + SPELL_ENRAGE = 28798, + SPELL_ENRAGE_H = 54100, + SPELL_RAIN_OF_FIRE = 28794, + SPELL_RAIN_OF_FIRE_H = 54099, + SPELL_WIDOWS_EMBRACE = 28732, + SPELL_WIDOWS_EMBRACE_H = 54097, +}; + +struct boss_faerlinaAI : public ScriptedAI +{ + boss_faerlinaAI(Creature* pCreature) : ScriptedAI(pCreature) + { + m_pInstance = (instance_naxxramas*)pCreature->GetInstanceData(); + m_bIsRegularMode = pCreature->GetMap()->IsRegularDifficulty(); + m_bHasTaunted = false; + Reset(); + } + + instance_naxxramas* m_pInstance; + bool m_bIsRegularMode; + + uint32 m_uiPoisonBoltVolleyTimer; + uint32 m_uiRainOfFireTimer; + uint32 m_uiEnrageTimer; + bool m_bHasTaunted; + + void Reset() override + { + m_uiPoisonBoltVolleyTimer = 8000; + m_uiRainOfFireTimer = 16000; + m_uiEnrageTimer = 60000; + } + + void Aggro(Unit* /*pWho*/) override + { + switch (urand(0, 3)) + { + case 0: DoScriptText(SAY_AGGRO_1, m_creature); break; + case 1: DoScriptText(SAY_AGGRO_2, m_creature); break; + case 2: DoScriptText(SAY_AGGRO_3, m_creature); break; + case 3: DoScriptText(SAY_AGGRO_4, m_creature); break; + } + + if (m_pInstance) + m_pInstance->SetData(TYPE_FAERLINA, IN_PROGRESS); + } + + void MoveInLineOfSight(Unit* pWho) override + { + if (!m_bHasTaunted && pWho->GetTypeId() == TYPEID_PLAYER && m_creature->IsWithinDistInMap(pWho, 80.0f) && m_creature->IsWithinLOSInMap(pWho)) + { + DoScriptText(SAY_GREET, m_creature); + m_bHasTaunted = true; + } + + ScriptedAI::MoveInLineOfSight(pWho); + } + + void KilledUnit(Unit* /*pVictim*/) override + { + DoScriptText(urand(0, 1) ? SAY_SLAY_1 : SAY_SLAY_2, m_creature); + } + + void JustDied(Unit* /*pKiller*/) override + { + DoScriptText(SAY_DEATH, m_creature); + + if (m_pInstance) + m_pInstance->SetData(TYPE_FAERLINA, DONE); + } + + void JustReachedHome() override + { + if (m_pInstance) + m_pInstance->SetData(TYPE_FAERLINA, FAIL); + } + + // Widow's Embrace prevents frenzy and poison bolt, if it removes frenzy, next frenzy is sceduled in 60s + // It is likely that this _should_ be handled with some dummy aura(s) - but couldn't find any + void SpellHit(Unit* /*pCaster*/, const SpellEntry* pSpellEntry) override + { + // Check if we hit with Widow's Embrave + if (pSpellEntry->Id == SPELL_WIDOWS_EMBRACE || pSpellEntry->Id == SPELL_WIDOWS_EMBRACE_H) + { + bool bIsFrenzyRemove = false; + + // If we remove the Frenzy, the Enrage Timer is reseted to 60s + if (m_creature->HasAura(m_bIsRegularMode ? SPELL_ENRAGE : SPELL_ENRAGE_H)) + { + m_uiEnrageTimer = 60000; + m_creature->RemoveAurasDueToSpell(m_bIsRegularMode ? SPELL_ENRAGE : SPELL_ENRAGE_H); + + bIsFrenzyRemove = true; + } + + // Achievement 'Momma said Knock you out': If we removed OR delayed the frenzy, the criteria is failed + if ((bIsFrenzyRemove || m_uiEnrageTimer < 30000) && m_pInstance) + m_pInstance->SetSpecialAchievementCriteria(TYPE_ACHIEV_KNOCK_YOU_OUT, false); + + // In any case we prevent Frenzy and Poison Bolt Volley for Widow's Embrace Duration (30s) + // We do this be setting the timers to at least bigger than 30s + m_uiEnrageTimer = std::max(m_uiEnrageTimer, (uint32)30000); + m_uiPoisonBoltVolleyTimer = std::max(m_uiPoisonBoltVolleyTimer, urand(33000, 38000)); + } + } + + void UpdateAI(const uint32 uiDiff) override + { + if (!m_creature->SelectHostileTarget() || !m_creature->getVictim()) + return; + + // Poison Bolt Volley + if (m_uiPoisonBoltVolleyTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature->getVictim(), m_bIsRegularMode ? SPELL_POSIONBOLT_VOLLEY : SPELL_POSIONBOLT_VOLLEY_H) == CAST_OK) + m_uiPoisonBoltVolleyTimer = 11000; + } + else + m_uiPoisonBoltVolleyTimer -= uiDiff; + + // Rain Of Fire + if (m_uiRainOfFireTimer < uiDiff) + { + if (Unit* pTarget = m_creature->SelectAttackingTarget(ATTACKING_TARGET_RANDOM, 0)) + { + if (DoCastSpellIfCan(pTarget, m_bIsRegularMode ? SPELL_RAIN_OF_FIRE : SPELL_RAIN_OF_FIRE_H) == CAST_OK) + m_uiRainOfFireTimer = 16000; + } + } + else + m_uiRainOfFireTimer -= uiDiff; + + // Enrage Timer + if (m_uiEnrageTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature, m_bIsRegularMode ? SPELL_ENRAGE : SPELL_ENRAGE_H) == CAST_OK) + { + DoScriptText(EMOTE_BOSS_GENERIC_FRENZY, m_creature); + m_uiEnrageTimer = 60000; + } + } + else + m_uiEnrageTimer -= uiDiff; + + DoMeleeAttackIfReady(); + } +}; + +CreatureAI* GetAI_boss_faerlina(Creature* pCreature) +{ + return new boss_faerlinaAI(pCreature); +} + +void AddSC_boss_faerlina() +{ + Script* pNewScript; + + pNewScript = new Script; + pNewScript->Name = "boss_faerlina"; + pNewScript->GetAI = &GetAI_boss_faerlina; + pNewScript->RegisterSelf(); +} diff --git a/src/modules/SD2/scripts/northrend/naxxramas/boss_four_horsemen.cpp b/src/modules/SD2/scripts/northrend/naxxramas/boss_four_horsemen.cpp new file mode 100644 index 000000000..1791fdbe3 --- /dev/null +++ b/src/modules/SD2/scripts/northrend/naxxramas/boss_four_horsemen.cpp @@ -0,0 +1,596 @@ +/* Copyright (C) 2006 - 2013 ScriptDev2 + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +/* ScriptData +SDName: Boss_Four_Horsemen +SD%Complete: 90 +SDComment: Lady Blaumeux, Thane Korthazz, Sir Zeliek, Baron Rivendare; Berserk NYI. +SDCategory: Naxxramas +EndScriptData */ + +#include "precompiled.h" +#include "naxxramas.h" + +enum +{ + // ***** Yells ***** + // lady blaumeux + SAY_BLAU_AGGRO = -1533044, + SAY_BLAU_SPECIAL = -1533048, + SAY_BLAU_SLAY = -1533049, + SAY_BLAU_DEATH = -1533050, + EMOTE_UNYIELDING_PAIN = -1533156, + + // baron rivendare + SAY_RIVE_AGGRO1 = -1533065, + SAY_RIVE_AGGRO2 = -1533066, + SAY_RIVE_AGGRO3 = -1533067, + SAY_RIVE_SLAY1 = -1533068, + SAY_RIVE_SLAY2 = -1533069, + SAY_RIVE_SPECIAL = -1533070, + SAY_RIVE_DEATH = -1533074, + + // thane korthazz + SAY_KORT_AGGRO = -1533051, + SAY_KORT_SPECIAL = -1533055, + SAY_KORT_SLAY = -1533056, + SAY_KORT_DEATH = -1533057, + + // sir zeliek + SAY_ZELI_AGGRO = -1533058, + SAY_ZELI_SPECIAL = -1533062, + SAY_ZELI_SLAY = -1533063, + SAY_ZELI_DEATH = -1533064, + EMOTE_CONDEMATION = -1533157, + + // ***** Spells ***** + // all horsemen + // SPELL_SHIELDWALL = 29061, // not used in 3.x.x + SPELL_BESERK = 26662, + SPELL_ACHIEV_CHECK = 59450, + // Note: Berserk should be applied once 100 marks are casted. + + // lady blaumeux + SPELL_MARK_OF_BLAUMEUX = 28833, + SPELL_VOID_ZONE = 28863, + SPELL_VOID_ZONE_H = 57463, + SPELL_SHADOW_BOLT = 57374, + SPELL_SHADOW_BOLT_H = 57464, + SPELL_UNYILDING_PAIN = 57381, + + // baron rivendare + SPELL_MARK_OF_RIVENDARE = 28834, + SPELL_UNHOLY_SHADOW = 28882, + SPELL_UNHOLY_SHADOW_H = 57369, + + // thane korthazz + SPELL_MARK_OF_KORTHAZZ = 28832, + SPELL_METEOR = 28884, + SPELL_METEOR_H = 57467, + + // sir zeliek + SPELL_MARK_OF_ZELIEK = 28835, + SPELL_HOLY_WRATH = 28883, + SPELL_HOLY_WRATH_H = 57466, + SPELL_HOLY_BOLT = 57376, + SPELL_HOLY_BOLT_H = 57465, + SPELL_CONDEMNATION = 57377, + + // horseman spirits (not used in 3.x.x) + // NPC_SPIRIT_OF_BLAUMEUX = 16776, + // NPC_SPIRIT_OF_MOGRAINE = 16775, + // NPC_SPIRIT_OF_KORTHAZZ = 16778, + // NPC_SPIRIT_OF_ZELIREK = 16777 +}; + +static const float aHorseMenMoveCoords[4][3] = +{ + {2469.4f, -2947.6f, 241.28f}, // lady blaumeux + {2583.9f, -2971.67f, 241.35f}, // baron rivendare + {2542.9f, -3015.0f, 241.35f}, // thane korthazz + {2517.8f, -2896.6f, 241.28f}, // sir zeliek +}; + +struct boss_lady_blaumeuxAI : public ScriptedAI +{ + boss_lady_blaumeuxAI(Creature* pCreature) : ScriptedAI(pCreature) + { + m_bIsRegularMode = pCreature->GetMap()->IsRegularDifficulty(); + m_pInstance = (ScriptedInstance*)pCreature->GetInstanceData(); + Reset(); + } + + ScriptedInstance* m_pInstance; + bool m_bIsRegularMode; + + bool m_bIsCornerMovement; + uint32 m_uiMarkTimer; + uint32 m_uiVoidZoneTimer; + uint32 m_uiShadowBoltTimer; + + void Reset() override + { + m_uiMarkTimer = 20000; + m_uiVoidZoneTimer = 15000; + m_uiShadowBoltTimer = 10000; + m_bIsCornerMovement = true; + } + + void Aggro(Unit* /*pWho*/) override + { + DoScriptText(SAY_BLAU_AGGRO, m_creature); + + SetCombatMovement(false); + m_creature->SetWalk(false); + m_creature->GetMotionMaster()->MovePoint(1, aHorseMenMoveCoords[0][0], aHorseMenMoveCoords[0][1], aHorseMenMoveCoords[0][2]); + + if (m_pInstance) + m_pInstance->SetData(TYPE_FOUR_HORSEMEN, IN_PROGRESS); + } + + void KilledUnit(Unit* /*pVictim*/) override + { + DoScriptText(SAY_BLAU_SLAY, m_creature); + } + + void JustDied(Unit* /*pKiller*/) override + { + DoScriptText(SAY_BLAU_DEATH, m_creature); + + if (m_pInstance) + { + m_pInstance->SetData(TYPE_FOUR_HORSEMEN, SPECIAL); + + // Cast achiev check for last boss killed + if (m_pInstance->GetData(TYPE_FOUR_HORSEMEN) == DONE) + m_creature->CastSpell(m_creature, SPELL_ACHIEV_CHECK, true); + } + } + + void JustReachedHome() override + { + if (m_pInstance) + m_pInstance->SetData(TYPE_FOUR_HORSEMEN, FAIL); + } + + void MovementInform(uint32 uiMotionType, uint32 uiPointId) override + { + if (uiMotionType != POINT_MOTION_TYPE || !uiPointId) + return; + + // Stop moving when it reaches the corner + m_bIsCornerMovement = false; + m_creature->GetMotionMaster()->Clear(); + m_creature->GetMotionMaster()->MoveIdle(); + } + + void UpdateAI(const uint32 uiDiff) override + { + if (!m_creature->SelectHostileTarget() || !m_creature->getVictim()) + return; + + // Don't attack while moving + if (m_bIsCornerMovement) + return; + + if (m_uiMarkTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature, SPELL_MARK_OF_BLAUMEUX) == CAST_OK) + m_uiMarkTimer = 12000; + } + else + m_uiMarkTimer -= uiDiff; + + if (m_uiVoidZoneTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature->getVictim(), m_bIsRegularMode ? SPELL_VOID_ZONE : SPELL_VOID_ZONE_H) == CAST_OK) + m_uiVoidZoneTimer = 15000; + } + else + m_uiVoidZoneTimer -= uiDiff; + + if (m_uiShadowBoltTimer < uiDiff) + { + // If we can find a target in range of 45.0f, then cast Shadowbolt + if (m_creature->IsWithinDist(m_creature->getVictim(), 45.0f)) + DoCastSpellIfCan(m_creature->getVictim(), m_bIsRegularMode ? SPELL_SHADOW_BOLT : SPELL_SHADOW_BOLT_H); + else + { + DoCastSpellIfCan(m_creature, SPELL_UNYILDING_PAIN); + DoScriptText(EMOTE_UNYIELDING_PAIN, m_creature); + } + m_uiShadowBoltTimer = urand(2000, 3000); + } + else + m_uiShadowBoltTimer -= uiDiff; + + DoMeleeAttackIfReady(); + } +}; + +CreatureAI* GetAI_boss_lady_blaumeux(Creature* pCreature) +{ + return new boss_lady_blaumeuxAI(pCreature); +} + +struct boss_rivendare_naxxAI : public ScriptedAI +{ + boss_rivendare_naxxAI(Creature* pCreature) : ScriptedAI(pCreature) + { + m_bIsRegularMode = pCreature->GetMap()->IsRegularDifficulty(); + m_pInstance = (ScriptedInstance*)pCreature->GetInstanceData(); + Reset(); + } + + ScriptedInstance* m_pInstance; + bool m_bIsRegularMode; + + bool m_bIsCornerMovement; + uint32 m_uiMarkTimer; + uint32 m_uiUnholyShadowTimer; + + void Reset() override + { + m_uiMarkTimer = 20000; + m_uiUnholyShadowTimer = 15000; + m_bIsCornerMovement = true; + } + + void Aggro(Unit* /*pWho*/) override + { + switch (urand(0, 2)) + { + case 0: DoScriptText(SAY_RIVE_AGGRO1, m_creature); break; + case 1: DoScriptText(SAY_RIVE_AGGRO2, m_creature); break; + case 2: DoScriptText(SAY_RIVE_AGGRO3, m_creature); break; + } + + SetCombatMovement(false); + m_creature->SetWalk(false); + m_creature->GetMotionMaster()->MovePoint(1, aHorseMenMoveCoords[1][0], aHorseMenMoveCoords[1][1], aHorseMenMoveCoords[1][2]); + + if (m_pInstance) + m_pInstance->SetData(TYPE_FOUR_HORSEMEN, IN_PROGRESS); + } + + void KilledUnit(Unit* /*pVictim*/) override + { + DoScriptText(urand(0, 1) ? SAY_RIVE_SLAY1 : SAY_RIVE_SLAY2, m_creature); + } + + void JustDied(Unit* /*pKiller*/) override + { + DoScriptText(SAY_RIVE_DEATH, m_creature); + + if (m_pInstance) + { + m_pInstance->SetData(TYPE_FOUR_HORSEMEN, SPECIAL); + + // Cast achiev check for last boss killed + if (m_pInstance->GetData(TYPE_FOUR_HORSEMEN) == DONE) + m_creature->CastSpell(m_creature, SPELL_ACHIEV_CHECK, true); + } + } + + void JustReachedHome() override + { + if (m_pInstance) + m_pInstance->SetData(TYPE_FOUR_HORSEMEN, FAIL); + } + + void MovementInform(uint32 uiMotionType, uint32 uiPointId) override + { + if (uiMotionType != POINT_MOTION_TYPE || !uiPointId) + return; + + // Start moving when it reaches the corner + SetCombatMovement(true); + m_bIsCornerMovement = false; + m_creature->GetMotionMaster()->Clear(); + if (m_creature->getVictim()) + m_creature->GetMotionMaster()->MoveChase(m_creature->getVictim()); + } + + void UpdateAI(const uint32 uiDiff) override + { + if (!m_creature->SelectHostileTarget() || !m_creature->getVictim()) + return; + + // Don't attack while moving + if (m_bIsCornerMovement) + return; + + if (m_uiMarkTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature, SPELL_MARK_OF_RIVENDARE) == CAST_OK) + m_uiMarkTimer = 12000; + } + else + m_uiMarkTimer -= uiDiff; + + if (m_uiUnholyShadowTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature->getVictim(), m_bIsRegularMode ? SPELL_UNHOLY_SHADOW : SPELL_UNHOLY_SHADOW_H) == CAST_OK) + m_uiUnholyShadowTimer = 15000; + } + else + m_uiUnholyShadowTimer -= uiDiff; + + DoMeleeAttackIfReady(); + } +}; + +CreatureAI* GetAI_boss_rivendare_naxx(Creature* pCreature) +{ + return new boss_rivendare_naxxAI(pCreature); +} + +struct boss_thane_korthazzAI : public ScriptedAI +{ + boss_thane_korthazzAI(Creature* pCreature) : ScriptedAI(pCreature) + { + m_bIsRegularMode = pCreature->GetMap()->IsRegularDifficulty(); + m_pInstance = (ScriptedInstance*)pCreature->GetInstanceData(); + Reset(); + } + + ScriptedInstance* m_pInstance; + bool m_bIsRegularMode; + + bool m_bIsCornerMovement; + uint32 m_uiMarkTimer; + uint32 m_uiMeteorTimer; + + void Reset() override + { + m_uiMarkTimer = 20000; + m_uiMeteorTimer = 30000; + m_bIsCornerMovement = true; + } + + void Aggro(Unit* /*pWho*/) override + { + DoScriptText(SAY_KORT_AGGRO, m_creature); + + SetCombatMovement(false); + m_creature->SetWalk(false); + m_creature->GetMotionMaster()->MovePoint(1, aHorseMenMoveCoords[2][0], aHorseMenMoveCoords[2][1], aHorseMenMoveCoords[2][2]); + + if (m_pInstance) + m_pInstance->SetData(TYPE_FOUR_HORSEMEN, IN_PROGRESS); + } + + void KilledUnit(Unit* /*pVictim*/) override + { + DoScriptText(SAY_KORT_SLAY, m_creature); + } + + void JustDied(Unit* /*pKiller*/) override + { + DoScriptText(SAY_KORT_DEATH, m_creature); + + if (m_pInstance) + { + m_pInstance->SetData(TYPE_FOUR_HORSEMEN, SPECIAL); + + // Cast achiev check for last boss killed + if (m_pInstance->GetData(TYPE_FOUR_HORSEMEN) == DONE) + m_creature->CastSpell(m_creature, SPELL_ACHIEV_CHECK, true); + } + } + + void JustReachedHome() override + { + if (m_pInstance) + m_pInstance->SetData(TYPE_FOUR_HORSEMEN, FAIL); + } + + void MovementInform(uint32 uiMotionType, uint32 uiPointId) override + { + if (uiMotionType != POINT_MOTION_TYPE || !uiPointId) + return; + + // Start moving when it reaches the corner + SetCombatMovement(true); + m_bIsCornerMovement = false; + m_creature->GetMotionMaster()->Clear(); + if (m_creature->getVictim()) + m_creature->GetMotionMaster()->MoveChase(m_creature->getVictim()); + } + + void UpdateAI(const uint32 uiDiff) override + { + if (!m_creature->SelectHostileTarget() || !m_creature->getVictim()) + return; + + // Don't attack while moving + if (m_bIsCornerMovement) + return; + + if (m_uiMarkTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature, SPELL_MARK_OF_KORTHAZZ) == CAST_OK) + m_uiMarkTimer = 12000; + } + else + m_uiMarkTimer -= uiDiff; + + if (m_uiMeteorTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature->getVictim(), m_bIsRegularMode ? SPELL_METEOR : SPELL_METEOR_H) == CAST_OK) + m_uiMeteorTimer = 20000; + } + else + m_uiMeteorTimer -= uiDiff; + + DoMeleeAttackIfReady(); + } +}; + +CreatureAI* GetAI_boss_thane_korthazz(Creature* pCreature) +{ + return new boss_thane_korthazzAI(pCreature); +} + +struct boss_sir_zeliekAI : public ScriptedAI +{ + boss_sir_zeliekAI(Creature* pCreature) : ScriptedAI(pCreature) + { + m_bIsRegularMode = pCreature->GetMap()->IsRegularDifficulty(); + m_pInstance = (ScriptedInstance*)pCreature->GetInstanceData(); + Reset(); + } + + ScriptedInstance* m_pInstance; + bool m_bIsRegularMode; + + bool m_bIsCornerMovement; + uint32 m_uiMarkTimer; + uint32 m_uiHolyWrathTimer; + uint32 m_uiHolyBoltTimer; + + void Reset() override + { + m_uiMarkTimer = 20000; + m_uiHolyWrathTimer = 12000; + m_uiHolyBoltTimer = 10000; + m_bIsCornerMovement = true; + } + + void Aggro(Unit* /*pWho*/) override + { + DoScriptText(SAY_ZELI_AGGRO, m_creature); + + SetCombatMovement(false); + m_creature->SetWalk(false); + m_creature->GetMotionMaster()->MovePoint(1, aHorseMenMoveCoords[3][0], aHorseMenMoveCoords[3][1], aHorseMenMoveCoords[3][2]); + + if (m_pInstance) + m_pInstance->SetData(TYPE_FOUR_HORSEMEN, IN_PROGRESS); + } + + void KilledUnit(Unit* /*pVictim*/) override + { + DoScriptText(SAY_ZELI_SLAY, m_creature); + } + + void JustDied(Unit* /*pKiller*/) override + { + DoScriptText(SAY_ZELI_DEATH, m_creature); + + if (m_pInstance) + { + m_pInstance->SetData(TYPE_FOUR_HORSEMEN, SPECIAL); + + // Cast achiev check for last boss killed + if (m_pInstance->GetData(TYPE_FOUR_HORSEMEN) == DONE) + m_creature->CastSpell(m_creature, SPELL_ACHIEV_CHECK, true); + } + } + + void JustReachedHome() override + { + if (m_pInstance) + m_pInstance->SetData(TYPE_FOUR_HORSEMEN, FAIL); + } + + void MovementInform(uint32 uiMotionType, uint32 uiPointId) override + { + if (uiMotionType != POINT_MOTION_TYPE || !uiPointId) + return; + + // Stop moving when it reaches the corner + m_bIsCornerMovement = false; + m_creature->GetMotionMaster()->Clear(); + m_creature->GetMotionMaster()->MoveIdle(); + } + + void UpdateAI(const uint32 uiDiff) override + { + if (!m_creature->SelectHostileTarget() || !m_creature->getVictim()) + return; + + // Don't attack while moving + if (m_bIsCornerMovement) + return; + + if (m_uiMarkTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature, SPELL_MARK_OF_ZELIEK) == CAST_OK) + m_uiMarkTimer = 12000; + } + else + m_uiMarkTimer -= uiDiff; + + if (m_uiHolyWrathTimer < uiDiff) + { + if (Unit* pTarget = m_creature->SelectAttackingTarget(ATTACKING_TARGET_RANDOM, 0)) + { + if (DoCastSpellIfCan(pTarget, SPELL_HOLY_WRATH) == CAST_OK) + m_uiHolyWrathTimer = 15000; + } + } + else + m_uiHolyWrathTimer -= uiDiff; + + if (m_uiHolyBoltTimer < uiDiff) + { + // If we can find a target in range of 45.0f, then cast Holy Bolt + if (m_creature->IsWithinDist(m_creature->getVictim(), 45.0f)) + DoCastSpellIfCan(m_creature->getVictim(), m_bIsRegularMode ? SPELL_HOLY_BOLT : SPELL_HOLY_BOLT_H); + else + { + DoCastSpellIfCan(m_creature, SPELL_CONDEMNATION); + DoScriptText(EMOTE_CONDEMATION, m_creature); + } + m_uiHolyBoltTimer = urand(2000, 3000); + } + else + m_uiHolyBoltTimer -= uiDiff; + + DoMeleeAttackIfReady(); + } +}; + +CreatureAI* GetAI_boss_sir_zeliek(Creature* pCreature) +{ + return new boss_sir_zeliekAI(pCreature); +} + +void AddSC_boss_four_horsemen() +{ + Script* pNewScript; + + pNewScript = new Script; + pNewScript->Name = "boss_lady_blaumeux"; + pNewScript->GetAI = &GetAI_boss_lady_blaumeux; + pNewScript->RegisterSelf(); + + pNewScript = new Script; + pNewScript->Name = "boss_rivendare_naxx"; + pNewScript->GetAI = &GetAI_boss_rivendare_naxx; + pNewScript->RegisterSelf(); + + pNewScript = new Script; + pNewScript->Name = "boss_thane_korthazz"; + pNewScript->GetAI = &GetAI_boss_thane_korthazz; + pNewScript->RegisterSelf(); + + pNewScript = new Script; + pNewScript->Name = "boss_sir_zeliek"; + pNewScript->GetAI = &GetAI_boss_sir_zeliek; + pNewScript->RegisterSelf(); +} diff --git a/src/modules/SD2/scripts/northrend/naxxramas/boss_gluth.cpp b/src/modules/SD2/scripts/northrend/naxxramas/boss_gluth.cpp new file mode 100644 index 000000000..fb323db82 --- /dev/null +++ b/src/modules/SD2/scripts/northrend/naxxramas/boss_gluth.cpp @@ -0,0 +1,245 @@ +/* Copyright (C) 2006 - 2013 ScriptDev2 + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +/* ScriptData +SDName: Boss_Gluth +SD%Complete: 95 +SDComment: Gluth should turn around to face the victim when he devours a Zombie +SDCategory: Naxxramas +EndScriptData */ + +#include "precompiled.h" +#include "naxxramas.h" + +enum +{ + EMOTE_ZOMBIE = -1533119, + EMOTE_BOSS_GENERIC_ENRAGED = -1000006, + EMOTE_DECIMATE = -1533152, + + SPELL_MORTALWOUND = 54378, // old vanilla spell was 25646, + SPELL_DECIMATE = 28374, + SPELL_DECIMATE_H = 54426, + SPELL_ENRAGE = 28371, + SPELL_ENRAGE_H = 54427, + SPELL_BERSERK = 26662, + // SPELL_TERRIFYING_ROAR = 29685, // no longer used in 3.x.x + // SPELL_SUMMON_ZOMBIE_CHOW = 28216, // removed from dbc: triggers 28217 every 6 secs + // SPELL_CALL_ALL_ZOMBIE_CHOW = 29681, // removed from dbc: triggers 29682 + // SPELL_ZOMBIE_CHOW_SEARCH = 28235, // removed from dbc: triggers 28236 every 3 secs + + NPC_ZOMBIE_CHOW = 16360, // old vanilla summoning spell 28217 + + MAX_ZOMBIE_LOCATIONS = 3, +}; + +static const float aZombieSummonLoc[MAX_ZOMBIE_LOCATIONS][3] = +{ + {3267.9f, -3172.1f, 297.42f}, + {3253.2f, -3132.3f, 297.42f}, + {3308.3f, -3185.8f, 297.42f}, +}; + +struct boss_gluthAI : public ScriptedAI +{ + boss_gluthAI(Creature* pCreature) : ScriptedAI(pCreature) + { + m_pInstance = (instance_naxxramas*)pCreature->GetInstanceData(); + m_bIsRegularMode = pCreature->GetMap()->IsRegularDifficulty(); + Reset(); + } + + instance_naxxramas* m_pInstance; + bool m_bIsRegularMode; + + uint32 m_uiMortalWoundTimer; + uint32 m_uiDecimateTimer; + uint32 m_uiEnrageTimer; + uint32 m_uiSummonTimer; + uint32 m_uiZombieSearchTimer; + + uint32 m_uiBerserkTimer; + + GuidList m_lZombieChowGuidList; + + void Reset() override + { + m_uiMortalWoundTimer = 10000; + m_uiDecimateTimer = 110000; + m_uiEnrageTimer = 25000; + m_uiSummonTimer = 15000; + m_uiZombieSearchTimer = 3000; + + m_uiBerserkTimer = MINUTE * 8 * IN_MILLISECONDS; + } + + void JustDied(Unit* /*pKiller*/) override + { + if (m_pInstance) + m_pInstance->SetData(TYPE_GLUTH, DONE); + } + + void Aggro(Unit* /*pWho*/) override + { + if (m_pInstance) + m_pInstance->SetData(TYPE_GLUTH, IN_PROGRESS); + } + + void KilledUnit(Unit* pVictim) override + { + // Restore 5% hp when killing a zombie + if (pVictim->GetEntry() == NPC_ZOMBIE_CHOW) + { + DoScriptText(EMOTE_ZOMBIE, m_creature); + m_creature->SetHealth(m_creature->GetHealth() + m_creature->GetMaxHealth() * 0.05f); + } + } + + void JustReachedHome() override + { + if (m_pInstance) + m_pInstance->SetData(TYPE_GLUTH, FAIL); + } + + void JustSummoned(Creature* pSummoned) override + { + pSummoned->GetMotionMaster()->MoveFollow(m_creature, ATTACK_DISTANCE, 0); + m_lZombieChowGuidList.push_back(pSummoned->GetObjectGuid()); + } + + void SummonedCreatureDespawn(Creature* pSummoned) override + { + m_lZombieChowGuidList.remove(pSummoned->GetObjectGuid()); + } + + // Replaces missing spell 29682 + void DoCallAllZombieChow() + { + for (GuidList::const_iterator itr = m_lZombieChowGuidList.begin(); itr != m_lZombieChowGuidList.end(); ++itr) + { + if (Creature* pZombie = m_creature->GetMap()->GetCreature(*itr)) + pZombie->GetMotionMaster()->MoveFollow(m_creature, ATTACK_DISTANCE, 0); + } + } + + // Replaces missing spell 28236 + void DoSearchZombieChow() + { + for (GuidList::const_iterator itr = m_lZombieChowGuidList.begin(); itr != m_lZombieChowGuidList.end(); ++itr) + { + if (Creature* pZombie = m_creature->GetMap()->GetCreature(*itr)) + { + if (!pZombie->IsAlive()) + continue; + + // Devour a Zombie + if (pZombie->IsWithinDistInMap(m_creature, 15.0f)) + m_creature->DealDamage(pZombie, pZombie->GetHealth(), NULL, DIRECT_DAMAGE, SPELL_SCHOOL_MASK_NORMAL, NULL, false); + } + } + } + + void UpdateAI(const uint32 uiDiff) override + { + if (!m_creature->SelectHostileTarget() || !m_creature->getVictim()) + return; + + if (m_uiZombieSearchTimer < uiDiff) + { + DoSearchZombieChow(); + m_uiZombieSearchTimer = 3000; + } + else + m_uiZombieSearchTimer -= uiDiff; + + // Mortal Wound + if (m_uiMortalWoundTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature->getVictim(), SPELL_MORTALWOUND) == CAST_OK) + m_uiMortalWoundTimer = 10000; + } + else + m_uiMortalWoundTimer -= uiDiff; + + // Decimate + if (m_uiDecimateTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature, m_bIsRegularMode ? SPELL_DECIMATE : SPELL_DECIMATE_H) == CAST_OK) + { + DoScriptText(EMOTE_DECIMATE, m_creature); + DoCallAllZombieChow(); + m_uiDecimateTimer = 100000; + } + } + else + m_uiDecimateTimer -= uiDiff; + + // Enrage + if (m_uiEnrageTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature, m_bIsRegularMode ? SPELL_ENRAGE : SPELL_ENRAGE_H) == CAST_OK) + { + DoScriptText(EMOTE_BOSS_GENERIC_ENRAGED, m_creature); + m_uiEnrageTimer = urand(20000, 30000); + } + } + else + m_uiEnrageTimer -= uiDiff; + + // Summon + if (m_uiSummonTimer < uiDiff) + { + uint8 uiPos1 = urand(0, MAX_ZOMBIE_LOCATIONS - 1); + m_creature->SummonCreature(NPC_ZOMBIE_CHOW, aZombieSummonLoc[uiPos1][0], aZombieSummonLoc[uiPos1][1], aZombieSummonLoc[uiPos1][2], 0.0f, TEMPSUMMON_DEAD_DESPAWN, 0); + + if (!m_bIsRegularMode) + { + uint8 uiPos2 = (uiPos1 + urand(1, MAX_ZOMBIE_LOCATIONS - 1)) % MAX_ZOMBIE_LOCATIONS; + m_creature->SummonCreature(NPC_ZOMBIE_CHOW, aZombieSummonLoc[uiPos2][0], aZombieSummonLoc[uiPos2][1], aZombieSummonLoc[uiPos2][2], 0.0f, TEMPSUMMON_DEAD_DESPAWN, 0); + } + + m_uiSummonTimer = 10000; + } + else + m_uiSummonTimer -= uiDiff; + + // Berserk + if (m_uiBerserkTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature, SPELL_BERSERK) == CAST_OK) + m_uiBerserkTimer = MINUTE * 5 * IN_MILLISECONDS; + } + else + m_uiBerserkTimer -= uiDiff; + + DoMeleeAttackIfReady(); + } +}; + +CreatureAI* GetAI_boss_gluth(Creature* pCreature) +{ + return new boss_gluthAI(pCreature); +} + +void AddSC_boss_gluth() +{ + Script* pNewScript; + + pNewScript = new Script; + pNewScript->Name = "boss_gluth"; + pNewScript->GetAI = &GetAI_boss_gluth; + pNewScript->RegisterSelf(); +} diff --git a/src/modules/SD2/scripts/northrend/naxxramas/boss_gothik.cpp b/src/modules/SD2/scripts/northrend/naxxramas/boss_gothik.cpp new file mode 100644 index 000000000..5732c14ce --- /dev/null +++ b/src/modules/SD2/scripts/northrend/naxxramas/boss_gothik.cpp @@ -0,0 +1,547 @@ +/* Copyright (C) 2006 - 2013 ScriptDev2 + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +/* ScriptData +SDName: Boss_Gothik +SD%Complete: 95 +SDComment: Prevent Gothik from turning and "in combat state" while on balcony +SDCategory: Naxxramas +EndScriptData */ + +#include "precompiled.h" +#include "naxxramas.h" + +enum +{ + SAY_SPEECH_1 = -1533040, + SAY_SPEECH_2 = -1533140, + SAY_SPEECH_3 = -1533141, + SAY_SPEECH_4 = -1533142, + + SAY_KILL = -1533041, + SAY_DEATH = -1533042, + SAY_TELEPORT = -1533043, + + EMOTE_TO_FRAY = -1533138, + EMOTE_GATE = -1533139, + + PHASE_SPEECH = 0, + PHASE_BALCONY = 1, + PHASE_STOP_SUMMONING = 2, + PHASE_TELEPORTING = 3, + PHASE_STOP_TELEPORTING = 4, + + // Right is right side from gothik (eastern) + SPELL_TELEPORT_RIGHT = 28025, + SPELL_TELEPORT_LEFT = 28026, + + SPELL_HARVESTSOUL = 28679, + SPELL_SHADOWBOLT = 29317, + SPELL_SHADOWBOLT_H = 56405, +}; + +enum eSpellDummy +{ + SPELL_A_TO_ANCHOR_1 = 27892, + SPELL_B_TO_ANCHOR_1 = 27928, + SPELL_C_TO_ANCHOR_1 = 27935, + + SPELL_A_TO_ANCHOR_2 = 27893, + SPELL_B_TO_ANCHOR_2 = 27929, + SPELL_C_TO_ANCHOR_2 = 27936, + + SPELL_A_TO_SKULL = 27915, + SPELL_B_TO_SKULL = 27931, + SPELL_C_TO_SKULL = 27937 +}; + +struct boss_gothikAI : public ScriptedAI +{ + boss_gothikAI(Creature* pCreature) : ScriptedAI(pCreature) + { + m_pInstance = (instance_naxxramas*)pCreature->GetInstanceData(); + m_bIsRegularMode = pCreature->GetMap()->IsRegularDifficulty(); + SetCombatMovement(false); + Reset(); + } + + instance_naxxramas* m_pInstance; + bool m_bIsRegularMode; + + GuidList m_lSummonedAddGuids; + GuidList m_lTraineeSummonPosGuids; + GuidList m_lDeathKnightSummonPosGuids; + GuidList m_lRiderSummonPosGuids; + + uint8 m_uiPhase; + uint8 m_uiSpeech; + + uint32 m_uiTraineeTimer; + uint32 m_uiDeathKnightTimer; + uint32 m_uiRiderTimer; + uint32 m_uiTeleportTimer; + uint32 m_uiShadowboltTimer; + uint32 m_uiHarvestSoulTimer; + uint32 m_uiPhaseTimer; + uint32 m_uiControlZoneTimer; + uint32 m_uiSpeechTimer; + + void Reset() override + { + // Remove immunity + m_creature->ApplySpellImmune(0, IMMUNITY_DAMAGE, SPELL_SCHOOL_MASK_ALL, false); + + m_uiPhase = PHASE_SPEECH; + m_uiSpeech = 1; + + m_uiTraineeTimer = 24 * IN_MILLISECONDS; + m_uiDeathKnightTimer = 74 * IN_MILLISECONDS; + m_uiRiderTimer = 134 * IN_MILLISECONDS; + m_uiTeleportTimer = 20 * IN_MILLISECONDS; + m_uiShadowboltTimer = 2 * IN_MILLISECONDS; + m_uiHarvestSoulTimer = 2500; + m_uiPhaseTimer = 4 * MINUTE * IN_MILLISECONDS + 7 * IN_MILLISECONDS; // last summon at 4:04, next would be 4:09 + m_uiControlZoneTimer = urand(120 * IN_MILLISECONDS, 150 * IN_MILLISECONDS); + m_uiSpeechTimer = 1 * IN_MILLISECONDS; + + // Despawn Adds + for (GuidList::const_iterator itr = m_lSummonedAddGuids.begin(); itr != m_lSummonedAddGuids.end(); itr++) + { + if (Creature* pCreature = m_creature->GetMap()->GetCreature(*itr)) + pCreature->ForcedDespawn(); + } + + m_lSummonedAddGuids.clear(); + m_lTraineeSummonPosGuids.clear(); + m_lDeathKnightSummonPosGuids.clear(); + m_lRiderSummonPosGuids.clear(); + } + + void Aggro(Unit* /*pWho*/) override + { + if (!m_pInstance) + return; + + m_pInstance->SetData(TYPE_GOTHIK, IN_PROGRESS); + + // Make immune + m_creature->ApplySpellImmune(0, IMMUNITY_DAMAGE, SPELL_SCHOOL_MASK_ALL, true); + + m_pInstance->SetGothTriggers(); + PrepareSummonPlaces(); + } + + bool IsCentralDoorClosed() + { + return m_pInstance && m_pInstance->GetData(TYPE_GOTHIK) != SPECIAL; + } + + void ProcessCentralDoor() + { + if (IsCentralDoorClosed()) + { + m_pInstance->SetData(TYPE_GOTHIK, SPECIAL); + DoScriptText(EMOTE_GATE, m_creature); + } + } + + bool HasPlayersInLeftSide() + { + Map::PlayerList const& lPlayers = m_pInstance->instance->GetPlayers(); + + if (lPlayers.isEmpty()) + return false; + + for (Map::PlayerList::const_iterator itr = lPlayers.begin(); itr != lPlayers.end(); ++itr) + { + if (Player* pPlayer = itr->getSource()) + { + if (!m_pInstance->IsInRightSideGothArea(pPlayer) && pPlayer->IsAlive()) + return true; + } + } + + return false; + } + + void KilledUnit(Unit* pVictim) override + { + if (pVictim->GetTypeId() == TYPEID_PLAYER) + DoScriptText(SAY_KILL, m_creature); + } + + void JustDied(Unit* /*pKiller*/) override + { + DoScriptText(SAY_DEATH, m_creature); + + if (m_pInstance) + m_pInstance->SetData(TYPE_GOTHIK, DONE); + } + + void JustReachedHome() override + { + if (m_pInstance) + m_pInstance->SetData(TYPE_GOTHIK, FAIL); + } + + void PrepareSummonPlaces() + { + std::list lSummonList; + m_pInstance->GetGothSummonPointCreatures(lSummonList, true); + + if (lSummonList.empty()) + return; + + // Trainees and Rider + uint8 index = 0; + uint8 uiTraineeCount = m_bIsRegularMode ? 2 : 3; + lSummonList.sort(ObjectDistanceOrder(m_creature)); + for (std::list::iterator itr = lSummonList.begin(); itr != lSummonList.end(); ++itr) + { + if (*itr) + { + if (uiTraineeCount == 0) + break; + if (index == 1) + m_lRiderSummonPosGuids.push_back((*itr)->GetObjectGuid()); + else + { + m_lTraineeSummonPosGuids.push_back((*itr)->GetObjectGuid()); + --uiTraineeCount; + } + index++; + } + } + + // DeathKnights + uint8 uiDeathKnightCount = m_bIsRegularMode ? 1 : 2; + lSummonList.sort(ObjectDistanceOrderReversed(m_creature)); + for (std::list::iterator itr = lSummonList.begin(); itr != lSummonList.end(); ++itr) + { + if (*itr) + { + if (uiDeathKnightCount == 0) + break; + m_lDeathKnightSummonPosGuids.push_back((*itr)->GetObjectGuid()); + --uiDeathKnightCount; + } + } + } + + void SummonAdds(bool /*bRightSide*/, uint32 uiSummonEntry) + { + GuidList* plSummonPosGuids; + switch (uiSummonEntry) + { + case NPC_UNREL_TRAINEE: plSummonPosGuids = &m_lTraineeSummonPosGuids; break; + case NPC_UNREL_DEATH_KNIGHT: plSummonPosGuids = &m_lDeathKnightSummonPosGuids; break; + case NPC_UNREL_RIDER: plSummonPosGuids = &m_lRiderSummonPosGuids; break; + default: + return; + } + if (plSummonPosGuids->empty()) + return; + + for (GuidList::iterator itr = plSummonPosGuids->begin(); itr != plSummonPosGuids->end(); ++itr) + { + if (Creature* pPos = m_creature->GetMap()->GetCreature(*itr)) + m_creature->SummonCreature(uiSummonEntry, pPos->GetPositionX(), pPos->GetPositionY(), pPos->GetPositionZ(), pPos->GetOrientation(), TEMPSUMMON_DEAD_DESPAWN, 0); + } + } + + void JustSummoned(Creature* pSummoned) override + { + m_lSummonedAddGuids.push_back(pSummoned->GetObjectGuid()); + if (!IsCentralDoorClosed()) + pSummoned->SetInCombatWithZone(); + } + + void SummonedCreatureJustDied(Creature* pSummoned) override + { + m_lSummonedAddGuids.remove(pSummoned->GetObjectGuid()); + + if (!m_pInstance) + return; + + if (Creature* pAnchor = m_pInstance->GetClosestAnchorForGoth(pSummoned, true)) + { + switch (pSummoned->GetEntry()) + { + // Wrong caster, it expected to be pSummoned. + // Mangos deletes the spell event at caster death, so for delayed spell like this + // it's just a workaround. Does not affect other than the visual though (+ spell takes longer to "travel") + case NPC_UNREL_TRAINEE: m_creature->CastSpell(pAnchor, SPELL_A_TO_ANCHOR_1, true, NULL, NULL, pSummoned->GetObjectGuid()); break; + case NPC_UNREL_DEATH_KNIGHT: m_creature->CastSpell(pAnchor, SPELL_B_TO_ANCHOR_1, true, NULL, NULL, pSummoned->GetObjectGuid()); break; + case NPC_UNREL_RIDER: m_creature->CastSpell(pAnchor, SPELL_C_TO_ANCHOR_1, true, NULL, NULL, pSummoned->GetObjectGuid()); break; + } + } + } + + void UpdateAI(const uint32 uiDiff) override + { + if (!m_creature->SelectHostileTarget() || !m_creature->getVictim()) + return; + + switch (m_uiPhase) + { + case PHASE_SPEECH: + if (m_uiSpeechTimer < uiDiff) + { + switch (m_uiSpeech) + { + case 1: DoScriptText(SAY_SPEECH_1, m_creature); m_uiSpeechTimer = 4 * IN_MILLISECONDS; break; + case 2: DoScriptText(SAY_SPEECH_2, m_creature); m_uiSpeechTimer = 6 * IN_MILLISECONDS; break; + case 3: DoScriptText(SAY_SPEECH_3, m_creature); m_uiSpeechTimer = 5 * IN_MILLISECONDS; break; + case 4: DoScriptText(SAY_SPEECH_4, m_creature); m_uiPhase = PHASE_BALCONY; break; + } + m_uiSpeech++; + } + else + m_uiSpeechTimer -= uiDiff; + + // No break here + + case PHASE_BALCONY: // Do summoning + if (m_uiTraineeTimer < uiDiff) + { + SummonAdds(true, NPC_UNREL_TRAINEE); + m_uiTraineeTimer = 20 * IN_MILLISECONDS; + } + else + m_uiTraineeTimer -= uiDiff; + if (m_uiDeathKnightTimer < uiDiff) + { + SummonAdds(true, NPC_UNREL_DEATH_KNIGHT); + m_uiDeathKnightTimer = 25 * IN_MILLISECONDS; + } + else + m_uiDeathKnightTimer -= uiDiff; + if (m_uiRiderTimer < uiDiff) + { + SummonAdds(true, NPC_UNREL_RIDER); + m_uiRiderTimer = 30 * IN_MILLISECONDS; + } + else + m_uiRiderTimer -= uiDiff; + + if (m_uiPhaseTimer < uiDiff) + { + m_uiPhase = PHASE_STOP_SUMMONING; + m_uiPhaseTimer = 27 * IN_MILLISECONDS; + } + else + m_uiPhaseTimer -= uiDiff; + + break; + + case PHASE_STOP_SUMMONING: + if (m_uiPhaseTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature, SPELL_TELEPORT_RIGHT, CAST_TRIGGERED) == CAST_OK) + { + m_uiPhase = m_pInstance ? PHASE_TELEPORTING : PHASE_STOP_TELEPORTING; + + DoScriptText(SAY_TELEPORT, m_creature); + DoScriptText(EMOTE_TO_FRAY, m_creature); + + // Remove Immunity + m_creature->ApplySpellImmune(0, IMMUNITY_DAMAGE, SPELL_SCHOOL_MASK_ALL, false); + + DoResetThreat(); + m_creature->SetInCombatWithZone(); + } + } + else + m_uiPhaseTimer -= uiDiff; + + break; + + case PHASE_TELEPORTING: // Phase is only reached if m_pInstance is valid + if (m_uiTeleportTimer < uiDiff) + { + uint32 uiTeleportSpell = m_pInstance->IsInRightSideGothArea(m_creature) ? SPELL_TELEPORT_LEFT : SPELL_TELEPORT_RIGHT; + if (DoCastSpellIfCan(m_creature, uiTeleportSpell) == CAST_OK) + { + m_uiTeleportTimer = 20 * IN_MILLISECONDS; + m_uiShadowboltTimer = 2 * IN_MILLISECONDS; + } + } + else + m_uiTeleportTimer -= uiDiff; + + if (m_creature->GetHealthPercent() <= 30.0f) + { + m_uiPhase = PHASE_STOP_TELEPORTING; + ProcessCentralDoor(); + // as the doors now open, recheck whether mobs are standing around + m_uiControlZoneTimer = 1; + } + // no break here + + case PHASE_STOP_TELEPORTING: + if (m_uiHarvestSoulTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature, SPELL_HARVESTSOUL) == CAST_OK) + m_uiHarvestSoulTimer = 15 * IN_MILLISECONDS; + } + else + m_uiHarvestSoulTimer -= uiDiff; + + if (m_uiShadowboltTimer) + { + if (m_uiShadowboltTimer <= uiDiff) + m_uiShadowboltTimer = 0; + else + m_uiShadowboltTimer -= uiDiff; + } + // Shadowbold cooldown finished, cast when ready + else if (!m_creature->IsNonMeleeSpellCasted(true)) + { + // Select valid target + if (Unit* pTarget = m_creature->SelectAttackingTarget(ATTACKING_TARGET_TOPAGGRO, 0, SPELL_SHADOWBOLT, SELECT_FLAG_IN_LOS)) + DoCastSpellIfCan(pTarget, m_bIsRegularMode ? SPELL_SHADOWBOLT : SPELL_SHADOWBOLT_H); + } + + break; + } + + // Control Check, if Death zone empty + if (m_uiControlZoneTimer) + { + if (m_uiControlZoneTimer <= uiDiff) + { + m_uiControlZoneTimer = 0; + + if (m_pInstance && !HasPlayersInLeftSide()) + { + ProcessCentralDoor(); + for (GuidList::const_iterator itr = m_lSummonedAddGuids.begin(); itr != m_lSummonedAddGuids.end(); itr++) + { + if (Creature* pCreature = m_pInstance->instance->GetCreature(*itr)) + { + if (!pCreature->IsInCombat()) + pCreature->SetInCombatWithZone(); + } + } + } + } + else + m_uiControlZoneTimer -= uiDiff; + } + } +}; + +CreatureAI* GetAI_boss_gothik(Creature* pCreature) +{ + return new boss_gothikAI(pCreature); +} + +bool EffectDummyCreature_spell_anchor(Unit* /*pCaster*/, uint32 uiSpellId, SpellEffectIndex uiEffIndex, Creature* pCreatureTarget, ObjectGuid /*originalCasterGuid*/) +{ + if (uiEffIndex != EFFECT_INDEX_0 || pCreatureTarget->GetEntry() != NPC_SUB_BOSS_TRIGGER) + return true; + + instance_naxxramas* pInstance = (instance_naxxramas*)pCreatureTarget->GetInstanceData(); + + if (!pInstance) + return true; + + switch (uiSpellId) + { + case SPELL_A_TO_ANCHOR_1: // trigger mobs at high right side + case SPELL_B_TO_ANCHOR_1: + case SPELL_C_TO_ANCHOR_1: + { + if (Creature* pAnchor2 = pInstance->GetClosestAnchorForGoth(pCreatureTarget, false)) + { + uint32 uiTriggered = SPELL_A_TO_ANCHOR_2; + + if (uiSpellId == SPELL_B_TO_ANCHOR_1) + uiTriggered = SPELL_B_TO_ANCHOR_2; + else if (uiSpellId == SPELL_C_TO_ANCHOR_1) + uiTriggered = SPELL_C_TO_ANCHOR_2; + + pCreatureTarget->CastSpell(pAnchor2, uiTriggered, true); + } + + return true; + } + case SPELL_A_TO_ANCHOR_2: // trigger mobs at high left side + case SPELL_B_TO_ANCHOR_2: + case SPELL_C_TO_ANCHOR_2: + { + std::list lTargets; + pInstance->GetGothSummonPointCreatures(lTargets, false); + + if (!lTargets.empty()) + { + std::list::iterator itr = lTargets.begin(); + uint32 uiPosition = urand(0, lTargets.size() - 1); + advance(itr, uiPosition); + + if (Creature* pTarget = (*itr)) + { + uint32 uiTriggered = SPELL_A_TO_SKULL; + + if (uiSpellId == SPELL_B_TO_ANCHOR_2) + uiTriggered = SPELL_B_TO_SKULL; + else if (uiSpellId == SPELL_C_TO_ANCHOR_2) + uiTriggered = SPELL_C_TO_SKULL; + + pCreatureTarget->CastSpell(pTarget, uiTriggered, true); + } + } + return true; + } + case SPELL_A_TO_SKULL: // final destination trigger mob + case SPELL_B_TO_SKULL: + case SPELL_C_TO_SKULL: + { + if (Creature* pGoth = pInstance->GetSingleCreatureFromStorage(NPC_GOTHIK)) + { + uint32 uiNpcEntry = NPC_SPECT_TRAINEE; + + if (uiSpellId == SPELL_B_TO_SKULL) + uiNpcEntry = NPC_SPECT_DEATH_KNIGHT; + else if (uiSpellId == SPELL_C_TO_SKULL) + uiNpcEntry = NPC_SPECT_RIDER; + + pGoth->SummonCreature(uiNpcEntry, pCreatureTarget->GetPositionX(), pCreatureTarget->GetPositionY(), pCreatureTarget->GetPositionZ(), pCreatureTarget->GetOrientation(), TEMPSUMMON_DEAD_DESPAWN, 0); + + if (uiNpcEntry == NPC_SPECT_RIDER) + pGoth->SummonCreature(NPC_SPECT_HORSE, pCreatureTarget->GetPositionX(), pCreatureTarget->GetPositionY(), pCreatureTarget->GetPositionZ(), pCreatureTarget->GetOrientation(), TEMPSUMMON_DEAD_DESPAWN, 0); + } + return true; + } + } + + return true; +}; + +void AddSC_boss_gothik() +{ + Script* pNewScript; + + pNewScript = new Script; + pNewScript->Name = "boss_gothik"; + pNewScript->GetAI = &GetAI_boss_gothik; + pNewScript->RegisterSelf(); + + pNewScript = new Script; + pNewScript->Name = "spell_anchor"; + pNewScript->pEffectDummyNPC = &EffectDummyCreature_spell_anchor; + pNewScript->RegisterSelf(); +} diff --git a/src/modules/SD2/scripts/northrend/naxxramas/boss_grobbulus.cpp b/src/modules/SD2/scripts/northrend/naxxramas/boss_grobbulus.cpp new file mode 100644 index 000000000..ed8b055aa --- /dev/null +++ b/src/modules/SD2/scripts/northrend/naxxramas/boss_grobbulus.cpp @@ -0,0 +1,221 @@ +/* Copyright (C) 2006 - 2013 ScriptDev2 + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +/* ScriptData +SDName: Boss_Grobbulus +SD%Complete: 80 +SDComment: Timer need more care; Spells of Adds (Posion Cloud) need Mangos Fixes, and further handling +SDCategory: Naxxramas +EndScriptData */ + +/*Poison Cloud 26590 +Slime Spray 28157 +Fallout slime 28218 +Mutating Injection 28169 +Enrages 26527*/ + +#include "precompiled.h" +#include "naxxramas.h" + +enum +{ + EMOTE_SPRAY_SLIME = -1533021, + EMOTE_INJECTION = -1533158, + + SPELL_SLIME_STREAM = 28137, + SPELL_MUTATING_INJECTION = 28169, + SPELL_POISON_CLOUD = 28240, + SPELL_SLIME_SPRAY = 28157, + SPELL_SLIME_SPRAY_H = 54364, + SPELL_BERSERK = 26662, + + NPC_FALLOUT_SLIME = 16290 +}; + +struct boss_grobbulusAI : public ScriptedAI +{ + boss_grobbulusAI(Creature* pCreature) : ScriptedAI(pCreature) + { + m_pInstance = (instance_naxxramas*)pCreature->GetInstanceData(); + m_bIsRegularMode = pCreature->GetMap()->IsRegularDifficulty(); + + Reset(); + } + + instance_naxxramas* m_pInstance; + bool m_bIsRegularMode; + + uint32 m_uiInjectionTimer; + uint32 m_uiPoisonCloudTimer; + uint32 m_uiSlimeSprayTimer; + uint32 m_uiBerserkTimeSecs; + uint32 m_uiBerserkTimer; + uint32 m_uiSlimeStreamTimer; + + void Reset() override + { + m_uiInjectionTimer = 12 * IN_MILLISECONDS; + m_uiPoisonCloudTimer = urand(20 * IN_MILLISECONDS, 25 * IN_MILLISECONDS); + m_uiSlimeSprayTimer = urand(20 * IN_MILLISECONDS, 30 * IN_MILLISECONDS); + m_uiBerserkTimeSecs = m_bIsRegularMode ? 12 * MINUTE : 9 * MINUTE; + m_uiBerserkTimer = m_uiBerserkTimeSecs * IN_MILLISECONDS; + m_uiSlimeStreamTimer = 5 * IN_MILLISECONDS; // The first few secs it is ok to be out of range + } + + void Aggro(Unit* /*pWho*/) override + { + if (m_pInstance) + m_pInstance->SetData(TYPE_GROBBULUS, IN_PROGRESS); + } + + void JustDied(Unit* /*pKiller*/) override + { + if (m_pInstance) + m_pInstance->SetData(TYPE_GROBBULUS, DONE); + } + + void JustReachedHome() override + { + if (m_pInstance) + m_pInstance->SetData(TYPE_GROBBULUS, FAIL); + } + + // This custom selecting function, because we only want to select players without mutagen aura + bool DoCastMutagenInjection() + { + if (m_creature->IsNonMeleeSpellCasted(true)) + return false; + + std::vector suitableTargets; + ThreatList const& threatList = m_creature->GetThreatManager().getThreatList(); + + for (ThreatList::const_iterator itr = threatList.begin(); itr != threatList.end(); ++itr) + { + if (Unit* pTarget = m_creature->GetMap()->GetUnit((*itr)->getUnitGuid())) + { + if (pTarget->GetTypeId() == TYPEID_PLAYER && !pTarget->HasAura(SPELL_MUTATING_INJECTION)) + suitableTargets.push_back(pTarget); + } + } + + if (suitableTargets.empty()) + return false; + + Unit* pTarget = suitableTargets[urand(0, suitableTargets.size() - 1)]; + if (DoCastSpellIfCan(pTarget, SPELL_MUTATING_INJECTION) == CAST_OK) + { + DoScriptText(EMOTE_INJECTION, m_creature, pTarget); + return true; + } + else + return false; + } + + void SpellHitTarget(Unit* pTarget, const SpellEntry* pSpell) override + { + if ((pSpell->Id == SPELL_SLIME_SPRAY || pSpell->Id == SPELL_SLIME_SPRAY_H) && pTarget->GetTypeId() == TYPEID_PLAYER) + m_creature->SummonCreature(NPC_FALLOUT_SLIME, pTarget->GetPositionX(), pTarget->GetPositionY(), pTarget->GetPositionZ(), 0.0f, TEMPSUMMON_TIMED_OOC_DESPAWN, 10 * IN_MILLISECONDS); + } + + void UpdateAI(const uint32 uiDiff) override + { + if (!m_creature->SelectHostileTarget() || !m_creature->getVictim()) + return; + + // Slime Stream + if (!m_uiSlimeStreamTimer) + { + if (!m_creature->CanReachWithMeleeAttack(m_creature->getVictim())) + { + if (DoCastSpellIfCan(m_creature, SPELL_SLIME_STREAM) == CAST_OK) + // Give some time, to re-reach grobbulus + m_uiSlimeStreamTimer = 3 * IN_MILLISECONDS; + } + } + else + { + if (m_uiSlimeStreamTimer < uiDiff) + m_uiSlimeStreamTimer = 0; + else + m_uiSlimeStreamTimer -= uiDiff; + } + + // Berserk + if (m_uiBerserkTimer) + { + if (m_uiBerserkTimer <= uiDiff) + { + if (DoCastSpellIfCan(m_creature, SPELL_BERSERK) == CAST_OK) + m_uiBerserkTimer = 0; + } + else + m_uiBerserkTimer -= uiDiff; + } + + // SlimeSpray + if (m_uiSlimeSprayTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature->getVictim(), m_bIsRegularMode ? SPELL_SLIME_SPRAY : SPELL_SLIME_SPRAY_H) == CAST_OK) + { + m_uiSlimeSprayTimer = urand(30 * IN_MILLISECONDS, 60 * IN_MILLISECONDS); + DoScriptText(EMOTE_SPRAY_SLIME, m_creature); + } + } + else + m_uiSlimeSprayTimer -= uiDiff; + + // Mutagen Injection + if (m_uiInjectionTimer < uiDiff) + { + if (DoCastMutagenInjection()) + { + // Timer dependend on time of encounter - on enrage time between 5-8s, heroic 2-5s (TODO no reliable source for heroic) + if (m_bIsRegularMode) + m_uiInjectionTimer = urand(10 * IN_MILLISECONDS, 13 * IN_MILLISECONDS) - 5 * (m_uiBerserkTimeSecs * IN_MILLISECONDS - m_uiBerserkTimer) / m_uiBerserkTimeSecs; + else + m_uiInjectionTimer = urand(10 * IN_MILLISECONDS, 13 * IN_MILLISECONDS) - 8 * (m_uiBerserkTimeSecs * IN_MILLISECONDS - m_uiBerserkTimer) / m_uiBerserkTimeSecs; + } + } + else + m_uiInjectionTimer -= uiDiff; + + // Poison Cloud + if (m_uiPoisonCloudTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature, SPELL_POISON_CLOUD) == CAST_OK) + m_uiPoisonCloudTimer = 15 * IN_MILLISECONDS; + } + else + m_uiPoisonCloudTimer -= uiDiff; + + DoMeleeAttackIfReady(); + } +}; + +CreatureAI* GetAI_boss_grobbulus(Creature* pCreature) +{ + return new boss_grobbulusAI(pCreature); +} + +void AddSC_boss_grobbulus() +{ + Script* pNewScript; + + pNewScript = new Script; + pNewScript->Name = "boss_grobbulus"; + pNewScript->GetAI = &GetAI_boss_grobbulus; + pNewScript->RegisterSelf(); +} diff --git a/src/modules/SD2/scripts/northrend/naxxramas/boss_heigan.cpp b/src/modules/SD2/scripts/northrend/naxxramas/boss_heigan.cpp new file mode 100644 index 000000000..304a12215 --- /dev/null +++ b/src/modules/SD2/scripts/northrend/naxxramas/boss_heigan.cpp @@ -0,0 +1,247 @@ +/* Copyright (C) 2006 - 2013 ScriptDev2 + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +/* ScriptData +SDName: Boss_Heigan +SD%Complete: 65 +SDComment: Missing traps dance +SDCategory: Naxxramas +EndScriptData */ + +#include "precompiled.h" +#include "naxxramas.h" + +enum +{ + PHASE_GROUND = 1, + PHASE_PLATFORM = 2, + + SAY_AGGRO1 = -1533109, + SAY_AGGRO2 = -1533110, + SAY_AGGRO3 = -1533111, + SAY_SLAY = -1533112, + SAY_TAUNT1 = -1533113, + SAY_TAUNT2 = -1533114, + SAY_TAUNT3 = -1533115, + SAY_TAUNT4 = -1533117, + SAY_CHANNELING = -1533116, + SAY_DEATH = -1533118, + EMOTE_TELEPORT = -1533136, + EMOTE_RETURN = -1533137, + + // Spells by boss + SPELL_DECREPIT_FEVER = 29998, + SPELL_DECREPIT_FEVER_H = 55011, + SPELL_DISRUPTION = 29310, + SPELL_TELEPORT = 30211, + SPELL_PLAGUE_CLOUD = 29350 +}; + +struct boss_heiganAI : public ScriptedAI +{ + boss_heiganAI(Creature* pCreature) : ScriptedAI(pCreature) + { + m_pInstance = (instance_naxxramas*)pCreature->GetInstanceData(); + m_bIsRegularMode = pCreature->GetMap()->IsRegularDifficulty(); + Reset(); + } + + instance_naxxramas* m_pInstance; + bool m_bIsRegularMode; + + uint8 m_uiPhase; + uint8 m_uiPhaseEruption; + + uint32 m_uiFeverTimer; + uint32 m_uiDisruptionTimer; + uint32 m_uiEruptionTimer; + uint32 m_uiPhaseTimer; + uint32 m_uiTauntTimer; + uint32 m_uiStartChannelingTimer; + + void ResetPhase() + { + m_uiPhaseEruption = 0; + m_uiFeverTimer = 4000; + m_uiEruptionTimer = m_uiPhase == PHASE_GROUND ? 15000 : 7500; + m_uiDisruptionTimer = 5000; + m_uiStartChannelingTimer = 1000; + m_uiPhaseTimer = m_uiPhase == PHASE_GROUND ? 90000 : 45000; + } + + void Reset() override + { + m_uiPhase = PHASE_GROUND; + m_uiTauntTimer = urand(20000, 60000); // TODO, find information + ResetPhase(); + } + + void Aggro(Unit* /*pWho*/) override + { + switch (urand(0, 2)) + { + case 0: DoScriptText(SAY_AGGRO1, m_creature); break; + case 1: DoScriptText(SAY_AGGRO2, m_creature); break; + case 2: DoScriptText(SAY_AGGRO3, m_creature); break; + } + + if (m_pInstance) + m_pInstance->SetData(TYPE_HEIGAN, IN_PROGRESS); + } + + void KilledUnit(Unit* /*pVictim*/) override + { + DoScriptText(SAY_SLAY, m_creature); + } + + void JustDied(Unit* /*pKiller*/) override + { + DoScriptText(SAY_DEATH, m_creature); + + if (m_pInstance) + m_pInstance->SetData(TYPE_HEIGAN, DONE); + } + + void JustReachedHome() override + { + if (m_pInstance) + m_pInstance->SetData(TYPE_HEIGAN, FAIL); + } + + void UpdateAI(const uint32 uiDiff) override + { + if (!m_creature->SelectHostileTarget() || !m_creature->getVictim()) + return; + + if (m_uiPhase == PHASE_GROUND) + { + // Teleport to platform + if (m_uiPhaseTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature, SPELL_TELEPORT) == CAST_OK) + { + DoScriptText(EMOTE_TELEPORT, m_creature); + m_creature->GetMotionMaster()->MoveIdle(); + + m_uiPhase = PHASE_PLATFORM; + ResetPhase(); + return; + } + } + else + m_uiPhaseTimer -= uiDiff; + + // Fever + if (m_uiFeverTimer < uiDiff) + { + DoCastSpellIfCan(m_creature, m_bIsRegularMode ? SPELL_DECREPIT_FEVER : SPELL_DECREPIT_FEVER_H); + m_uiFeverTimer = 21000; + } + else + m_uiFeverTimer -= uiDiff; + + // Disruption + if (m_uiDisruptionTimer < uiDiff) + { + DoCastSpellIfCan(m_creature, SPELL_DISRUPTION); + m_uiDisruptionTimer = 10000; + } + else + m_uiDisruptionTimer -= uiDiff; + } + else // Platform Phase + { + if (m_uiPhaseTimer < uiDiff) // Return to fight + { + m_creature->InterruptNonMeleeSpells(true); + DoScriptText(EMOTE_RETURN, m_creature); + m_creature->GetMotionMaster()->MoveChase(m_creature->getVictim()); + + m_uiPhase = PHASE_GROUND; + ResetPhase(); + return; + } + else + m_uiPhaseTimer -= uiDiff; + + if (m_uiStartChannelingTimer) + { + if (m_uiStartChannelingTimer <= uiDiff) + { + DoScriptText(SAY_CHANNELING, m_creature); + DoCastSpellIfCan(m_creature, SPELL_PLAGUE_CLOUD); + m_uiStartChannelingTimer = 0; // no more + } + else + m_uiStartChannelingTimer -= uiDiff; + } + } + + // Taunt + if (m_uiTauntTimer < uiDiff) + { + switch (urand(0, 3)) + { + case 0: DoScriptText(SAY_TAUNT1, m_creature); break; + case 1: DoScriptText(SAY_TAUNT2, m_creature); break; + case 2: DoScriptText(SAY_TAUNT3, m_creature); break; + case 3: DoScriptText(SAY_TAUNT4, m_creature); break; + } + m_uiTauntTimer = urand(20000, 70000); + } + else + m_uiTauntTimer -= uiDiff; + + DoMeleeAttackIfReady(); + + // Handling of the erruptions, this is not related to melee attack or spell-casting + if (!m_pInstance) + return; + + // Eruption + if (m_uiEruptionTimer < uiDiff) + { + for (uint8 uiArea = 0; uiArea < MAX_HEIGAN_TRAP_AREAS; ++uiArea) + { + // Actually this is correct :P + if (uiArea == (m_uiPhaseEruption % 6) || uiArea == 6 - (m_uiPhaseEruption % 6)) + continue; + + m_pInstance->DoTriggerHeiganTraps(m_creature, uiArea); + } + + m_uiEruptionTimer = m_uiPhase == PHASE_GROUND ? 10000 : 3000; + ++m_uiPhaseEruption; + } + else + m_uiEruptionTimer -= uiDiff; + } +}; + +CreatureAI* GetAI_boss_heigan(Creature* pCreature) +{ + return new boss_heiganAI(pCreature); +} + +void AddSC_boss_heigan() +{ + Script* pNewScript; + + pNewScript = new Script; + pNewScript->Name = "boss_heigan"; + pNewScript->GetAI = &GetAI_boss_heigan; + pNewScript->RegisterSelf(); +} diff --git a/src/modules/SD2/scripts/northrend/naxxramas/boss_kelthuzad.cpp b/src/modules/SD2/scripts/northrend/naxxramas/boss_kelthuzad.cpp new file mode 100644 index 000000000..072026ae1 --- /dev/null +++ b/src/modules/SD2/scripts/northrend/naxxramas/boss_kelthuzad.cpp @@ -0,0 +1,576 @@ +/* Copyright (C) 2006 - 2013 ScriptDev2 + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +/* ScriptData +SDName: Boss_KelThuzad +SD%Complete: 75 +SDComment: Timers will need adjustments, along with tweaking positions and amounts +SDCategory: Naxxramas +EndScriptData */ + +// some not answered questions: +// - will intro mobs, not sent to center, despawn when phase 2 start? +// - what happens if raid fail, can they start the event as soon after as they want? + +#include "precompiled.h" +#include "naxxramas.h" + +enum +{ + SAY_SUMMON_MINIONS = -1533105, // start of phase 1 + + EMOTE_PHASE2 = -1533135, // start of phase 2 + SAY_AGGRO1 = -1533094, + SAY_AGGRO2 = -1533095, + SAY_AGGRO3 = -1533096, + + SAY_SLAY1 = -1533097, + SAY_SLAY2 = -1533098, + + SAY_DEATH = -1533099, + + SAY_CHAIN1 = -1533100, + SAY_CHAIN2 = -1533101, + SAY_FROST_BLAST = -1533102, + + SAY_REQUEST_AID = -1533103, // start of phase 3 + SAY_ANSWER_REQUEST = -1533104, // lich king answer + + SAY_SPECIAL1_MANA_DET = -1533106, + SAY_SPECIAL3_MANA_DET = -1533107, + SAY_SPECIAL2_DISPELL = -1533108, + + EMOTE_GUARDIAN = -1533134, // at each guardian summon + + // spells to be casted + SPELL_FROST_BOLT = 28478, + SPELL_FROST_BOLT_H = 55802, + SPELL_FROST_BOLT_NOVA = 28479, + SPELL_FROST_BOLT_NOVA_H = 55807, + + SPELL_CHAINS_OF_KELTHUZAD = 28408, // 3.x, heroic only + SPELL_CHAINS_OF_KELTHUZAD_TARGET = 28410, + + SPELL_MANA_DETONATION = 27819, + SPELL_SHADOW_FISSURE = 27810, + SPELL_FROST_BLAST = 27808, + + SPELL_CHANNEL_VISUAL = 29423, + + MAX_SOLDIER_COUNT = 71, + MAX_ABOMINATION_COUNT = 8, + MAX_BANSHEE_COUNT = 8, + + ACHIEV_REQ_KILLED_ABOMINATIONS = 18, +}; + +static float M_F_ANGLE = 0.2f; // to adjust for map rotation +static float M_F_HEIGHT = 2.0f; // adjust for height difference +static float M_F_RANGE = 55.0f; // ~ range from center of chamber to center of alcove + +enum Phase +{ + PHASE_INTRO, + PHASE_NORMAL, + PHASE_GUARDIANS, +}; + +struct boss_kelthuzadAI : public ScriptedAI +{ + boss_kelthuzadAI(Creature* pCreature) : ScriptedAI(pCreature) + { + m_pInstance = (instance_naxxramas*)pCreature->GetInstanceData(); + m_bIsRegularMode = pCreature->GetMap()->IsRegularDifficulty(); + + m_uiGuardiansCountMax = m_bIsRegularMode ? 2 : 4; + Reset(); + } + + instance_naxxramas* m_pInstance; + bool m_bIsRegularMode; + + uint32 m_uiGuardiansCount; + uint32 m_uiGuardiansCountMax; + uint32 m_uiGuardiansTimer; + uint32 m_uiLichKingAnswerTimer; + uint32 m_uiFrostBoltTimer; + uint32 m_uiFrostBoltNovaTimer; + uint32 m_uiChainsTimer; + uint32 m_uiManaDetonationTimer; + uint32 m_uiShadowFissureTimer; + uint32 m_uiFrostBlastTimer; + + uint32 m_uiPhase1Timer; + uint32 m_uiSoldierTimer; + uint32 m_uiBansheeTimer; + uint32 m_uiAbominationTimer; + uint8 m_uiPhase; + uint32 m_uiSoldierCount; + uint32 m_uiBansheeCount; + uint32 m_uiAbominationCount; + uint32 m_uiSummonIntroTimer; + uint32 m_uiIntroPackCount; + uint32 m_uiKilledAbomination; + + GuidSet m_lIntroMobsSet; + GuidSet m_lAddsSet; + + void Reset() override + { + m_uiFrostBoltTimer = urand(1000, 60000); // It won't be more than a minute without cast it + m_uiFrostBoltNovaTimer = 15000; // Cast every 15 seconds + m_uiChainsTimer = urand(30000, 60000); // Cast no sooner than once every 30 seconds + m_uiManaDetonationTimer = 20000; // Seems to cast about every 20 seconds + m_uiShadowFissureTimer = 25000; // 25 seconds + m_uiFrostBlastTimer = urand(30000, 60000); // Random time between 30-60 seconds + m_uiGuardiansTimer = 5000; // 5 seconds for summoning each Guardian of Icecrown in phase 3 + m_uiLichKingAnswerTimer = 4000; + m_uiGuardiansCount = 0; + m_uiSummonIntroTimer = 0; + m_uiIntroPackCount = 0; + + m_uiPhase1Timer = 228000; // Phase 1 lasts "3 minutes and 48 seconds" + m_uiSoldierTimer = 5000; + m_uiBansheeTimer = 5000; + m_uiAbominationTimer = 5000; + m_uiSoldierCount = 0; + m_uiBansheeCount = 0; + m_uiAbominationCount = 0; + m_uiKilledAbomination = 0; + m_uiPhase = PHASE_INTRO; + + // it may be some spell should be used instead, to control the intro phase + m_creature->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE); + SetCombatMovement(false); + } + + void KilledUnit(Unit* pVictim) override + { + if (pVictim->GetTypeId() != TYPEID_PLAYER) + return; + + if (urand(0, 1)) + DoScriptText(urand(0, 1) ? SAY_SLAY1 : SAY_SLAY2, m_creature); + } + + void JustDied(Unit* /*pKiller*/) override + { + DoScriptText(SAY_DEATH, m_creature); + DespawnAdds(); + + if (m_pInstance) + m_pInstance->SetData(TYPE_KELTHUZAD, DONE); + } + + void JustReachedHome() override + { + DespawnIntroCreatures(); + DespawnAdds(); + + if (m_pInstance) + m_pInstance->SetData(TYPE_KELTHUZAD, NOT_STARTED); + } + + void MoveInLineOfSight(Unit* pWho) override + { + if (m_pInstance && m_pInstance->GetData(TYPE_KELTHUZAD) != IN_PROGRESS) + return; + + ScriptedAI::MoveInLineOfSight(pWho); + } + + void DespawnIntroCreatures() + { + if (m_pInstance) + { + for (GuidSet::const_iterator itr = m_lIntroMobsSet.begin(); itr != m_lIntroMobsSet.end(); ++itr) + { + if (Creature* pCreature = m_pInstance->instance->GetCreature(*itr)) + pCreature->ForcedDespawn(); + } + } + + m_lIntroMobsSet.clear(); + } + + void DespawnAdds() + { + if (m_pInstance) + { + for (GuidSet::const_iterator itr = m_lAddsSet.begin(); itr != m_lAddsSet.end(); ++itr) + { + if (Creature* pCreature = m_pInstance->instance->GetCreature(*itr)) + { + if (pCreature->IsAlive()) + { + pCreature->AI()->EnterEvadeMode(); + pCreature->ForcedDespawn(15000); + } + } + } + } + + m_lAddsSet.clear(); + } + + float GetLocationAngle(uint32 uiId) + { + switch (uiId) + { + case 1: return M_PI_F - M_F_ANGLE; // south + case 2: return M_PI_F / 2 * 3 - M_F_ANGLE; // east + case 3: return M_PI_F / 2 - M_F_ANGLE; // west + case 4: return M_PI_F / 4 - M_F_ANGLE; // north-west + case 5: return M_PI_F / 4 * 7 - M_F_ANGLE; // north-east + case 6: return M_PI_F / 4 * 5 - M_F_ANGLE; // south-east + case 7: return M_PI_F / 4 * 3 - M_F_ANGLE; // south-west + } + + return M_F_ANGLE; + } + + void SummonIntroCreatures(uint32 packId) + { + if (!m_pInstance) + return; + + float fAngle = GetLocationAngle(packId + 1); + + float fX, fY, fZ; + m_pInstance->GetChamberCenterCoords(fX, fY, fZ); + + fX += M_F_RANGE * cos(fAngle); + fY += M_F_RANGE * sin(fAngle); + fZ += M_F_HEIGHT; + + MaNGOS::NormalizeMapCoord(fX); + MaNGOS::NormalizeMapCoord(fY); + + uint32 uiNpcEntry = NPC_SOUL_WEAVER; + + for (uint8 uiI = 0; uiI < 14; ++uiI) + { + if (uiI > 0) + { + if (uiI < 4) + uiNpcEntry = NPC_UNSTOPPABLE_ABOM; + else + uiNpcEntry = NPC_SOLDIER_FROZEN; + } + + float fNewX, fNewY, fNewZ; + m_creature->GetRandomPoint(fX, fY, fZ, 12.0f, fNewX, fNewY, fNewZ); + + m_creature->SummonCreature(uiNpcEntry, fNewX, fNewY, fNewZ, fAngle + M_PI_F, TEMPSUMMON_CORPSE_DESPAWN, 5000); + } + } + + void SummonMob(uint32 uiType) + { + if (!m_pInstance) + return; + + float fAngle = GetLocationAngle(urand(1, 7)); + + float fX, fY, fZ; + m_pInstance->GetChamberCenterCoords(fX, fY, fZ); + + fX += M_F_RANGE * cos(fAngle); + fY += M_F_RANGE * sin(fAngle); + fZ += M_F_HEIGHT; + + MaNGOS::NormalizeMapCoord(fX); + MaNGOS::NormalizeMapCoord(fY); + + m_creature->SummonCreature(uiType, fX, fY, fZ, 0.0f, TEMPSUMMON_CORPSE_DESPAWN, 5000); + } + + void JustSummoned(Creature* pSummoned) override + { + switch (pSummoned->GetEntry()) + { + case NPC_GUARDIAN: + { + DoScriptText(EMOTE_GUARDIAN, m_creature); + + m_lAddsSet.insert(pSummoned->GetObjectGuid()); + ++m_uiGuardiansCount; + + pSummoned->SetInCombatWithZone(); + break; + } + case NPC_SOLDIER_FROZEN: + case NPC_UNSTOPPABLE_ABOM: + case NPC_SOUL_WEAVER: + { + if (m_uiIntroPackCount < 7) + m_lIntroMobsSet.insert(pSummoned->GetObjectGuid()); + else + { + m_lAddsSet.insert(pSummoned->GetObjectGuid()); + + if (m_pInstance) + { + float fX, fY, fZ; + m_pInstance->GetChamberCenterCoords(fX, fY, fZ); + pSummoned->GetMotionMaster()->MovePoint(0, fX, fY, fZ); + } + } + + break; + } + } + } + + void SummonedCreatureJustDied(Creature* pSummoned) override + { + switch (pSummoned->GetEntry()) + { + case NPC_GUARDIAN: + case NPC_SOLDIER_FROZEN: + case NPC_SOUL_WEAVER: + m_lAddsSet.erase(pSummoned->GetObjectGuid()); + break; + case NPC_UNSTOPPABLE_ABOM: + m_lAddsSet.erase(pSummoned->GetObjectGuid()); + + ++m_uiKilledAbomination; + if (m_uiKilledAbomination >= ACHIEV_REQ_KILLED_ABOMINATIONS) + m_pInstance->SetSpecialAchievementCriteria(TYPE_ACHIEV_GET_ENOUGH, true); + + break; + } + } + + void SummonedMovementInform(Creature* pSummoned, uint32 uiMotionType, uint32 uiPointId) override + { + if (uiMotionType == POINT_MOTION_TYPE && uiPointId == 0) + pSummoned->SetInCombatWithZone(); + } + + void UpdateAI(const uint32 uiDiff) override + { + if (!m_creature->SelectHostileTarget() || !m_creature->getVictim()) + return; + + if (!m_pInstance || m_pInstance->GetData(TYPE_KELTHUZAD) != IN_PROGRESS) + return; + + if (m_uiPhase == PHASE_INTRO) + { + if (m_uiIntroPackCount < 7) + { + if (m_uiSummonIntroTimer < uiDiff) + { + if (!m_uiIntroPackCount) + DoScriptText(SAY_SUMMON_MINIONS, m_creature); + + SummonIntroCreatures(m_uiIntroPackCount); + ++m_uiIntroPackCount; + m_uiSummonIntroTimer = 2000; + } + else + m_uiSummonIntroTimer -= uiDiff; + } + else + { + if (m_uiPhase1Timer < uiDiff) + { + m_uiPhase = PHASE_NORMAL; + DespawnIntroCreatures(); + + m_creature->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE); + SetCombatMovement(true); + m_creature->GetMotionMaster()->MoveChase(m_creature->getVictim()); + + DoScriptText(EMOTE_PHASE2, m_creature); + + switch (urand(0, 2)) + { + case 0: DoScriptText(SAY_AGGRO1, m_creature); break; + case 1: DoScriptText(SAY_AGGRO2, m_creature); break; + case 2: DoScriptText(SAY_AGGRO3, m_creature); break; + }; + } + else + m_uiPhase1Timer -= uiDiff; + + if (m_uiSoldierCount < MAX_SOLDIER_COUNT) + { + if (m_uiSoldierTimer < uiDiff) + { + SummonMob(NPC_SOLDIER_FROZEN); + ++m_uiSoldierCount; + m_uiSoldierTimer = 3000; + } + else + m_uiSoldierTimer -= uiDiff; + } + + if (m_uiAbominationCount < MAX_ABOMINATION_COUNT) + { + if (m_uiAbominationTimer < uiDiff) + { + SummonMob(NPC_UNSTOPPABLE_ABOM); + ++m_uiAbominationCount; + m_uiAbominationTimer = 25000; + } + else + m_uiAbominationTimer -= uiDiff; + } + + if (m_uiBansheeCount < MAX_BANSHEE_COUNT) + { + if (m_uiBansheeTimer < uiDiff) + { + SummonMob(NPC_SOUL_WEAVER); + ++m_uiBansheeCount; + m_uiBansheeTimer = 25000; + } + else + m_uiBansheeTimer -= uiDiff; + } + } + } + else // normal or guardian phase + { + if (m_uiFrostBoltTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature->getVictim(), m_bIsRegularMode ? SPELL_FROST_BOLT : SPELL_FROST_BOLT_H) == CAST_OK) + m_uiFrostBoltTimer = urand(1000, 60000); + } + else + m_uiFrostBoltTimer -= uiDiff; + + if (m_uiFrostBoltNovaTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature->getVictim(), m_bIsRegularMode ? SPELL_FROST_BOLT_NOVA : SPELL_FROST_BOLT_NOVA_H) == CAST_OK) + m_uiFrostBoltNovaTimer = 15000; + } + else + m_uiFrostBoltNovaTimer -= uiDiff; + + if (m_uiManaDetonationTimer < uiDiff) + { + if (Unit* pTarget = m_creature->SelectAttackingTarget(ATTACKING_TARGET_RANDOM, 0, SPELL_MANA_DETONATION, SELECT_FLAG_PLAYER | SELECT_FLAG_POWER_MANA)) + { + if (DoCastSpellIfCan(pTarget, SPELL_MANA_DETONATION) == CAST_OK) + { + if (urand(0, 1)) + DoScriptText(SAY_SPECIAL1_MANA_DET, m_creature); + + m_uiManaDetonationTimer = 20000; + } + } + } + else + m_uiManaDetonationTimer -= uiDiff; + + if (m_uiShadowFissureTimer < uiDiff) + { + if (Unit* pTarget = m_creature->SelectAttackingTarget(ATTACKING_TARGET_RANDOM, 0)) + { + if (DoCastSpellIfCan(pTarget, SPELL_SHADOW_FISSURE) == CAST_OK) + { + if (urand(0, 1)) + DoScriptText(SAY_SPECIAL3_MANA_DET, m_creature); + + m_uiShadowFissureTimer = 25000; + } + } + } + else + m_uiShadowFissureTimer -= uiDiff; + + if (m_uiFrostBlastTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature->getVictim(), SPELL_FROST_BLAST) == CAST_OK) + { + if (urand(0, 1)) + DoScriptText(SAY_FROST_BLAST, m_creature); + + m_uiFrostBlastTimer = urand(30000, 60000); + } + } + else + m_uiFrostBlastTimer -= uiDiff; + + if (!m_bIsRegularMode) + { + if (m_uiChainsTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature->getVictim(), SPELL_CHAINS_OF_KELTHUZAD) == CAST_OK) + { + DoScriptText(urand(0, 1) ? SAY_CHAIN1 : SAY_CHAIN2, m_creature); + + m_uiChainsTimer = urand(30000, 60000); + } + } + else + m_uiChainsTimer -= uiDiff; + } + + if (m_uiPhase == PHASE_NORMAL) + { + if (m_creature->GetHealthPercent() < 45.0f) + { + m_uiPhase = PHASE_GUARDIANS; + DoScriptText(SAY_REQUEST_AID, m_creature); + } + } + else if (m_uiPhase == PHASE_GUARDIANS && m_uiGuardiansCount < m_uiGuardiansCountMax) + { + if (m_uiGuardiansTimer < uiDiff) + { + // Summon a Guardian of Icecrown in a random alcove + SummonMob(NPC_GUARDIAN); + m_uiGuardiansTimer = 5000; + } + else + m_uiGuardiansTimer -= uiDiff; + + if (m_uiLichKingAnswerTimer && m_pInstance) + { + if (m_uiLichKingAnswerTimer <= uiDiff) + { + if (Creature* pLichKing = m_pInstance->GetSingleCreatureFromStorage(NPC_THE_LICHKING)) + DoScriptText(SAY_ANSWER_REQUEST, pLichKing); + m_uiLichKingAnswerTimer = 0; + } + else + m_uiLichKingAnswerTimer -= uiDiff; + } + } + + DoMeleeAttackIfReady(); + } + } +}; + +CreatureAI* GetAI_boss_kelthuzad(Creature* pCreature) +{ + return new boss_kelthuzadAI(pCreature); +} + +void AddSC_boss_kelthuzad() +{ + Script* pNewScript; + + pNewScript = new Script; + pNewScript->Name = "boss_kelthuzad"; + pNewScript->GetAI = &GetAI_boss_kelthuzad; + pNewScript->RegisterSelf(); +} diff --git a/src/modules/SD2/scripts/northrend/naxxramas/boss_loatheb.cpp b/src/modules/SD2/scripts/northrend/naxxramas/boss_loatheb.cpp new file mode 100644 index 000000000..30dd9df50 --- /dev/null +++ b/src/modules/SD2/scripts/northrend/naxxramas/boss_loatheb.cpp @@ -0,0 +1,191 @@ +/* Copyright (C) 2006 - 2013 ScriptDev2 + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +/* ScriptData +SDName: Boss_Loatheb +SD%Complete: 100 +SDComment: +SDCategory: Naxxramas +EndScriptData */ + +#include "precompiled.h" +#include "naxxramas.h" + +enum +{ + EMOTE_AURA_BLOCKING = -1533143, + EMOTE_AURA_WANE = -1533144, + EMOTE_AURA_FADING = -1533145, + + SPELL_DEATHBLOOM = 29865, + SPELL_DEATHBLOOM_H = 55053, + SPELL_INEVITABLE_DOOM = 29204, + SPELL_INEVITABLE_DOOM_H = 55052, + SPELL_NECROTIC_AURA = 55593, + SPELL_SUMMON_SPORE = 29234, + SPELL_BERSERK = 26662, + + NPC_SPORE = 16286 +}; + +struct boss_loathebAI : public ScriptedAI +{ + boss_loathebAI(Creature* pCreature) : ScriptedAI(pCreature) + { + m_pInstance = (instance_naxxramas*)pCreature->GetInstanceData(); + m_bIsRegularMode = pCreature->GetMap()->IsRegularDifficulty(); + Reset(); + } + + instance_naxxramas* m_pInstance; + bool m_bIsRegularMode; + + uint32 m_uiDeathbloomTimer; + uint32 m_uiNecroticAuraTimer; + uint32 m_uiInevitableDoomTimer; + uint32 m_uiSummonTimer; + uint32 m_uiBerserkTimer; + uint8 m_uiNecroticAuraCount; // Used for emotes, 5min check + + void Reset() override + { + m_uiDeathbloomTimer = 5000; + m_uiNecroticAuraTimer = 12000; + m_uiInevitableDoomTimer = MINUTE * 2 * IN_MILLISECONDS; + m_uiSummonTimer = urand(10000, 15000); // first seen in vid after approx 12s + m_uiBerserkTimer = MINUTE * 12 * IN_MILLISECONDS; // only in heroic, after 12min + m_uiNecroticAuraCount = 0; + } + + void Aggro(Unit* /*pWho*/) override + { + if (m_pInstance) + m_pInstance->SetData(TYPE_LOATHEB, IN_PROGRESS); + } + + void JustDied(Unit* /*pKiller*/) override + { + if (m_pInstance) + m_pInstance->SetData(TYPE_LOATHEB, DONE); + } + + void JustReachedHome() override + { + if (m_pInstance) + m_pInstance->SetData(TYPE_LOATHEB, NOT_STARTED); + } + + void JustSummoned(Creature* pSummoned) override + { + if (pSummoned->GetEntry() != NPC_SPORE) + return; + + if (Unit* pTarget = m_creature->SelectAttackingTarget(ATTACKING_TARGET_RANDOM, 0)) + pSummoned->AddThreat(pTarget); + } + + void SummonedCreatureJustDied(Creature* pSummoned) override + { + if (pSummoned->GetEntry() == NPC_SPORE && m_pInstance) + m_pInstance->SetSpecialAchievementCriteria(TYPE_ACHIEV_SPORE_LOSER, false); + } + + void UpdateAI(const uint32 uiDiff) override + { + if (!m_creature->SelectHostileTarget() || !m_creature->getVictim()) + return; + + // Berserk (only heroic) + if (!m_bIsRegularMode) + { + if (m_uiBerserkTimer < uiDiff) + { + DoCastSpellIfCan(m_creature, SPELL_BERSERK); + m_uiBerserkTimer = 300000; + } + else + m_uiBerserkTimer -= uiDiff; + } + + // Inevitable Doom + if (m_uiInevitableDoomTimer < uiDiff) + { + DoCastSpellIfCan(m_creature, m_bIsRegularMode ? SPELL_INEVITABLE_DOOM : SPELL_INEVITABLE_DOOM_H); + m_uiInevitableDoomTimer = (m_uiNecroticAuraCount <= 40) ? 30000 : 15000; + } + else + m_uiInevitableDoomTimer -= uiDiff; + + // Necrotic Aura + if (m_uiNecroticAuraTimer < uiDiff) + { + switch (m_uiNecroticAuraCount % 3) + { + case 0: + DoCastSpellIfCan(m_creature, SPELL_NECROTIC_AURA); + DoScriptText(EMOTE_AURA_BLOCKING, m_creature); + m_uiNecroticAuraTimer = 14000; + break; + case 1: + DoScriptText(EMOTE_AURA_WANE, m_creature); + m_uiNecroticAuraTimer = 3000; + break; + case 2: + DoScriptText(EMOTE_AURA_FADING, m_creature); + m_uiNecroticAuraTimer = 3000; + break; + } + ++m_uiNecroticAuraCount; + } + else + m_uiNecroticAuraTimer -= uiDiff; + + // Summon + if (m_uiSummonTimer < uiDiff) + { + DoCastSpellIfCan(m_creature, SPELL_SUMMON_SPORE); + m_uiSummonTimer = m_bIsRegularMode ? 36000 : 18000; + } + else + m_uiSummonTimer -= uiDiff; + + // Deathbloom + if (m_uiDeathbloomTimer < uiDiff) + { + DoCastSpellIfCan(m_creature, m_bIsRegularMode ? SPELL_DEATHBLOOM : SPELL_DEATHBLOOM_H); + m_uiDeathbloomTimer = 30000; + } + else + m_uiDeathbloomTimer -= uiDiff; + + DoMeleeAttackIfReady(); + } +}; + +CreatureAI* GetAI_boss_loatheb(Creature* pCreature) +{ + return new boss_loathebAI(pCreature); +} + +void AddSC_boss_loatheb() +{ + Script* pNewScript; + + pNewScript = new Script; + pNewScript->Name = "boss_loatheb"; + pNewScript->GetAI = &GetAI_boss_loatheb; + pNewScript->RegisterSelf(); +} diff --git a/src/modules/SD2/scripts/northrend/naxxramas/boss_maexxna.cpp b/src/modules/SD2/scripts/northrend/naxxramas/boss_maexxna.cpp new file mode 100644 index 000000000..309c0d637 --- /dev/null +++ b/src/modules/SD2/scripts/northrend/naxxramas/boss_maexxna.cpp @@ -0,0 +1,326 @@ +/* Copyright (C) 2006 - 2013 ScriptDev2 + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +/* ScriptData +SDName: Boss_Maexxna +SD%Complete: 90 +SDComment: Web wrap effect still needs more love and research. +SDCategory: Naxxramas +EndScriptData */ + +#include "precompiled.h" +#include "naxxramas.h" + +enum +{ + EMOTE_SPIN_WEB = -1533146, + EMOTE_SPIDERLING = -1533147, + EMOTE_SPRAY = -1533148, + EMOTE_BOSS_GENERIC_FRENZY = -1000005, + + SPELL_WEBWRAP = 28622, + SPELL_WEBWRAP_2 = 28673, // purpose unknown + + SPELL_WEBSPRAY = 29484, + SPELL_WEBSPRAY_H = 54125, + + SPELL_POISONSHOCK = 28741, + SPELL_POISONSHOCK_H = 54122, + + SPELL_NECROTICPOISON = 28776, + SPELL_NECROTICPOISON_H = 54121, + + SPELL_FRENZY = 54123, + SPELL_FRENZY_H = 54124, + + // SPELL_SUMMON_SPIDERLING_1 = 29434, // removed from dbc. Summons 10 spiderlings + // SPELL_SUMMON_SPIDERLING_2 = 30076, // removed from dbc. Summons 3 spiderlings + // SPELL_SUMMON_WEB_WRAP = 28627, // removed from dbc. Summons one web wrap and transforms it into creature 17286 + + NPC_WEB_WRAP = 16486, + NPC_SPIDERLING = 17055, + + MAX_SPIDERLINGS = 8, + MAX_WEB_WRAP_POSITIONS = 3, +}; + +static const float aWebWrapLoc[MAX_WEB_WRAP_POSITIONS][3] = +{ + {3546.796f, -3869.082f, 296.450f}, + {3531.271f, -3847.424f, 299.450f}, + {3497.067f, -3843.384f, 302.384f} +}; + +struct npc_web_wrapAI : public ScriptedAI +{ + npc_web_wrapAI(Creature* pCreature) : ScriptedAI(pCreature) { Reset(); } + + ObjectGuid m_victimGuid; + uint32 m_uiWebWrapTimer; + + void Reset() override + { + m_uiWebWrapTimer = 0; + } + + void MoveInLineOfSight(Unit* /*pWho*/) override {} + void AttackStart(Unit* /*pWho*/) override {} + + void SetVictim(Unit* pVictim) + { + if (pVictim) + { + // Vanilla spell 28618, 28619, 28620, 28621 had effect SPELL_EFFECT_PLAYER_PULL with EffectMiscValue = 200, 300, 400 and 500 + // All these spells trigger 28622 after 1 or 2 seconds + // the EffectMiscValue may have been based on the distance between the victim and the target + + // NOTE: This implementation may not be 100% correct, but it gets very close to the expected result + + float fDist = m_creature->GetDistance2d(pVictim); + // Switch the speed multiplier based on the distance from the web wrap + uint32 uiEffectMiscValue = 500; + if (fDist < 25.0f) + uiEffectMiscValue = 200; + else if (fDist < 50.0f) + uiEffectMiscValue = 300; + else if (fDist < 75.0f) + uiEffectMiscValue = 400; + + // Note: normally we should use the Knockback effect to handle this, but because this doesn't behave as expected we'll just use Jump Movement + // pVictim->KnockBackFrom(m_creature, -fDist, uiEffectMiscValue * 0.1f); + + float fSpeed = fDist * (uiEffectMiscValue * 0.01f); + pVictim->GetMotionMaster()->MoveJump(m_creature->GetPositionX(), m_creature->GetPositionY(), m_creature->GetPositionZ(), fSpeed, 0.0f); + + m_victimGuid = pVictim->GetObjectGuid(); + m_uiWebWrapTimer = uiEffectMiscValue == 200 ? 1000 : 2000; + } + } + + void JustDied(Unit* /*pKiller*/) override + { + if (m_victimGuid) + { + if (Player* pVictim = m_creature->GetMap()->GetPlayer(m_victimGuid)) + { + if (pVictim->IsAlive()) + pVictim->RemoveAurasDueToSpell(SPELL_WEBWRAP); + } + } + } + + void UpdateAI(const uint32 uiDiff) override + { + if (m_uiWebWrapTimer) + { + // Finally the player gets web wrapped and he should change the display id until the creature is killed + if (m_uiWebWrapTimer <= uiDiff) + { + if (Player* pVictim = m_creature->GetMap()->GetPlayer(m_victimGuid)) + pVictim->CastSpell(pVictim, SPELL_WEBWRAP, true, NULL, NULL, m_creature->GetObjectGuid()); + + m_uiWebWrapTimer = 0; + } + else + m_uiWebWrapTimer -= uiDiff; + } + } +}; + +struct boss_maexxnaAI : public ScriptedAI +{ + boss_maexxnaAI(Creature* pCreature) : ScriptedAI(pCreature) + { + m_pInstance = (instance_naxxramas*)pCreature->GetInstanceData(); + m_bIsRegularMode = pCreature->GetMap()->IsRegularDifficulty(); + Reset(); + } + + instance_naxxramas* m_pInstance; + bool m_bIsRegularMode; + + uint32 m_uiWebWrapTimer; + uint32 m_uiWebSprayTimer; + uint32 m_uiPoisonShockTimer; + uint32 m_uiNecroticPoisonTimer; + uint32 m_uiSummonSpiderlingTimer; + bool m_bEnraged; + + void Reset() override + { + m_uiWebWrapTimer = 15000; + m_uiWebSprayTimer = 40000; + m_uiPoisonShockTimer = urand(10000, 20000); + m_uiNecroticPoisonTimer = urand(20000, 30000); + m_uiSummonSpiderlingTimer = 30000; + m_bEnraged = false; + } + + void Aggro(Unit* /*pWho*/) override + { + if (m_pInstance) + m_pInstance->SetData(TYPE_MAEXXNA, IN_PROGRESS); + } + + void JustDied(Unit* /*pKiller*/) override + { + if (m_pInstance) + m_pInstance->SetData(TYPE_MAEXXNA, DONE); + } + + void JustReachedHome() override + { + if (m_pInstance) + m_pInstance->SetData(TYPE_MAEXXNA, FAIL); + } + + void JustSummoned(Creature* pSummoned) override + { + if (pSummoned->GetEntry() == NPC_WEB_WRAP) + { + if (Unit* pTarget = m_creature->SelectAttackingTarget(ATTACKING_TARGET_RANDOM, 1, SPELL_WEBWRAP, SELECT_FLAG_PLAYER)) + { + if (npc_web_wrapAI* pWebAI = dynamic_cast(pSummoned->AI())) + pWebAI->SetVictim(pTarget); + } + } + else if (pSummoned->GetEntry() == NPC_SPIDERLING) + { + if (Unit* pTarget = m_creature->SelectAttackingTarget(ATTACKING_TARGET_RANDOM, 0)) + pSummoned->AI()->AttackStart(pTarget); + } + } + + bool DoCastWebWrap() + { + // If we can't select a player for web wrap then skip the summoning + if (!m_creature->SelectAttackingTarget(ATTACKING_TARGET_RANDOM, 1, uint32(0), SELECT_FLAG_PLAYER)) + return false; + + uint8 uiPos1 = urand(0, MAX_WEB_WRAP_POSITIONS - 1); + m_creature->SummonCreature(NPC_WEB_WRAP, aWebWrapLoc[uiPos1][0], aWebWrapLoc[uiPos1][1], aWebWrapLoc[uiPos1][2], 0, TEMPSUMMON_TIMED_OOC_OR_DEAD_DESPAWN, 60000); + + // Summon a second web wrap on heroic + if (!m_bIsRegularMode) + { + uint8 uiPos2 = (uiPos1 + urand(1, MAX_WEB_WRAP_POSITIONS - 1)) % MAX_WEB_WRAP_POSITIONS; + m_creature->SummonCreature(NPC_WEB_WRAP, aWebWrapLoc[uiPos2][0], aWebWrapLoc[uiPos2][1], aWebWrapLoc[uiPos2][2], 0, TEMPSUMMON_TIMED_OOC_OR_DEAD_DESPAWN, 60000); + } + + return true; + } + + // Summons spiderlings around the boss + void SummonSpiderlings() + { + for (uint8 i = 0; i < MAX_SPIDERLINGS; ++i) + m_creature->SummonCreature(NPC_SPIDERLING, 0.0f, 0.0f, 0.0f, 0.0f, TEMPSUMMON_TIMED_OOC_OR_DEAD_DESPAWN, 60000); + } + + void UpdateAI(const uint32 uiDiff) override + { + if (!m_creature->SelectHostileTarget() || !m_creature->getVictim()) + return; + + // Web Wrap + if (m_uiWebWrapTimer < uiDiff) + { + if (DoCastWebWrap()) + DoScriptText(EMOTE_SPIN_WEB, m_creature); + + m_uiWebWrapTimer = 40000; + } + else + m_uiWebWrapTimer -= uiDiff; + + // Web Spray + if (m_uiWebSprayTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature, m_bIsRegularMode ? SPELL_WEBSPRAY : SPELL_WEBSPRAY_H) == CAST_OK) + { + DoScriptText(EMOTE_SPRAY, m_creature); + m_uiWebSprayTimer = 40000; + } + } + else + m_uiWebSprayTimer -= uiDiff; + + // Poison Shock + if (m_uiPoisonShockTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature, m_bIsRegularMode ? SPELL_POISONSHOCK : SPELL_POISONSHOCK_H) == CAST_OK) + m_uiPoisonShockTimer = urand(10000, 20000); + } + else + m_uiPoisonShockTimer -= uiDiff; + + // Necrotic Poison + if (m_uiNecroticPoisonTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature->getVictim(), m_bIsRegularMode ? SPELL_NECROTICPOISON : SPELL_NECROTICPOISON_H) == CAST_OK) + m_uiNecroticPoisonTimer = urand(20000, 30000); + } + else + m_uiNecroticPoisonTimer -= uiDiff; + + // Summon Spiderling + if (m_uiSummonSpiderlingTimer < uiDiff) + { + SummonSpiderlings(); + DoScriptText(EMOTE_SPIDERLING, m_creature); + m_uiSummonSpiderlingTimer = 30000; + } + else + m_uiSummonSpiderlingTimer -= uiDiff; + + // Enrage if not already enraged and below 30% + if (!m_bEnraged && m_creature->GetHealthPercent() < 30.0f) + { + if (DoCastSpellIfCan(m_creature, m_bIsRegularMode ? SPELL_FRENZY : SPELL_FRENZY_H) == CAST_OK) + { + DoScriptText(EMOTE_BOSS_GENERIC_FRENZY, m_creature); + m_bEnraged = true; + } + } + + DoMeleeAttackIfReady(); + } +}; + +CreatureAI* GetAI_npc_web_wrap(Creature* pCreature) +{ + return new npc_web_wrapAI(pCreature); +} + +CreatureAI* GetAI_boss_maexxna(Creature* pCreature) +{ + return new boss_maexxnaAI(pCreature); +} + +void AddSC_boss_maexxna() +{ + Script* pNewScript; + + pNewScript = new Script; + pNewScript->Name = "boss_maexxna"; + pNewScript->GetAI = &GetAI_boss_maexxna; + pNewScript->RegisterSelf(); + + pNewScript = new Script; + pNewScript->Name = "npc_web_wrap"; + pNewScript->GetAI = &GetAI_npc_web_wrap; + pNewScript->RegisterSelf(); +} diff --git a/src/modules/SD2/scripts/northrend/naxxramas/boss_noth.cpp b/src/modules/SD2/scripts/northrend/naxxramas/boss_noth.cpp new file mode 100644 index 000000000..2e1536b58 --- /dev/null +++ b/src/modules/SD2/scripts/northrend/naxxramas/boss_noth.cpp @@ -0,0 +1,341 @@ +/* Copyright (C) 2006 - 2013 ScriptDev2 + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +/* ScriptData +SDName: Boss_Noth +SD%Complete: 80 +SDComment: Summons need verify, need better phase-switch support (unattackable?) +SDCategory: Naxxramas +EndScriptData */ + +#include "precompiled.h" +#include "naxxramas.h" + +enum +{ + SAY_AGGRO1 = -1533075, + SAY_AGGRO2 = -1533076, + SAY_AGGRO3 = -1533077, + SAY_SUMMON = -1533078, + SAY_SLAY1 = -1533079, + SAY_SLAY2 = -1533080, + SAY_DEATH = -1533081, + + EMOTE_WARRIOR = -1533130, + EMOTE_SKELETON = -1533131, + EMOTE_TELEPORT = -1533132, + EMOTE_TELEPORT_RETURN = -1533133, + + SPELL_TELEPORT = 29216, + SPELL_TELEPORT_RETURN = 29231, + + SPELL_BLINK_1 = 29208, + SPELL_BLINK_2 = 29209, + SPELL_BLINK_3 = 29210, + SPELL_BLINK_4 = 29211, + + SPELL_CRIPPLE = 29212, + SPELL_CRIPPLE_H = 54814, + SPELL_CURSE_PLAGUEBRINGER = 29213, + SPELL_CURSE_PLAGUEBRINGER_H = 54835, + + SPELL_BERSERK = 26662, // guesswork, but very common berserk spell in naxx + + SPELL_SUMMON_WARRIOR_1 = 29247, + SPELL_SUMMON_WARRIOR_2 = 29248, + SPELL_SUMMON_WARRIOR_3 = 29249, + + SPELL_SUMMON_WARRIOR_THREE = 29237, + + SPELL_SUMMON_CHAMP01 = 29217, + SPELL_SUMMON_CHAMP02 = 29224, + SPELL_SUMMON_CHAMP03 = 29225, + SPELL_SUMMON_CHAMP04 = 29227, + SPELL_SUMMON_CHAMP05 = 29238, + SPELL_SUMMON_CHAMP06 = 29255, + SPELL_SUMMON_CHAMP07 = 29257, + SPELL_SUMMON_CHAMP08 = 29258, + SPELL_SUMMON_CHAMP09 = 29262, + SPELL_SUMMON_CHAMP10 = 29267, + + SPELL_SUMMON_GUARD01 = 29226, + SPELL_SUMMON_GUARD02 = 29239, + SPELL_SUMMON_GUARD03 = 29256, + SPELL_SUMMON_GUARD04 = 29268, + + PHASE_GROUND = 0, + PHASE_BALCONY = 1, + + PHASE_SKELETON_1 = 1, + PHASE_SKELETON_2 = 2, + PHASE_SKELETON_3 = 3 +}; + +struct boss_nothAI : public ScriptedAI +{ + boss_nothAI(Creature* pCreature) : ScriptedAI(pCreature) + { + m_pInstance = (instance_naxxramas*)pCreature->GetInstanceData(); + m_bIsRegularMode = pCreature->GetMap()->IsRegularDifficulty(); + Reset(); + } + + instance_naxxramas* m_pInstance; + bool m_bIsRegularMode; + + uint8 m_uiPhase; + uint8 m_uiPhaseSub; + uint32 m_uiPhaseTimer; + + uint32 m_uiBlinkTimer; + uint32 m_uiCurseTimer; + uint32 m_uiSummonTimer; + + void Reset() override + { + m_uiPhase = PHASE_GROUND; + m_uiPhaseSub = PHASE_GROUND; + m_uiPhaseTimer = 90000; + + m_uiBlinkTimer = 25000; + m_uiCurseTimer = 4000; + m_uiSummonTimer = 12000; + } + + void Aggro(Unit* /*pWho*/) override + { + switch (urand(0, 2)) + { + case 0: DoScriptText(SAY_AGGRO1, m_creature); break; + case 1: DoScriptText(SAY_AGGRO2, m_creature); break; + case 2: DoScriptText(SAY_AGGRO3, m_creature); break; + } + + if (m_pInstance) + m_pInstance->SetData(TYPE_NOTH, IN_PROGRESS); + } + + void JustSummoned(Creature* pSummoned) override + { + pSummoned->SetInCombatWithZone(); + } + + void KilledUnit(Unit* /*pVictim*/) override + { + DoScriptText(urand(0, 1) ? SAY_SLAY1 : SAY_SLAY2, m_creature); + } + + void JustDied(Unit* /*pKiller*/) override + { + DoScriptText(SAY_DEATH, m_creature); + + if (m_pInstance) + m_pInstance->SetData(TYPE_NOTH, DONE); + } + + void JustReachedHome() override + { + if (m_pInstance) + m_pInstance->SetData(TYPE_NOTH, FAIL); + } + + void SpellHit(Unit* pCaster, const SpellEntry* pSpell) override + { + if (pCaster != m_creature) + return; + + if (SpellEffectEntry const* pSpellEffect = pSpell->GetSpellEffect(EFFECT_INDEX_0)) + { + if (pSpellEffect->Effect == SPELL_EFFECT_LEAP) + DoCastSpellIfCan(m_creature, m_bIsRegularMode ? SPELL_CRIPPLE : SPELL_CRIPPLE_H); + } + } + + void UpdateAI(const uint32 uiDiff) override + { + if (!m_creature->SelectHostileTarget() || !m_creature->getVictim()) + return; + + if (m_uiPhase == PHASE_GROUND) + { + if (m_uiPhaseTimer) // After PHASE_SKELETON_3 we won't have a balcony phase + { + if (m_uiPhaseTimer <= uiDiff) + { + if (DoCastSpellIfCan(m_creature, SPELL_TELEPORT) == CAST_OK) + { + DoScriptText(EMOTE_TELEPORT, m_creature); + m_creature->GetMotionMaster()->MoveIdle(); + m_uiPhase = PHASE_BALCONY; + ++m_uiPhaseSub; + + switch (m_uiPhaseSub) // Set Duration of Skeleton phase + { + case PHASE_SKELETON_1: m_uiPhaseTimer = 70000; break; + case PHASE_SKELETON_2: m_uiPhaseTimer = 97000; break; + case PHASE_SKELETON_3: m_uiPhaseTimer = 120000; break; + } + return; + } + } + else + m_uiPhaseTimer -= uiDiff; + } + + if (!m_bIsRegularMode) // Blink is used only in 25man + { + if (m_uiBlinkTimer < uiDiff) + { + static uint32 const auiSpellBlink[4] = + { + SPELL_BLINK_1, SPELL_BLINK_2, SPELL_BLINK_3, SPELL_BLINK_4 + }; + + if (DoCastSpellIfCan(m_creature, auiSpellBlink[urand(0, 3)]) == CAST_OK) + { + DoResetThreat(); + m_uiBlinkTimer = 25000; + } + } + else + m_uiBlinkTimer -= uiDiff; + } + + if (m_uiCurseTimer < uiDiff) + { + DoCastSpellIfCan(m_creature, m_bIsRegularMode ? SPELL_CURSE_PLAGUEBRINGER : SPELL_CURSE_PLAGUEBRINGER_H); + m_uiCurseTimer = 28000; + } + else + m_uiCurseTimer -= uiDiff; + + if (m_uiSummonTimer < uiDiff) + { + DoScriptText(SAY_SUMMON, m_creature); + DoScriptText(EMOTE_WARRIOR, m_creature); + + if (m_bIsRegularMode) + { + static uint32 const auiSpellSummonPlaguedWarrior[3] = + { + SPELL_SUMMON_WARRIOR_1, SPELL_SUMMON_WARRIOR_2, SPELL_SUMMON_WARRIOR_3 + }; + + for (uint8 i = 0; i < 2; ++i) + DoCastSpellIfCan(m_creature, auiSpellSummonPlaguedWarrior[urand(0, 2)], CAST_TRIGGERED); + } + else + { + DoCastSpellIfCan(m_creature, SPELL_SUMMON_WARRIOR_THREE, CAST_TRIGGERED); + } + + m_uiSummonTimer = 30000; + } + else + m_uiSummonTimer -= uiDiff; + + DoMeleeAttackIfReady(); + } + else // PHASE_BALCONY + { + if (m_uiPhaseTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature, SPELL_TELEPORT_RETURN) == CAST_OK) + { + DoScriptText(EMOTE_TELEPORT_RETURN, m_creature); + m_creature->GetMotionMaster()->MoveChase(m_creature->getVictim()); + switch (m_uiPhaseSub) + { + case PHASE_SKELETON_1: m_uiPhaseTimer = 110000; break; + case PHASE_SKELETON_2: m_uiPhaseTimer = 180000; break; + case PHASE_SKELETON_3: + m_uiPhaseTimer = 0; + // Go Berserk after third Balcony Phase + DoCastSpellIfCan(m_creature, SPELL_BERSERK, CAST_TRIGGERED); + break; + } + m_uiPhase = PHASE_GROUND; + + return; + } + } + else + m_uiPhaseTimer -= uiDiff; + + if (m_uiSummonTimer < uiDiff) + { + DoScriptText(EMOTE_SKELETON, m_creature); + + static uint32 const auiSpellSummonPlaguedChampion[10] = + { + SPELL_SUMMON_CHAMP01, SPELL_SUMMON_CHAMP02, SPELL_SUMMON_CHAMP03, SPELL_SUMMON_CHAMP04, SPELL_SUMMON_CHAMP05, SPELL_SUMMON_CHAMP06, SPELL_SUMMON_CHAMP07, SPELL_SUMMON_CHAMP08, SPELL_SUMMON_CHAMP09, SPELL_SUMMON_CHAMP10 + }; + + static uint32 const auiSpellSummonPlaguedGuardian[4] = + { + SPELL_SUMMON_GUARD01, SPELL_SUMMON_GUARD02, SPELL_SUMMON_GUARD03, SPELL_SUMMON_GUARD04 + }; + + // A bit unclear how many in each sub phase + switch (m_uiPhaseSub) + { + case PHASE_SKELETON_1: + { + for (uint8 i = 0; i < (m_bIsRegularMode ? 2 : 4); ++i) + DoCastSpellIfCan(m_creature, auiSpellSummonPlaguedChampion[urand(0, 9)], CAST_TRIGGERED); + + break; + } + case PHASE_SKELETON_2: + { + for (uint8 i = 0; i < (m_bIsRegularMode ? 1 : 2); ++i) + { + DoCastSpellIfCan(m_creature, auiSpellSummonPlaguedChampion[urand(0, 9)], CAST_TRIGGERED); + DoCastSpellIfCan(m_creature, auiSpellSummonPlaguedGuardian[urand(0, 3)], CAST_TRIGGERED); + } + break; + } + case PHASE_SKELETON_3: + { + for (uint8 i = 0; i < (m_bIsRegularMode ? 2 : 4); ++i) + DoCastSpellIfCan(m_creature, auiSpellSummonPlaguedGuardian[urand(0, 3)], CAST_TRIGGERED); + + break; + } + } + + m_uiSummonTimer = 30000; + } + else + m_uiSummonTimer -= uiDiff; + } + } +}; + +CreatureAI* GetAI_boss_noth(Creature* pCreature) +{ + return new boss_nothAI(pCreature); +} + +void AddSC_boss_noth() +{ + Script* pNewScript; + + pNewScript = new Script; + pNewScript->Name = "boss_noth"; + pNewScript->GetAI = &GetAI_boss_noth; + pNewScript->RegisterSelf(); +} diff --git a/src/modules/SD2/scripts/northrend/naxxramas/boss_patchwerk.cpp b/src/modules/SD2/scripts/northrend/naxxramas/boss_patchwerk.cpp new file mode 100644 index 000000000..75e0e9073 --- /dev/null +++ b/src/modules/SD2/scripts/northrend/naxxramas/boss_patchwerk.cpp @@ -0,0 +1,209 @@ +/* Copyright (C) 2006 - 2013 ScriptDev2 + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +/* ScriptData +SDName: Boss_Patchwerk +SD%Complete: 100 +SDComment: +SDCategory: Naxxramas +EndScriptData */ + +#include "precompiled.h" +#include "naxxramas.h" + +enum +{ + SAY_AGGRO1 = -1533017, + SAY_AGGRO2 = -1533018, + SAY_SLAY = -1533019, + SAY_DEATH = -1533020, + + EMOTE_GENERIC_BERSERK = -1000004, + EMOTE_GENERIC_ENRAGED = -1000003, + + SPELL_HATEFULSTRIKE = 28308, + SPELL_HATEFULSTRIKE_H = 59192, + SPELL_ENRAGE = 28131, + SPELL_BERSERK = 26662, + SPELL_SLIMEBOLT = 32309 +}; + +struct boss_patchwerkAI : public ScriptedAI +{ + boss_patchwerkAI(Creature* pCreature) : ScriptedAI(pCreature) + { + m_pInstance = (instance_naxxramas*)pCreature->GetInstanceData(); + m_bIsRegularMode = pCreature->GetMap()->IsRegularDifficulty(); + Reset(); + } + + instance_naxxramas* m_pInstance; + bool m_bIsRegularMode; + + uint32 m_uiHatefulStrikeTimer; + uint32 m_uiBerserkTimer; + uint32 m_uiSlimeboltTimer; + bool m_bEnraged; + bool m_bBerserk; + + void Reset() override + { + m_uiHatefulStrikeTimer = 1000; // 1 second + m_uiBerserkTimer = MINUTE * 6 * IN_MILLISECONDS; // 6 minutes + m_uiSlimeboltTimer = 10000; + m_bEnraged = false; + m_bBerserk = false; + } + + void KilledUnit(Unit* /*pVictim*/) override + { + if (urand(0, 4)) + return; + + DoScriptText(SAY_SLAY, m_creature); + } + + void JustDied(Unit* /*pKiller*/) override + { + DoScriptText(SAY_DEATH, m_creature); + + if (m_pInstance) + m_pInstance->SetData(TYPE_PATCHWERK, DONE); + } + + void Aggro(Unit* /*pWho*/) override + { + DoScriptText(urand(0, 1) ? SAY_AGGRO1 : SAY_AGGRO2, m_creature); + + if (m_pInstance) + m_pInstance->SetData(TYPE_PATCHWERK, IN_PROGRESS); + } + + void JustReachedHome() override + { + if (m_pInstance) + m_pInstance->SetData(TYPE_PATCHWERK, FAIL); + } + + void DoHatefulStrike() + { + // The ability is used on highest HP target choosen of the top 2 (3 heroic) targets on threat list being in melee range + Unit* pTarget = NULL; + uint32 uiHighestHP = 0; + uint32 uiTargets = m_bIsRegularMode ? 1 : 2; + + ThreatList const& tList = m_creature->GetThreatManager().getThreatList(); + if (tList.size() > 1) // Check if more than two targets, and start loop with second-most aggro + { + ThreatList::const_iterator iter = tList.begin(); + std::advance(iter, 1); + for (; iter != tList.end(); ++iter) + { + if (!uiTargets) + break; + + if (Unit* pTempTarget = m_creature->GetMap()->GetUnit((*iter)->getUnitGuid())) + { + if (m_creature->CanReachWithMeleeAttack(pTempTarget)) + { + if (pTempTarget->GetHealth() > uiHighestHP) + { + uiHighestHP = pTempTarget->GetHealth(); + pTarget = pTempTarget; + } + --uiTargets; + } + } + } + } + + if (!pTarget) + pTarget = m_creature->getVictim(); + + DoCastSpellIfCan(pTarget, m_bIsRegularMode ? SPELL_HATEFULSTRIKE : SPELL_HATEFULSTRIKE_H); + } + + void UpdateAI(const uint32 uiDiff) override + { + if (!m_creature->SelectHostileTarget() || !m_creature->getVictim()) + return; + + // Hateful Strike + if (m_uiHatefulStrikeTimer < uiDiff) + { + DoHatefulStrike(); + m_uiHatefulStrikeTimer = 1000; + } + else + m_uiHatefulStrikeTimer -= uiDiff; + + // Soft Enrage at 5% + if (!m_bEnraged) + { + if (m_creature->GetHealthPercent() < 5.0f) + { + if (DoCastSpellIfCan(m_creature, SPELL_ENRAGE) == CAST_OK) + { + DoScriptText(EMOTE_GENERIC_ENRAGED, m_creature); + m_bEnraged = true; + } + } + } + + // Berserk after 6 minutes + if (!m_bBerserk) + { + if (m_uiBerserkTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature, SPELL_BERSERK) == CAST_OK) + { + DoScriptText(EMOTE_GENERIC_BERSERK, m_creature); + m_bBerserk = true; + } + } + else + m_uiBerserkTimer -= uiDiff; + } + else + { + // Slimebolt - casted only while Berserking to prevent kiting + if (m_uiSlimeboltTimer < uiDiff) + { + DoCastSpellIfCan(m_creature->getVictim(), SPELL_SLIMEBOLT); + m_uiSlimeboltTimer = 5000; + } + else + m_uiSlimeboltTimer -= uiDiff; + } + + DoMeleeAttackIfReady(); + } +}; + +CreatureAI* GetAI_boss_patchwerk(Creature* pCreature) +{ + return new boss_patchwerkAI(pCreature); +} + +void AddSC_boss_patchwerk() +{ + Script* pNewScript; + + pNewScript = new Script; + pNewScript->Name = "boss_patchwerk"; + pNewScript->GetAI = &GetAI_boss_patchwerk; + pNewScript->RegisterSelf(); +} diff --git a/src/modules/SD2/scripts/northrend/naxxramas/boss_razuvious.cpp b/src/modules/SD2/scripts/northrend/naxxramas/boss_razuvious.cpp new file mode 100644 index 000000000..c1f4319df --- /dev/null +++ b/src/modules/SD2/scripts/northrend/naxxramas/boss_razuvious.cpp @@ -0,0 +1,181 @@ +/* Copyright (C) 2006 - 2013 ScriptDev2 + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +/* ScriptData +SDName: Boss_Razuvious +SD%Complete: 85% +SDComment: TODO: Timers and sounds need confirmation - orb handling for normal-mode is missing +SDCategory: Naxxramas +EndScriptData */ + +#include "precompiled.h" +#include "naxxramas.h" + +enum +{ + SAY_AGGRO1 = -1533120, + SAY_AGGRO2 = -1533121, + SAY_AGGRO3 = -1533122, + SAY_SLAY1 = -1533123, + SAY_SLAY2 = -1533124, + SAY_COMMAND1 = -1533125, + SAY_COMMAND2 = -1533126, + SAY_COMMAND3 = -1533127, + SAY_COMMAND4 = -1533128, + SAY_DEATH = -1533129, + + SPELL_UNBALANCING_STRIKE = 55470, + SPELL_DISRUPTING_SHOUT = 55543, + SPELL_DISRUPTING_SHOUT_H = 29107, + SPELL_JAGGED_KNIFE = 55550, + SPELL_HOPELESS = 29125 +}; + +struct boss_razuviousAI : public ScriptedAI +{ + boss_razuviousAI(Creature* pCreature) : ScriptedAI(pCreature) + { + m_pInstance = (instance_naxxramas*)pCreature->GetInstanceData(); + m_bIsRegularMode = pCreature->GetMap()->IsRegularDifficulty(); + Reset(); + } + + instance_naxxramas* m_pInstance; + bool m_bIsRegularMode; + + uint32 m_uiUnbalancingStrikeTimer; + uint32 m_uiDisruptingShoutTimer; + uint32 m_uiJaggedKnifeTimer; + uint32 m_uiCommandSoundTimer; + + void Reset() override + { + m_uiUnbalancingStrikeTimer = 30000; // 30 seconds + m_uiDisruptingShoutTimer = 15000; // 15 seconds + m_uiJaggedKnifeTimer = urand(10000, 15000); + m_uiCommandSoundTimer = 40000; // 40 seconds + } + + void KilledUnit(Unit* /*Victim*/) override + { + if (urand(0, 3)) + return; + + switch (urand(0, 1)) + { + case 0: DoScriptText(SAY_SLAY1, m_creature); break; + case 1: DoScriptText(SAY_SLAY2, m_creature); break; + } + } + + void JustDied(Unit* /*pKiller*/) override + { + DoScriptText(SAY_DEATH, m_creature); + + DoCastSpellIfCan(m_creature, SPELL_HOPELESS, CAST_TRIGGERED); + + if (m_pInstance) + m_pInstance->SetData(TYPE_RAZUVIOUS, DONE); + } + + void Aggro(Unit* /*pWho*/) override + { + switch (urand(0, 2)) + { + case 0: DoScriptText(SAY_AGGRO1, m_creature); break; + case 1: DoScriptText(SAY_AGGRO2, m_creature); break; + case 2: DoScriptText(SAY_AGGRO3, m_creature); break; + } + + if (m_pInstance) + m_pInstance->SetData(TYPE_RAZUVIOUS, IN_PROGRESS); + } + + void JustReachedHome() override + { + if (m_pInstance) + m_pInstance->SetData(TYPE_RAZUVIOUS, FAIL); + } + + void UpdateAI(const uint32 uiDiff) override + { + if (!m_creature->SelectHostileTarget() || !m_creature->getVictim()) + return; + + // Unbalancing Strike + if (m_uiUnbalancingStrikeTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature->getVictim(), SPELL_UNBALANCING_STRIKE) == CAST_OK) + m_uiUnbalancingStrikeTimer = 30000; + } + else + m_uiUnbalancingStrikeTimer -= uiDiff; + + // Disrupting Shout + if (m_uiDisruptingShoutTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature, m_bIsRegularMode ? SPELL_DISRUPTING_SHOUT : SPELL_DISRUPTING_SHOUT_H) == CAST_OK) + m_uiDisruptingShoutTimer = 25000; + } + else + m_uiDisruptingShoutTimer -= uiDiff; + + // Jagged Knife + if (m_uiJaggedKnifeTimer < uiDiff) + { + if (Unit* pTarget = m_creature->SelectAttackingTarget(ATTACKING_TARGET_RANDOM, 0)) + { + if (DoCastSpellIfCan(pTarget, SPELL_JAGGED_KNIFE) == CAST_OK) + m_uiJaggedKnifeTimer = 10000; + } + } + else + m_uiJaggedKnifeTimer -= uiDiff; + + // Random say + if (m_uiCommandSoundTimer < uiDiff) + { + switch (urand(0, 3)) + { + case 0: DoScriptText(SAY_COMMAND1, m_creature); break; + case 1: DoScriptText(SAY_COMMAND2, m_creature); break; + case 2: DoScriptText(SAY_COMMAND3, m_creature); break; + case 3: DoScriptText(SAY_COMMAND4, m_creature); break; + } + + m_uiCommandSoundTimer = 40000; + } + else + m_uiCommandSoundTimer -= uiDiff; + + DoMeleeAttackIfReady(); + } +}; + +CreatureAI* GetAI_boss_razuvious(Creature* pCreature) +{ + return new boss_razuviousAI(pCreature); +} + +void AddSC_boss_razuvious() +{ + Script* pNewScript; + + pNewScript = new Script; + pNewScript->Name = "boss_razuvious"; + pNewScript->GetAI = &GetAI_boss_razuvious; + pNewScript->RegisterSelf(); +} diff --git a/src/modules/SD2/scripts/northrend/naxxramas/boss_sapphiron.cpp b/src/modules/SD2/scripts/northrend/naxxramas/boss_sapphiron.cpp new file mode 100644 index 000000000..0535f5b57 --- /dev/null +++ b/src/modules/SD2/scripts/northrend/naxxramas/boss_sapphiron.cpp @@ -0,0 +1,357 @@ +/* Copyright (C) 2006 - 2013 ScriptDev2 + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +/* ScriptData +SDName: Boss_Sapphiron +SD%Complete: 80 +SDComment: Some spells need core implementation +SDCategory: Naxxramas +EndScriptData */ + +/* Additional comments: + * Bugged spells: 28560 (needs maxTarget = 1, Summon of 16474 implementation, TODO, 30s duration) + * 28526 (needs ScriptEffect to cast 28522 onto random target) + * + * Achievement-criteria check needs implementation + * + * Frost-Breath ability: the dummy spell 30101 is self cast, so it won't take the needed delay of ~7s until it reaches its target + * As Sapphiron is displayed visually in hight (hovering), and the spell is cast with target=self-location + * which is still on the ground, the client shows a nice slow falling of the visual animation + * Insisting on using the Dummy-Effect to cast the breath-dmg spells, would require, to relocate Sapphi internally, + * and to hack the targeting to be "on the ground" - Hence the prefered way as it is now! + */ + +#include "precompiled.h" +#include "naxxramas.h" + +enum +{ + EMOTE_BREATH = -1533082, + EMOTE_GENERIC_ENRAGED = -1000003, + EMOTE_FLY = -1533022, + EMOTE_GROUND = -1533083, + + SPELL_CLEAVE = 19983, + SPELL_TAIL_SWEEP = 55697, + SPELL_TAIL_SWEEP_H = 55696, + SPELL_ICEBOLT = 28526, + SPELL_FROST_BREATH_DUMMY = 30101, + SPELL_FROST_BREATH_A = 28524, + SPELL_FROST_BREATH_B = 29318, + SPELL_FROST_AURA = 28531, + SPELL_FROST_AURA_H = 55799, + SPELL_LIFE_DRAIN = 28542, + SPELL_LIFE_DRAIN_H = 55665, + SPELL_CHILL = 28547, + SPELL_CHILL_H = 55699, + SPELL_SUMMON_BLIZZARD = 28560, + SPELL_BESERK = 26662, + SPELL_ACHIEVEMENT_CHECK = 60539, // unused + + NPC_YOU_KNOW_WHO = 16474, +}; + +static const float aLiftOffPosition[3] = {3522.386f, -5236.784f, 137.709f}; + +enum Phases +{ + PHASE_GROUND = 1, + PHASE_LIFT_OFF = 2, + PHASE_AIR_BOLTS = 3, + PHASE_AIR_BREATH = 4, + PHASE_LANDING = 5, +}; + +struct boss_sapphironAI : public ScriptedAI +{ + boss_sapphironAI(Creature* pCreature) : ScriptedAI(pCreature) + { + m_pInstance = (instance_naxxramas*)pCreature->GetInstanceData(); + m_bIsRegularMode = pCreature->GetMap()->IsRegularDifficulty(); + Reset(); + } + + instance_naxxramas* m_pInstance; + bool m_bIsRegularMode; + + uint32 m_uiCleaveTimer; + uint32 m_uiTailSweepTimer; + uint32 m_uiIceboltTimer; + uint32 m_uiFrostBreathTimer; + uint32 m_uiLifeDrainTimer; + uint32 m_uiBlizzardTimer; + uint32 m_uiFlyTimer; + uint32 m_uiBerserkTimer; + uint32 m_uiLandTimer; + + uint32 m_uiIceboltCount; + Phases m_Phase; + + void Reset() override + { + m_uiCleaveTimer = 5000; + m_uiTailSweepTimer = 12000; + m_uiFrostBreathTimer = 7000; + m_uiLifeDrainTimer = 11000; + m_uiBlizzardTimer = 15000; // "Once the encounter starts,based on your version of Naxx, this will be used x2 for normal and x6 on HC" + m_uiFlyTimer = 46000; + m_uiIceboltTimer = 5000; + m_uiLandTimer = 0; + m_uiBerserkTimer = 15 * MINUTE * IN_MILLISECONDS; + m_Phase = PHASE_GROUND; + m_uiIceboltCount = 0; + + SetCombatMovement(true); + m_creature->SetLevitate(false); + // m_creature->ApplySpellMod(SPELL_FROST_AURA, SPELLMOD_DURATION, -1); + } + + void Aggro(Unit* /*pWho*/) override + { + DoCastSpellIfCan(m_creature, m_bIsRegularMode ? SPELL_FROST_AURA : SPELL_FROST_AURA_H); + + if (m_pInstance) + m_pInstance->SetData(TYPE_SAPPHIRON, IN_PROGRESS); + } + + void JustDied(Unit* /*pKiller*/) override + { + if (m_pInstance) + m_pInstance->SetData(TYPE_SAPPHIRON, DONE); + } + + void JustReachedHome() override + { + if (m_pInstance) + m_pInstance->SetData(TYPE_SAPPHIRON, FAIL); + } + + void JustSummoned(Creature* pSummoned) override + { + if (pSummoned->GetEntry() == NPC_YOU_KNOW_WHO) + { + if (Unit* pEnemy = m_creature->SelectAttackingTarget(ATTACKING_TARGET_RANDOM, 0)) + pSummoned->AI()->AttackStart(pEnemy); + } + } + + void MovementInform(uint32 uiType, uint32 /*uiPointId*/) override + { + if (uiType == POINT_MOTION_TYPE && m_Phase == PHASE_LIFT_OFF) + { + DoScriptText(EMOTE_FLY, m_creature); + m_creature->HandleEmote(EMOTE_ONESHOT_LIFTOFF); + m_creature->SetLevitate(true); + m_Phase = PHASE_AIR_BOLTS; + + m_uiFrostBreathTimer = 5000; + m_uiIceboltTimer = 5000; + m_uiIceboltCount = 0; + } + } + + void UpdateAI(const uint32 uiDiff) override + { + if (!m_creature->SelectHostileTarget() || !m_creature->getVictim()) + return; + + switch (m_Phase) + { + case PHASE_GROUND: + if (m_uiCleaveTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature->getVictim(), SPELL_CLEAVE) == CAST_OK) + m_uiCleaveTimer = urand(5000, 10000); + } + else + m_uiCleaveTimer -= uiDiff; + + if (m_uiTailSweepTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature, m_bIsRegularMode ? SPELL_TAIL_SWEEP : SPELL_TAIL_SWEEP_H) == CAST_OK) + m_uiTailSweepTimer = urand(7000, 10000); + } + else + m_uiTailSweepTimer -= uiDiff; + + if (m_uiLifeDrainTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature, m_bIsRegularMode ? SPELL_LIFE_DRAIN : SPELL_LIFE_DRAIN_H) == CAST_OK) + m_uiLifeDrainTimer = 23000; + } + else + m_uiLifeDrainTimer -= uiDiff; + + if (m_uiBlizzardTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature, SPELL_SUMMON_BLIZZARD) == CAST_OK) + m_uiBlizzardTimer = 20000; + } + else + m_uiBlizzardTimer -= uiDiff; + + if (m_creature->GetHealthPercent() > 10.0f) + { + if (m_uiFlyTimer < uiDiff) + { + m_Phase = PHASE_LIFT_OFF; + m_creature->InterruptNonMeleeSpells(false); + SetCombatMovement(false); + m_creature->GetMotionMaster()->Clear(false); + m_creature->GetMotionMaster()->MovePoint(1, aLiftOffPosition[0], aLiftOffPosition[1], aLiftOffPosition[2]); + // TODO This should clear the target, too + + return; + } + else + m_uiFlyTimer -= uiDiff; + } + + // Only Phase in which we have melee attack! + DoMeleeAttackIfReady(); + break; + case PHASE_LIFT_OFF: + break; + case PHASE_AIR_BOLTS: + // WOTLK Changed, originally was 5 + if (m_uiIceboltCount == (uint32)(m_bIsRegularMode ? 2 : 3)) + { + if (m_uiFrostBreathTimer < uiDiff) + { + m_Phase = PHASE_AIR_BREATH; + DoScriptText(EMOTE_BREATH, m_creature); + DoCastSpellIfCan(m_creature, SPELL_FROST_BREATH_DUMMY); + m_uiFrostBreathTimer = 7000; + } + else + m_uiFrostBreathTimer -= uiDiff; + } + else + { + if (m_uiIceboltTimer < uiDiff) + { + DoCastSpellIfCan(m_creature, SPELL_ICEBOLT); + + ++m_uiIceboltCount; + m_uiIceboltTimer = 4000; + } + else + m_uiIceboltTimer -= uiDiff; + } + + break; + case PHASE_AIR_BREATH: + if (m_uiFrostBreathTimer) + { + if (m_uiFrostBreathTimer <= uiDiff) + { + DoCastSpellIfCan(m_creature, SPELL_FROST_BREATH_A, CAST_TRIGGERED); + DoCastSpellIfCan(m_creature, SPELL_FROST_BREATH_B, CAST_TRIGGERED); + m_uiFrostBreathTimer = 0; + + m_uiLandTimer = 4000; + } + else + m_uiFrostBreathTimer -= uiDiff; + } + + if (m_uiLandTimer) + { + if (m_uiLandTimer <= uiDiff) + { + // Begin Landing + DoScriptText(EMOTE_GROUND, m_creature); + m_creature->HandleEmote(EMOTE_ONESHOT_LAND); + m_creature->SetLevitate(false); + + m_Phase = PHASE_LANDING; + m_uiLandTimer = 2000; + } + else + m_uiLandTimer -= uiDiff; + } + + break; + case PHASE_LANDING: + if (m_uiLandTimer < uiDiff) + { + m_Phase = PHASE_GROUND; + + SetCombatMovement(true); + m_creature->GetMotionMaster()->Clear(false); + m_creature->GetMotionMaster()->MoveChase(m_creature->getVictim()); + + m_uiFlyTimer = 67000; + m_uiLandTimer = 0; + } + else + m_uiLandTimer -= uiDiff; + + break; + } + + // Enrage can happen in any phase + if (m_uiBerserkTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature, SPELL_BESERK) == CAST_OK) + { + DoScriptText(EMOTE_GENERIC_ENRAGED, m_creature); + m_uiBerserkTimer = 300000; + } + } + else + m_uiBerserkTimer -= uiDiff; + } +}; + +CreatureAI* GetAI_boss_sapphiron(Creature* pCreature) +{ + return new boss_sapphironAI(pCreature); +} + +bool GOUse_go_sapphiron_birth(Player* pPlayer, GameObject* pGo) +{ + ScriptedInstance* pInstance = (ScriptedInstance*)pGo->GetInstanceData(); + + if (!pInstance) + return true; + + if (pInstance->GetData(TYPE_SAPPHIRON) != NOT_STARTED) + return true; + + // If already summoned return (safety check) + if (pInstance->GetSingleCreatureFromStorage(NPC_SAPPHIRON, true)) + return true; + + // Set data to special and allow the Go animation to proceed + pInstance->SetData(TYPE_SAPPHIRON, SPECIAL); + return false; +} + +void AddSC_boss_sapphiron() +{ + Script* pNewScript; + + pNewScript = new Script; + pNewScript->Name = "boss_sapphiron"; + pNewScript->GetAI = &GetAI_boss_sapphiron; + pNewScript->RegisterSelf(); + + pNewScript = new Script; + pNewScript->Name = "go_sapphiron_birth"; + pNewScript->pGOUse = &GOUse_go_sapphiron_birth; + pNewScript->RegisterSelf(); +} diff --git a/src/modules/SD2/scripts/northrend/naxxramas/boss_thaddius.cpp b/src/modules/SD2/scripts/northrend/naxxramas/boss_thaddius.cpp new file mode 100644 index 000000000..f148a049e --- /dev/null +++ b/src/modules/SD2/scripts/northrend/naxxramas/boss_thaddius.cpp @@ -0,0 +1,772 @@ +/* Copyright (C) 2006 - 2013 ScriptDev2 + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +/* ScriptData +SDName: Boss_Thaddius +SD%Complete: 85 +SDComment: Magnetic Pull, Tesla-Chains, Polaritiy-Shift missing (core!) +SDCategory: Naxxramas +EndScriptData */ + +/* ContentData +boss_thaddius +npc_tesla_coil +boss_stalagg +boss_feugen +EndContentData */ + +#include "precompiled.h" +#include "naxxramas.h" + +enum +{ + // Stalagg + SAY_STAL_AGGRO = -1533023, + SAY_STAL_SLAY = -1533024, + SAY_STAL_DEATH = -1533025, + + // Feugen + SAY_FEUG_AGGRO = -1533026, + SAY_FEUG_SLAY = -1533027, + SAY_FEUG_DEATH = -1533028, + + // Tesla Coils + EMOTE_LOSING_LINK = -1533149, + EMOTE_TESLA_OVERLOAD = -1533150, + + // Thaddus + SAY_AGGRO_1 = -1533030, + SAY_AGGRO_2 = -1533031, + SAY_AGGRO_3 = -1533032, + SAY_SLAY = -1533033, + SAY_ELECT = -1533034, + SAY_DEATH = -1533035, + // Background screams in Instance if Thaddius still alive, needs general support most likely + SAY_SCREAM1 = -1533036, + SAY_SCREAM2 = -1533037, + SAY_SCREAM3 = -1533038, + SAY_SCREAM4 = -1533039, + EMOTE_POLARITY_SHIFT = -1533151, + + // Thaddius Spells + SPELL_THADIUS_SPAWN = 28160, + SPELL_THADIUS_LIGHTNING_VISUAL = 28136, + SPELL_BALL_LIGHTNING = 28299, + SPELL_CHAIN_LIGHTNING = 28167, + SPELL_CHAIN_LIGHTNING_H = 54531, + SPELL_POLARITY_SHIFT = 28089, + SPELL_BESERK = 27680, + SPELL_CLEAR_CHARGES = 63133, // TODO NYI, cast on death, most likely to remove remaining buffs + + // Stalagg & Feugen Spells + // SPELL_WARSTOMP = 28125, // Not used in Wotlk Version + SPELL_MAGNETIC_PULL_A = 28338, + SPELL_MAGNETIC_PULL_B = 54517, // used by Feugen (wotlk) + SPELL_STATIC_FIELD = 28135, + SPELL_STATIC_FIELD_H = 54528, + SPELL_POWERSURGE_H = 28134, + SPELL_POWERSURGE = 54529, + + // Tesla Spells + SPELL_FEUGEN_CHAIN = 28111, + SPELL_STALAGG_CHAIN = 28096, + SPELL_FEUGEN_TESLA_PASSIVE = 28109, + SPELL_STALAGG_TESLA_PASSIVE = 28097, + SPELL_SHOCK_OVERLOAD = 28159, + SPELL_SHOCK = 28099, +}; + +/************ +** boss_thaddius +************/ + +// Actually this boss behaves like a NoMovement Boss (SPELL_BALL_LIGHTNING) - but there are some movement packages used, unknown what this means! +struct boss_thaddiusAI : public Scripted_NoMovementAI +{ + boss_thaddiusAI(Creature* pCreature) : Scripted_NoMovementAI(pCreature) + { + m_pInstance = (instance_naxxramas*)pCreature->GetInstanceData(); + m_bIsRegularMode = pCreature->GetMap()->IsRegularDifficulty(); + + Reset(); + } + + instance_naxxramas* m_pInstance; + bool m_bIsRegularMode; + + uint32 m_uiPolarityShiftTimer; + uint32 m_uiChainLightningTimer; + uint32 m_uiBallLightningTimer; + uint32 m_uiBerserkTimer; + + void Reset() override + { + m_uiPolarityShiftTimer = 15 * IN_MILLISECONDS; + m_uiChainLightningTimer = 8 * IN_MILLISECONDS; + m_uiBallLightningTimer = 1 * IN_MILLISECONDS; + m_uiBerserkTimer = 6 * MINUTE * IN_MILLISECONDS; + + m_creature->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE); + } + + void Aggro(Unit* /*pWho*/) override + { + switch (urand(0, 2)) + { + case 0: DoScriptText(SAY_AGGRO_1, m_creature); break; + case 1: DoScriptText(SAY_AGGRO_2, m_creature); break; + case 2: DoScriptText(SAY_AGGRO_3, m_creature); break; + } + + // Make Attackable + m_creature->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE); + } + + void JustReachedHome() override + { + if (m_pInstance) + { + m_pInstance->SetData(TYPE_THADDIUS, FAIL); + + // Respawn Adds: + Creature* pFeugen = m_pInstance->GetSingleCreatureFromStorage(NPC_FEUGEN); + Creature* pStalagg = m_pInstance->GetSingleCreatureFromStorage(NPC_STALAGG); + if (pFeugen) + { + pFeugen->ForcedDespawn(); + pFeugen->Respawn(); + } + if (pStalagg) + { + pStalagg->ForcedDespawn(); + pStalagg->Respawn(); + } + } + } + + void KilledUnit(Unit* pVictim) override + { + if (pVictim->GetTypeId() != TYPEID_PLAYER) + return; + + DoScriptText(SAY_SLAY, m_creature); + } + + void JustDied(Unit* /*pKiller*/) override + { + DoScriptText(SAY_DEATH, m_creature); + + if (m_pInstance) + { + m_pInstance->SetData(TYPE_THADDIUS, DONE); + + // Force Despawn of Adds + Creature* pFeugen = m_pInstance->GetSingleCreatureFromStorage(NPC_FEUGEN); + Creature* pStalagg = m_pInstance->GetSingleCreatureFromStorage(NPC_STALAGG); + + if (pFeugen) + pFeugen->ForcedDespawn(); + if (pStalagg) + pStalagg->ForcedDespawn(); + } + } + + void UpdateAI(const uint32 uiDiff) override + { + if (!m_pInstance) + return; + + if (!m_creature->SelectHostileTarget() || !m_creature->getVictim()) + return; + + // Berserk + if (m_uiBerserkTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature, SPELL_BESERK) == CAST_OK) // allow combat movement? + m_uiBerserkTimer = 10 * MINUTE * IN_MILLISECONDS; + } + else + m_uiBerserkTimer -= uiDiff; + + // Polarity Shift + if (m_uiPolarityShiftTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature, SPELL_POLARITY_SHIFT, CAST_INTERRUPT_PREVIOUS) == CAST_OK) + { + DoScriptText(SAY_ELECT, m_creature); + DoScriptText(EMOTE_POLARITY_SHIFT, m_creature); + m_uiPolarityShiftTimer = 30 * IN_MILLISECONDS; + } + } + else + m_uiPolarityShiftTimer -= uiDiff; + + // Chain Lightning + if (m_uiChainLightningTimer < uiDiff) + { + Unit* pTarget = m_creature->SelectAttackingTarget(ATTACKING_TARGET_RANDOM, 0); + if (pTarget && DoCastSpellIfCan(pTarget, m_bIsRegularMode ? SPELL_CHAIN_LIGHTNING : SPELL_CHAIN_LIGHTNING_H) == CAST_OK) + m_uiChainLightningTimer = 15 * IN_MILLISECONDS; + } + else + m_uiChainLightningTimer -= uiDiff; + + // Ball Lightning if target not in melee range + // TODO: Verify, likely that the boss should attack any enemy in melee range before starting to cast + if (!m_creature->CanReachWithMeleeAttack(m_creature->getVictim())) + { + if (m_uiBallLightningTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature->getVictim(), SPELL_BALL_LIGHTNING) == CAST_OK) + m_uiBallLightningTimer = 1 * IN_MILLISECONDS; + } + else + m_uiBallLightningTimer -= uiDiff; + } + else + DoMeleeAttackIfReady(); + } +}; + +CreatureAI* GetAI_boss_thaddius(Creature* pCreature) +{ + return new boss_thaddiusAI(pCreature); +} + +bool EffectDummyNPC_spell_thaddius_encounter(Unit* /*pCaster*/, uint32 uiSpellId, SpellEffectIndex uiEffIndex, Creature* pCreatureTarget, ObjectGuid /*originalCasterGuid*/) +{ + switch (uiSpellId) + { + case SPELL_SHOCK_OVERLOAD: + if (uiEffIndex == EFFECT_INDEX_0) + { + // Only do something to Thaddius, and on the first hit. + if (pCreatureTarget->GetEntry() != NPC_THADDIUS || !pCreatureTarget->HasAura(SPELL_THADIUS_SPAWN)) + return true; + // remove Stun and then Cast + pCreatureTarget->RemoveAurasDueToSpell(SPELL_THADIUS_SPAWN); + pCreatureTarget->CastSpell(pCreatureTarget, SPELL_THADIUS_LIGHTNING_VISUAL, false); + } + return true; + case SPELL_THADIUS_LIGHTNING_VISUAL: + if (uiEffIndex == EFFECT_INDEX_0 && pCreatureTarget->GetEntry() == NPC_THADDIUS) + { + if (instance_naxxramas* pInstance = (instance_naxxramas*)pCreatureTarget->GetInstanceData()) + { + if (Player* pPlayer = pInstance->GetPlayerInMap(true, false)) + pCreatureTarget->AI()->AttackStart(pPlayer); + } + } + return true; + } + return false; +} + +/************ +** npc_tesla_coil +************/ + +struct npc_tesla_coilAI : public Scripted_NoMovementAI +{ + npc_tesla_coilAI(Creature* pCreature) : Scripted_NoMovementAI(pCreature) + { + m_pInstance = (instance_naxxramas*)pCreature->GetInstanceData(); + m_uiSetupTimer = 1 * IN_MILLISECONDS; + m_uiOverloadTimer = 0; + m_bReapply = false; + Reset(); + } + + instance_naxxramas* m_pInstance; + bool m_bToFeugen; + bool m_bReapply; + + uint32 m_uiSetupTimer; + uint32 m_uiOverloadTimer; + + void Reset() override {} + void MoveInLineOfSight(Unit* /*pWho*/) override {} + + void Aggro(Unit* /*pWho*/) override + { + DoScriptText(EMOTE_LOSING_LINK, m_creature); + } + + // Overwrite this function here to + // * Keep Chain spells casted when evading after useless EnterCombat caused by 'buffing' the add + // * To not remove the Passive spells when evading after ie killed a player + void EnterEvadeMode() override + { + m_creature->DeleteThreatList(); + m_creature->CombatStop(); + } + + bool SetupChain() + { + // Check, if instance_ script failed or encounter finished + if (!m_pInstance || m_pInstance->GetData(TYPE_THADDIUS) == DONE) + return true; + + GameObject* pNoxTeslaFeugen = m_pInstance->GetSingleGameObjectFromStorage(GO_CONS_NOX_TESLA_FEUGEN); + GameObject* pNoxTeslaStalagg = m_pInstance->GetSingleGameObjectFromStorage(GO_CONS_NOX_TESLA_STALAGG); + + // Try again, till Tesla GOs are spawned + if (!pNoxTeslaFeugen || !pNoxTeslaStalagg) + return false; + + m_bToFeugen = m_creature->GetDistanceOrder(pNoxTeslaFeugen, pNoxTeslaStalagg); + + if (DoCastSpellIfCan(m_creature, m_bToFeugen ? SPELL_FEUGEN_CHAIN : SPELL_STALAGG_CHAIN) == CAST_OK) + return true; + + return false; + } + + void ReApplyChain(uint32 uiEntry) + { + if (uiEntry) // called from Stalagg/Feugen with their entry + { + // Only apply chain to own add + if ((uiEntry == NPC_FEUGEN && !m_bToFeugen) || (uiEntry == NPC_STALAGG && m_bToFeugen)) + return; + + m_bReapply = true; // Reapply Chains on next tick + } + else // if called from next tick, needed because otherwise the spell doesn't bind + { + m_bReapply = false; + m_creature->InterruptNonMeleeSpells(true); + GameObject* pGo = m_pInstance->GetSingleGameObjectFromStorage(m_bToFeugen ? GO_CONS_NOX_TESLA_FEUGEN : GO_CONS_NOX_TESLA_STALAGG); + + if (pGo && pGo->GetGoType() == GAMEOBJECT_TYPE_BUTTON && pGo->getLootState() == GO_ACTIVATED) + pGo->ResetDoorOrButton(); + + DoCastSpellIfCan(m_creature, m_bToFeugen ? SPELL_FEUGEN_CHAIN : SPELL_STALAGG_CHAIN); + } + } + + void SetOverloading() + { + m_uiOverloadTimer = 14 * IN_MILLISECONDS; // it takes some time to overload and activate Thaddius + } + + void UpdateAI(const uint32 uiDiff) override + { + m_creature->SelectHostileTarget(); + + if (!m_uiOverloadTimer && !m_uiSetupTimer && !m_bReapply) + return; // Nothing to do this tick + + if (m_uiSetupTimer) + { + if (m_uiSetupTimer <= uiDiff) + { + if (SetupChain()) + m_uiSetupTimer = 0; + else + m_uiSetupTimer = 5 * IN_MILLISECONDS; + } + else + m_uiSetupTimer -= uiDiff; + } + + if (m_uiOverloadTimer) + { + if (m_uiOverloadTimer <= uiDiff) + { + m_uiOverloadTimer = 0; + m_creature->RemoveAurasDueToSpell(m_bToFeugen ? SPELL_FEUGEN_TESLA_PASSIVE : SPELL_STALAGG_TESLA_PASSIVE); + DoCastSpellIfCan(m_creature, SPELL_SHOCK_OVERLOAD, CAST_INTERRUPT_PREVIOUS); + DoScriptText(EMOTE_TESLA_OVERLOAD, m_creature); + m_pInstance->DoUseDoorOrButton(m_bToFeugen ? GO_CONS_NOX_TESLA_FEUGEN : GO_CONS_NOX_TESLA_STALAGG); + } + else + m_uiOverloadTimer -= uiDiff; + } + + if (m_bReapply) + ReApplyChain(0); + } +}; + +CreatureAI* GetAI_npc_tesla_coil(Creature* pCreature) +{ + return new npc_tesla_coilAI(pCreature); +} + +/************ +** boss_thaddiusAddsAI - Superclass for Feugen & Stalagg +************/ + +struct boss_thaddiusAddsAI : public ScriptedAI +{ + boss_thaddiusAddsAI(Creature* pCreature) : ScriptedAI(pCreature) + { + m_pInstance = (instance_naxxramas*)pCreature->GetInstanceData(); + m_bIsRegularMode = pCreature->GetMap()->IsRegularDifficulty(); + + Reset(); + } + + instance_naxxramas* m_pInstance; + bool m_bIsRegularMode; + + bool m_bFakeDeath; + bool m_bBothDead; + + uint32 m_uiHoldTimer; + // uint32 m_uiWarStompTimer; + uint32 m_uiReviveTimer; + + void Reset() override + { + m_bFakeDeath = false; + m_bBothDead = false; + + m_uiReviveTimer = 5 * IN_MILLISECONDS; + m_uiHoldTimer = 2 * IN_MILLISECONDS; + // m_uiWarStompTimer = urand(8*IN_MILLISECONDS, 10*IN_MILLISECONDS); + + // We might Reset while faking death, so undo this + m_creature->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE); + m_creature->SetHealth(m_creature->GetMaxHealth()); + m_creature->SetStandState(UNIT_STAND_STATE_STAND); + } + + Creature* GetOtherAdd() // For Stalagg returns pFeugen, for Feugen returns pStalagg + { + switch (m_creature->GetEntry()) + { + case NPC_FEUGEN: return m_pInstance->GetSingleCreatureFromStorage(NPC_STALAGG); + case NPC_STALAGG: return m_pInstance->GetSingleCreatureFromStorage(NPC_FEUGEN); + default: + return NULL; + } + } + + void Aggro(Unit* pWho) override + { + if (!m_pInstance) + return; + + m_pInstance->SetData(TYPE_THADDIUS, IN_PROGRESS); + + if (Creature* pOtherAdd = GetOtherAdd()) + { + if (!pOtherAdd->IsInCombat()) + pOtherAdd->AI()->AttackStart(pWho); + } + } + + void JustRespawned() override + { + Reset(); // Needed to reset the flags properly + + GuidList lTeslaGUIDList; + if (!m_pInstance) + return; + + m_pInstance->GetThadTeslaCreatures(lTeslaGUIDList); + if (lTeslaGUIDList.empty()) + return; + + for (GuidList::const_iterator itr = lTeslaGUIDList.begin(); itr != lTeslaGUIDList.end(); ++itr) + { + if (Creature* pTesla = m_pInstance->instance->GetCreature(*itr)) + { + if (npc_tesla_coilAI* pTeslaAI = dynamic_cast(pTesla->AI())) + pTeslaAI->ReApplyChain(m_creature->GetEntry()); + } + } + } + + void JustReachedHome() override + { + if (!m_pInstance) + return; + + if (Creature* pOther = GetOtherAdd()) + { + if (boss_thaddiusAddsAI* pOtherAI = dynamic_cast(pOther->AI())) + { + if (pOtherAI->IsCountingDead()) + { + pOther->ForcedDespawn(); + pOther->Respawn(); + } + } + } + + // Reapply Chains if needed + if (!m_creature->HasAura(SPELL_FEUGEN_CHAIN) && !m_creature->HasAura(SPELL_STALAGG_CHAIN)) + JustRespawned(); + + m_pInstance->SetData(TYPE_THADDIUS, FAIL); + } + + void Revive() + { + DoResetThreat(); + PauseCombatMovement(); + Reset(); + } + + bool IsCountingDead() + { + return m_bFakeDeath || m_creature->IsDead(); + } + + void PauseCombatMovement() + { + SetCombatMovement(false); + m_uiHoldTimer = 1500; + } + + virtual void UpdateAddAI(const uint32 /*uiDiff*/) {} // Used for Add-specific spells + + void UpdateAI(const uint32 uiDiff) override + { + if (m_bBothDead) // This is the case while fighting Thaddius + return; + + if (m_bFakeDeath) + { + if (m_uiReviveTimer < uiDiff) + { + if (Creature* pOther = GetOtherAdd()) + { + if (boss_thaddiusAddsAI* pOtherAI = dynamic_cast(pOther->AI())) + { + if (!pOtherAI->IsCountingDead()) // Raid was to slow to kill the second add + Revive(); + else + { + m_bBothDead = true; // Now both adds are counting dead + pOtherAI->m_bBothDead = true; + // Set both Teslas to overload + GuidList lTeslaGUIDList; + m_pInstance->GetThadTeslaCreatures(lTeslaGUIDList); + for (GuidList::const_iterator itr = lTeslaGUIDList.begin(); itr != lTeslaGUIDList.end(); ++itr) + { + if (Creature* pTesla = m_pInstance->instance->GetCreature(*itr)) + { + if (npc_tesla_coilAI* pTeslaAI = dynamic_cast(pTesla->AI())) + pTeslaAI->SetOverloading(); + } + } + } + } + } + } + else + m_uiReviveTimer -= uiDiff; + return; + } + + if (!m_creature->SelectHostileTarget() || !m_creature->getVictim()) + return; + + if (m_uiHoldTimer) // A short timer preventing combat movement after revive + { + if (m_uiHoldTimer <= uiDiff) + { + SetCombatMovement(true); + m_creature->GetMotionMaster()->MoveChase(m_creature->getVictim()); + m_uiHoldTimer = 0; + } + else + m_uiHoldTimer -= uiDiff; + } + + /* Doesn't happen in wotlk version any more + if (m_uiWarStompTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature, SPELL_WARSTOMP) == CAST_OK) + m_uiWarStompTimer = urand(8*IN_MILLISECONDS, 10*IN_MILLISECONDS); + } + else + m_uiWarStompTimer -= uiDiff;*/ + + UpdateAddAI(uiDiff); // For Add Specific Abilities + + DoMeleeAttackIfReady(); + } + + void DamageTaken(Unit* pKiller, uint32& uiDamage) override + { + if (uiDamage < m_creature->GetHealth()) + return; + + // Prevent glitch if in fake death + if (m_bFakeDeath) + { + uiDamage = 0; + return; + } + + // prevent death + uiDamage = 0; + m_bFakeDeath = true; + + m_creature->InterruptNonMeleeSpells(false); + m_creature->SetHealth(0); + m_creature->StopMoving(); + m_creature->ClearComboPointHolders(); + m_creature->RemoveAllAurasOnDeath(); + m_creature->ModifyAuraState(AURA_STATE_HEALTHLESS_20_PERCENT, false); + m_creature->ModifyAuraState(AURA_STATE_HEALTHLESS_35_PERCENT, false); + m_creature->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE); + m_creature->ClearAllReactives(); + m_creature->GetMotionMaster()->Clear(); + m_creature->GetMotionMaster()->MoveIdle(); + m_creature->SetStandState(UNIT_STAND_STATE_DEAD); + + JustDied(pKiller); // Texts + } +}; + +/************ +** boss_stalagg +************/ + +struct boss_stalaggAI : public boss_thaddiusAddsAI +{ + boss_stalaggAI(Creature* pCreature) : boss_thaddiusAddsAI(pCreature) + { + Reset(); + } + uint32 m_uiPowerSurgeTimer; + + void Reset() override + { + boss_thaddiusAddsAI::Reset(); + m_uiPowerSurgeTimer = urand(10 * IN_MILLISECONDS, 15 * IN_MILLISECONDS); + } + + void Aggro(Unit* pWho) override + { + DoScriptText(SAY_STAL_AGGRO, m_creature); + boss_thaddiusAddsAI::Aggro(pWho); + } + + void JustDied(Unit* /*pKiller*/) override + { + DoScriptText(SAY_STAL_DEATH, m_creature); + } + + void KilledUnit(Unit* pVictim) override + { + if (pVictim->GetTypeId() == TYPEID_PLAYER) + DoScriptText(SAY_STAL_SLAY, m_creature); + } + + void UpdateAddAI(const uint32 uiDiff) + { + if (m_uiPowerSurgeTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature, m_bIsRegularMode ? SPELL_POWERSURGE : SPELL_POWERSURGE_H) == CAST_OK) + m_uiPowerSurgeTimer = urand(10 * IN_MILLISECONDS, 15 * IN_MILLISECONDS); + } + else + m_uiPowerSurgeTimer -= uiDiff; + } +}; + +CreatureAI* GetAI_boss_stalagg(Creature* pCreature) +{ + return new boss_stalaggAI(pCreature); +} + +/************ +** boss_feugen +************/ + +struct boss_feugenAI : public boss_thaddiusAddsAI +{ + boss_feugenAI(Creature* pCreature) : boss_thaddiusAddsAI(pCreature) + { + Reset(); + } + uint32 m_uiStaticFieldTimer; + uint32 m_uiMagneticPullTimer; // TODO, missing + + void Reset() override + { + boss_thaddiusAddsAI::Reset(); + m_uiStaticFieldTimer = urand(10 * IN_MILLISECONDS, 15 * IN_MILLISECONDS); + m_uiMagneticPullTimer = 20 * IN_MILLISECONDS; + } + + void Aggro(Unit* pWho) override + { + DoScriptText(SAY_FEUG_AGGRO, m_creature); + boss_thaddiusAddsAI::Aggro(pWho); + } + + void JustDied(Unit* /*pKiller*/) override + { + DoScriptText(SAY_FEUG_DEATH, m_creature); + } + + void KilledUnit(Unit* pVictim) override + { + if (pVictim->GetTypeId() == TYPEID_PLAYER) + DoScriptText(SAY_FEUG_SLAY, m_creature); + } + + void UpdateAddAI(const uint32 uiDiff) + { + if (m_uiStaticFieldTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature, m_bIsRegularMode ? SPELL_STATIC_FIELD : SPELL_STATIC_FIELD_H) == CAST_OK) + m_uiStaticFieldTimer = urand(10 * IN_MILLISECONDS, 15 * IN_MILLISECONDS); + } + else + m_uiStaticFieldTimer -= uiDiff; + } +}; + +CreatureAI* GetAI_boss_feugen(Creature* pCreature) +{ + return new boss_feugenAI(pCreature); +} + +void AddSC_boss_thaddius() +{ + Script* pNewScript; + + pNewScript = new Script; + pNewScript->Name = "boss_thaddius"; + pNewScript->GetAI = &GetAI_boss_thaddius; + pNewScript->pEffectDummyNPC = &EffectDummyNPC_spell_thaddius_encounter; + pNewScript->RegisterSelf(); + + pNewScript = new Script; + pNewScript->Name = "boss_stalagg"; + pNewScript->GetAI = &GetAI_boss_stalagg; + pNewScript->RegisterSelf(); + + pNewScript = new Script; + pNewScript->Name = "boss_feugen"; + pNewScript->GetAI = &GetAI_boss_feugen; + pNewScript->RegisterSelf(); + + pNewScript = new Script; + pNewScript->Name = "npc_tesla_coil"; + pNewScript->GetAI = &GetAI_npc_tesla_coil; + pNewScript->pEffectDummyNPC = &EffectDummyNPC_spell_thaddius_encounter; + pNewScript->RegisterSelf(); +} diff --git a/src/modules/SD2/scripts/northrend/naxxramas/instance_naxxramas.cpp b/src/modules/SD2/scripts/northrend/naxxramas/instance_naxxramas.cpp new file mode 100644 index 000000000..3bcd88f23 --- /dev/null +++ b/src/modules/SD2/scripts/northrend/naxxramas/instance_naxxramas.cpp @@ -0,0 +1,804 @@ +/* Copyright (C) 2006 - 2013 ScriptDev2 + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +/* ScriptData +SDName: Instance_Naxxramas +SD%Complete: 90% +SDComment: +SDCategory: Naxxramas +EndScriptData */ + +#include "precompiled.h" +#include "naxxramas.h" + +static const DialogueEntry aNaxxDialogue[] = +{ + {NPC_KELTHUZAD, 0, 10000}, + {SAY_SAPP_DIALOG1, NPC_KELTHUZAD, 8000}, + {SAY_SAPP_DIALOG2_LICH, NPC_THE_LICHKING, 14000}, + {SAY_SAPP_DIALOG3, NPC_KELTHUZAD, 10000}, + {SAY_SAPP_DIALOG4_LICH, NPC_THE_LICHKING, 12000}, + {SAY_SAPP_DIALOG5, NPC_KELTHUZAD, 0}, + {NPC_THANE, 0, 10000}, + {SAY_KORT_TAUNT1, NPC_THANE, 5000}, + {SAY_ZELI_TAUNT1, NPC_ZELIEK, 6000}, + {SAY_BLAU_TAUNT1, NPC_BLAUMEUX, 6000}, + {SAY_RIVE_TAUNT1, NPC_RIVENDARE, 6000}, + {SAY_BLAU_TAUNT2, NPC_BLAUMEUX, 6000}, + {SAY_ZELI_TAUNT2, NPC_ZELIEK, 5000}, + {SAY_KORT_TAUNT2, NPC_THANE, 7000}, + {SAY_RIVE_TAUNT2, NPC_RIVENDARE, 0}, + {0, 0, 0} +}; + +instance_naxxramas::instance_naxxramas(Map* pMap) : ScriptedInstance(pMap), + m_fChamberCenterX(0.0f), + m_fChamberCenterY(0.0f), + m_fChamberCenterZ(0.0f), + m_uiSapphSpawnTimer(0), + m_uiTauntTimer(0), + m_uiHorsemenAchievTimer(0), + m_uiHorseMenKilled(0), + m_dialogueHelper(aNaxxDialogue) +{ + Initialize(); +} + +void instance_naxxramas::Initialize() +{ + memset(&m_auiEncounter, 0, sizeof(m_auiEncounter)); + + for (uint8 i = 0; i < MAX_SPECIAL_ACHIEV_CRITS; ++i) + m_abAchievCriteria[i] = false; + + m_dialogueHelper.InitializeDialogueHelper(this, true); +} + +void instance_naxxramas::OnPlayerEnter(Player* pPlayer) +{ + // Function only used to summon Sapphiron in case of server reload + if (GetData(TYPE_SAPPHIRON) != SPECIAL) + return; + + // Check if already summoned + if (GetSingleCreatureFromStorage(NPC_SAPPHIRON, true)) + return; + + pPlayer->SummonCreature(NPC_SAPPHIRON, aSapphPositions[0], aSapphPositions[1], aSapphPositions[2], aSapphPositions[3], TEMPSUMMON_DEAD_DESPAWN, 0); +} + +void instance_naxxramas::OnCreatureCreate(Creature* pCreature) +{ + switch (pCreature->GetEntry()) + { + case NPC_ANUB_REKHAN: + case NPC_FAERLINA: + case NPC_THADDIUS: + case NPC_STALAGG: + case NPC_FEUGEN: + case NPC_ZELIEK: + case NPC_THANE: + case NPC_BLAUMEUX: + case NPC_RIVENDARE: + case NPC_GOTHIK: + case NPC_SAPPHIRON: + case NPC_KELTHUZAD: + case NPC_THE_LICHKING: + m_mNpcEntryGuidStore[pCreature->GetEntry()] = pCreature->GetObjectGuid(); + break; + + case NPC_SUB_BOSS_TRIGGER: m_lGothTriggerList.push_back(pCreature->GetObjectGuid()); break; + case NPC_TESLA_COIL: m_lThadTeslaCoilList.push_back(pCreature->GetObjectGuid()); break; + } +} + +void instance_naxxramas::OnObjectCreate(GameObject* pGo) +{ + switch (pGo->GetEntry()) + { + // Arachnid Quarter + case GO_ARAC_ANUB_DOOR: + break; + case GO_ARAC_ANUB_GATE: + if (m_auiEncounter[TYPE_ANUB_REKHAN] == DONE) + pGo->SetGoState(GO_STATE_ACTIVE); + break; + case GO_ARAC_FAER_WEB: + break; + case GO_ARAC_FAER_DOOR: + if (m_auiEncounter[TYPE_FAERLINA] == DONE) + pGo->SetGoState(GO_STATE_ACTIVE); + break; + case GO_ARAC_MAEX_INNER_DOOR: + break; + case GO_ARAC_MAEX_OUTER_DOOR: + if (m_auiEncounter[TYPE_FAERLINA] == DONE) + pGo->SetGoState(GO_STATE_ACTIVE); + break; + + // Plague Quarter + case GO_PLAG_NOTH_ENTRY_DOOR: + break; + case GO_PLAG_NOTH_EXIT_DOOR: + if (m_auiEncounter[TYPE_NOTH] == DONE) + pGo->SetGoState(GO_STATE_ACTIVE); + break; + case GO_PLAG_HEIG_ENTRY_DOOR: + if (m_auiEncounter[TYPE_NOTH] == DONE) + pGo->SetGoState(GO_STATE_ACTIVE); + break; + case GO_PLAG_HEIG_EXIT_DOOR: + if (m_auiEncounter[TYPE_HEIGAN] == DONE) + pGo->SetGoState(GO_STATE_ACTIVE); + break; + case GO_PLAG_LOAT_DOOR: + break; + + // Military Quarter + case GO_MILI_GOTH_ENTRY_GATE: + break; + case GO_MILI_GOTH_EXIT_GATE: + if (m_auiEncounter[TYPE_GOTHIK] == DONE) + pGo->SetGoState(GO_STATE_ACTIVE); + break; + case GO_MILI_GOTH_COMBAT_GATE: + break; + case GO_MILI_HORSEMEN_DOOR: + if (m_auiEncounter[TYPE_GOTHIK] == DONE) + pGo->SetGoState(GO_STATE_ACTIVE); + break; + case GO_CHEST_HORSEMEN_NORM: + case GO_CHEST_HORSEMEN_HERO: + break; + + // Construct Quarter + case GO_CONS_PATH_EXIT_DOOR: + if (m_auiEncounter[TYPE_PATCHWERK] == DONE) + pGo->SetGoState(GO_STATE_ACTIVE); + break; + case GO_CONS_GLUT_EXIT_DOOR: + if (m_auiEncounter[TYPE_GLUTH] == DONE) + pGo->SetGoState(GO_STATE_ACTIVE); + break; + case GO_CONS_THAD_DOOR: + if (m_auiEncounter[TYPE_GLUTH] == DONE) + pGo->SetGoState(GO_STATE_ACTIVE); + break; + case GO_CONS_NOX_TESLA_FEUGEN: + if (m_auiEncounter[TYPE_THADDIUS] == DONE) + pGo->SetGoState(GO_STATE_READY); + break; + case GO_CONS_NOX_TESLA_STALAGG: + if (m_auiEncounter[TYPE_THADDIUS] == DONE) + pGo->SetGoState(GO_STATE_READY); + break; + + // Frostwyrm Lair + case GO_KELTHUZAD_WATERFALL_DOOR: + if (m_auiEncounter[TYPE_SAPPHIRON] == DONE) + pGo->SetGoState(GO_STATE_ACTIVE); + break; + case GO_KELTHUZAD_EXIT_DOOR: + break; + + // Eyes + case GO_ARAC_EYE_RAMP: + case GO_ARAC_EYE_BOSS: + if (m_auiEncounter[TYPE_MAEXXNA] == DONE) + pGo->SetGoState(GO_STATE_ACTIVE); + break; + case GO_PLAG_EYE_RAMP: + case GO_PLAG_EYE_BOSS: + if (m_auiEncounter[TYPE_LOATHEB] == DONE) + pGo->SetGoState(GO_STATE_ACTIVE); + break; + case GO_MILI_EYE_RAMP: + case GO_MILI_EYE_BOSS: + if (m_auiEncounter[TYPE_FOUR_HORSEMEN] == DONE) + pGo->SetGoState(GO_STATE_ACTIVE); + break; + case GO_CONS_EYE_RAMP: + case GO_CONS_EYE_BOSS: + if (m_auiEncounter[TYPE_THADDIUS] == DONE) + pGo->SetGoState(GO_STATE_ACTIVE); + break; + + // Portals + case GO_ARAC_PORTAL: + if (m_auiEncounter[TYPE_MAEXXNA] == DONE) + pGo->RemoveFlag(GAMEOBJECT_FLAGS, GO_FLAG_NO_INTERACT); + break; + case GO_PLAG_PORTAL: + if (m_auiEncounter[TYPE_LOATHEB] == DONE) + pGo->RemoveFlag(GAMEOBJECT_FLAGS, GO_FLAG_NO_INTERACT); + break; + case GO_MILI_PORTAL: + if (m_auiEncounter[TYPE_FOUR_HORSEMEN] == DONE) + pGo->RemoveFlag(GAMEOBJECT_FLAGS, GO_FLAG_NO_INTERACT); + break; + case GO_CONS_PORTAL: + if (m_auiEncounter[TYPE_THADDIUS] == DONE) + pGo->RemoveFlag(GAMEOBJECT_FLAGS, GO_FLAG_NO_INTERACT); + break; + + default: + // Heigan Traps - many different entries which are only required for sorting + if (pGo->GetGoType() == GAMEOBJECT_TYPE_TRAP) + { + uint32 uiGoEntry = pGo->GetEntry(); + + if ((uiGoEntry >= 181517 && uiGoEntry <= 181524) || uiGoEntry == 181678) + m_alHeiganTrapGuids[0].push_back(pGo->GetObjectGuid()); + else if ((uiGoEntry >= 181510 && uiGoEntry <= 181516) || (uiGoEntry >= 181525 && uiGoEntry <= 181531) || uiGoEntry == 181533 || uiGoEntry == 181676) + m_alHeiganTrapGuids[1].push_back(pGo->GetObjectGuid()); + else if ((uiGoEntry >= 181534 && uiGoEntry <= 181544) || uiGoEntry == 181532 || uiGoEntry == 181677) + m_alHeiganTrapGuids[2].push_back(pGo->GetObjectGuid()); + else if ((uiGoEntry >= 181545 && uiGoEntry <= 181552) || uiGoEntry == 181695) + m_alHeiganTrapGuids[3].push_back(pGo->GetObjectGuid()); + } + + return; + } + m_mGoEntryGuidStore[pGo->GetEntry()] = pGo->GetObjectGuid(); +} + +void instance_naxxramas::OnPlayerDeath(Player* /*pPlayer*/) +{ + if (IsEncounterInProgress()) + SetData(TYPE_UNDYING_FAILED, DONE); + + if (GetData(TYPE_HEIGAN) == IN_PROGRESS) + SetSpecialAchievementCriteria(TYPE_ACHIEV_SAFETY_DANCE, false); +} + +void instance_naxxramas::OnCreatureDeath(Creature* pCreature) +{ + if (pCreature->GetEntry() == NPC_MR_BIGGLESWORTH && m_auiEncounter[TYPE_KELTHUZAD] != DONE) + DoOrSimulateScriptTextForThisInstance(SAY_KELTHUZAD_CAT_DIED, NPC_KELTHUZAD); +} + +bool instance_naxxramas::IsEncounterInProgress() const +{ + for (uint8 i = 0; i <= TYPE_KELTHUZAD; ++i) + { + if (m_auiEncounter[i] == IN_PROGRESS) + return true; + } + + // Some Encounters use SPECIAL while in progress + if (m_auiEncounter[TYPE_GOTHIK] == SPECIAL) + return true; + + return false; +} + +void instance_naxxramas::SetData(uint32 uiType, uint32 uiData) +{ + switch (uiType) + { + case TYPE_ANUB_REKHAN: + m_auiEncounter[uiType] = uiData; + DoUseDoorOrButton(GO_ARAC_ANUB_DOOR); + if (uiData == DONE) + { + DoUseDoorOrButton(GO_ARAC_ANUB_GATE); + DoStartTimedAchievement(ACHIEVEMENT_CRITERIA_TYPE_KILL_CREATURE, ACHIEV_START_MAEXXNA_ID); + } + break; + case TYPE_FAERLINA: + DoUseDoorOrButton(GO_ARAC_FAER_WEB); + if (uiData == IN_PROGRESS) + SetSpecialAchievementCriteria(TYPE_ACHIEV_KNOCK_YOU_OUT, true); + else if (uiData == DONE) + { + DoUseDoorOrButton(GO_ARAC_FAER_DOOR); + DoUseDoorOrButton(GO_ARAC_MAEX_OUTER_DOOR); + } + m_auiEncounter[uiType] = uiData; + break; + case TYPE_MAEXXNA: + m_auiEncounter[uiType] = uiData; + DoUseDoorOrButton(GO_ARAC_MAEX_INNER_DOOR, uiData); + if (uiData == DONE) + { + DoUseDoorOrButton(GO_ARAC_EYE_RAMP); + DoUseDoorOrButton(GO_ARAC_EYE_BOSS); + DoRespawnGameObject(GO_ARAC_PORTAL, 30 * MINUTE); + DoToggleGameObjectFlags(GO_ARAC_PORTAL, GO_FLAG_NO_INTERACT, false); + m_uiTauntTimer = 5000; + } + break; + case TYPE_NOTH: + m_auiEncounter[uiType] = uiData; + DoUseDoorOrButton(GO_PLAG_NOTH_ENTRY_DOOR); + if (uiData == DONE) + { + DoUseDoorOrButton(GO_PLAG_NOTH_EXIT_DOOR); + DoUseDoorOrButton(GO_PLAG_HEIG_ENTRY_DOOR); + } + break; + case TYPE_HEIGAN: + m_auiEncounter[uiType] = uiData; + DoUseDoorOrButton(GO_PLAG_HEIG_ENTRY_DOOR); + if (uiData == IN_PROGRESS) + SetSpecialAchievementCriteria(TYPE_ACHIEV_SAFETY_DANCE, true); + else if (uiData == DONE) + DoUseDoorOrButton(GO_PLAG_HEIG_EXIT_DOOR); + break; + case TYPE_LOATHEB: + m_auiEncounter[uiType] = uiData; + DoUseDoorOrButton(GO_PLAG_LOAT_DOOR); + if (uiData == IN_PROGRESS) + SetSpecialAchievementCriteria(TYPE_ACHIEV_SPORE_LOSER, true); + else if (uiData == DONE) + { + DoUseDoorOrButton(GO_PLAG_EYE_RAMP); + DoUseDoorOrButton(GO_PLAG_EYE_BOSS); + DoRespawnGameObject(GO_PLAG_PORTAL, 30 * MINUTE); + DoToggleGameObjectFlags(GO_PLAG_PORTAL, GO_FLAG_NO_INTERACT, false); + m_uiTauntTimer = 5000; + } + break; + case TYPE_RAZUVIOUS: + m_auiEncounter[uiType] = uiData; + break; + case TYPE_GOTHIK: + switch (uiData) + { + case IN_PROGRESS: + DoUseDoorOrButton(GO_MILI_GOTH_ENTRY_GATE); + DoUseDoorOrButton(GO_MILI_GOTH_COMBAT_GATE); + break; + case SPECIAL: + DoUseDoorOrButton(GO_MILI_GOTH_COMBAT_GATE); + break; + case FAIL: + if (m_auiEncounter[uiType] == IN_PROGRESS) + DoUseDoorOrButton(GO_MILI_GOTH_COMBAT_GATE); + + DoUseDoorOrButton(GO_MILI_GOTH_ENTRY_GATE); + break; + case DONE: + DoUseDoorOrButton(GO_MILI_GOTH_ENTRY_GATE); + DoUseDoorOrButton(GO_MILI_GOTH_EXIT_GATE); + DoUseDoorOrButton(GO_MILI_HORSEMEN_DOOR); + + m_dialogueHelper.StartNextDialogueText(NPC_THANE); + break; + } + m_auiEncounter[uiType] = uiData; + break; + case TYPE_FOUR_HORSEMEN: + // Skip if already set + if (m_auiEncounter[uiType] == uiData) + return; + + if (uiData == SPECIAL) + { + // Start the achiev countdown + if (!m_uiHorseMenKilled) + m_uiHorsemenAchievTimer = 15000; + + ++m_uiHorseMenKilled; + + if (m_uiHorseMenKilled == 4) + SetData(TYPE_FOUR_HORSEMEN, DONE); + + // Don't store special data + return; + } + else if (uiData == FAIL) + m_uiHorseMenKilled = 0; + else if (uiData == DONE) + { + DoUseDoorOrButton(GO_MILI_EYE_RAMP); + DoUseDoorOrButton(GO_MILI_EYE_BOSS); + DoRespawnGameObject(GO_MILI_PORTAL, 30 * MINUTE); + DoToggleGameObjectFlags(GO_MILI_PORTAL, GO_FLAG_NO_INTERACT, false); + DoRespawnGameObject(instance->IsRegularDifficulty() ? GO_CHEST_HORSEMEN_NORM : GO_CHEST_HORSEMEN_HERO, 30 * MINUTE); + m_uiTauntTimer = 5000; + } + DoUseDoorOrButton(GO_MILI_HORSEMEN_DOOR); + m_auiEncounter[uiType] = uiData; + break; + case TYPE_PATCHWERK: + m_auiEncounter[uiType] = uiData; + if (uiData == IN_PROGRESS) + DoStartTimedAchievement(ACHIEVEMENT_CRITERIA_TYPE_KILL_CREATURE, ACHIEV_START_PATCHWERK_ID); + else if (uiData == DONE) + DoUseDoorOrButton(GO_CONS_PATH_EXIT_DOOR); + break; + case TYPE_GROBBULUS: + m_auiEncounter[uiType] = uiData; + break; + case TYPE_GLUTH: + m_auiEncounter[uiType] = uiData; + if (uiData == DONE) + { + DoUseDoorOrButton(GO_CONS_GLUT_EXIT_DOOR); + DoUseDoorOrButton(GO_CONS_THAD_DOOR); + } + break; + case TYPE_THADDIUS: + // Only process real changes here + if (m_auiEncounter[uiType] == uiData) + return; + + m_auiEncounter[uiType] = uiData; + if (uiData != SPECIAL) + DoUseDoorOrButton(GO_CONS_THAD_DOOR, uiData); + // Uncomment when this achievement is implemented + // if (uiData == IN_PROGRESS) + // SetSpecialAchievementCriteria(TYPE_ACHIEV_SHOCKING, true); + if (uiData == DONE) + { + DoUseDoorOrButton(GO_CONS_EYE_RAMP); + DoUseDoorOrButton(GO_CONS_EYE_BOSS); + DoRespawnGameObject(GO_CONS_PORTAL, 30 * MINUTE); + DoToggleGameObjectFlags(GO_CONS_PORTAL, GO_FLAG_NO_INTERACT, false); + m_uiTauntTimer = 5000; + } + break; + case TYPE_SAPPHIRON: + m_auiEncounter[uiType] = uiData; + // Uncomment when achiev check implemented + // if (uiData == IN_PROGRESS) + // SetSpecialAchievementCriteria(TYPE_ACHIEV_HUNDRED_CLUB, true); + if (uiData == DONE) + { + DoUseDoorOrButton(GO_KELTHUZAD_WATERFALL_DOOR); + m_dialogueHelper.StartNextDialogueText(NPC_KELTHUZAD); + } + // Start Sapph summoning process + if (uiData == SPECIAL) + m_uiSapphSpawnTimer = 22000; + break; + case TYPE_KELTHUZAD: + m_auiEncounter[uiType] = uiData; + DoUseDoorOrButton(GO_KELTHUZAD_EXIT_DOOR); + if (uiData == IN_PROGRESS) + SetSpecialAchievementCriteria(TYPE_ACHIEV_GET_ENOUGH, false); + break; + case TYPE_UNDYING_FAILED: + m_auiEncounter[uiType] = uiData; + break; + } + + if (uiData == DONE || (uiData == SPECIAL && uiType == TYPE_SAPPHIRON)) + { + 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] << " " + << m_auiEncounter[12] << " " << m_auiEncounter[13] << " " << m_auiEncounter[14] << " " << m_auiEncounter[15]; + + m_strInstData = saveStream.str(); + + SaveToDB(); + OUT_SAVE_INST_DATA_COMPLETE; + } +} + +void instance_naxxramas::Load(const char* chrIn) +{ + if (!chrIn) + { + 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] + >> m_auiEncounter[12] >> m_auiEncounter[13] >> m_auiEncounter[14] >> m_auiEncounter[15]; + + for (uint8 i = 0; i < MAX_ENCOUNTER; ++i) + { + if (m_auiEncounter[i] == IN_PROGRESS) + m_auiEncounter[i] = NOT_STARTED; + } + + OUT_LOAD_INST_DATA_COMPLETE; +} + +uint32 instance_naxxramas::GetData(uint32 uiType) const +{ + if (uiType < MAX_ENCOUNTER) + return m_auiEncounter[uiType]; + + return 0; +} + +void instance_naxxramas::SetSpecialAchievementCriteria(uint32 uiType, bool bIsMet) +{ + if (uiType < MAX_SPECIAL_ACHIEV_CRITS) + m_abAchievCriteria[uiType] = bIsMet; +} + +bool instance_naxxramas::CheckAchievementCriteriaMeet(uint32 uiCriteriaId, Player const* /*pSource*/, Unit const* /*pTarget*/, uint32 /*uiMiscValue1 = 0*/) const +{ + switch (uiCriteriaId) + { + case ACHIEV_CRIT_SAFETY_DANCE_N: + case ACHIEV_CRIT_SAFETY_DANCE_H: + return m_abAchievCriteria[TYPE_ACHIEV_SAFETY_DANCE]; + case ACHIEV_CRIT_KNOCK_YOU_OUT_N: + case ACHIEV_CRIT_KNOCK_YOU_OUT_H: + return m_abAchievCriteria[TYPE_ACHIEV_KNOCK_YOU_OUT]; + case ACHIEV_CRIT_HUNDRED_CLUB_N: + case ACHIEV_CRIT_HUNDRED_CLUB_H: + return m_abAchievCriteria[TYPE_ACHIEV_HUNDRED_CLUB]; + case ACHIEV_CRIT_SHOCKING_N: + case ACHIEV_CRIT_SHOCKING_H: + return m_abAchievCriteria[TYPE_ACHIEV_SHOCKING]; + case ACHIEV_CRIT_SPORE_LOSER_N: + case ACHIEV_CRIT_SPORE_LOSER_H: + return m_abAchievCriteria[TYPE_ACHIEV_SPORE_LOSER]; + case ACHIEV_CRIT_GET_ENOUGH_N: + case ACHIEV_CRIT_GET_ENOUGH_H: + return m_abAchievCriteria[TYPE_ACHIEV_GET_ENOUGH]; + case ACHIEV_CRIT_TOGETHER_N: + case ACHIEV_CRIT_TOGETHER_H: + return m_uiHorsemenAchievTimer > 0; + // 'The Immortal'(25m) or 'Undying'(10m) - (achievs 2186, 2187) + case ACHIEV_CRIT_IMMORTAL_KEL: + case ACHIEV_CRIT_IMMOORTAL_LOA: + case ACHIEV_CRIT_IMMOORTAL_THAD: + case ACHIEV_CRIT_IMMOORTAL_MAEX: + case ACHIEV_CRIT_IMMOORTAL_HORSE: + case ACHIEV_CRIT_UNDYING_KEL: + case ACHIEV_CRIT_UNDYING_HORSE: + case ACHIEV_CRIT_UNDYING_MAEX: + case ACHIEV_CRIT_UNDYING_LOA: + case ACHIEV_CRIT_UNDYING_THAD: + { + // First, check if all bosses are killed (except the last encounter) + uint8 uiEncounterDone = 0; + for (uint8 i = 0; i < TYPE_KELTHUZAD; ++i) + if (m_auiEncounter[i] == DONE) + ++uiEncounterDone; + + return uiEncounterDone >= 14 && GetData(TYPE_UNDYING_FAILED) != DONE; + } + default: + return false; + } +} + +void instance_naxxramas::Update(uint32 uiDiff) +{ + if (m_uiTauntTimer) + { + if (m_uiTauntTimer <= uiDiff) + { + DoTaunt(); + m_uiTauntTimer = 0; + } + else + m_uiTauntTimer -= uiDiff; + } + + if (m_uiHorsemenAchievTimer) + { + if (m_uiHorsemenAchievTimer <= uiDiff) + m_uiHorsemenAchievTimer = 0; + else + m_uiHorsemenAchievTimer -= uiDiff; + } + + if (m_uiSapphSpawnTimer) + { + if (m_uiSapphSpawnTimer <= uiDiff) + { + if (Player* pPlayer = GetPlayerInMap()) + pPlayer->SummonCreature(NPC_SAPPHIRON, aSapphPositions[0], aSapphPositions[1], aSapphPositions[2], aSapphPositions[3], TEMPSUMMON_DEAD_DESPAWN, 0); + + m_uiSapphSpawnTimer = 0; + } + else + m_uiSapphSpawnTimer -= uiDiff; + } + + m_dialogueHelper.DialogueUpdate(uiDiff); +} + +void instance_naxxramas::SetGothTriggers() +{ + Creature* pGoth = GetSingleCreatureFromStorage(NPC_GOTHIK); + + if (!pGoth) + return; + + for (GuidList::const_iterator itr = m_lGothTriggerList.begin(); itr != m_lGothTriggerList.end(); ++itr) + { + if (Creature* pTrigger = instance->GetCreature(*itr)) + { + GothTrigger pGt; + pGt.bIsAnchorHigh = (pTrigger->GetPositionZ() >= (pGoth->GetPositionZ() - 5.0f)); + pGt.bIsRightSide = IsInRightSideGothArea(pTrigger); + + m_mGothTriggerMap[pTrigger->GetObjectGuid()] = pGt; + } + } +} + +Creature* instance_naxxramas::GetClosestAnchorForGoth(Creature* pSource, bool bRightSide) +{ + std::list lList; + + for (UNORDERED_MAP::iterator itr = m_mGothTriggerMap.begin(); itr != m_mGothTriggerMap.end(); ++itr) + { + if (!itr->second.bIsAnchorHigh) + continue; + + if (itr->second.bIsRightSide != bRightSide) + continue; + + if (Creature* pCreature = instance->GetCreature(itr->first)) + lList.push_back(pCreature); + } + + if (!lList.empty()) + { + lList.sort(ObjectDistanceOrder(pSource)); + return lList.front(); + } + + return NULL; +} + +void instance_naxxramas::GetGothSummonPointCreatures(std::list& lList, bool bRightSide) +{ + for (UNORDERED_MAP::iterator itr = m_mGothTriggerMap.begin(); itr != m_mGothTriggerMap.end(); ++itr) + { + if (itr->second.bIsAnchorHigh) + continue; + + if (itr->second.bIsRightSide != bRightSide) + continue; + + if (Creature* pCreature = instance->GetCreature(itr->first)) + lList.push_back(pCreature); + } +} + +// Right is right side from gothik (eastern) +bool instance_naxxramas::IsInRightSideGothArea(Unit* pUnit) +{ + if (GameObject* pCombatGate = GetSingleGameObjectFromStorage(GO_MILI_GOTH_COMBAT_GATE)) + return (pCombatGate->GetPositionY() >= pUnit->GetPositionY()); + + script_error_log("left/right side check, Gothik combat area failed."); + return true; +} + +void instance_naxxramas::DoTriggerHeiganTraps(Creature* pHeigan, uint32 uiAreaIndex) +{ + if (uiAreaIndex >= MAX_HEIGAN_TRAP_AREAS) + return; + + for (GuidList::const_iterator itr = m_alHeiganTrapGuids[uiAreaIndex].begin(); itr != m_alHeiganTrapGuids[uiAreaIndex].end(); ++itr) + { + if (GameObject* pTrap = instance->GetGameObject(*itr)) + pTrap->Use(pHeigan); + } +} + +void instance_naxxramas::SetChamberCenterCoords(float fX, float fY, float fZ) +{ + m_fChamberCenterX = fX; + m_fChamberCenterY = fY; + m_fChamberCenterZ = fZ; +} + +void instance_naxxramas::DoTaunt() +{ + if (m_auiEncounter[TYPE_KELTHUZAD] != DONE) + { + uint8 uiWingsCleared = 0; + + if (m_auiEncounter[TYPE_MAEXXNA] == DONE) + ++uiWingsCleared; + + if (m_auiEncounter[TYPE_LOATHEB] == DONE) + ++uiWingsCleared; + + if (m_auiEncounter[TYPE_FOUR_HORSEMEN] == DONE) + ++uiWingsCleared; + + if (m_auiEncounter[TYPE_THADDIUS] == DONE) + ++uiWingsCleared; + + switch (uiWingsCleared) + { + case 1: DoOrSimulateScriptTextForThisInstance(SAY_KELTHUZAD_TAUNT1, NPC_KELTHUZAD); break; + case 2: DoOrSimulateScriptTextForThisInstance(SAY_KELTHUZAD_TAUNT2, NPC_KELTHUZAD); break; + case 3: DoOrSimulateScriptTextForThisInstance(SAY_KELTHUZAD_TAUNT3, NPC_KELTHUZAD); break; + case 4: DoOrSimulateScriptTextForThisInstance(SAY_KELTHUZAD_TAUNT4, NPC_KELTHUZAD); break; + } + } +} + +InstanceData* GetInstanceData_instance_naxxramas(Map* pMap) +{ + return new instance_naxxramas(pMap); +} + +bool AreaTrigger_at_naxxramas(Player* pPlayer, AreaTriggerEntry const* pAt) +{ + if (pAt->id == AREATRIGGER_KELTHUZAD) + { + if (pPlayer->isGameMaster() || !pPlayer->IsAlive()) + return false; + + instance_naxxramas* pInstance = (instance_naxxramas*)pPlayer->GetInstanceData(); + + if (!pInstance) + return false; + + pInstance->SetChamberCenterCoords(pAt->x, pAt->y, pAt->z); + + if (pInstance->GetData(TYPE_KELTHUZAD) == NOT_STARTED) + { + if (Creature* pKelthuzad = pInstance->GetSingleCreatureFromStorage(NPC_KELTHUZAD)) + { + if (pKelthuzad->IsAlive()) + { + pInstance->SetData(TYPE_KELTHUZAD, IN_PROGRESS); + pKelthuzad->SetInCombatWithZone(); + } + } + } + } + + if (pAt->id == AREATRIGGER_THADDIUS_DOOR) + { + if (instance_naxxramas* pInstance = (instance_naxxramas*)pPlayer->GetInstanceData()) + { + if (pInstance->GetData(TYPE_THADDIUS) == NOT_STARTED) + { + if (Creature* pThaddius = pInstance->GetSingleCreatureFromStorage(NPC_THADDIUS)) + { + pInstance->SetData(TYPE_THADDIUS, SPECIAL); + DoScriptText(SAY_THADDIUS_GREET, pThaddius); + } + } + } + } + + return false; +} + +void AddSC_instance_naxxramas() +{ + Script* pNewScript; + + pNewScript = new Script; + pNewScript->Name = "instance_naxxramas"; + pNewScript->GetInstanceData = &GetInstanceData_instance_naxxramas; + pNewScript->RegisterSelf(); + + pNewScript = new Script; + pNewScript->Name = "at_naxxramas"; + pNewScript->pAreaTrigger = &AreaTrigger_at_naxxramas; + pNewScript->RegisterSelf(); +} diff --git a/src/modules/SD2/scripts/northrend/naxxramas/naxxramas.h b/src/modules/SD2/scripts/northrend/naxxramas/naxxramas.h new file mode 100644 index 000000000..de228d6ba --- /dev/null +++ b/src/modules/SD2/scripts/northrend/naxxramas/naxxramas.h @@ -0,0 +1,278 @@ +/* Copyright (C) 2006 - 2013 ScriptDev2 + * This program is free software licensed under GPL version 2 + * Please see the included DOCS/LICENSE.TXT for more information */ + +#ifndef DEF_NAXXRAMAS_H +#define DEF_NAXXRAMAS_H + +enum +{ + MAX_ENCOUNTER = 16, + + // A few instance-script related texts + SAY_THADDIUS_GREET = -1533029, + + // Kel'Thuzad + SAY_KELTHUZAD_CAT_DIED = -1533089, + // Kel'Thuzad's taunts after killing Wing Bosses + SAY_KELTHUZAD_TAUNT1 = -1533090, + SAY_KELTHUZAD_TAUNT2 = -1533091, + SAY_KELTHUZAD_TAUNT3 = -1533092, + SAY_KELTHUZAD_TAUNT4 = -1533093, + // Dialogues with Lich King + SAY_SAPP_DIALOG1 = -1533084, + SAY_SAPP_DIALOG2_LICH = -1533085, + SAY_SAPP_DIALOG3 = -1533086, + SAY_SAPP_DIALOG4_LICH = -1533087, + SAY_SAPP_DIALOG5 = -1533088, + // Horsemen dialogue texts + SAY_BLAU_TAUNT1 = -1533045, + SAY_BLAU_TAUNT2 = -1533046, + SAY_BLAU_TAUNT3 = -1533047, // NYI - requires additiona research + SAY_RIVE_TAUNT1 = -1533071, + SAY_RIVE_TAUNT2 = -1533072, + SAY_RIVE_TAUNT3 = -1533073, // NYI - requires additiona research + SAY_KORT_TAUNT1 = -1533052, + SAY_KORT_TAUNT2 = -1533053, + SAY_KORT_TAUNT3 = -1533054, // NYI - requires additiona research + SAY_ZELI_TAUNT1 = -1533059, + SAY_ZELI_TAUNT2 = -1533060, + SAY_ZELI_TAUNT3 = -1533061, // NYI - requires additiona research + + TYPE_ANUB_REKHAN = 0, + TYPE_FAERLINA = 1, + TYPE_MAEXXNA = 2, + + TYPE_NOTH = 3, + TYPE_HEIGAN = 4, + TYPE_LOATHEB = 5, + + TYPE_RAZUVIOUS = 6, + TYPE_GOTHIK = 7, + TYPE_FOUR_HORSEMEN = 8, + + TYPE_PATCHWERK = 9, + TYPE_GROBBULUS = 10, + TYPE_GLUTH = 11, + TYPE_THADDIUS = 12, + + TYPE_SAPPHIRON = 13, + TYPE_KELTHUZAD = 14, + + TYPE_UNDYING_FAILED = 15, // Achievements Undying and Immortal, needs to be saved to database + + MAX_SPECIAL_ACHIEV_CRITS = 6, + + TYPE_ACHIEV_SAFETY_DANCE = 0, + TYPE_ACHIEV_KNOCK_YOU_OUT = 1, + TYPE_ACHIEV_HUNDRED_CLUB = 2, + TYPE_ACHIEV_SHOCKING = 3, + TYPE_ACHIEV_SPORE_LOSER = 4, + TYPE_ACHIEV_GET_ENOUGH = 5, + + MAX_HEIGAN_TRAP_AREAS = 4, + + NPC_ANUB_REKHAN = 15956, + NPC_FAERLINA = 15953, + + NPC_THADDIUS = 15928, + NPC_STALAGG = 15929, + NPC_FEUGEN = 15930, + NPC_TESLA_COIL = 16218, + + NPC_ZELIEK = 16063, + NPC_THANE = 16064, + NPC_BLAUMEUX = 16065, + NPC_RIVENDARE = 30549, + + NPC_SAPPHIRON = 15989, + NPC_KELTHUZAD = 15990, + NPC_THE_LICHKING = 16980, + NPC_MR_BIGGLESWORTH = 16998, + + // Gothik + NPC_GOTHIK = 16060, + NPC_SUB_BOSS_TRIGGER = 16137, // summon locations + NPC_UNREL_TRAINEE = 16124, + NPC_UNREL_DEATH_KNIGHT = 16125, + NPC_UNREL_RIDER = 16126, + NPC_SPECT_TRAINEE = 16127, + NPC_SPECT_DEATH_KNIGHT = 16148, + NPC_SPECT_RIDER = 16150, + NPC_SPECT_HORSE = 16149, + + // Kel'Thuzad + NPC_SOLDIER_FROZEN = 16427, + NPC_UNSTOPPABLE_ABOM = 16428, + NPC_SOUL_WEAVER = 16429, + NPC_GUARDIAN = 16441, + + // Arachnid Quarter + GO_ARAC_ANUB_DOOR = 181126, // encounter door + GO_ARAC_ANUB_GATE = 181195, // open after boss is dead + GO_ARAC_FAER_WEB = 181235, // encounter door + GO_ARAC_FAER_DOOR = 194022, // after faerlina, to outer ring + GO_ARAC_MAEX_INNER_DOOR = 181197, // encounter door + GO_ARAC_MAEX_OUTER_DOOR = 181209, // right before maex + + // Plague Quarter + GO_PLAG_SLIME01_DOOR = 181198, // not used + GO_PLAG_SLIME02_DOOR = 181199, // not used + GO_PLAG_NOTH_ENTRY_DOOR = 181200, // encounter door + GO_PLAG_NOTH_EXIT_DOOR = 181201, // exit, open when boss dead + GO_PLAG_HEIG_ENTRY_DOOR = 181202, + GO_PLAG_HEIG_EXIT_DOOR = 181203, // exit, open when boss dead + GO_PLAG_LOAT_DOOR = 181241, // encounter door + + // Military Quarter + GO_MILI_GOTH_ENTRY_GATE = 181124, // used while encounter is in progress + GO_MILI_GOTH_EXIT_GATE = 181125, // exit, open at boss dead + GO_MILI_GOTH_COMBAT_GATE = 181170, // used while encounter is in progress + GO_MILI_HORSEMEN_DOOR = 181119, // encounter door + + GO_CHEST_HORSEMEN_NORM = 181366, // four horsemen event, DoRespawnGameObject() when event == DONE + GO_CHEST_HORSEMEN_HERO = 193426, + + // Construct Quarter + GO_CONS_PATH_EXIT_DOOR = 181123, + GO_CONS_GLUT_EXIT_DOOR = 181120, + GO_CONS_THAD_DOOR = 181121, // Thaddius enc door + GO_CONS_NOX_TESLA_FEUGEN = 181477, + GO_CONS_NOX_TESLA_STALAGG = 181478, + + // Frostwyrm Lair + GO_KELTHUZAD_WATERFALL_DOOR = 181225, // exit, open after sapphiron is dead + GO_KELTHUZAD_EXIT_DOOR = 181228, + + // Eyes + GO_ARAC_EYE_RAMP = 181212, + GO_PLAG_EYE_RAMP = 181211, + GO_MILI_EYE_RAMP = 181210, + GO_CONS_EYE_RAMP = 181213, + + GO_ARAC_EYE_BOSS = 181233, + GO_PLAG_EYE_BOSS = 181231, + GO_MILI_EYE_BOSS = 181230, + GO_CONS_EYE_BOSS = 181232, + + // Portals + GO_ARAC_PORTAL = 181575, + GO_PLAG_PORTAL = 181577, + GO_MILI_PORTAL = 181578, + GO_CONS_PORTAL = 181576, + + AREATRIGGER_FROSTWYRM = 4120, // not needed here, but AT to be scripted + AREATRIGGER_KELTHUZAD = 4112, + AREATRIGGER_GOTHIK = 4116, + AREATRIGGER_THADDIUS_DOOR = 4113, + + // Achievement related + ACHIEV_CRIT_SAFETY_DANCE_N = 7264, // Heigan, achievs 1996, 2139 + ACHIEV_CRIT_SAFETY_DANCE_H = 7548, + ACHIEV_CRIT_KNOCK_YOU_OUT_N = 7265, // Faerlina, achievs 1997, 2140 + ACHIEV_CRIT_KNOCK_YOU_OUT_H = 7549, + ACHIEV_CRIT_HUNDRED_CLUB_N = 7567, // Sapphiron, achievs 2146, 2147 + ACHIEV_CRIT_HUNDRED_CLUB_H = 7568, + ACHIEV_CRIT_TOGETHER_N = 7600, // Four Horsemen, achievs 2176, 2177 + ACHIEV_CRIT_TOGETHER_H = 7601, + ACHIEV_CRIT_SHOCKING_N = 7604, // Thaddius, achievs 2178, 2179 + ACHIEV_CRIT_SHOCKING_H = 7605, + ACHIEV_CRIT_SPORE_LOSER_N = 7612, // Loatheb, achievs 2182, 2183 + ACHIEV_CRIT_SPORE_LOSER_H = 7613, + ACHIEV_CRIT_GET_ENOUGH_N = 7614, // Kel'Thuzad, achievs 2184, 2185 + ACHIEV_CRIT_GET_ENOUGH_H = 7615, + + // 'The Immortal'(25m) or 'Undying'(10m) - (achievs 2186, 2187) + ACHIEV_CRIT_IMMORTAL_KEL = 7616, + ACHIEV_CRIT_IMMOORTAL_LOA = 13236, + ACHIEV_CRIT_IMMOORTAL_THAD = 13235, + ACHIEV_CRIT_IMMOORTAL_MAEX = 13234, + ACHIEV_CRIT_IMMOORTAL_HORSE = 13233, + ACHIEV_CRIT_UNDYING_KEL = 7617, + ACHIEV_CRIT_UNDYING_HORSE = 13237, + ACHIEV_CRIT_UNDYING_MAEX = 13238, + ACHIEV_CRIT_UNDYING_LOA = 13239, + ACHIEV_CRIT_UNDYING_THAD = 13240, + + // Timed achievement criterias + ACHIEV_START_PATCHWERK_ID = 10286, + ACHIEV_START_MAEXXNA_ID = 9891, +}; + +struct GothTrigger +{ + bool bIsRightSide; + bool bIsAnchorHigh; +}; + +static const float aSapphPositions[4] = {3521.48f, -5234.87f, 137.626f, 4.53329f}; + +class instance_naxxramas : public ScriptedInstance +{ + public: + instance_naxxramas(Map* pMap); + ~instance_naxxramas() {} + + void Initialize() override; + + bool IsEncounterInProgress() const override; + + void OnPlayerEnter(Player* pPlayer) override; + void OnCreatureCreate(Creature* pCreature) override; + void OnObjectCreate(GameObject* pGo) override; + + void OnPlayerDeath(Player* pPlayer) override; + void OnCreatureDeath(Creature* pCreature) override; + + void SetData(uint32 uiType, uint32 uiData) override; + uint32 GetData(uint32 uiType) const override; + + void SetSpecialAchievementCriteria(uint32 uiType, bool bIsMet); + bool CheckAchievementCriteriaMeet(uint32 uiCriteriaId, Player const* pSource, Unit const* pTarget, uint32 uiMiscValue1 /* = 0*/) const override; + + const char* Save() const override { return m_strInstData.c_str(); } + void Load(const char* chrIn) override; + + void Update(uint32 uiDiff) override; + + // Heigan + void DoTriggerHeiganTraps(Creature* pHeigan, uint32 uiAreaIndex); + + // goth + void SetGothTriggers(); + Creature* GetClosestAnchorForGoth(Creature* pSource, bool bRightSide); + void GetGothSummonPointCreatures(std::list& lList, bool bRightSide); + bool IsInRightSideGothArea(Unit* pUnit); + + // thaddius + void GetThadTeslaCreatures(GuidList& lList) { lList = m_lThadTeslaCoilList; }; + + // kel + void SetChamberCenterCoords(float fX, float fY, float fZ); + void GetChamberCenterCoords(float& fX, float& fY, float& fZ) { fX = m_fChamberCenterX; fY = m_fChamberCenterY; fZ = m_fChamberCenterZ; } + void DoTaunt(); + + protected: + uint32 m_auiEncounter[MAX_ENCOUNTER]; + bool m_abAchievCriteria[MAX_SPECIAL_ACHIEV_CRITS]; + std::string m_strInstData; + + GuidList m_lThadTeslaCoilList; + GuidList m_lGothTriggerList; + + UNORDERED_MAP m_mGothTriggerMap; + GuidList m_alHeiganTrapGuids[MAX_HEIGAN_TRAP_AREAS]; + + float m_fChamberCenterX; + float m_fChamberCenterY; + float m_fChamberCenterZ; + + uint32 m_uiSapphSpawnTimer; + uint32 m_uiTauntTimer; + uint32 m_uiHorsemenAchievTimer; + uint8 m_uiHorseMenKilled; + + DialogueHelper m_dialogueHelper; +}; + +#endif diff --git a/src/modules/SD2/scripts/northrend/nexus/eye_of_eternity/boss_malygos.cpp b/src/modules/SD2/scripts/northrend/nexus/eye_of_eternity/boss_malygos.cpp new file mode 100644 index 000000000..99ce4e3fe --- /dev/null +++ b/src/modules/SD2/scripts/northrend/nexus/eye_of_eternity/boss_malygos.cpp @@ -0,0 +1,723 @@ +/* Copyright (C) 2006 - 2013 ScriptDev2 + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +/* ScriptData +SDName: boss_malygos +SD%Complete: 80 +SDComment: Timers need adjustments; Vortex event NYI; Npc movement in Phase 2 NYI. +SDCategory: Eye of Eternity +EndScriptData */ + +#include "precompiled.h" +#include "eye_of_eternity.h" +#include "TemporarySummon.h" + +enum +{ + SAY_INTRO_1 = -1616000, + SAY_INTRO_2 = -1616001, + SAY_INTRO_3 = -1616002, + SAY_INTRO_4 = -1616003, + SAY_INTRO_5 = -1616004, + SAY_AGGRO = -1616005, + SAY_VORTEX = -1616006, + SAY_SPARK_BUFF = -1616007, + SAY_SLAY_1_A = -1616008, + SAY_SLAY_1_B = -1616009, + SAY_SLAY_1_C = -1616010, + SAY_END_PHASE_1 = -1616011, + SAY_START_PHASE_2 = -1616012, + SAY_DEEP_BREATH = -1616013, + SAY_SHELL = -1616014, + SAY_SLAY_2_A = -1616015, + SAY_SLAY_2_B = -1616016, + SAY_SLAY_2_C = -1616017, + SAY_END_PHASE_2 = -1616018, + SAY_INTRO_PHASE_3 = -1616019, + SAY_START_PHASE_3 = -1616020, + SAY_SLAY_3_A = -1616021, + SAY_SLAY_3_B = -1616022, + SAY_SLAY_3_C = -1616023, + SAY_SURGE = -1616024, + SAY_SPELL_1 = -1616025, + SAY_SPELL_2 = -1616026, + SAY_SPELL_3 = -1616027, + SAY_DEATH = -1616028, + + SAY_EMOTE_SPARK = -1616033, + SAY_EMOTE_BREATH = -1616034, + + // phase 1 spells + SPELL_BERSERK = 26662, + SPELL_ARCANE_BREATH = 56272, + SPELL_ARCANE_BREATH_H = 60072, + SPELL_SUMMON_SPARK = 56140, + SPELL_VORTEX = 56105, + + // phase 2 spells + SPELL_ARCANE_STORM = 57459, // related to spell 61693 + SPELL_ARCANE_STORM_H = 61694, + SPELL_SUMMON_ARCANE_BOMB = 56429, // summons 30282 + SPELL_ARCANE_BOMB = 56430, // triggers 56432 and 56431 on target hit + SPELL_SURGE_OF_POWER_PULSE = 56505, // deep breath spell + // SPELL_ARCANE_PULSE = 57432, // purpose unk + + // transition spells + SPELL_DESTROY_PLATFORM_PRE = 58842, + SPELL_DESTROY_PLATFORM_BOOM = 59084, + SPELL_DESTROY_PLATFORM_EVENT = 59099, + SPELL_SUMMON_RED_DRAGON = 58846, + + // phase 3 spells + SPELL_STATIC_FIELD_SUMMON = 57430, // cast on 1 or 3 targets based on difficulty + SPELL_SURGE_OF_POWER = 57407, // related to 60936 and 60939 + + // power spark + SPELL_POWER_SPARK_MALYGOS = 56152, + SPELL_POWER_SPARK_PLAYERS = 55852, + SPELL_POWER_SPARK_VISUAL = 55845, + + // vortex - thse spells require additional research + // related auras: 55853, 55883, 56263, 56264, 56265, 56266, 59666, 61071, 61072, 61073, 61074, 61075 + SPELL_VORTEX_SPAWN = 59670, + SPELL_VORTEX_VISUAL = 55873, + SPELL_VORTEX_CHANNEL = 56237, + + // arcane overload - handled in core + // SPELL_ARCANE_OVERLOAD = 56432, + // SPELL_ARCANE_BOMB_KNOCKBACK = 56431, + + // static field + SPELL_STATIC_FIELD = 57428, + + // vehicle related + SPELL_SUMMON_DISC = 56378, // summons npc 30234 for players + SPELL_RIDE_RED_DRAGON = 56072, + SPELL_FLIGHT = 60534, // ToDo: check if id is correct! + + // summoned npcs + NPC_VORTEX = 30090, + NPC_POWER_SPARK = 30084, + + NPC_NEXUS_LORD = 30245, + NPC_HOVER_DISK = 30248, + NPC_SCION_OF_ETERNITY = 30249, + NPC_ARCANE_OVERLOAD = 30282, + + NPC_STATIC_FIELD = 30592, + + // phases + PHASE_FLOOR = 1, + PHASE_TRANSITION_1 = 2, + PHASE_DISCS = 3, + PHASE_TRANSITION_2 = 4, + PHASE_DRAGONS = 5, + + POINT_ID_COMBAT = 1, +}; + +static const DialogueEntry aIntroDialogue[] = +{ + // Intro dialogue + {SAY_INTRO_1, NPC_MALYGOS, 11000}, + {SAY_INTRO_2, NPC_MALYGOS, 13000}, + {SAY_INTRO_3, NPC_MALYGOS, 14000}, + {SAY_INTRO_4, NPC_MALYGOS, 12000}, + {SAY_INTRO_5, NPC_MALYGOS, 0}, + + // Phase transitions + {SAY_END_PHASE_1, NPC_MALYGOS, 25000}, + {PHASE_DISCS, 0, 0}, + {SAY_END_PHASE_2, NPC_MALYGOS, 13000}, + {SPELL_DESTROY_PLATFORM_BOOM, 0, 2000}, + {SPELL_SUMMON_RED_DRAGON, 0, 5000}, + {SAY_INTRO_PHASE_3, NPC_MALYGOS, 0}, + {0, 0, 0}, +}; + +static const float aCenterMovePos[3] = {754.395f, 1301.270f, 266.253f}; +static const float aAlextraszaSpawnPos[4] = {700.354f, 1310.718f, 298.13f, 6.02f}; +static const float aAlextraszaMovePos[3] = {726.754f, 1307.259f, 282.679f}; + +/*###### +## boss_malygos +######*/ + +struct boss_malygosAI : public ScriptedAI, private DialogueHelper +{ + boss_malygosAI(Creature* pCreature) : ScriptedAI(pCreature), + DialogueHelper(aIntroDialogue) + { + m_pInstance = (instance_eye_of_eternity*)pCreature->GetInstanceData(); + m_bIsRegularMode = pCreature->GetMap()->IsRegularDifficulty(); + InitializeDialogueHelper(m_pInstance); + + m_uiMaxStaticFieldTargets = m_bIsRegularMode ? 1 : 3; + m_uiMaxNexusLords = m_bIsRegularMode ? 2 : 4; + m_uiMaxScions = m_bIsRegularMode ? 4 : 8; + + m_bHasDoneIntro = false; + Reset(); + } + + instance_eye_of_eternity* m_pInstance; + bool m_bIsRegularMode; + + bool m_bHasDoneIntro; + + uint8 m_uiPhase; + uint8 m_uiMaxNexusLords; + uint8 m_uiMaxScions; + uint8 m_uiAddsDeadCount; + uint8 m_uiMaxStaticFieldTargets; + + uint32 m_uiBerserkTimer; + uint32 m_uiVortexTimer; + uint32 m_uiArcaneBreathTimer; + uint32 m_uiPowerSparkTimer; + + uint32 m_uiArcanePulseTimer; + uint32 m_uiOverloadTimer; + uint32 m_uiArcaneStormTimer; + + uint32 m_uiStaticFieldTimer; + uint32 m_uiSurgeOfPowerTimer; + + void Reset() override + { + m_uiPhase = PHASE_FLOOR; + + m_uiBerserkTimer = 10 * MINUTE * IN_MILLISECONDS; + m_uiVortexTimer = 60000; + m_uiArcaneBreathTimer = 15000; + m_uiPowerSparkTimer = 30000; + + m_uiArcanePulseTimer = 60000; + m_uiOverloadTimer = 1000; + m_uiArcaneStormTimer = 15000; + m_uiAddsDeadCount = 0; + + m_uiStaticFieldTimer = 15000; + m_uiSurgeOfPowerTimer = 30000; + + // reset flags + m_creature->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE); + m_creature->SetByteFlag(UNIT_FIELD_BYTES_1, 3, UNIT_BYTE1_FLAG_ALWAYS_STAND | UNIT_BYTE1_FLAG_UNK_2); + + SetCombatMovement(false); + } + + void Aggro(Unit* /*pWho*/) override + { + DoScriptText(SAY_AGGRO, m_creature); + + if (m_pInstance) + m_pInstance->SetData(TYPE_MALYGOS, IN_PROGRESS); + } + + void MoveInLineOfSight(Unit* pWho) override + { + if (!m_bHasDoneIntro && pWho->GetTypeId() == TYPEID_PLAYER && !((Player*)pWho)->isGameMaster() && m_creature->IsWithinDistInMap(pWho, 110.0f)) + { + StartNextDialogueText(SAY_INTRO_1); + m_bHasDoneIntro = true; + } + + ScriptedAI::MoveInLineOfSight(pWho); + } + + void KilledUnit(Unit* /*pVictim*/) override + { + uint8 uiTextId = 0; + switch (m_uiPhase) + { + case PHASE_FLOOR: uiTextId = urand(0, 2); break; + case PHASE_DISCS: uiTextId = urand(3, 5); break; + case PHASE_DRAGONS: uiTextId = urand(6, 8); break; + } + + switch (uiTextId) + { + case 0: DoScriptText(SAY_SLAY_1_A, m_creature); break; + case 1: DoScriptText(SAY_SLAY_1_B, m_creature); break; + case 2: DoScriptText(SAY_SLAY_1_C, m_creature); break; + + case 3: DoScriptText(SAY_SLAY_2_A, m_creature); break; + case 4: DoScriptText(SAY_SLAY_2_B, m_creature); break; + case 5: DoScriptText(SAY_SLAY_2_C, m_creature); break; + + case 6: DoScriptText(SAY_SLAY_3_A, m_creature); break; + case 7: DoScriptText(SAY_SLAY_3_B, m_creature); break; + case 8: DoScriptText(SAY_SLAY_3_C, m_creature); break; + } + } + + void JustDied(Unit* /*pKiller*/) override + { + DoScriptText(SAY_DEATH, m_creature); + m_creature->SummonCreature(NPC_ALEXSTRASZA, aAlextraszaSpawnPos[0], aAlextraszaSpawnPos[1], aAlextraszaSpawnPos[2], aAlextraszaSpawnPos[3], TEMPSUMMON_TIMED_DESPAWN, 5 * MINUTE * IN_MILLISECONDS); + + if (m_pInstance) + m_pInstance->SetData(TYPE_MALYGOS, DONE); + } + + void JustReachedHome() override + { + if (m_pInstance) + m_pInstance->SetData(TYPE_MALYGOS, FAIL); + } + + void MovementInform(uint32 uiMoveType, uint32 uiPointId) override + { + if (uiMoveType != POINT_MOTION_TYPE) + return; + + if (uiPointId == POINT_ID_COMBAT) + { + m_creature->SetLevitate(false); + SetCombatMovement(true); + DoStartMovement(m_creature->getVictim()); + m_creature->RemoveByteFlag(UNIT_FIELD_BYTES_1, 3, UNIT_BYTE1_FLAG_ALWAYS_STAND | UNIT_BYTE1_FLAG_UNK_2); + } + } + + void JustSummoned(Creature* pSummoned) override + { + switch (pSummoned->GetEntry()) + { + case NPC_ALEXSTRASZA: + pSummoned->SetByteFlag(UNIT_FIELD_BYTES_1, 3, UNIT_BYTE1_FLAG_ALWAYS_STAND | UNIT_BYTE1_FLAG_UNK_2); + pSummoned->GetMotionMaster()->MovePoint(0, aAlextraszaMovePos[0], aAlextraszaMovePos[1], aAlextraszaMovePos[2]); + break; + case NPC_POWER_SPARK: + pSummoned->GetMotionMaster()->MoveFollow(m_creature, 0, 0); + break; + case NPC_ARCANE_OVERLOAD: + DoCastSpellIfCan(pSummoned, SPELL_ARCANE_BOMB, CAST_TRIGGERED); + break; + case NPC_STATIC_FIELD: + pSummoned->CastSpell(pSummoned, SPELL_STATIC_FIELD, false); + break; + case NPC_NEXUS_LORD: + case NPC_SCION_OF_ETERNITY: + if (Creature* pDisk = GetClosestCreatureWithEntry(pSummoned, NPC_HOVER_DISK, 10.0f)) + pSummoned->CastSpell(pDisk, SPELL_RIDE_VEHICLE_HARDCODED, true); + pSummoned->SetInCombatWithZone(); + break; + case NPC_HOVER_DISK: + pSummoned->CastSpell(pSummoned, SPELL_FLIGHT, true); + break; + } + } + + void SummonedCreatureJustDied(Creature* pSummoned) override + { + if (pSummoned->GetEntry() == NPC_NEXUS_LORD || pSummoned->GetEntry() == NPC_SCION_OF_ETERNITY) + { + pSummoned->CastSpell(pSummoned, SPELL_SUMMON_DISC, true); + ++m_uiAddsDeadCount; + + // When all adds are killed start phase 3 + if (m_uiAddsDeadCount == m_uiMaxScions + m_uiMaxNexusLords) + { + StartNextDialogueText(SAY_END_PHASE_2); + m_uiPhase = PHASE_TRANSITION_2; + + // Start platform animation - not sure if this is cast by the right npc + if (m_pInstance) + { + if (Creature* pTrigger = m_pInstance->GetSingleCreatureFromStorage(NPC_LARGE_TRIGGER)) + pTrigger->CastSpell(pTrigger, SPELL_DESTROY_PLATFORM_PRE, false); + } + } + } + } + + void SpellHit(Unit* pCaster, const SpellEntry* pSpell) override + { + // Handle yell on Power Spark hit + if (pSpell->Id == SPELL_POWER_SPARK_MALYGOS && pCaster->GetEntry() == NPC_POWER_SPARK && m_uiPhase == PHASE_FLOOR) + DoScriptText(SAY_SPARK_BUFF, m_creature); + } + + void JustDidDialogueStep(int32 iEntry) override + { + switch (iEntry) + { + case PHASE_DISCS: + // ToDo: start some movement over the platform + m_creature->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE); + m_uiPhase = PHASE_DISCS; + DoSpawnAdds(); + break; + case SPELL_DESTROY_PLATFORM_BOOM: + if (m_pInstance) + { + if (Creature* pTrigger = m_pInstance->GetSingleCreatureFromStorage(NPC_LARGE_TRIGGER)) + pTrigger->CastSpell(pTrigger, SPELL_DESTROY_PLATFORM_BOOM, false); + } + break; + case SPELL_SUMMON_RED_DRAGON: + if (m_pInstance) + { + // Destroy the platform + if (GameObject* pPlatform = m_pInstance->GetSingleGameObjectFromStorage(GO_PLATFORM)) + pPlatform->SetFlag(GAMEOBJECT_FLAGS, GO_FLAG_UNK_11); + } + + DoCastSpellIfCan(m_creature, SPELL_SUMMON_RED_DRAGON); + break; + case SAY_INTRO_PHASE_3: + m_creature->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE); + m_uiPhase = PHASE_DRAGONS; + break; + } + } + + // Wrapper to spawn the adds in phase 2 + void DoSpawnAdds() + { + float fX, fY, fZ; + for (uint8 i = 0; i < m_uiMaxNexusLords; ++i) + { + m_creature->GetRandomPoint(aCenterMovePos[0], aCenterMovePos[1], aCenterMovePos[2], 50.0f, fX, fY, fZ); + m_creature->SummonCreature(NPC_HOVER_DISK, fX, fY, fZ + 30.0f, 0, TEMPSUMMON_CORPSE_DESPAWN, 0); + m_creature->SummonCreature(NPC_NEXUS_LORD, fX, fY, fZ + 30.0f, 0, TEMPSUMMON_DEAD_DESPAWN, 0); + } + + for (uint8 i = 0; i < m_uiMaxScions; ++i) + { + m_creature->GetRandomPoint(aCenterMovePos[0], aCenterMovePos[1], aCenterMovePos[2], 50.0f, fX, fY, fZ); + m_creature->SummonCreature(NPC_HOVER_DISK, fX, fY, fZ + 30.0f, 0, TEMPSUMMON_CORPSE_DESPAWN, 0); + m_creature->SummonCreature(NPC_SCION_OF_ETERNITY, fX, fY, fZ + 30.0f, 0, TEMPSUMMON_DEAD_DESPAWN, 0); + } + } + + void UpdateAI(const uint32 uiDiff) override + { + DialogueUpdate(uiDiff); + + if (!m_creature->SelectHostileTarget() || !m_creature->getVictim()) + return; + + if (m_uiBerserkTimer) + { + if (m_uiBerserkTimer <= uiDiff) + { + if (DoCastSpellIfCan(m_creature, SPELL_BERSERK) == CAST_OK) + m_uiBerserkTimer = 0; + } + else + m_uiBerserkTimer -= uiDiff; + } + + switch (m_uiPhase) + { + case PHASE_FLOOR: + + /* ToDo: Enable this when the spells are properly supported in core + if (m_uiVortexTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature, SPELL_VORTEX) == CAST_OK) + { + DoScriptText(SAY_VORTEX, m_creature); + m_uiVortexTimer = 60000; + } + } + else + m_uiVortexTimer -= uiDiff; + */ + + if (m_uiArcaneBreathTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature, m_bIsRegularMode ? SPELL_ARCANE_BREATH : SPELL_ARCANE_BREATH_H) == CAST_OK) + m_uiArcaneBreathTimer = urand(13000, 16000); + } + else + m_uiArcaneBreathTimer -= uiDiff; + + if (m_uiPowerSparkTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature, SPELL_SUMMON_SPARK) == CAST_OK) + { + DoScriptText(SAY_EMOTE_SPARK, m_creature); + m_uiPowerSparkTimer = 30000; + } + } + else + m_uiPowerSparkTimer -= uiDiff; + + if (m_creature->GetHealthPercent() < 50.0f) + { + SetCombatMovement(false); + m_creature->SetLevitate(true); + m_creature->SetByteFlag(UNIT_FIELD_BYTES_1, 3, UNIT_BYTE1_FLAG_ALWAYS_STAND | UNIT_BYTE1_FLAG_UNK_2); + // Move idle first, so we can avoid evading, because of the waypoint movement + m_creature->GetMotionMaster()->MoveIdle(); + m_creature->GetMotionMaster()->MovePoint(0, aCenterMovePos[0], aCenterMovePos[1], aCenterMovePos[2] + 30.0f); + + StartNextDialogueText(SAY_END_PHASE_1); + m_uiPhase = PHASE_TRANSITION_1; + } + + DoMeleeAttackIfReady(); + + break; + case PHASE_DISCS: + + if (m_uiOverloadTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature, SPELL_SUMMON_ARCANE_BOMB) == CAST_OK) + { + if (!urand(0, 3)) + DoScriptText(SAY_SHELL, m_creature); + + m_uiOverloadTimer = urand(16000, 19000); + } + } + else + m_uiOverloadTimer -= uiDiff; + + // Note: the boss should move in certain points before he does the breath ability + if (m_uiArcanePulseTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature, SPELL_SURGE_OF_POWER_PULSE) == CAST_OK) + { + DoScriptText(SAY_DEEP_BREATH, m_creature); + DoScriptText(SAY_EMOTE_BREATH, m_creature); + m_uiArcanePulseTimer = 60000; + } + } + else + m_uiArcanePulseTimer -= uiDiff; + + if (m_uiArcaneStormTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature, m_bIsRegularMode ? SPELL_ARCANE_STORM : SPELL_ARCANE_STORM_H) == CAST_OK) + m_uiArcaneStormTimer = urand(15000, 17000); + } + else + m_uiArcaneStormTimer -= uiDiff; + + break; + case PHASE_DRAGONS: + + if (m_uiStaticFieldTimer < uiDiff) + { + // Cast Static Field spell on a number of targets, based on difficulty + for (uint8 i = 0; i < m_uiMaxStaticFieldTargets; ++i) + { + if (Unit* pTarget = m_creature->SelectAttackingTarget(ATTACKING_TARGET_RANDOM, 0)) + { + if (DoCastSpellIfCan(pTarget, SPELL_STATIC_FIELD_SUMMON, CAST_TRIGGERED) == CAST_OK) + { + switch (urand(0, 2)) + { + case 0: DoScriptText(SAY_SPELL_1, m_creature); break; + case 1: DoScriptText(SAY_SPELL_2, m_creature); break; + case 2: DoScriptText(SAY_SPELL_3, m_creature); break; + } + m_uiStaticFieldTimer = urand(10000, 17000); + } + } + } + } + else + m_uiStaticFieldTimer -= uiDiff; + + if (m_uiSurgeOfPowerTimer < uiDiff) + { + if (Unit* pTarget = m_creature->SelectAttackingTarget(ATTACKING_TARGET_RANDOM, 0)) + { + if (DoCastSpellIfCan(pTarget, SPELL_SURGE_OF_POWER) == CAST_OK) + { + if (!urand(0, 3)) + DoScriptText(SAY_SURGE, m_creature); + + m_uiSurgeOfPowerTimer = urand(5000, 15000); + } + } + } + else + m_uiSurgeOfPowerTimer -= uiDiff; + + break; + case PHASE_TRANSITION_1: + case PHASE_TRANSITION_2: + // Nothing here - wait for transition to finish + break; + } + } +}; + +CreatureAI* GetAI_boss_malygos(Creature* pCreature) +{ + return new boss_malygosAI(pCreature); +} + +/*###### +## npc_power_spark +######*/ + +struct npc_power_sparkAI : public ScriptedAI +{ + npc_power_sparkAI(Creature* pCreature) : ScriptedAI(pCreature) { Reset(); } + + void Reset() override + { + DoCastSpellIfCan(m_creature, SPELL_POWER_SPARK_VISUAL); + } + + void MoveInLineOfSight(Unit* pWho) override + { + if (pWho->GetEntry() == NPC_MALYGOS && m_creature->CanReachWithMeleeAttack(pWho)) + { + DoCastSpellIfCan(m_creature, SPELL_POWER_SPARK_MALYGOS, CAST_TRIGGERED); + m_creature->ForcedDespawn(); + } + } + + void JustDied(Unit* /*pKiller*/) override + { + DoCastSpellIfCan(m_creature, SPELL_POWER_SPARK_PLAYERS, CAST_TRIGGERED); + } + + void AttackStart(Unit* /*pWho*/) override { } + + void UpdateAI(const uint32 /*uiDiff*/) override { } +}; + +CreatureAI* GetAI_npc_power_spark(Creature* pCreature) +{ + return new npc_power_sparkAI(pCreature); +} + +/*###### +## npc_wyrmrest_skytalon +######*/ + +struct npc_wyrmrest_skytalonAI : public ScriptedAI +{ + npc_wyrmrest_skytalonAI(Creature* pCreature) : ScriptedAI(pCreature) + { + SetCombatMovement(false); + m_bHasMounted = false; + Reset(); + } + + bool m_bHasMounted; + + void Reset() override { } + + // TODO: Temporary workaround - please remove when the boarding wrappers are implemented in core + void SpellHit(Unit* pCaster, const SpellEntry* pSpell) override + { + if (pCaster->GetTypeId() != TYPEID_PLAYER) + return; + + if (pSpell->Id == 56071) + DoCastSpellIfCan(m_creature, SPELL_FLIGHT, CAST_TRIGGERED); + } + + // TODO: Enable the wrappers below, when they will be properly supported by the core + /* + void PassengerBoarded(Unit* pPassenger, uint8 uiSeat) override + { + if (pPassenger->GetTypeId() != TYPEID_PLAYER) + return; + + // Set vehicle auras + DoCastSpellIfCan(m_creature, SPELL_FLIGHT, CAST_TRIGGERED); + } + */ + + void UpdateAI(const uint32 /*uiDiff*/) override + { + if (!m_bHasMounted) + { + if (m_creature->IsTemporarySummon()) + { + TemporarySummon* pTemporary = (TemporarySummon*)m_creature; + + // Force player to mount + if (Player* pSummoner = m_creature->GetMap()->GetPlayer(pTemporary->GetSummonerGuid())) + pSummoner->CastSpell(m_creature, SPELL_RIDE_RED_DRAGON, true); + } + + m_bHasMounted = true; + } + } +}; + +CreatureAI* GetAI_npc_wyrmrest_skytalon(Creature* pCreature) +{ + return new npc_wyrmrest_skytalonAI(pCreature); +} + +/*###### +## event_go_focusing_iris +######*/ + +bool ProcessEventId_event_go_focusing_iris(uint32 /*uiEventId*/, Object* pSource, Object* /*pTarget*/, bool /*bIsStart*/) +{ + if (instance_eye_of_eternity* pInstance = (instance_eye_of_eternity*)((Creature*)pSource)->GetInstanceData()) + { + if (pSource->GetTypeId() != TYPEID_PLAYER) + return false; + + if (pInstance->GetData(TYPE_MALYGOS) == IN_PROGRESS || pInstance->GetData(TYPE_MALYGOS) == DONE) + return false; + + Creature* pMalygos = pInstance->GetSingleCreatureFromStorage(NPC_MALYGOS); + Creature* pTrigger = pInstance->GetSingleCreatureFromStorage(NPC_LARGE_TRIGGER); + if (!pMalygos || !pTrigger) + return false; + + // Enter combat area - Move to ground point first, then start chasing target + float fX, fY, fZ; + pTrigger->GetNearPoint(pTrigger, fX, fY, fZ, 0, 30.0f, pTrigger->GetAngle(pMalygos)); + pMalygos->GetMotionMaster()->MovePoint(POINT_ID_COMBAT, fX, fY, fZ); + pMalygos->AI()->AttackStart((Player*)pSource); + + return true; + } + return false; +} + +void AddSC_boss_malygos() +{ + Script* pNewScript; + + pNewScript = new Script; + pNewScript->Name = "boss_malygos"; + pNewScript->GetAI = &GetAI_boss_malygos; + pNewScript->RegisterSelf(); + + pNewScript = new Script; + pNewScript->Name = "npc_power_spark"; + pNewScript->GetAI = &GetAI_npc_power_spark; + pNewScript->RegisterSelf(); + + pNewScript = new Script; + pNewScript->Name = "npc_wyrmrest_skytalon"; + pNewScript->GetAI = &GetAI_npc_wyrmrest_skytalon; + pNewScript->RegisterSelf(); + + pNewScript = new Script; + pNewScript->Name = "event_go_focusing_iris"; + pNewScript->pProcessEventId = &ProcessEventId_event_go_focusing_iris; + pNewScript->RegisterSelf(); +} diff --git a/src/modules/SD2/scripts/northrend/nexus/eye_of_eternity/eye_of_eternity.h b/src/modules/SD2/scripts/northrend/nexus/eye_of_eternity/eye_of_eternity.h new file mode 100644 index 000000000..63444d33d --- /dev/null +++ b/src/modules/SD2/scripts/northrend/nexus/eye_of_eternity/eye_of_eternity.h @@ -0,0 +1,62 @@ +/* Copyright (C) 2006 - 2013 ScriptDev2 + * This program is free software licensed under GPL version 2 + * Please see the included DOCS/LICENSE.TXT for more information */ + +#ifndef DEF_EYE_ETERNITY_H +#define DEF_EYE_ETERNITY_H + +enum +{ + TYPE_MALYGOS = 0, + + NPC_MALYGOS = 28859, + NPC_ALEXSTRASZA = 32295, + NPC_LARGE_TRIGGER = 22517, + NPC_ALEXSTRASZAS_GIFT = 32448, + + GO_EXIT_PORTAL = 193908, + GO_PLATFORM = 193070, + GO_FOCUSING_IRIS = 193958, + GO_FOCUSING_IRIS_H = 193960, + + GO_HEART_OF_MAGIC = 194158, + GO_HEART_OF_MAGIC_H = 194159, + GO_ALEXSTRASZAS_GIFT = 193905, + GO_ALEXSTRASZAS_GIFT_H = 193967, + + ACHIEV_START_MALYGOS_ID = 20387, + + // epilogue related + SAY_OUTRO_1 = -1616029, + SAY_OUTRO_2 = -1616030, + SAY_OUTRO_3 = -1616031, + SAY_OUTRO_4 = -1616032, + + SPELL_ALEXSTRASZAS_GIFT_BEAM = 61028, + SPELL_ALEXSTRASZAS_GIFT_VISUAL = 61023, +}; + +class instance_eye_of_eternity : public ScriptedInstance, private DialogueHelper +{ + public: + instance_eye_of_eternity(Map* pMap); + ~instance_eye_of_eternity() {} + + void Initialize() override; + + bool IsEncounterInProgress() const override; + + void OnCreatureCreate(Creature* pCreature) override; + void OnObjectCreate(GameObject* pGo) override; + + void SetData(uint32 uiType, uint32 uiData) override; + + void Update(uint32 uiDiff) { DialogueUpdate(uiDiff); } + + protected: + void JustDidDialogueStep(int32 iEntry) override; + + uint32 m_uiEncounter; +}; + +#endif diff --git a/src/modules/SD2/scripts/northrend/nexus/eye_of_eternity/instance_eye_of_eternity.cpp b/src/modules/SD2/scripts/northrend/nexus/eye_of_eternity/instance_eye_of_eternity.cpp new file mode 100644 index 000000000..d5d77e518 --- /dev/null +++ b/src/modules/SD2/scripts/northrend/nexus/eye_of_eternity/instance_eye_of_eternity.cpp @@ -0,0 +1,148 @@ +/* Copyright (C) 2006 - 2013 ScriptDev2 + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along 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_eye_of_eternity +SD%Complete: 50 +SDComment: +SDCategory: Eye of Eternity +EndScriptData */ + +#include "precompiled.h" +#include "eye_of_eternity.h" + +static const DialogueEntry aEpilogueDialogue[] = +{ + {NPC_ALEXSTRASZA, 0, 10000}, + {SPELL_ALEXSTRASZAS_GIFT_BEAM, 0, 3000}, + {NPC_ALEXSTRASZAS_GIFT, 0, 2000}, + {SAY_OUTRO_1, NPC_ALEXSTRASZA, 6000}, + {SAY_OUTRO_2, NPC_ALEXSTRASZA, 4000}, + {SAY_OUTRO_3, NPC_ALEXSTRASZA, 23000}, + {SAY_OUTRO_4, NPC_ALEXSTRASZA, 20000}, + {GO_PLATFORM, 0, 0}, + {0, 0, 0}, +}; + +instance_eye_of_eternity::instance_eye_of_eternity(Map* pMap) : ScriptedInstance(pMap), + DialogueHelper(aEpilogueDialogue) +{ + Initialize(); +} + +void instance_eye_of_eternity::Initialize() +{ + m_uiEncounter = NOT_STARTED; + InitializeDialogueHelper(this); +} + +bool instance_eye_of_eternity::IsEncounterInProgress() const +{ + return m_uiEncounter == IN_PROGRESS; +} + +void instance_eye_of_eternity::OnCreatureCreate(Creature* pCreature) +{ + switch (pCreature->GetEntry()) + { + case NPC_MALYGOS: + case NPC_ALEXSTRASZA: + case NPC_LARGE_TRIGGER: + case NPC_ALEXSTRASZAS_GIFT: + m_mNpcEntryGuidStore[pCreature->GetEntry()] = pCreature->GetObjectGuid(); + break; + } +} + +void instance_eye_of_eternity::OnObjectCreate(GameObject* pGo) +{ + switch (pGo->GetEntry()) + { + case GO_EXIT_PORTAL: + case GO_PLATFORM: + case GO_FOCUSING_IRIS: + case GO_FOCUSING_IRIS_H: + case GO_HEART_OF_MAGIC: + case GO_HEART_OF_MAGIC_H: + case GO_ALEXSTRASZAS_GIFT: + case GO_ALEXSTRASZAS_GIFT_H: + m_mGoEntryGuidStore[pGo->GetEntry()] = pGo->GetObjectGuid(); + break; + } +} + +void instance_eye_of_eternity::SetData(uint32 uiType, uint32 uiData) +{ + if (uiType != TYPE_MALYGOS) + return; + + m_uiEncounter = uiData; + if (uiData == IN_PROGRESS) + { + // ToDo: Despawn the exit portal + + DoStartTimedAchievement(ACHIEVEMENT_CRITERIA_TYPE_KILL_CREATURE, ACHIEV_START_MALYGOS_ID); + } + else if (uiData == FAIL) + { + // ToDo: respawn the focus iris and the portal + + if (GameObject* pPlatform = GetSingleGameObjectFromStorage(GO_PLATFORM)) + pPlatform->RemoveFlag(GAMEOBJECT_FLAGS, GO_FLAG_UNK_11); + } + else if (uiData == DONE) + StartNextDialogueText(NPC_ALEXSTRASZA); + + // Currently no reason to save anything +} + +void instance_eye_of_eternity::JustDidDialogueStep(int32 iEntry) +{ + switch (iEntry) + { + case SPELL_ALEXSTRASZAS_GIFT_BEAM: + if (Creature* pAlextrasza = GetSingleCreatureFromStorage(NPC_ALEXSTRASZA)) + pAlextrasza->CastSpell(pAlextrasza, SPELL_ALEXSTRASZAS_GIFT_BEAM, false); + break; + case NPC_ALEXSTRASZAS_GIFT: + if (Creature* pGift = GetSingleCreatureFromStorage(NPC_ALEXSTRASZAS_GIFT)) + pGift->CastSpell(pGift, SPELL_ALEXSTRASZAS_GIFT_VISUAL, false); + DoRespawnGameObject(instance->IsRegularDifficulty() ? GO_ALEXSTRASZAS_GIFT : GO_ALEXSTRASZAS_GIFT_H, 30 * MINUTE); + break; + case GO_PLATFORM: + // ToDo: respawn the portal + if (GameObject* pPlatform = GetSingleGameObjectFromStorage(GO_PLATFORM)) + pPlatform->RemoveFlag(GAMEOBJECT_FLAGS, GO_FLAG_UNK_11); + // Spawn the Heart of Malygos + DoRespawnGameObject(instance->IsRegularDifficulty() ? GO_HEART_OF_MAGIC : GO_HEART_OF_MAGIC_H, 30 * MINUTE); + break; + } +} + +InstanceData* GetInstanceData_instance_eye_of_eternity(Map* pMap) +{ + return new instance_eye_of_eternity(pMap); +} + +void AddSC_instance_eye_of_eternity() +{ + Script* pNewScript; + + pNewScript = new Script; + pNewScript->Name = "instance_eye_of_eternity"; + pNewScript->GetInstanceData = &GetInstanceData_instance_eye_of_eternity; + pNewScript->RegisterSelf(); +} diff --git a/src/modules/SD2/scripts/northrend/nexus/nexus/boss_anomalus.cpp b/src/modules/SD2/scripts/northrend/nexus/nexus/boss_anomalus.cpp new file mode 100644 index 000000000..b67426e0f --- /dev/null +++ b/src/modules/SD2/scripts/northrend/nexus/nexus/boss_anomalus.cpp @@ -0,0 +1,269 @@ +/* Copyright (C) 2006 - 2013 ScriptDev2 + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with 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: 90% +SDComment: Small adjustments required +SDCategory: Nexus +EndScriptData */ + +#include "precompiled.h" +#include "nexus.h" + +enum +{ + SAY_AGGRO = -1576006, + SAY_RIFT = -1576007, + SAY_SHIELD = -1576008, + SAY_KILL = -1576009, + SAY_DEATH = -1576010, + EMOTE_OPEN_RIFT = -1576021, + EMOTE_SHIELD = -1576022, + + // Anomalus + SPELL_CREATE_RIFT = 47743, + SPELL_CHARGE_RIFT = 47747, + SPELL_RIFT_SHIELD = 47748, + + SPELL_SPARK = 47751, + SPELL_SPARK_H = 57062, + + // Chaotic Rift + SPELL_RIFT_AURA = 47687, + SPELL_RIFT_SUMMON_AURA = 47732, + + // Charged Chaotic Rift + SPELL_CHARGED_RIFT_AURA = 47733, + SPELL_CHARGED_RIFT_SUMMON_AURA = 47742, + + NPC_CHAOTIC_RIFT = 26918, + NPC_CRAZED_MANA_WRAITH = 26746 +}; + +/*###### +## boss_anomalus +######*/ + +struct boss_anomalusAI : public ScriptedAI +{ + boss_anomalusAI(Creature* pCreature) : ScriptedAI(pCreature) + { + m_pInstance = (instance_nexus*)pCreature->GetInstanceData(); + m_bIsRegularMode = pCreature->GetMap()->IsRegularDifficulty(); + Reset(); + } + + instance_nexus* m_pInstance; + bool m_bIsRegularMode; + + bool m_bChaoticRift; + uint32 m_uiSparkTimer; + uint32 m_uiCreateRiftTimer; + uint8 m_uiChaoticRiftCount; + + void Reset() override + { + m_bChaoticRift = false; + m_uiSparkTimer = 5000; + m_uiCreateRiftTimer = 25000; + m_uiChaoticRiftCount = 0; + } + + void Aggro(Unit* /*pWho*/) override + { + DoScriptText(SAY_AGGRO, m_creature); + + if (m_pInstance) + m_pInstance->SetData(TYPE_ANOMALUS, IN_PROGRESS); + } + + void JustDied(Unit* /*pKiller*/) override + { + DoScriptText(SAY_DEATH, m_creature); + + if (m_pInstance) + m_pInstance->SetData(TYPE_ANOMALUS, DONE); + } + + void KilledUnit(Unit* /*pVictim*/) override + { + if (urand(0, 1)) + DoScriptText(SAY_KILL, m_creature); + } + + void JustSummoned(Creature* pSummoned) override + { + if (pSummoned->GetEntry() == NPC_CHAOTIC_RIFT) + { + ++m_uiChaoticRiftCount; + + DoScriptText(SAY_RIFT, m_creature); + + if (Unit* pTarget = m_creature->SelectAttackingTarget(ATTACKING_TARGET_RANDOM, 0)) + pSummoned->AI()->AttackStart(pTarget); + } + } + + void SummonedCreatureJustDied(Creature* pSummoned) override + { + if (pSummoned->GetEntry() == NPC_CHAOTIC_RIFT) + { + --m_uiChaoticRiftCount; + + // If players kill the Chaotic Rifts then mark the achievement as false + if (m_pInstance) + m_pInstance->SetSpecialAchievementCriteria(TYPE_ACHIEV_CHAOS_THEORY, false); + + if (!m_uiChaoticRiftCount) + { + if (m_creature->HasAura(SPELL_RIFT_SHIELD)) + m_creature->RemoveAurasDueToSpell(SPELL_RIFT_SHIELD); + } + } + } + + void UpdateAI(const uint32 uiDiff) override + { + if (!m_creature->SelectHostileTarget() || !m_creature->getVictim() || m_creature->HasAura(SPELL_RIFT_SHIELD)) + return; + + // Create additional Chaotic Rift at 50% HP + if (!m_bChaoticRift && m_creature->GetHealthPercent() < 50.0f) + { + // create a rift then set shield up and finally charge rift + if (DoCastSpellIfCan(m_creature, SPELL_CREATE_RIFT, CAST_TRIGGERED) == CAST_OK) + { + // emotes are in this order + DoScriptText(EMOTE_SHIELD, m_creature); + DoScriptText(SAY_SHIELD, m_creature); + DoScriptText(EMOTE_OPEN_RIFT, m_creature); + + DoCastSpellIfCan(m_creature, SPELL_RIFT_SHIELD, CAST_TRIGGERED); + DoCastSpellIfCan(m_creature, SPELL_CHARGE_RIFT, CAST_TRIGGERED); + m_bChaoticRift = true; + } + return; + } + + if (m_uiCreateRiftTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature, SPELL_CREATE_RIFT) == CAST_OK) + { + DoScriptText(SAY_RIFT, m_creature); + DoScriptText(EMOTE_OPEN_RIFT, m_creature); + m_uiCreateRiftTimer = 25000; + } + } + else + m_uiCreateRiftTimer -= uiDiff; + + if (m_uiSparkTimer < uiDiff) + { + if (Unit* pTarget = m_creature->SelectAttackingTarget(ATTACKING_TARGET_RANDOM, 0)) + DoCastSpellIfCan(pTarget, m_bIsRegularMode ? SPELL_SPARK : SPELL_SPARK_H); + + m_uiSparkTimer = 5000; + } + else + m_uiSparkTimer -= uiDiff; + + DoMeleeAttackIfReady(); + } +}; + +CreatureAI* GetAI_boss_anomalus(Creature* pCreature) +{ + return new boss_anomalusAI(pCreature); +} + +struct mob_chaotic_riftAI : public Scripted_NoMovementAI +{ + mob_chaotic_riftAI(Creature* pCreature) : Scripted_NoMovementAI(pCreature) { Reset(); } + + uint32 m_uiChargedRemoveTimer; + + void Reset() override + { + m_uiChargedRemoveTimer = 0; + } + + void Aggro(Unit* /*pWho*/) override + { + // Auras are applied on aggro because there are many npcs with this entry in the instance + DoCastSpellIfCan(m_creature, SPELL_RIFT_AURA, CAST_TRIGGERED); + DoCastSpellIfCan(m_creature, SPELL_RIFT_SUMMON_AURA, CAST_TRIGGERED); + } + + void JustSummoned(Creature* pSummoned) override + { + if (pSummoned->GetEntry() == NPC_CRAZED_MANA_WRAITH) + { + if (Unit* pTarget = m_creature->SelectAttackingTarget(ATTACKING_TARGET_RANDOM, 0)) + pSummoned->AI()->AttackStart(pTarget); + } + } + + void SpellHit(Unit* /*pCaster*/, const SpellEntry* pSpell) override + { + // When hit with Charge Rift cast the Charged Rift spells + if (pSpell->Id == SPELL_CHARGE_RIFT) + { + DoCastSpellIfCan(m_creature, SPELL_CHARGED_RIFT_AURA, CAST_TRIGGERED); + DoCastSpellIfCan(m_creature, SPELL_CHARGED_RIFT_SUMMON_AURA, CAST_TRIGGERED); + m_uiChargedRemoveTimer = 45000; + } + } + + void UpdateAI(const uint32 uiDiff) override + { + if (!m_creature->SelectHostileTarget() || !m_creature->getVictim()) + return; + + if (m_uiChargedRemoveTimer) + { + // The charged spells need to be removed by casting the normal ones in case the npc isn't killed + if (m_uiChargedRemoveTimer <= uiDiff) + { + DoCastSpellIfCan(m_creature, SPELL_RIFT_AURA, CAST_TRIGGERED); + DoCastSpellIfCan(m_creature, SPELL_RIFT_SUMMON_AURA, CAST_TRIGGERED); + m_uiChargedRemoveTimer = 0; + } + else + m_uiChargedRemoveTimer -= uiDiff; + } + } +}; + +CreatureAI* GetAI_mob_chaotic_rift(Creature* pCreature) +{ + return new mob_chaotic_riftAI(pCreature); +} + +void AddSC_boss_anomalus() +{ + Script* pNewScript; + + pNewScript = new Script; + pNewScript->Name = "boss_anomalus"; + pNewScript->GetAI = &GetAI_boss_anomalus; + pNewScript->RegisterSelf(); + + pNewScript = new Script; + pNewScript->Name = "mob_chaotic_rift"; + pNewScript->GetAI = &GetAI_mob_chaotic_rift; + pNewScript->RegisterSelf(); +} diff --git a/src/modules/SD2/scripts/northrend/nexus/nexus/boss_keristrasza.cpp b/src/modules/SD2/scripts/northrend/nexus/nexus/boss_keristrasza.cpp new file mode 100644 index 000000000..5cf33fd1f --- /dev/null +++ b/src/modules/SD2/scripts/northrend/nexus/nexus/boss_keristrasza.cpp @@ -0,0 +1,255 @@ +/* Copyright (C) 2006 - 2013 ScriptDev2 + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with 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: 95% +SDComment: timers tuning +SDCategory: Nexus +EndScriptData */ + +#include "precompiled.h" +#include "nexus.h" + +enum +{ + SAY_AGGRO = -1576016, + SAY_CRYSTAL_NOVA = -1576017, + SAY_ENRAGE = -1576018, + SAY_KILL = -1576019, + SAY_DEATH = -1576020, + + MAX_INTENSE_COLD_STACK = 2, // the max allowed stacks for the achiev to pass + + SPELL_INTENSE_COLD = 48094, + SPELL_INTENSE_COLD_AURA = 48095, // used for Intense cold achiev + + SPELL_CRYSTALFIRE_BREATH = 48096, + SPELL_CRYSTALFIRE_BREATH_H = 57091, + + SPELL_CRYSTALLIZE = 48179, + + SPELL_CRYSTAL_CHAINS = 50997, + + SPELL_TAIL_SWEEP = 50155, + + SPELL_ENRAGE = 8599 +}; + +/*###### +## boss_keristrasza +######*/ + +struct boss_keristraszaAI : public ScriptedAI +{ + boss_keristraszaAI(Creature* pCreature) : ScriptedAI(pCreature) + { + m_pInstance = (ScriptedInstance*)pCreature->GetInstanceData(); + m_bIsRegularMode = pCreature->GetMap()->IsRegularDifficulty(); + Reset(); + } + + ScriptedInstance* m_pInstance; + bool m_bIsRegularMode; + + uint32 uiCrystalChainTimer; + uint32 uiTailSweepTimer; + uint32 uiCrystalfireBreathTimer; + uint32 uiCrystallizeTimer; + uint32 uiCheckIntenseColdTimer; + + bool m_bIsEnraged; + + void Reset() override + { + uiCrystalChainTimer = 30000; + uiTailSweepTimer = urand(5000, 7500); + uiCrystalfireBreathTimer = urand(10000, 20000); + uiCrystallizeTimer = urand(20000, 30000); + uiCheckIntenseColdTimer = 2000; + + m_bIsEnraged = false; + + if (!m_pInstance) + return; + + if (m_creature->IsAlive()) + { + if (m_pInstance->GetData(TYPE_KERISTRASZA) != SPECIAL) + DoCastSpellIfCan(m_creature, SPELL_FROZEN_PRISON, CAST_TRIGGERED); + } + } + + void Aggro(Unit* /*pWho*/) override + { + DoScriptText(SAY_AGGRO, m_creature); + + m_creature->CastSpell(m_creature, SPELL_INTENSE_COLD, true); + + if (m_pInstance) + m_pInstance->SetData(TYPE_KERISTRASZA, IN_PROGRESS); + } + + void JustDied(Unit* /*pKiller*/) override + { + DoScriptText(SAY_DEATH, m_creature); + + if (m_pInstance) + m_pInstance->SetData(TYPE_KERISTRASZA, DONE); + } + + void KilledUnit(Unit* /*pVictim*/) override + { + if (urand(0, 1)) + DoScriptText(SAY_KILL, m_creature); + } + + void UpdateAI(const uint32 uiDiff) override + { + if (!m_creature->SelectHostileTarget() || !m_creature->getVictim()) + return; + + // This needs to be checked only on heroic + if (!m_bIsRegularMode) + { + if (uiCheckIntenseColdTimer < uiDiff) + { + ThreatList playerList = m_creature->GetThreatManager().getThreatList(); + for (ThreatList::const_iterator itr = playerList.begin(); itr != playerList.end(); ++itr) + { + if (Player* pTarget = m_creature->GetMap()->GetPlayer((*itr)->getUnitGuid())) + { + Aura* pAuraIntenseCold = pTarget->GetAura(SPELL_INTENSE_COLD_AURA, EFFECT_INDEX_0); + + if (pAuraIntenseCold) + { + if (pAuraIntenseCold->GetStackAmount() > MAX_INTENSE_COLD_STACK) + { + if (m_pInstance) + m_pInstance->SetData(TYPE_INTENSE_COLD_FAILED, pTarget->GetGUIDLow()); + } + } + } + } + uiCheckIntenseColdTimer = 1000; + } + else + uiCheckIntenseColdTimer -= uiDiff; + } + + if (!m_bIsEnraged && m_creature->GetHealthPercent() < 25.0f) + { + if (!m_creature->IsNonMeleeSpellCasted(false)) + { + m_bIsEnraged = true; + DoScriptText(SAY_ENRAGE, m_creature); + DoCastSpellIfCan(m_creature, SPELL_ENRAGE); + } + } + + if (uiCrystalChainTimer < uiDiff) + { + if (!m_creature->IsNonMeleeSpellCasted(false)) + { + if (m_bIsRegularMode) + { + if (Unit* pTarget = m_creature->SelectAttackingTarget(ATTACKING_TARGET_RANDOM, 1)) + { + if (Player* pPlayer = pTarget->GetCharmerOrOwnerPlayerOrPlayerItself()) + DoCastSpellIfCan(pPlayer, SPELL_CRYSTAL_CHAINS); + + uiCrystalChainTimer = 30000; + } + } + else + { + if (Unit* pSource = m_creature->getVictim()) + { + uiCrystalChainTimer = 15000; + + Player* pPlayer = pSource->GetCharmerOrOwnerPlayerOrPlayerItself(); + + if (!pPlayer) + return; + + if (Group* pGroup = pPlayer->GetGroup()) + { + for (GroupReference* pRef = pGroup->GetFirstMember(); pRef != NULL; pRef = pRef->next()) + { + if (Player* pMember = pRef->getSource()) + { + if (pMember->IsAlive() && pMember->IsWithinDistInMap(m_creature, 50.0f)) + m_creature->CastSpell(pMember, SPELL_CRYSTAL_CHAINS, true); + } + } + } + else + m_creature->CastSpell(pPlayer, SPELL_CRYSTAL_CHAINS, false); + } + } + } + } + else + uiCrystalChainTimer -= uiDiff; + + if (uiTailSweepTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature, SPELL_TAIL_SWEEP) == CAST_OK) + uiTailSweepTimer = urand(2500, 7500); + } + else + uiCrystalChainTimer -= uiDiff; + + if (uiCrystalfireBreathTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature, m_bIsRegularMode ? SPELL_CRYSTALFIRE_BREATH : SPELL_CRYSTALFIRE_BREATH_H) == CAST_OK) + uiCrystalfireBreathTimer = urand(15000, 20000); + } + else + uiCrystalfireBreathTimer -= uiDiff; + + if (!m_bIsRegularMode) + { + if (uiCrystallizeTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature, SPELL_CRYSTALLIZE) == CAST_OK) + { + uiCrystallizeTimer = urand(15000, 25000); + DoScriptText(SAY_CRYSTAL_NOVA, m_creature); + } + } + else + uiCrystallizeTimer -= uiDiff; + } + + DoMeleeAttackIfReady(); + } +}; + +CreatureAI* GetAI_boss_keristrasza(Creature* pCreature) +{ + return new boss_keristraszaAI(pCreature); +} + +void AddSC_boss_keristrasza() +{ + Script* pNewScript; + + pNewScript = new Script; + pNewScript->Name = "boss_keristrasza"; + pNewScript->GetAI = &GetAI_boss_keristrasza; + pNewScript->RegisterSelf(); +} diff --git a/src/modules/SD2/scripts/northrend/nexus/nexus/boss_ormorok.cpp b/src/modules/SD2/scripts/northrend/nexus/nexus/boss_ormorok.cpp new file mode 100644 index 000000000..c6b013094 --- /dev/null +++ b/src/modules/SD2/scripts/northrend/nexus/nexus/boss_ormorok.cpp @@ -0,0 +1,278 @@ +/* Copyright (C) 2006 - 2013 ScriptDev2 + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with 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: 90% +SDComment: Crystal spikes may need small adjustments. +SDCategory: Nexus +EndScriptData */ + +#include "precompiled.h" +#include "nexus.h" + +enum +{ + SAY_AGGRO = -1576011, + SAY_KILL = -1576012, + SAY_REFLECT = -1576013, + SAY_ICESPIKE = -1576014, + SAY_DEATH = -1576015, + EMOTE_BOSS_GENERIC_FRENZY = -1000005, + + SPELL_REFLECTION = 47981, + SPELL_CRYSTAL_SPIKES = 47958, + SPELL_CRYSTAL_SPIKES_H1 = 57082, + SPELL_CRYSTAL_SPIKES_H2 = 57083, + SPELL_FRENZY = 48017, + SPELL_FRENZY_H = 57086, + SPELL_TRAMPLE = 48016, + SPELL_TRAMPLE_H = 57066, + SPELL_SUMMON_TANGLER_H = 61564, + + // crystal spike spells + SPELL_CRYSTAL_SPIKE_BACK = 47936, + SPELL_CRYSTAL_SPIKE_LEFT = 47942, + SPELL_CRYSTAL_SPIKE_RIGHT = 47943, + SPELL_CRYSTAL_SPIKE_AURA = 47941, + SPELL_CRYSTAL_SPIKE_PRE = 50442, + + //SPELL_CRYSTAL_SPIKE_DMG = 47944, + //SPELL_CRYSTAL_SPIKE_DMG_H = 57067, + + // summons + NPC_CRYSTAL_SPIKE_INITIAL = 27101, + NPC_CRYSTAL_SPIKE_TRIGGER = 27079, + //NPC_CRYSTAL_SPIKE = 27099, // summoned by 47947 - handled in eventAI + NPC_CRYSTALLINE_TANGLER = 32665, + + GO_CRYSTAL_SPIKE = 188537, + + MAX_ALLOWED_SPIKES = 28, // this defines the maximum number of spikes summoned per turn +}; + +/*###### +## boss_ormorok +######*/ + +struct boss_ormorokAI : public ScriptedAI +{ + boss_ormorokAI(Creature* pCreature) : ScriptedAI(pCreature) + { + m_pInstance = (ScriptedInstance*)pCreature->GetInstanceData(); + m_bIsRegularMode = pCreature->GetMap()->IsRegularDifficulty(); + Reset(); + } + + ScriptedInstance* m_pInstance; + bool m_bIsRegularMode; + + bool m_bIsEnraged; + + uint32 m_uiTrampleTimer; + uint32 m_uiSpellReflectTimer; + uint32 m_uiCrystalSpikeTimer; + uint32 m_uiTanglerTimer; + uint8 m_uiSpikeCount; + + void Reset() override + { + m_bIsEnraged = false; + + m_uiTrampleTimer = urand(10000, 15000); + m_uiSpellReflectTimer = urand(20000, 23000); + m_uiCrystalSpikeTimer = urand(10000, 15000); + m_uiTanglerTimer = urand(17000, 20000); + m_uiSpikeCount = 0; + } + + void Aggro(Unit* /*pWho*/) override + { + DoScriptText(SAY_AGGRO, m_creature); + } + + void JustDied(Unit* /*pKiller*/) override + { + DoScriptText(SAY_DEATH, m_creature); + + if (m_pInstance) + m_pInstance->SetData(TYPE_ORMOROK, DONE); + } + + void KilledUnit(Unit* /*pVictim*/) override + { + if (urand(0, 1)) + DoScriptText(SAY_KILL, m_creature); + } + + void JustSummoned(Creature* pSummoned) override + { + switch (pSummoned->GetEntry()) + { + case NPC_CRYSTALLINE_TANGLER: + if (Unit* pTarget = m_creature->SelectAttackingTarget(ATTACKING_TARGET_RANDOM, 1)) + pSummoned->AI()->AttackStart(pTarget); + break; + case NPC_CRYSTAL_SPIKE_TRIGGER: + pSummoned->CastSpell(pSummoned, SPELL_CRYSTAL_SPIKE_PRE, true); + ++m_uiSpikeCount; + // no break; + case NPC_CRYSTAL_SPIKE_INITIAL: + // Update orientation so we can always face the boss + pSummoned->SetFacingToObject(m_creature); + + // allow continuous summoning only until we reach the limit + if (m_uiSpikeCount < MAX_ALLOWED_SPIKES) + pSummoned->CastSpell(pSummoned, SPELL_CRYSTAL_SPIKE_AURA, true); + break; + } + } + + void UpdateAI(const uint32 uiDiff) override + { + if (!m_creature->SelectHostileTarget() || !m_creature->getVictim()) + return; + + if (!m_bIsEnraged && m_creature->GetHealthPercent() < 25.0f) + { + if (DoCastSpellIfCan(m_creature, m_bIsRegularMode ? SPELL_FRENZY : SPELL_FRENZY_H) == CAST_OK) + { + DoScriptText(EMOTE_BOSS_GENERIC_FRENZY, m_creature); + m_bIsEnraged = true; + } + } + + if (m_uiTrampleTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature, m_bIsRegularMode ? SPELL_TRAMPLE : SPELL_TRAMPLE_H) == CAST_OK) + m_uiTrampleTimer = urand(20000, 25000); + } + else + m_uiTrampleTimer -= uiDiff; + + if (m_uiSpellReflectTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature, SPELL_REFLECTION) == CAST_OK) + m_uiSpellReflectTimer = urand(26000, 30000); + } + else + m_uiSpellReflectTimer -= uiDiff; + + if (m_uiCrystalSpikeTimer < uiDiff) + { + uint32 uiSpikeSpell = SPELL_CRYSTAL_SPIKES; + if (!m_bIsRegularMode) + uiSpikeSpell = urand(0, 1) ? SPELL_CRYSTAL_SPIKES_H1 : SPELL_CRYSTAL_SPIKES_H2; + + if (DoCastSpellIfCan(m_creature, uiSpikeSpell) == CAST_OK) + { + DoScriptText(SAY_ICESPIKE, m_creature); + m_uiCrystalSpikeTimer = urand(13000, 15000); + m_uiSpikeCount = 0; + } + } + else + m_uiCrystalSpikeTimer -= uiDiff; + + if (!m_bIsRegularMode) + { + if (m_uiTanglerTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature, SPELL_SUMMON_TANGLER_H) == CAST_OK) + m_uiTanglerTimer = urand(15000, 25000); + } + else + m_uiTanglerTimer -= uiDiff; + } + + DoMeleeAttackIfReady(); + } +}; + +CreatureAI* GetAI_boss_ormorok(Creature* pCreature) +{ + return new boss_ormorokAI(pCreature); +} + +bool EffectDummyCreature_npc_crystal_spike_trigger(Unit* /*pCaster*/, uint32 uiSpellId, SpellEffectIndex uiEffIndex, Creature* pCreatureTarget, ObjectGuid /*originalCasterGuid*/) +{ + // always check spellid and effectindex + if (uiSpellId == SPELL_CRYSTAL_SPIKE_AURA && uiEffIndex == EFFECT_INDEX_0) + { + if (pCreatureTarget->GetEntry() == NPC_CRYSTAL_SPIKE_INITIAL || pCreatureTarget->GetEntry() == NPC_CRYSTAL_SPIKE_TRIGGER) + { + ScriptedInstance* pInstance = (ScriptedInstance*)pCreatureTarget->GetInstanceData(); + if (!pInstance) + return true; + + Creature* pOrmorok = pInstance->GetSingleCreatureFromStorage(NPC_ORMOROK); + if (!pOrmorok) + return true; + + // The following spells define the direction of the spike line + // All of the spells are targeting the back of the caster, but some take a small turn to left or right + // The exact algorithm is unk but we know that the chances of getting a straight line are about 75%. The other two directions are about 12.5% each + uint32 uiSpellId = 0; + if (roll_chance_i(75)) + uiSpellId = SPELL_CRYSTAL_SPIKE_BACK; + else + uiSpellId = urand(0, 1) ? SPELL_CRYSTAL_SPIKE_LEFT : SPELL_CRYSTAL_SPIKE_RIGHT; + + pCreatureTarget->CastSpell(pCreatureTarget, uiSpellId, true, NULL, NULL, pOrmorok->GetObjectGuid()); + // always return true when we are handling this spell and effect + return true; + } + } + + return false; +} + +bool EffectAuraDummy_spell_aura_dummy_crystal_spike_visual(const Aura* pAura, bool bApply) +{ + if (pAura->GetId() == SPELL_CRYSTAL_SPIKE_PRE && pAura->GetEffIndex() == EFFECT_INDEX_0 && !bApply) + { + if (Creature* pTarget = (Creature*)pAura->GetTarget()) + { + if (pTarget->GetEntry() != NPC_CRYSTAL_SPIKE_TRIGGER) + return true; + + // Use the Spike gameobject so we can summon the npc which actual does the damage + if (GameObject* pSpike = GetClosestGameObjectWithEntry(pTarget, GO_CRYSTAL_SPIKE, 10.0f)) + { + pSpike->Use(pTarget); + // Note: the following command should be handled in core by the trap GO code + pSpike->SetLootState(GO_JUST_DEACTIVATED); + } + } + } + return true; +} + +void AddSC_boss_ormorok() +{ + Script* pNewScript; + + pNewScript = new Script; + pNewScript->Name = "boss_ormorok"; + pNewScript->GetAI = &GetAI_boss_ormorok; + pNewScript->RegisterSelf(); + + pNewScript = new Script; + pNewScript->Name = "npc_crystal_spike_trigger"; + pNewScript->pEffectDummyNPC = &EffectDummyCreature_npc_crystal_spike_trigger; + pNewScript->pEffectAuraDummy = &EffectAuraDummy_spell_aura_dummy_crystal_spike_visual; + pNewScript->RegisterSelf(); +} diff --git a/src/modules/SD2/scripts/northrend/nexus/nexus/boss_telestra.cpp b/src/modules/SD2/scripts/northrend/nexus/nexus/boss_telestra.cpp new file mode 100644 index 000000000..fd8a6fef9 --- /dev/null +++ b/src/modules/SD2/scripts/northrend/nexus/nexus/boss_telestra.cpp @@ -0,0 +1,287 @@ +/* Copyright (C) 2006 - 2013 ScriptDev2 + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +/* ScriptData +SDName: Boss_Telestra +SD%Complete: 80% +SDComment: script depend on database spell support and eventAi for clones. transition to phase 2 also not fully implemented +SDCategory: Nexus +EndScriptData */ + +#include "precompiled.h" +#include "nexus.h" + +enum +{ + SAY_AGGRO = -1576000, + SAY_SPLIT_1 = -1576001, + SAY_SPLIT_2 = -1576002, + SAY_MERGE = -1576003, + SAY_KILL = -1576004, + SAY_DEATH = -1576005, + + SPELL_FIREBOMB = 47773, + SPELL_FIREBOMB_H = 56934, + + SPELL_ICE_NOVA = 47772, + SPELL_ICE_NOVA_H = 56935, + + SPELL_GRAVITY_WELL = 47756, + + SPELL_SUMMON_CLONES = 47710, + + SPELL_ARCANE_VISUAL = 47704, + SPELL_FIRE_VISUAL = 47705, + SPELL_FROST_VISUAL = 47706, + + SPELL_SUMMON_FIRE = 47707, + SPELL_SUMMON_ARCANE = 47708, + SPELL_SUMMON_FROST = 47709, + + SPELL_FIRE_DIES = 47711, // cast by clones at their death + SPELL_ARCANE_DIES = 47713, + SPELL_FROST_DIES = 47712, + + SPELL_SPAWN_BACK_IN = 47714, + + NPC_TELEST_FIRE = 26928, + NPC_TELEST_ARCANE = 26929, + NPC_TELEST_FROST = 26930, + + PHASE_1 = 1, + PHASE_2 = 2, + PHASE_3 = 3, + PHASE_4 = 4 +}; + +/*###### +## boss_telestra +######*/ + +struct boss_telestraAI : public ScriptedAI +{ + boss_telestraAI(Creature* pCreature) : ScriptedAI(pCreature) + { + m_pInstance = (instance_nexus*)pCreature->GetInstanceData(); + m_bIsRegularMode = pCreature->GetMap()->IsRegularDifficulty(); + Reset(); + } + + instance_nexus* m_pInstance; + bool m_bIsRegularMode; + + uint8 m_uiPhase; + uint8 m_uiCloneDeadCount; + + uint32 m_uiPersonalityTimer; + uint32 m_uiFirebombTimer; + uint32 m_uiIceNovaTimer; + uint32 m_uiGravityWellTimer; + + bool m_bCanCheckAchiev; + + void Reset() override + { + m_uiPhase = PHASE_1; + m_uiCloneDeadCount = 0; + + m_uiPersonalityTimer = 0; + m_uiFirebombTimer = urand(2000, 4000); + m_uiIceNovaTimer = urand(8000, 12000); + m_uiGravityWellTimer = urand(15000, 25000); + + m_bCanCheckAchiev = false; + } + + void JustReachedHome() override + { + m_creature->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE); + } + + void AttackStart(Unit* pWho) override + { + if (m_creature->Attack(pWho, true)) + { + m_creature->AddThreat(pWho); + m_creature->SetInCombatWith(pWho); + pWho->SetInCombatWith(m_creature); + + m_creature->GetMotionMaster()->MoveChase(pWho, 15.0f); + } + } + + void Aggro(Unit* /*pWho*/) override + { + DoScriptText(SAY_AGGRO, m_creature); + + if (m_pInstance) + m_pInstance->SetData(TYPE_TELESTRA, IN_PROGRESS); + } + + void JustDied(Unit* /*pKiller*/) override + { + DoScriptText(SAY_DEATH, m_creature); + + if (m_pInstance) + m_pInstance->SetData(TYPE_TELESTRA, DONE); + } + + void KilledUnit(Unit* /*pVictim*/) override + { + if (urand(0, 1)) + DoScriptText(SAY_KILL, m_creature); + } + + void SpellHit(Unit* /*pCaster*/, const SpellEntry* pSpell) override + { + switch (pSpell->Id) + { + // eventAi must make sure clones cast spells when each of them die + case SPELL_FIRE_DIES: + case SPELL_ARCANE_DIES: + case SPELL_FROST_DIES: + { + ++m_uiCloneDeadCount; + + // After the first clone from each split phase is dead start the achiev timer + if (m_uiCloneDeadCount == 1 || m_uiCloneDeadCount == 4) + { + m_bCanCheckAchiev = true; + m_uiPersonalityTimer = 0; + } + + if (m_uiCloneDeadCount == 3 || m_uiCloneDeadCount == 6) + { + m_creature->RemoveAurasDueToSpell(SPELL_SUMMON_CLONES); + m_creature->CastSpell(m_creature, SPELL_SPAWN_BACK_IN, false); + + m_creature->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE); + + DoScriptText(SAY_MERGE, m_creature); + + // Check if it took longer than 5 sec + if (m_uiPersonalityTimer > 5000) + { + if (m_pInstance) + m_pInstance->SetSpecialAchievementCriteria(TYPE_ACHIEV_SPLIT_PERSONALITY, false); + } + m_bCanCheckAchiev = false; + + m_uiPhase = m_uiCloneDeadCount == 3 ? PHASE_3 : PHASE_4; + } + break; + } + case SPELL_SUMMON_CLONES: + m_creature->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE); + break; + } + } + + void JustSummoned(Creature* pSummoned) override + { + switch (pSummoned->GetEntry()) + { + case NPC_TELEST_FIRE: pSummoned->CastSpell(pSummoned, SPELL_FIRE_VISUAL, true); break; + case NPC_TELEST_ARCANE: pSummoned->CastSpell(pSummoned, SPELL_ARCANE_VISUAL, true); break; + case NPC_TELEST_FROST: pSummoned->CastSpell(pSummoned, SPELL_FROST_VISUAL, true); break; + } + } + + void UpdateAI(const uint32 uiDiff) override + { + if (!m_creature->SelectHostileTarget() || !m_creature->getVictim()) + return; + + if (m_bCanCheckAchiev) + m_uiPersonalityTimer += uiDiff; + + switch (m_uiPhase) + { + case PHASE_1: + case PHASE_3: + case PHASE_4: + { + if (!m_creature->GetCurrentSpell(CURRENT_CHANNELED_SPELL)) + { + if (m_uiFirebombTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature->getVictim(), m_bIsRegularMode ? SPELL_FIREBOMB : SPELL_FIREBOMB_H) == CAST_OK) + m_uiFirebombTimer = urand(4000, 6000); + } + else + m_uiFirebombTimer -= uiDiff; + + if (m_uiIceNovaTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature, m_bIsRegularMode ? SPELL_ICE_NOVA : SPELL_ICE_NOVA_H) == CAST_OK) + m_uiIceNovaTimer = urand(10000, 15000); + } + else + m_uiIceNovaTimer -= uiDiff; + + if (m_uiPhase == PHASE_1 && m_creature->GetHealthPercent() < 50.0f) + { + if (DoCastSpellIfCan(m_creature, SPELL_SUMMON_CLONES, CAST_INTERRUPT_PREVIOUS) == CAST_OK) + { + DoScriptText(urand(0, 1) ? SAY_SPLIT_1 : SAY_SPLIT_2, m_creature); + m_uiPhase = PHASE_2; + } + } + + if (m_uiPhase == PHASE_3 && !m_bIsRegularMode && m_creature->GetHealthPercent() < 15.0f) + { + if (DoCastSpellIfCan(m_creature, SPELL_SUMMON_CLONES, CAST_INTERRUPT_PREVIOUS) == CAST_OK) + { + DoScriptText(urand(0, 1) ? SAY_SPLIT_1 : SAY_SPLIT_2, m_creature); + m_uiPhase = PHASE_2; + } + } + + DoMeleeAttackIfReady(); + } + + if (m_uiGravityWellTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature, SPELL_GRAVITY_WELL) == CAST_OK) + m_uiGravityWellTimer = urand(15000, 30000); + } + else + m_uiGravityWellTimer -= uiDiff; + + break; + } + case PHASE_2: + { + break; + } + } + } +}; + +CreatureAI* GetAI_boss_telestra(Creature* pCreature) +{ + return new boss_telestraAI(pCreature); +} + +void AddSC_boss_telestra() +{ + Script* pNewScript; + + pNewScript = new Script; + pNewScript->Name = "boss_telestra"; + pNewScript->GetAI = &GetAI_boss_telestra; + pNewScript->RegisterSelf(); +} diff --git a/src/modules/SD2/scripts/northrend/nexus/nexus/instance_nexus.cpp b/src/modules/SD2/scripts/northrend/nexus/nexus/instance_nexus.cpp new file mode 100644 index 000000000..06d3a3933 --- /dev/null +++ b/src/modules/SD2/scripts/northrend/nexus/nexus/instance_nexus.cpp @@ -0,0 +1,228 @@ +/* Copyright (C) 2006 - 2013 ScriptDev2 + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along 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: 75% +SDComment: +SDCategory: The Nexus +EndScriptData */ + +#include "precompiled.h" +#include "nexus.h" + +bool GOUse_go_containment_sphere(Player* /*pPlayer*/, GameObject* pGo) +{ + ScriptedInstance* pInstance = (ScriptedInstance*)pGo->GetInstanceData(); + + if (!pInstance) + return false; + + switch (pGo->GetEntry()) + { + case GO_CONTAINMENT_SPHERE_TELESTRA: pInstance->SetData(TYPE_TELESTRA, SPECIAL); break; + case GO_CONTAINMENT_SPHERE_ANOMALUS: pInstance->SetData(TYPE_ANOMALUS, SPECIAL); break; + case GO_CONTAINMENT_SPHERE_ORMOROK: pInstance->SetData(TYPE_ORMOROK, SPECIAL); break; + } + + pGo->SetFlag(GAMEOBJECT_FLAGS, GO_FLAG_NO_INTERACT); + return false; +} + +instance_nexus::instance_nexus(Map* pMap) : ScriptedInstance(pMap) +{ + Initialize(); + + for (uint8 i = 0; i < MAX_SPECIAL_ACHIEV_CRITS; ++i) + m_abAchievCriteria[i] = false; +} + +void instance_nexus::Initialize() +{ + memset(&m_auiEncounter, 0, sizeof(m_auiEncounter)); +} + +void instance_nexus::OnObjectCreate(GameObject* pGo) +{ + switch (pGo->GetEntry()) + { + case GO_CONTAINMENT_SPHERE_TELESTRA: + if (m_auiEncounter[TYPE_TELESTRA] == DONE) + pGo->RemoveFlag(GAMEOBJECT_FLAGS, GO_FLAG_NO_INTERACT); + break; + case GO_CONTAINMENT_SPHERE_ANOMALUS: + if (m_auiEncounter[TYPE_ANOMALUS] == DONE) + pGo->RemoveFlag(GAMEOBJECT_FLAGS, GO_FLAG_NO_INTERACT); + break; + case GO_CONTAINMENT_SPHERE_ORMOROK: + if (m_auiEncounter[TYPE_ORMOROK] == DONE) + pGo->RemoveFlag(GAMEOBJECT_FLAGS, GO_FLAG_NO_INTERACT); + break; + + default: + return; + } + m_mGoEntryGuidStore[pGo->GetEntry()] = pGo->GetObjectGuid(); +} + +void instance_nexus::OnCreatureCreate(Creature* pCreature) +{ + switch (pCreature->GetEntry()) + { + case NPC_ORMOROK: + case NPC_KERISTRASZA: + m_mNpcEntryGuidStore[pCreature->GetEntry()] = pCreature->GetObjectGuid(); + break; + } +} + +uint32 instance_nexus::GetData(uint32 uiType) const +{ + if (uiType < MAX_ENCOUNTER) + return m_auiEncounter[uiType]; + + return 0; +} + +void instance_nexus::SetData(uint32 uiType, uint32 uiData) +{ + switch (uiType) + { + case TYPE_TELESTRA: + m_auiEncounter[uiType] = uiData; + if (uiData == IN_PROGRESS) + SetSpecialAchievementCriteria(TYPE_ACHIEV_SPLIT_PERSONALITY, true); + if (uiData == DONE) + DoToggleGameObjectFlags(GO_CONTAINMENT_SPHERE_TELESTRA, GO_FLAG_NO_INTERACT, false); + break; + case TYPE_ANOMALUS: + m_auiEncounter[uiType] = uiData; + if (uiData == IN_PROGRESS) + SetSpecialAchievementCriteria(TYPE_ACHIEV_CHAOS_THEORY, true); + if (uiData == DONE) + DoToggleGameObjectFlags(GO_CONTAINMENT_SPHERE_ANOMALUS, GO_FLAG_NO_INTERACT, false); + break; + case TYPE_ORMOROK: + m_auiEncounter[uiType] = uiData; + if (uiData == DONE) + DoToggleGameObjectFlags(GO_CONTAINMENT_SPHERE_ORMOROK, GO_FLAG_NO_INTERACT, false); + break; + case TYPE_KERISTRASZA: + m_auiEncounter[uiType] = uiData; + if (uiData == IN_PROGRESS) + m_sIntenseColdFailPlayers.clear(); + break; + case TYPE_INTENSE_COLD_FAILED: + // Insert the players who fail the achiev and haven't been already inserted in the set + if (m_sIntenseColdFailPlayers.find(uiData) == m_sIntenseColdFailPlayers.end()) + m_sIntenseColdFailPlayers.insert(uiData); + break; + default: + script_error_log("Instance Nexus: ERROR SetData = %u for type %u does not exist/not implemented.", uiType, uiData); + return; + } + + if (m_auiEncounter[TYPE_TELESTRA] == SPECIAL && m_auiEncounter[TYPE_ANOMALUS] == SPECIAL && m_auiEncounter[TYPE_ORMOROK] == SPECIAL) + { + // release Keristrasza from her prison here + m_auiEncounter[TYPE_KERISTRASZA] = SPECIAL; + + Creature* pCreature = GetSingleCreatureFromStorage(NPC_KERISTRASZA); + if (pCreature && pCreature->IsAlive()) + { + pCreature->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_PASSIVE | UNIT_FLAG_OOC_NOT_ATTACKABLE); + pCreature->RemoveAurasDueToSpell(SPELL_FROZEN_PRISON); + } + } + + if (uiData == DONE) + { + OUT_SAVE_INST_DATA; + + std::ostringstream saveStream; + saveStream << m_auiEncounter[0] << " " << m_auiEncounter[1] << " " << m_auiEncounter[2] << " " << m_auiEncounter[3]; + + m_strInstData = saveStream.str(); + + SaveToDB(); + OUT_SAVE_INST_DATA_COMPLETE; + } +} + +void instance_nexus::SetSpecialAchievementCriteria(uint32 uiType, bool bIsMet) +{ + if (uiType < MAX_SPECIAL_ACHIEV_CRITS) + m_abAchievCriteria[uiType] = bIsMet; +} + +bool instance_nexus::CheckAchievementCriteriaMeet(uint32 uiCriteriaId, Player const* pSource, Unit const* /*pTarget*/, uint32 /*uiMiscValue1 = 0*/) const +{ + switch (uiCriteriaId) + { + case ACHIEV_CRIT_CHAOS_THEORY: + return m_abAchievCriteria[TYPE_ACHIEV_CHAOS_THEORY]; + case ACHIEV_CRIT_SPLIT_PERSONALITY: + return m_abAchievCriteria[TYPE_ACHIEV_SPLIT_PERSONALITY]; + case ACHIEV_CRIT_INTENSE_COLD: + // Return true if not found in the set + return m_sIntenseColdFailPlayers.find(pSource->GetGUIDLow()) == m_sIntenseColdFailPlayers.end(); + + default: + return false; + } +} + +void instance_nexus::Load(const char* chrIn) +{ + if (!chrIn) + { + 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 < MAX_ENCOUNTER; ++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* pNewScript; + + pNewScript = new Script; + pNewScript->Name = "instance_nexus"; + pNewScript->GetInstanceData = &GetInstanceData_instance_nexus; + pNewScript->RegisterSelf(); + + pNewScript = new Script; + pNewScript->Name = "go_containment_sphere"; + pNewScript->pGOUse = &GOUse_go_containment_sphere; + pNewScript->RegisterSelf(); +} diff --git a/src/modules/SD2/scripts/northrend/nexus/nexus/nexus.h b/src/modules/SD2/scripts/northrend/nexus/nexus/nexus.h new file mode 100644 index 000000000..c50f316b2 --- /dev/null +++ b/src/modules/SD2/scripts/northrend/nexus/nexus/nexus.h @@ -0,0 +1,67 @@ +/* Copyright (C) 2006 - 2013 ScriptDev2 + * This program is free software licensed under GPL version 2 + * Please see the included DOCS/LICENSE.TXT for more information */ + +#ifndef DEF_NEXUS_H +#define DEF_NEXUS_H + +enum +{ + MAX_ENCOUNTER = 4, + MAX_SPECIAL_ACHIEV_CRITS = 2, + + TYPE_TELESTRA = 0, + TYPE_ANOMALUS = 1, + TYPE_ORMOROK = 2, + TYPE_KERISTRASZA = 3, + TYPE_INTENSE_COLD_FAILED = 4, + + TYPE_ACHIEV_CHAOS_THEORY = 0, + TYPE_ACHIEV_SPLIT_PERSONALITY = 1, + + NPC_TELESTRA = 26731, + NPC_ANOMALUS = 26763, + NPC_ORMOROK = 26794, + NPC_KERISTRASZA = 26723, + + GO_CONTAINMENT_SPHERE_TELESTRA = 188526, + GO_CONTAINMENT_SPHERE_ANOMALUS = 188527, + GO_CONTAINMENT_SPHERE_ORMOROK = 188528, + + SPELL_FROZEN_PRISON = 47854, + + ACHIEV_CRIT_CHAOS_THEORY = 7316, // Anomalus, achiev 2037 + ACHIEV_CRIT_INTENSE_COLD = 7315, // Keristrasza, achiev 2036 + ACHIEV_CRIT_SPLIT_PERSONALITY = 7577, // Telestra, achiev 2150 +}; + +class instance_nexus : public ScriptedInstance +{ + public: + instance_nexus(Map* pMap); + + void Initialize() override; + + void OnObjectCreate(GameObject* pGo) override; + void OnCreatureCreate(Creature* pCreature) override; + + uint32 GetData(uint32 uiType) const override; + void SetData(uint32 uiType, uint32 uiData) override; + + void SetSpecialAchievementCriteria(uint32 uiType, bool bIsMet); + bool CheckAchievementCriteriaMeet(uint32 uiCriteriaId, Player const* pSource, Unit const* pTarget, uint32 uiMiscValue1 /* = 0*/) const override; + + const char* Save() const override { return m_strInstData.c_str(); } + + void Load(const char* chrIn) override; + + private: + uint32 m_auiEncounter[MAX_ENCOUNTER]; + std::string m_strInstData; + + bool m_abAchievCriteria[MAX_SPECIAL_ACHIEV_CRITS]; + + std::set m_sIntenseColdFailPlayers; +}; + +#endif diff --git a/src/modules/SD2/scripts/northrend/nexus/oculus/boss_eregos.cpp b/src/modules/SD2/scripts/northrend/nexus/oculus/boss_eregos.cpp new file mode 100644 index 000000000..c3af36ac2 --- /dev/null +++ b/src/modules/SD2/scripts/northrend/nexus/oculus/boss_eregos.cpp @@ -0,0 +1,322 @@ +/* Copyright (C) 2006 - 2013 ScriptDev2 + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +/* ScriptData +SDName: boss_eregos +SD%Complete: 90 +SDComment: Small adjustments may be required. +SDCategory: Oculus +EndScriptData */ + +#include "precompiled.h" +#include "oculus.h" + +enum +{ + SAY_AGGRO = -1578011, + SAY_ARCANE_SHIELD = -1578012, + SAY_FIRE_SHIELD = -1578013, + SAY_NATURE_SHIELD = -1578014, + SAY_FRENZY = -1578015, + SAY_KILL_1 = -1578016, + SAY_KILL_2 = -1578017, + SAY_KILL_3 = -1578018, + SAY_DEATH = -1578019, + EMOTE_ASTRAL_PLANE = -1578024, + + SPELL_ARCANE_BARRAGE = 50804, + SPELL_ARCANE_BARRAGE_H = 59381, + SPELL_ARCANE_VOLLEY = 51153, + SPELL_ARCANE_VOLLEY_H = 59382, + SPELL_ENRAGED_ASSAULT = 51170, + SPELL_PLANAR_ANOMALIES = 57959, + SPELL_SUMMON_LEY_WHELP = 51175, + SPELL_PLANAR_SHIFT = 51162, + + SPELL_PLANAR_ANOMALY_AGGRO = 57971, + SPELL_PLANAR_BLAST = 57976, + + NPC_PLANAR_ANOMALY = 30879, + NPC_GREATER_LEY_WHELP = 28276, +}; + +/*###### +## boss_eregos +######*/ + +struct boss_eregosAI : public ScriptedAI +{ + boss_eregosAI(Creature* pCreature) : ScriptedAI(pCreature) + { + m_pInstance = (ScriptedInstance*)pCreature->GetInstanceData(); + m_bIsRegularMode = pCreature->GetMap()->IsRegularDifficulty(); + Reset(); + } + + ScriptedInstance* m_pInstance; + bool m_bIsRegularMode; + + uint32 m_uiArcaneBarrageTimer; + uint32 m_uiArcaneVolleyTimer; + uint32 m_uiEnrageTimer; + uint32 m_uiSummonWhelpsTimer; + float m_fHpPercent; + + uint8 m_uiAnomalyTargetIndex; + GuidVector m_vAnomalyTargets; + + void Reset() override + { + m_uiArcaneBarrageTimer = 0; + m_uiArcaneVolleyTimer = 20000; + m_uiEnrageTimer = 35000; + m_uiSummonWhelpsTimer = urand(15000, 20000); + m_fHpPercent = 60.0f; + m_uiAnomalyTargetIndex = 0; + } + + void Aggro(Unit* /*pWho*/) override + { + DoScriptText(SAY_AGGRO, m_creature); + + if (m_pInstance) + m_pInstance->SetData(TYPE_EREGOS, IN_PROGRESS); + } + + void AttackStart(Unit* pWho) override + { + if (m_creature->Attack(pWho, false)) + { + m_creature->AddThreat(pWho); + m_creature->SetInCombatWith(pWho); + pWho->SetInCombatWith(m_creature); + DoStartMovement(pWho, 20.0f); + } + } + + void KilledUnit(Unit* /*pVictim*/) override + { + switch (urand(0, 2)) + { + case 0: DoScriptText(SAY_KILL_1, m_creature); break; + case 1: DoScriptText(SAY_KILL_2, m_creature); break; + case 2: DoScriptText(SAY_KILL_3, m_creature); break; + } + } + + void JustDied(Unit* /*pKiller*/) override + { + DoScriptText(SAY_DEATH, m_creature); + + if (m_pInstance) + m_pInstance->SetData(TYPE_EREGOS, DONE); + } + + void JustReachedHome() override + { + if (m_pInstance) + m_pInstance->SetData(TYPE_EREGOS, FAIL); + } + + void JustSummoned(Creature* pSummoned) override + { + if (pSummoned->GetEntry() == NPC_PLANAR_ANOMALY) + { + pSummoned->CastSpell(pSummoned, SPELL_PLANAR_ANOMALY_AGGRO, true); + + // If this happens then something is really wrong + if (m_vAnomalyTargets.empty()) + return; + + if (Unit* pTarget = m_creature->GetMap()->GetUnit(m_vAnomalyTargets[m_uiAnomalyTargetIndex])) + pSummoned->GetMotionMaster()->MoveFollow(pTarget, 0, 0); + + if (m_uiAnomalyTargetIndex < m_vAnomalyTargets.size() - 1) + ++m_uiAnomalyTargetIndex; + } + else if (pSummoned->GetEntry() == NPC_GREATER_LEY_WHELP) + { + if (Unit* pTarget = m_creature->SelectAttackingTarget(ATTACKING_TARGET_RANDOM, 0)) + pSummoned->AI()->AttackStart(pTarget); + } + } + + void UpdateAI(const uint32 uiDiff) override + { + if (!m_creature->SelectHostileTarget() || !m_creature->getVictim()) + return; + + if (m_creature->HasAura(SPELL_PLANAR_SHIFT)) + return; + + if (m_creature->GetHealthPercent() < m_fHpPercent) + { + if (DoCastSpellIfCan(m_creature, SPELL_PLANAR_SHIFT) == CAST_OK) + { + // Get all the vehicle entries which are in combat with the boss + m_vAnomalyTargets.clear(); + m_uiAnomalyTargetIndex = 0; + + ThreatList const& threatList = m_creature->GetThreatManager().getThreatList(); + for (ThreatList::const_iterator itr = threatList.begin(); itr != threatList.end(); ++itr) + { + if (Unit* pTarget = m_creature->GetMap()->GetUnit((*itr)->getUnitGuid())) + { + if (pTarget->GetEntry() == NPC_RUBY_DRAKE || pTarget->GetEntry() == NPC_AMBER_DRAKE || pTarget->GetEntry() == NPC_EMERALD_DRAKE) + m_vAnomalyTargets.push_back(pTarget->GetObjectGuid()); + } + } + + // This will summon an anomaly for each player (vehicle) + DoCastSpellIfCan(m_creature, SPELL_PLANAR_ANOMALIES, CAST_TRIGGERED); + + switch (urand(0, 2)) + { + case 0: DoScriptText(SAY_ARCANE_SHIELD, m_creature); break; + case 1: DoScriptText(SAY_FIRE_SHIELD, m_creature); break; + case 2: DoScriptText(SAY_NATURE_SHIELD, m_creature); break; + } + DoScriptText(EMOTE_ASTRAL_PLANE, m_creature); + + // set next phase to 20% + m_fHpPercent -= 40; + } + } + + if (m_uiArcaneBarrageTimer < uiDiff) + { + if (Unit* pTarget = m_creature->SelectAttackingTarget(ATTACKING_TARGET_RANDOM, 0)) + { + if (DoCastSpellIfCan(pTarget, m_bIsRegularMode ? SPELL_ARCANE_BARRAGE : SPELL_ARCANE_BARRAGE_H) == CAST_OK) + m_uiArcaneBarrageTimer = urand(2000, 3000); + } + } + else + m_uiArcaneBarrageTimer -= uiDiff; + + if (m_uiSummonWhelpsTimer < uiDiff) + { + // ToDo: the number of whelps summoned may be different based on difficulty. Needs research! + for (uint8 i = 0; i < 4; ++i) + { + if (DoCastSpellIfCan(m_creature, SPELL_SUMMON_LEY_WHELP, CAST_TRIGGERED) == CAST_OK) + m_uiSummonWhelpsTimer = 20000; + } + } + else + m_uiSummonWhelpsTimer -= uiDiff; + + if (m_uiArcaneVolleyTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature, m_bIsRegularMode ? SPELL_ARCANE_VOLLEY : SPELL_ARCANE_VOLLEY_H) == CAST_OK) + m_uiArcaneVolleyTimer = urand(10000, 15000); + } + else + m_uiArcaneVolleyTimer -= uiDiff; + + if (m_uiEnrageTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature, SPELL_ENRAGED_ASSAULT) == CAST_OK) + { + DoScriptText(SAY_FRENZY, m_creature); + m_uiEnrageTimer = urand(40000, 50000); + } + } + else + m_uiEnrageTimer -= uiDiff; + } +}; + +CreatureAI* GetAI_boss_eregos(Creature* pCreature) +{ + return new boss_eregosAI(pCreature); +} + +/*###### +## npc_planar_anomaly +######*/ + +struct npc_planar_anomalyAI : public ScriptedAI +{ + npc_planar_anomalyAI(Creature* pCreature) : ScriptedAI(pCreature) { Reset(); } + + uint32 m_uiPlanarBlastTimer; + bool m_bHasBlastCasted; + + void Reset() override + { + m_uiPlanarBlastTimer = 15000; + m_bHasBlastCasted = false; + } + + void AttackStart(Unit* /*pWho*/) override { } + + void MoveInLineOfSight(Unit* pWho) override + { + if (m_bHasBlastCasted) + return; + + // Check for the players mounted on the vehicles + if (pWho->GetTypeId() == TYPEID_PLAYER) + { + if (m_creature->IsWithinDistInMap(pWho, INTERACTION_DISTANCE)) + { + if (DoCastSpellIfCan(m_creature, SPELL_PLANAR_BLAST) == CAST_OK) + { + m_bHasBlastCasted = true; + m_creature->ForcedDespawn(1000); + } + } + } + } + + void UpdateAI(const uint32 uiDiff) override + { + if (m_bHasBlastCasted) + return; + + if (m_uiPlanarBlastTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature, SPELL_PLANAR_BLAST) == CAST_OK) + { + m_bHasBlastCasted = true; + m_creature->ForcedDespawn(1000); + } + } + else + m_uiPlanarBlastTimer -= uiDiff; + } +}; + +CreatureAI* GetAI_npc_planar_anomaly(Creature* pCreature) +{ + return new npc_planar_anomalyAI(pCreature); +} + +void AddSC_boss_eregos() +{ + Script* pNewScript; + + pNewScript = new Script; + pNewScript->Name = "boss_eregos"; + pNewScript->GetAI = &GetAI_boss_eregos; + pNewScript->RegisterSelf(); + + pNewScript = new Script; + pNewScript->Name = "npc_planar_anomaly"; + pNewScript->GetAI = &GetAI_npc_planar_anomaly; + pNewScript->RegisterSelf(); +} diff --git a/src/modules/SD2/scripts/northrend/nexus/oculus/boss_urom.cpp b/src/modules/SD2/scripts/northrend/nexus/oculus/boss_urom.cpp new file mode 100644 index 000000000..4f6fb7a80 --- /dev/null +++ b/src/modules/SD2/scripts/northrend/nexus/oculus/boss_urom.cpp @@ -0,0 +1,374 @@ +/* Copyright (C) 2006 - 2013 ScriptDev2 + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +/* ScriptData +SDName: boss_urom +SD%Complete: 90 +SDComment: Small adjustments may be required. +SDCategory: Oculus +EndScriptData */ + +#include "precompiled.h" +#include "oculus.h" + +enum +{ + SAY_SUMMON_1 = -1578000, + SAY_SUMMON_2 = -1578001, + SAY_SUMMON_3 = -1578002, + SAY_AGGRO = -1578003, + SAY_EXPLOSION_1 = -1578004, + SAY_EXPLOSION_2 = -1578005, + SAY_KILL_1 = -1578006, + SAY_KILL_2 = -1578007, + SAY_KILL_3 = -1578008, + SAY_DEATH = -1578009, + EMOTE_EXPLOSION = -1578025, + + // spells + SPELL_ARCANE_SHIELD = 53813, // This spell id may be wrong. Needs research! + SPELL_ARCANE_EXPLOSION = 51110, + SPELL_ARCANE_EXPLOSION_H = 59377, + SPELL_FROSTBOMB = 51103, + SPELL_TIME_BOMB = 51121, + SPELL_TIME_BOMB_H = 59376, + SPELL_SUMMON_MENAGERIE_1 = 50476, + SPELL_SUMMON_MENAGERIE_2 = 50495, + SPELL_SUMMON_MENAGERIE_3 = 50496, + SPELL_TELEPORT = 51112, + + // npcs + NPC_PHANTASMAL_CLOUDSCRAPER = 27645, + NPC_PHANTASMAL_MAMMOTH = 27642, + NPC_PHANTASMAL_WOLF = 27644, + + NPC_PHANTASMAL_AIR = 27650, + NPC_PHANTASMAL_FIRE = 27651, + NPC_PHANTASMAL_WATER = 27653, + + NPC_PHANTASMAL_MURLOC = 27649, + NPC_PHANTASMAL_NAGAL = 27648, + NPC_PHANTASMAL_OGRE = 27647, + + MAX_PLATFORMS = 3, +}; + +static uint32 uiTrashPacks[MAX_PLATFORMS][MAX_PLATFORMS] = +{ + {NPC_PHANTASMAL_CLOUDSCRAPER, NPC_PHANTASMAL_MAMMOTH, NPC_PHANTASMAL_WOLF}, + {NPC_PHANTASMAL_AIR, NPC_PHANTASMAL_FIRE, NPC_PHANTASMAL_WATER}, + {NPC_PHANTASMAL_MURLOC, NPC_PHANTASMAL_NAGAL, NPC_PHANTASMAL_OGRE}, +}; + +struct boss_uromAI : public ScriptedAI +{ + boss_uromAI(Creature* pCreature) : ScriptedAI(pCreature) + { + m_pInstance = (ScriptedInstance*)pCreature->GetInstanceData(); + m_bIsRegularMode = pCreature->GetMap()->IsRegularDifficulty(); + + // Randomize the trash mobs packs + for (uint8 i = 0; i < MAX_PLATFORMS; ++i) + m_vuiTrashPacksIds.push_back(i); + + Reset(); + } + + ScriptedInstance* m_pInstance; + bool m_bIsRegularMode; + + bool m_bIsTeleporting; + bool m_bIsPlatformPhase; + uint8 m_uiPlatformPhase; + uint32 m_uiExplosionExpireTimer; + uint32 m_uiArcaneShieldTimer; + uint32 m_uiExplosionTimer; + uint32 m_uiTeleportTimer; + uint32 m_uiFrostBombTimer; + uint32 m_uiTimeBombTimer; + + float m_fX, m_fY, m_fZ; + + ObjectGuid m_attackTarget; + + std::vector m_vuiTrashPacksIds; + + void Reset() override + { + m_bIsPlatformPhase = true; + m_uiPlatformPhase = 0; + m_uiExplosionTimer = 0; + m_uiExplosionExpireTimer = 0; + m_uiTeleportTimer = 20000; + m_uiFrostBombTimer = 5000; + m_uiTimeBombTimer = urand(10000, 15000); + + ResetPlatformVariables(); + + std::random_shuffle(m_vuiTrashPacksIds.begin(), m_vuiTrashPacksIds.end()); + } + + void ResetPlatformVariables() + { + m_bIsTeleporting = false; + m_uiArcaneShieldTimer = 1000; + } + + void Aggro(Unit* /*pWho*/) override + { + if (m_pInstance) + m_pInstance->SetData(TYPE_UROM, IN_PROGRESS); + } + + void AttackStart(Unit* pWho) override + { + if (m_uiPlatformPhase < MAX_PLATFORMS) + { + if (m_bIsTeleporting) + return; + + // Summon the trash mobs pack + m_bIsTeleporting = true; + m_attackTarget = pWho->GetObjectGuid(); + m_creature->InterruptNonMeleeSpells(false); + DoSpawnTrashPack(); + + // teleport to next platform and spawn adds + switch (m_uiPlatformPhase) + { + case 0: + if (DoCastSpellIfCan(m_creature, SPELL_SUMMON_MENAGERIE_1) == CAST_OK) + DoScriptText(SAY_SUMMON_1, m_creature); + break; + case 1: + if (DoCastSpellIfCan(m_creature, SPELL_SUMMON_MENAGERIE_2) == CAST_OK) + DoScriptText(SAY_SUMMON_2, m_creature); + break; + case 2: + if (DoCastSpellIfCan(m_creature, SPELL_SUMMON_MENAGERIE_3) == CAST_OK) + DoScriptText(SAY_SUMMON_3, m_creature); + break; + } + } + // Boss has teleported in the central ring - start normal combat + else if (m_bIsPlatformPhase) + { + DoScriptText(SAY_AGGRO, m_creature); + m_creature->InterruptNonMeleeSpells(false); + m_bIsPlatformPhase = false; + + ScriptedAI::AttackStart(pWho); + } + } + + void KilledUnit(Unit* /*pVictim*/) override + { + switch (urand(0, 2)) + { + case 0: DoScriptText(SAY_KILL_1, m_creature); break; + case 1: DoScriptText(SAY_KILL_2, m_creature); break; + case 2: DoScriptText(SAY_KILL_3, m_creature); break; + } + } + + void JustDied(Unit* /*pKiller*/) override + { + DoScriptText(SAY_DEATH, m_creature); + DoCastSpellIfCan(m_creature, SPELL_DEATH_SPELL, CAST_TRIGGERED); + + if (m_pInstance) + m_pInstance->SetData(TYPE_UROM, DONE); + } + + void JustReachedHome() override + { + if (m_pInstance) + m_pInstance->SetData(TYPE_UROM, FAIL); + } + + void EnterEvadeMode() override + { + // Don't evade while casting explosion + if (m_uiExplosionExpireTimer) + return; + + if (m_bIsPlatformPhase) + { + m_creature->RemoveAllAurasOnEvade(); + m_creature->DeleteThreatList(); + m_creature->CombatStop(true); + + m_creature->SetLootRecipient(NULL); + + ResetPlatformVariables(); + } + else + { + // Teleport to home position, in order to override the movemaps + m_creature->NearTeleportTo(aOculusBossSpawnLocs[0][0], aOculusBossSpawnLocs[0][1], aOculusBossSpawnLocs[0][2], aOculusBossSpawnLocs[0][3]); + + ScriptedAI::EnterEvadeMode(); + } + } + + void JustSummoned(Creature* pSummon) override + { + if (Unit* pTarget = m_creature->GetMap()->GetUnit(m_attackTarget)) + pSummon->AI()->AttackStart(pTarget); + } + + void DoSpawnTrashPack() + { + float fX, fY, fZ; + + // Summon the 3 mobs contained in the pack + for (uint8 i = 0; i < MAX_PLATFORMS; ++i) + { + m_creature->GetNearPoint(m_creature, fX, fY, fZ, 0, 10.0f, M_PI_F / 2 * i); + m_creature->SummonCreature(uiTrashPacks[m_vuiTrashPacksIds[m_uiPlatformPhase]][i], fX, fY, fZ, 0.0f, TEMPSUMMON_TIMED_OOC_DESPAWN, 30000); + } + + // Summon a fourth mob, which can be random + uint32 uiEntry = uiTrashPacks[m_vuiTrashPacksIds[m_uiPlatformPhase]][urand(0, 2)]; + m_creature->GetNearPoint(m_creature, fX, fY, fZ, 0, 10.0f, M_PI_F / 2 * 3); + m_creature->SummonCreature(uiEntry, fX, fY, fZ, 0.0f, TEMPSUMMON_TIMED_OOC_DESPAWN, 30000); + } + + void SpellHit(Unit* /*pCaster*/, const SpellEntry* pSpell) override + { + switch (pSpell->Id) + { + case SPELL_SUMMON_MENAGERIE_3: + case SPELL_SUMMON_MENAGERIE_2: + case SPELL_SUMMON_MENAGERIE_1: + EnterEvadeMode(); + ++m_uiPlatformPhase; + break; + } + } + + void UpdateAI(const uint32 uiDiff) override + { + // Set the Arcane Shield on out of combat timer + if (m_uiArcaneShieldTimer) + { + if (m_uiArcaneShieldTimer <= uiDiff) + { + if (DoCastSpellIfCan(m_creature, SPELL_ARCANE_SHIELD) == CAST_OK) + m_uiArcaneShieldTimer = 0; + } + else + m_uiArcaneShieldTimer -= uiDiff; + } + + if (!m_creature->SelectHostileTarget() || !m_creature->getVictim()) + return; + + // Don't use any combat abilities during the platform transition + if (m_bIsPlatformPhase) + return; + + if (m_uiExplosionTimer) + { + if (m_uiExplosionTimer <= uiDiff) + { + if (DoCastSpellIfCan(m_creature, m_bIsRegularMode ? SPELL_ARCANE_EXPLOSION : SPELL_ARCANE_EXPLOSION_H) == CAST_OK) + { + DoScriptText(EMOTE_EXPLOSION, m_creature); + m_uiExplosionTimer = 0; + } + } + else + m_uiExplosionTimer -= uiDiff; + } + + if (m_uiExplosionExpireTimer) + { + if (m_uiExplosionExpireTimer <= uiDiff) + { + // Teleport to the original location + m_creature->NearTeleportTo(m_fX, m_fY, m_fZ, 0); + + // Resume combat movement + SetCombatMovement(true); + m_creature->GetMotionMaster()->Clear(); + m_creature->GetMotionMaster()->MoveChase(m_creature->getVictim()); + m_uiExplosionExpireTimer = 0; + } + else + m_uiExplosionExpireTimer -= uiDiff; + + // Don't decrease timers during the explosion event + return; + } + + if (m_uiTeleportTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature, SPELL_TELEPORT) == CAST_OK) + { + DoScriptText(urand(0, 1) ? SAY_EXPLOSION_1 : SAY_EXPLOSION_2, m_creature); + + // Store the original position - boss needs to be teleported back + m_creature->GetPosition(m_fX, m_fY, m_fZ); + + // Stop movement until he casts the arcane explosion + SetCombatMovement(false); + m_creature->GetMotionMaster()->MoveIdle(); + m_uiTeleportTimer = 20000; + m_uiExplosionExpireTimer = m_bIsRegularMode ? 9500 : 7500; + m_uiExplosionTimer = 1000; + } + } + else + m_uiTeleportTimer -= uiDiff; + + if (m_uiFrostBombTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature->getVictim(), SPELL_FROSTBOMB) == CAST_OK) + m_uiFrostBombTimer = urand(4000, 6000); + } + else + m_uiFrostBombTimer -= uiDiff; + + if (m_uiTimeBombTimer < uiDiff) + { + if (Unit* pTarget = m_creature->SelectAttackingTarget(ATTACKING_TARGET_RANDOM, 0)) + { + if (DoCastSpellIfCan(pTarget, m_bIsRegularMode ? SPELL_TIME_BOMB : SPELL_TIME_BOMB_H) == CAST_OK) + m_uiTimeBombTimer = urand(10000, 15000); + } + } + else + m_uiTimeBombTimer -= uiDiff; + + DoMeleeAttackIfReady(); + } +}; + +CreatureAI* GetAI_boss_urom(Creature* pCreature) +{ + return new boss_uromAI(pCreature); +} + +void AddSC_boss_urom() +{ + Script* pNewScript; + + pNewScript = new Script; + pNewScript->Name = "boss_urom"; + pNewScript->GetAI = &GetAI_boss_urom; + pNewScript->RegisterSelf(); +} diff --git a/src/modules/SD2/scripts/northrend/nexus/oculus/boss_varos.cpp b/src/modules/SD2/scripts/northrend/nexus/oculus/boss_varos.cpp new file mode 100644 index 000000000..eed2e246c --- /dev/null +++ b/src/modules/SD2/scripts/northrend/nexus/oculus/boss_varos.cpp @@ -0,0 +1,389 @@ +/* Copyright (C) 2006 - 2013 ScriptDev2 + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +/* ScriptData +SDName: boss_varos +SD%Complete: 90 +SDComment: Energize Cores spells requires additional research. +SDCategory: Oculus +EndScriptData */ + +#include "precompiled.h" +#include "oculus.h" +#include "TemporarySummon.h" + +enum +{ + SAY_AGGRO = -1578020, + SAY_CALL_CAPTAIN_1 = -1578021, + SAY_CALL_CAPTAIN_2 = -1578022, + SAY_CALL_CAPTAIN_3 = -1578023, + SAY_KILL_1 = -1578026, + SAY_KILL_2 = -1578027, + SAY_DEATH = -1578028, + EMOTE_CAPTAIN = -1578029, + + // spells + SPELL_CENTRIFUGE_SHIELD = 50053, + SPELL_AMPLIFY_MAGIC = 51054, + SPELL_AMPLIFY_MAGIC_H = 59371, + SPELL_ENERGIZE_CORES = 50785, + SPELL_ENERGIZE_CORES_H = 59372, + SPELL_CALL_CAPTAIN_1 = 51008, // sends event 18455 + SPELL_CALL_CAPTAIN_2 = 51002, // sends event 12229 + SPELL_CALL_CAPTAIN_3 = 51006, // sends event 10665 + SPELL_CALL_CAPTAIN_4 = 51007, // sends event 18454 + + // events + EVENT_ID_CALL_CAPTAIN_1 = 18455, + EVENT_ID_CALL_CAPTAIN_2 = 12229, + EVENT_ID_CALL_CAPTAIN_3 = 10665, + EVENT_ID_CALL_CAPTAIN_4 = 18454, + + MAX_CAPTAIN_EVENTS = 4, + + // other spells + SPELL_SUMMON_ARCANE_BEAM = 51014, + SPELL_ARCANE_BEAM_PERIODIC = 51019, + SPELL_ARCANE_BEAM_SPAWN = 51022, + + NPC_AZURE_RING_CAPTAIN = 28236, + NPC_ARCANE_BEAM = 28239, +}; + +struct CaptainData +{ + uint32 uiEventId; + float fX, fY, fZ, fO, fDestX, fDestY, fDestZ; +}; + +static const CaptainData aVarosCaptainData[4] = +{ + {EVENT_ID_CALL_CAPTAIN_1, 1205.74f, 1060.24f, 480.083f, 1.15f, 1239.198f, 1064.537f, 455.587f}, + {EVENT_ID_CALL_CAPTAIN_2, 1273.78f, 1159.366f, 480.083f, 4.79f, 1278.488f, 1119.482f, 455.634f}, // this one is guesswork + {EVENT_ID_CALL_CAPTAIN_3, 1356.845f, 1077.118f, 480.083f, 3.28f, 1331.333f, 1076.381f, 455.69f}, + {EVENT_ID_CALL_CAPTAIN_4, 1296.89f, 1002.76f, 480.083f, 1.71f, 1291.95f, 1024.354f, 455.739f}, +}; + +/*###### +## boss_varos +######*/ + +struct boss_varosAI : public ScriptedAI +{ + boss_varosAI(Creature* pCreature) : ScriptedAI(pCreature) + { + m_pInstance = (instance_oculus*)pCreature->GetInstanceData(); + m_bIsRegularMode = pCreature->GetMap()->IsRegularDifficulty(); + Reset(); + } + + instance_oculus* m_pInstance; + bool m_bIsRegularMode; + + uint32 m_uiShieldTimer; + uint32 m_uiAmplifyMagicTimer; + uint32 m_uiEnergizeCoresTimer; + uint32 m_uiCallCaptainTimer; + + void Reset() override + { + m_uiShieldTimer = 2000; + m_uiAmplifyMagicTimer = urand(8000, 15000); + m_uiEnergizeCoresTimer = urand(5000, 7000); + m_uiCallCaptainTimer = urand(10000, 15000); + } + + void Aggro(Unit* /*pWho*/) override + { + DoScriptText(SAY_AGGRO, m_creature); + + if (m_pInstance) + m_pInstance->SetData(TYPE_VAROS, IN_PROGRESS); + } + + void KilledUnit(Unit* /*pVictim*/) override + { + DoScriptText(urand(0, 1) ? SAY_KILL_1 : SAY_KILL_2, m_creature); + } + + void JustDied(Unit* /*pKiller*/) override + { + DoScriptText(SAY_DEATH, m_creature); + DoCastSpellIfCan(m_creature, SPELL_DEATH_SPELL, CAST_TRIGGERED); + + if (m_pInstance) + m_pInstance->SetData(TYPE_VAROS, DONE); + } + + void JustReachedHome() override + { + if (m_pInstance) + m_pInstance->SetData(TYPE_VAROS, FAIL); + } + + void UpdateAI(const uint32 uiDiff) override + { + if (m_uiShieldTimer) + { + if (m_uiShieldTimer <= uiDiff) + { + if (!m_pInstance) + return; + + // Check for shield first + if (m_pInstance->IsShieldBroken()) + { + m_uiShieldTimer = 0; + return; + } + + if (DoCastSpellIfCan(m_creature, SPELL_CENTRIFUGE_SHIELD) == CAST_OK) + { + m_creature->ApplySpellImmune(0, IMMUNITY_DAMAGE, SPELL_SCHOOL_MASK_ALL, true); + m_uiShieldTimer = 0; + } + } + else + m_uiShieldTimer -= uiDiff; + } + + if (!m_creature->SelectHostileTarget() || !m_creature->getVictim()) + return; + + if (m_uiAmplifyMagicTimer < uiDiff) + { + if (Unit* pTarget = m_creature->SelectAttackingTarget(ATTACKING_TARGET_RANDOM, 0)) + { + if (DoCastSpellIfCan(pTarget, m_bIsRegularMode ? SPELL_AMPLIFY_MAGIC : SPELL_AMPLIFY_MAGIC_H) == CAST_OK) + m_uiAmplifyMagicTimer = urand(15000, 20000); + } + } + else + m_uiAmplifyMagicTimer -= uiDiff; + + if (m_uiEnergizeCoresTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature, m_bIsRegularMode ? SPELL_ENERGIZE_CORES : SPELL_ENERGIZE_CORES_H) == CAST_OK) + m_uiEnergizeCoresTimer = urand(5000, 7000); + } + else + m_uiEnergizeCoresTimer -= uiDiff; + + if (m_uiCallCaptainTimer < uiDiff) + { + // choose a random captain spell + uint32 uiSpellId = 0; + switch (urand(0, 3)) + { + case 0: uiSpellId = SPELL_CALL_CAPTAIN_1; break; + case 1: uiSpellId = SPELL_CALL_CAPTAIN_2; break; + case 2: uiSpellId = SPELL_CALL_CAPTAIN_3; break; + case 3: uiSpellId = SPELL_CALL_CAPTAIN_4; break; + } + + if (DoCastSpellIfCan(m_creature, uiSpellId) == CAST_OK) + { + switch (urand(0, 2)) + { + case 0: DoScriptText(SAY_CALL_CAPTAIN_1, m_creature); break; + case 1: DoScriptText(SAY_CALL_CAPTAIN_2, m_creature); break; + case 2: DoScriptText(SAY_CALL_CAPTAIN_3, m_creature); break; + } + + DoScriptText(EMOTE_CAPTAIN, m_creature); + m_uiCallCaptainTimer = urand(13000, 23000); + } + } + else + m_uiCallCaptainTimer -= uiDiff; + + DoMeleeAttackIfReady(); + } +}; + +CreatureAI* GetAI_boss_varos(Creature* pCreature) +{ + return new boss_varosAI(pCreature); +} + +/*###### +## event_spell_call_captain +######*/ + +bool ProcessEventId_event_spell_call_captain(uint32 uiEventId, Object* pSource, Object* /*pTarget*/, bool bIsStart) +{ + if (bIsStart && pSource->GetTypeId() == TYPEID_UNIT) + { + Creature* pVaros = (Creature*)pSource; + if (!pVaros) + return false; + + // each guardian has it's own spawn position + for (uint8 i = 0; i < MAX_CAPTAIN_EVENTS; ++i) + { + if (uiEventId == aVarosCaptainData[i].uiEventId) + { + if (Creature* pGuardian = pVaros->SummonCreature(NPC_AZURE_RING_CAPTAIN, aVarosCaptainData[i].fX, aVarosCaptainData[i].fY, aVarosCaptainData[i].fZ, aVarosCaptainData[i].fO, TEMPSUMMON_DEAD_DESPAWN, 0)) + { + pGuardian->SetWalk(false); + pGuardian->GetMotionMaster()->MovePoint(1, aVarosCaptainData[i].fDestX, aVarosCaptainData[i].fDestY, aVarosCaptainData[i].fDestZ); + } + + return true; + } + } + } + + return false; +} + +/*###### +## npc_azure_ring_captain +######*/ + +struct npc_azure_ring_captainAI : public ScriptedAI +{ + npc_azure_ring_captainAI(Creature* pCreature) : ScriptedAI(pCreature) + { + SetCombatMovement(false); + Reset(); + } + + ObjectGuid m_arcaneBeamGuid; + + void Reset() override { } + void AttackStart(Unit* /*pWho*/) override { } + void MoveInLineOfSight(Unit* /*pWho*/) override { } + + void JustSummoned(Creature* pSummoned) override + { + if (pSummoned->GetEntry() == NPC_ARCANE_BEAM) + { + pSummoned->CastSpell(pSummoned, SPELL_ARCANE_BEAM_PERIODIC, true); + pSummoned->CastSpell(pSummoned, SPELL_ARCANE_BEAM_SPAWN, true); + m_arcaneBeamGuid = pSummoned->GetObjectGuid(); + } + } + + void JustDied(Unit* /*pKiller*/) override + { + // Despawn the arcane beam in case of getting killed + if (Creature* pTemp = m_creature->GetMap()->GetCreature(m_arcaneBeamGuid)) + pTemp->ForcedDespawn(); + } + + void MovementInform(uint32 uiMoveType, uint32 uiPointId) override + { + if (uiMoveType != POINT_MOTION_TYPE || !uiPointId) + return; + + // Spawn arcane beam when the position is reached. Also prepare to despawn after the beam event is finished + if (DoCastSpellIfCan(m_creature, SPELL_SUMMON_ARCANE_BEAM) == CAST_OK) + m_creature->ForcedDespawn(11000); + } + + void UpdateAI(const uint32 /*uiDiff*/) override { } +}; + +CreatureAI* GetAI_npc_azure_ring_captain(Creature* pCreature) +{ + return new npc_azure_ring_captainAI(pCreature); +} + +/*###### +## npc_arcane_beam +######*/ + +struct npc_arcane_beamAI : public ScriptedAI +{ + npc_arcane_beamAI(Creature* pCreature) : ScriptedAI(pCreature) { Reset(); } + + void Reset() override + { + // Start following the summoner (player) + if (m_creature->IsTemporarySummon()) + { + TemporarySummon* pTemporary = (TemporarySummon*)m_creature; + + if (Player* pSummoner = m_creature->GetMap()->GetPlayer(pTemporary->GetSummonerGuid())) + m_creature->GetMotionMaster()->MoveFollow(pSummoner, 0, 0); + } + + // despawn manually because of combat bug + m_creature->ForcedDespawn(10000); + } + + void AttackStart(Unit* /*pWho*/) override { } + void MoveInLineOfSight(Unit* /*pWho*/) override { } + void UpdateAI(const uint32 /*uiDiff*/) override { } +}; + +CreatureAI* GetAI_npc_arcane_beam(Creature* pCreature) +{ + return new npc_arcane_beamAI(pCreature); +} + +/*###### +## npc_centrifuge_core +######*/ + +// TODO Remove this 'script' when combat can be proper prevented from core-side +struct npc_centrifuge_coreAI : public Scripted_NoMovementAI +{ + npc_centrifuge_coreAI(Creature* pCreature) : Scripted_NoMovementAI(pCreature) { Reset(); } + + // Note: visual already handled in creature_template_addon + void Reset() override { } + void AttackStart(Unit* /*pWho*/) override { } + void MoveInLineOfSight(Unit* /*pWho*/) override { } + void UpdateAI(const uint32 /*uiDiff*/) override { } +}; + +CreatureAI* GetAI_npc_centrifuge_core(Creature* pCreature) +{ + return new npc_centrifuge_coreAI(pCreature); +} + +void AddSC_boss_varos() +{ + Script* pNewScript; + + pNewScript = new Script; + pNewScript->Name = "boss_varos"; + pNewScript->GetAI = &GetAI_boss_varos; + pNewScript->RegisterSelf(); + + pNewScript = new Script; + pNewScript->Name = "event_spell_call_captain"; + pNewScript->pProcessEventId = &ProcessEventId_event_spell_call_captain; + pNewScript->RegisterSelf(); + + pNewScript = new Script; + pNewScript->Name = "npc_azure_ring_captain"; + pNewScript->GetAI = &GetAI_npc_azure_ring_captain; + pNewScript->RegisterSelf(); + + pNewScript = new Script; + pNewScript->Name = "npc_arcane_beam"; + pNewScript->GetAI = &GetAI_npc_arcane_beam; + pNewScript->RegisterSelf(); + + pNewScript = new Script; + pNewScript->Name = "npc_centrifuge_core"; + pNewScript->GetAI = &GetAI_npc_centrifuge_core; + pNewScript->RegisterSelf(); +} diff --git a/src/modules/SD2/scripts/northrend/nexus/oculus/instance_oculus.cpp b/src/modules/SD2/scripts/northrend/nexus/oculus/instance_oculus.cpp new file mode 100644 index 000000000..48207698c --- /dev/null +++ b/src/modules/SD2/scripts/northrend/nexus/oculus/instance_oculus.cpp @@ -0,0 +1,260 @@ +/* Copyright (C) 2006 - 2013 ScriptDev2 + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along 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_oculus +SD%Complete: 50 +SDComment: Spawn instance bosses and handle Varos pre event; Dialogue handled by DBScripts +SDCategory: Oculus +EndScriptData */ + +#include "precompiled.h" +#include "oculus.h" + +instance_oculus::instance_oculus(Map* pMap) : ScriptedInstance(pMap) +{ + Initialize(); +} + +void instance_oculus::Initialize() +{ + memset(&m_auiEncounter, 0, sizeof(m_auiEncounter)); +} + +void instance_oculus::OnPlayerEnter(Player* pPlayer) +{ + if (GetData(TYPE_EREGOS) == DONE) + return; + + DoSpawnNextBossIfCan(); + + if (GetData(TYPE_DRAKOS) == DONE && GetData(TYPE_VAROS) == NOT_STARTED) + { + pPlayer->SendUpdateWorldState(WORLD_STATE_CONSTRUCTS, 1); + pPlayer->SendUpdateWorldState(WORLD_STATE_CONSTRUCTS_COUNT, m_sConstructsAliveGUIDSet.size()); + } +} + +void instance_oculus::OnCreatureCreate(Creature* pCreature) +{ + switch (pCreature->GetEntry()) + { + case NPC_VAROS: + case NPC_UROM: + case NPC_EREGOS: + m_mNpcEntryGuidStore[pCreature->GetEntry()] = pCreature->GetObjectGuid(); + break; + } +} + +void instance_oculus::OnObjectCreate(GameObject* pGo) +{ + switch (pGo->GetEntry()) + { + case GO_CACHE_EREGOS: + case GO_CACHE_EREGOS_H: + m_mGoEntryGuidStore[pGo->GetEntry()] = pGo->GetObjectGuid(); + break; + case GO_DRAGON_CAGE_DOOR: + m_lCageDoorGUIDs.push_back(pGo->GetObjectGuid()); + if (m_auiEncounter[TYPE_DRAKOS] == DONE) + pGo->SetGoState(GO_STATE_ACTIVE); + break; + } +} + +void instance_oculus::SetData(uint32 uiType, uint32 uiData) +{ + switch (uiType) + { + case TYPE_DRAKOS: + m_auiEncounter[TYPE_DRAKOS] = uiData; + if (uiData == DONE) + { + // Open all cages + for (GuidList::const_iterator itr = m_lCageDoorGUIDs.begin(); itr != m_lCageDoorGUIDs.end(); ++itr) + DoUseDoorOrButton(*itr); + + // Notes: The dialogue is handled by DB script + // Also the Centrifuge Constructs and the related npcs should be summoned - requires additional research + + // Activate the world state - the Centrifuge contructs should be loaded by now + DoUpdateWorldState(WORLD_STATE_CONSTRUCTS, 1); + DoUpdateWorldState(WORLD_STATE_CONSTRUCTS_COUNT, m_sConstructsAliveGUIDSet.size()); + + DoStartTimedAchievement(ACHIEVEMENT_CRITERIA_TYPE_KILL_CREATURE, ACHIEV_START_EREGOS_ID); + } + break; + case TYPE_VAROS: + m_auiEncounter[TYPE_VAROS] = uiData; + if (uiData == DONE) + { + // Note: Image of Belgaristrasz dialogue is handled by DB script + DoSpawnNextBossIfCan(); + DoUpdateWorldState(WORLD_STATE_CONSTRUCTS, 0); + } + break; + case TYPE_UROM: + m_auiEncounter[TYPE_UROM] = uiData; + // Note: Image of Belgaristrasz dialogue is handled by DB script + if (uiData == DONE) + DoSpawnNextBossIfCan(); + break; + case TYPE_EREGOS: + m_auiEncounter[TYPE_EREGOS] = uiData; + // Note: Image of Belgaristrasz teleports to the Cache location and does more dialogue - requires additional research + if (uiData == DONE) + { + // The data about the cache isn't consistent, so it's better to handle both cases + DoToggleGameObjectFlags(instance->IsRegularDifficulty() ? GO_CACHE_EREGOS : GO_CACHE_EREGOS_H, GO_FLAG_NO_INTERACT, false); + DoRespawnGameObject(instance->IsRegularDifficulty() ? GO_CACHE_EREGOS : GO_CACHE_EREGOS_H, 30 * MINUTE); + } + break; + } + + if (uiData == DONE) + { + OUT_SAVE_INST_DATA; + + std::ostringstream saveStream; + saveStream << m_auiEncounter[TYPE_DRAKOS] << " " << m_auiEncounter[TYPE_VAROS] << " " << m_auiEncounter[TYPE_UROM] << " " << m_auiEncounter[TYPE_EREGOS]; + + strInstData = saveStream.str(); + + SaveToDB(); + OUT_SAVE_INST_DATA_COMPLETE; + } +} + +uint32 instance_oculus::GetData(uint32 uiType) const +{ + if (uiType < MAX_ENCOUNTER) + return m_auiEncounter[uiType]; + + return 0; +} + +void instance_oculus::SetData64(uint32 uiData, uint64 uiGuid) +{ + // If Varos already completed, just ignore + if (GetData(TYPE_VAROS) == DONE) + return; + + // Note: this is handled in Acid. The purpose is check which Centrifuge Construct is alive, in case of server reset + // The function is triggered by eventAI on generic timer + if (uiData == DATA_CONSTRUCTS_EVENT) + { + m_sConstructsAliveGUIDSet.insert(ObjectGuid(uiGuid)); + + // Update world state in case of server reset + if (GetData(TYPE_DRAKOS) == DONE) + DoUpdateWorldState(WORLD_STATE_CONSTRUCTS_COUNT, m_sConstructsAliveGUIDSet.size()); + } +} + +void instance_oculus::OnCreatureEnterCombat(Creature* pCreature) +{ + if (pCreature->GetEntry() == NPC_DRAKOS) + SetData(TYPE_DRAKOS, IN_PROGRESS); +} + +void instance_oculus::OnCreatureEvade(Creature* pCreature) +{ + if (pCreature->GetEntry() == NPC_DRAKOS) + SetData(TYPE_DRAKOS, FAIL); +} + +void instance_oculus::OnCreatureDeath(Creature* pCreature) +{ + switch (pCreature->GetEntry()) + { + case NPC_DRAKOS: SetData(TYPE_DRAKOS, DONE); break; + case NPC_CENTRIFUGE_CONSTRUCT: + m_sConstructsAliveGUIDSet.erase(pCreature->GetObjectGuid()); + DoUpdateWorldState(WORLD_STATE_CONSTRUCTS_COUNT, m_sConstructsAliveGUIDSet.size()); + + if (m_sConstructsAliveGUIDSet.empty()) + { + if (Creature* pVaros = GetSingleCreatureFromStorage(NPC_VAROS)) + { + pVaros->ApplySpellImmune(0, IMMUNITY_DAMAGE, SPELL_SCHOOL_MASK_ALL, false); + pVaros->InterruptNonMeleeSpells(false); + } + } + break; + } +} + +void instance_oculus::DoSpawnNextBossIfCan() +{ + Player* pPlayer = GetPlayerInMap(); + if (!pPlayer) + return; + + if (GetData(TYPE_UROM) == DONE) + { + // return if already summoned + if (GetSingleCreatureFromStorage(NPC_EREGOS, true)) + return; + + pPlayer->SummonCreature(NPC_EREGOS, aOculusBossSpawnLocs[1][0], aOculusBossSpawnLocs[1][1], aOculusBossSpawnLocs[1][2], aOculusBossSpawnLocs[1][3], TEMPSUMMON_DEAD_DESPAWN, 0); + } + else if (GetData(TYPE_VAROS) == DONE) + { + // return if already summoned + if (GetSingleCreatureFromStorage(NPC_UROM, true)) + return; + + pPlayer->SummonCreature(NPC_UROM, aOculusBossSpawnLocs[0][0], aOculusBossSpawnLocs[0][1], aOculusBossSpawnLocs[0][2], aOculusBossSpawnLocs[0][3], TEMPSUMMON_DEAD_DESPAWN, 0); + } +} + +void instance_oculus::Load(const char* chrIn) +{ + if (!chrIn) + { + OUT_LOAD_INST_DATA_FAIL; + return; + } + + OUT_LOAD_INST_DATA(chrIn); + + std::istringstream loadStream(chrIn); + loadStream >> m_auiEncounter[TYPE_DRAKOS] >> m_auiEncounter[TYPE_VAROS] >> m_auiEncounter[TYPE_UROM] >> m_auiEncounter[TYPE_EREGOS]; + + 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_oculus(Map* pMap) +{ + return new instance_oculus(pMap); +} + +void AddSC_instance_oculus() +{ + Script* pNewScript; + + pNewScript = new Script; + pNewScript->Name = "instance_oculus"; + pNewScript->GetInstanceData = &GetInstanceData_instance_oculus; + pNewScript->RegisterSelf(); +} diff --git a/src/modules/SD2/scripts/northrend/nexus/oculus/oculus.cpp b/src/modules/SD2/scripts/northrend/nexus/oculus/oculus.cpp new file mode 100644 index 000000000..c8a2c5043 --- /dev/null +++ b/src/modules/SD2/scripts/northrend/nexus/oculus/oculus.cpp @@ -0,0 +1,155 @@ +/* Copyright (C) 2006 - 2013 ScriptDev2 + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +/* ScriptData +SDName: Oculus +SD%Complete: 80 +SDComment: Make use of the passenger boarding wrappers when supported by the core. +SDCategory: Oculus +EndScriptData */ + +#include "precompiled.h" +#include "oculus.h" +#include "TemporarySummon.h" + +enum +{ + EMOTE_FLY_AWAY = -1578030, + + SPELL_RIDE_RUBY_DRAKE_QUE = 49463, + SPELL_RIDE_EMERAL_DRAKE_QUE = 49427, + SPELL_RIDE_AMBER_DRAKE_QUE = 49459, + + SPELL_DRAKE_FLAG_VISUAL = 53797, + SPELL_PARACHUTE = 50550, // triggers 50553 + SPELL_FLIGHT = 50296, + SPELL_SOAR = 50325, + SPELL_EVASIVE_AURA = 50248, +}; + +/*###### +## npc_oculus_drake +######*/ + +struct npc_oculus_drakeAI : public ScriptedAI +{ + npc_oculus_drakeAI(Creature* pCreature) : ScriptedAI(pCreature) + { + if (m_creature->IsTemporarySummon()) + { + TemporarySummon* pTemporary = (TemporarySummon*)m_creature; + + uint32 uiMountSpell = 0; + switch (m_creature->GetEntry()) + { + case NPC_RUBY_DRAKE: uiMountSpell = SPELL_RIDE_RUBY_DRAKE_QUE; break; + case NPC_AMBER_DRAKE: uiMountSpell = SPELL_RIDE_AMBER_DRAKE_QUE; break; + case NPC_EMERALD_DRAKE: uiMountSpell = SPELL_RIDE_EMERAL_DRAKE_QUE; break; + } + + // Force player to mount + if (Player* pSummoner = m_creature->GetMap()->GetPlayer(pTemporary->GetSummonerGuid())) + { + pSummoner->CastSpell(pSummoner, uiMountSpell, true); + + // The dragon moves near the player after spawn + float fX, fY, fZ; + pSummoner->GetContactPoint(m_creature, fX, fY, fZ); + m_creature->GetMotionMaster()->MovePoint(0, fX, fY, fZ); + } + } + + SetCombatMovement(false); + Reset(); + } + + void Reset() override + { + DoCastSpellIfCan(m_creature, SPELL_SOAR, CAST_TRIGGERED | CAST_AURA_NOT_PRESENT); + + // Another aura for the Ruby drake + if (m_creature->GetEntry() == NPC_RUBY_DRAKE) + DoCastSpellIfCan(m_creature, SPELL_EVASIVE_AURA, CAST_TRIGGERED | CAST_AURA_NOT_PRESENT); + } + + void JustDied(Unit* /*pKiller*/) override + { + // Handle player parachute + if (m_creature->IsTemporarySummon()) + { + TemporarySummon* pTemporary = (TemporarySummon*)m_creature; + if (Player* pSummoner = m_creature->GetMap()->GetPlayer(pTemporary->GetSummonerGuid())) + { + pSummoner->RemoveAurasDueToSpell(SPELL_DRAKE_FLAG_VISUAL); + pSummoner->CastSpell(pSummoner, SPELL_PARACHUTE, true); + } + } + } + + // TODO: Temporary workaround - please remove when the boarding wrappers are implemented in core + void SpellHit(Unit* pCaster, const SpellEntry* pSpell) override + { + if (pCaster->GetTypeId() != TYPEID_PLAYER) + return; + + if (pSpell->Id == 49464 || pSpell->Id == 49346 || pSpell->Id == 49460) + DoCastSpellIfCan(m_creature, SPELL_FLIGHT, CAST_TRIGGERED); + } + + // TODO: Enable the wrappers below, when they will be properly supported by the core + /* + void PassengerBoarded(Unit* pPassenger, uint8 uiSeat) override + { + if (pPassenger->GetTypeId() != TYPEID_PLAYER) + return; + + // Set vehicle auras + DoCastSpellIfCan(m_creature, SPELL_FLIGHT, CAST_TRIGGERED); + + // Set passenger auras + pPassenger->CastSpell(pPassenger, SPELL_DRAKE_FLAG_VISUAL, true); + } + + void PassengerUnBoarded(Unit* pPassenger) override + { + pPassenger->RemoveAurasDueToSpell(SPELL_DRAKE_FLAG_VISUAL); + pPassenger->CastSpell(pPassenger, SPELL_PARACHUTE, true); + + DoScriptText(EMOTE_FLY_AWAY, m_creature); + + // The dragon runs away and despawns + float fX, fY, fZ; + m_creature->GetNearPoint(m_creature, fX, fY, fZ, 0, 20, frand(0, 2 * M_PI_F)); + m_creature->GetMotionMaster()->MovePoint(0, fX, fY, fZ + 20.0f); + m_creature->ForcedDespawn(5000); + } + */ +}; + +CreatureAI* GetAI_npc_oculus_drake(Creature* pCreature) +{ + return new npc_oculus_drakeAI(pCreature); +} + +void AddSC_oculus() +{ + Script* pNewScript; + + pNewScript = new Script; + pNewScript->Name = "npc_oculus_drake"; + pNewScript->GetAI = &GetAI_npc_oculus_drake; + pNewScript->RegisterSelf(); +} diff --git a/src/modules/SD2/scripts/northrend/nexus/oculus/oculus.h b/src/modules/SD2/scripts/northrend/nexus/oculus/oculus.h new file mode 100644 index 000000000..f59f3a470 --- /dev/null +++ b/src/modules/SD2/scripts/northrend/nexus/oculus/oculus.h @@ -0,0 +1,103 @@ +/* Copyright (C) 2006 - 2013 ScriptDev2 + * This program is free software licensed under GPL version 2 + * Please see the included DOCS/LICENSE.TXT for more information */ + +#ifndef DEF_OCULUS_H +#define DEF_OCULUS_H + +/* Encounters + * Drakos = 0 + * Varos = 1 + * Urom = 2 + * Eregos = 3 + */ + +enum +{ + MAX_ENCOUNTER = 4, + + TYPE_DRAKOS = 0, + TYPE_VAROS = 1, + TYPE_UROM = 2, + TYPE_EREGOS = 3, + + DATA_CONSTRUCTS_EVENT = 1, // DO NOT CHANGE! Used by Acid. - used to check the Centrifuge Constructs alive + + NPC_DRAKOS = 27654, + NPC_VAROS = 27447, + NPC_UROM = 27655, + NPC_EREGOS = 27656, + NPC_CENTRIFUGE_CONSTRUCT = 27641, + + NPC_ETERNOS = 27659, // bronze + NPC_VERDISA = 27657, // emerald + NPC_BELGARISTRASZ = 27658, // ruby + NPC_IMAGE_OF_BELGARISTRASZ = 28012, + + // Vehicle entries + NPC_EMERALD_DRAKE = 27692, + NPC_AMBER_DRAKE = 27755, + NPC_RUBY_DRAKE = 27756, + + // Cages in which the friendly dragons are hold + GO_DRAGON_CAGE_DOOR = 193995, + + // Loot + GO_CACHE_EREGOS = 191349, + GO_CACHE_EREGOS_H = 193603, + + SPELL_DEATH_SPELL = 50415, // summons 28012 + + // Instance event yells + SAY_EREGOS_SPAWN = -1578010, + + // world states to show how many constructs are still alive + WORLD_STATE_CONSTRUCTS = 3524, + WORLD_STATE_CONSTRUCTS_COUNT = 3486, + + ACHIEV_START_EREGOS_ID = 18153, // eregos timed kill achiev +}; + +static const float aOculusBossSpawnLocs[2][4] = +{ + {1177.47f, 937.722f, 527.405f, 2.21657f}, // Urom + {1077.04f, 1086.21f, 655.497f, 4.18879f} // Eregos +}; + +class instance_oculus : public ScriptedInstance +{ + public: + instance_oculus(Map* pMap); + + void Initialize() override; + + void OnPlayerEnter(Player* pPlayer) override; + void OnCreatureCreate(Creature* pCreature) override; + void OnObjectCreate(GameObject* pGo) override; + + void OnCreatureEnterCombat(Creature* pCreature) override; + void OnCreatureEvade(Creature* pCreature); + void OnCreatureDeath(Creature* pCreature) override; + + void SetData(uint32 uiType, uint32 uiData) override; + uint32 GetData(uint32 uiType) const override; + + void SetData64(uint32 uiType, uint64 uiGuid) override; + + const char* Save() const override { return strInstData.c_str(); } + void Load(const char* chrIn) override; + + // Check Varos' shield + bool IsShieldBroken() { return m_sConstructsAliveGUIDSet.empty(); } + + protected: + void DoSpawnNextBossIfCan(); + + uint32 m_auiEncounter[MAX_ENCOUNTER]; + std::string strInstData; + + GuidList m_lCageDoorGUIDs; + GuidSet m_sConstructsAliveGUIDSet; +}; + +#endif diff --git a/src/modules/SD2/scripts/northrend/obsidian_sanctum/boss_sartharion.cpp b/src/modules/SD2/scripts/northrend/obsidian_sanctum/boss_sartharion.cpp new file mode 100644 index 000000000..d8bb67953 --- /dev/null +++ b/src/modules/SD2/scripts/northrend/obsidian_sanctum/boss_sartharion.cpp @@ -0,0 +1,1374 @@ +/* Copyright (C) 2006 - 2013 ScriptDev2 + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with 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: 95% +SDComment: Portal event may be incomplete - additional reseach required. +SDCategory: Obsidian Sanctum +EndScriptData */ + +#include "precompiled.h" +#include "obsidian_sanctum.h" + +enum +{ + // Sartharion Yell + SAY_SARTHARION_AGGRO = -1615018, + SAY_SARTHARION_BERSERK = -1615019, + SAY_SARTHARION_BREATH = -1615020, + SAY_SARTHARION_CALL_SHADRON = -1615021, + SAY_SARTHARION_CALL_TENEBRON = -1615022, + SAY_SARTHARION_CALL_VESPERON = -1615023, + SAY_SARTHARION_DEATH = -1615024, + SAY_SARTHARION_SPECIAL_1 = -1615025, + SAY_SARTHARION_SPECIAL_2 = -1615026, + SAY_SARTHARION_SPECIAL_3 = -1615027, + SAY_SARTHARION_SPECIAL_4 = -1615028, + SAY_SARTHARION_SLAY_1 = -1615029, + SAY_SARTHARION_SLAY_2 = -1615030, + SAY_SARTHARION_SLAY_3 = -1615031, + + EMOTE_LAVA_CHURN = -1615032, + EMOTE_SHADRON_DICIPLE = -1615008, + EMOTE_VESPERON_DICIPLE = -1615041, + EMOTE_HATCH_EGGS = -1615017, + EMOTE_OPEN_PORTAL = -1615042, // emote 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. + SPELL_FLAME_BREATH = 56908, // Inflicts 8750 to 11250 Fire damage to enemies in a cone in front of the caster. + SPELL_FLAME_BREATH_H = 58956, // Inflicts 10938 to 14062 Fire damage to enemies in a cone in front of the caster. + SPELL_TAIL_LASH = 56910, // A sweeping tail strike hits all enemies behind the caster, inflicting 3063 to 3937 damage and stunning them for 2 sec. + SPELL_TAIL_LASH_H = 58957, // A sweeping tail strike hits all enemies behind the caster, inflicting 4375 to 5625 damage and stunning them for 2 sec. + 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_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) + + // phase spells + // 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 (handled in core) + + // 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 + 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 + + // Vesperon related npcs + NPC_DISCIPLE_OF_VESPERON = 30858, // Disciple of Vesperon + NPC_ACOLYTE_OF_VESPERON = 31219, // Acolyte of Vesperon - summoned during Sartharion event + // NPC_VESPERON_CONTROLLER = 30878, // not clear how to use this; used only to cast 61190 to eject players to normal realm + // NPC_VESPERON_CONTROLLER_DEBUFF_CLEAR = 32694, // not used + + // Shadron + // In portal is a disciple, when disciple killed remove Power_of_vesperon, portal open multiple times + 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 + + // Shadron related npcs + NPC_DISCIPLE_OF_SHADRON = 30688, // Disciple of Shadron + NPC_ACOLYTE_OF_SHADRON = 31218, // Acolyte of Shadron - summoned during Sartharion event + // NPC_SHADRON_PORTAL = 30741, // not used + // NPC_SHADRON_PORTAL_VISUAL = 30650, // not used + + // 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%. + SPELL_HATCH_EGGS_MAIN = 58793, // Tenebron's hatch eggs spell - not used because core can't target other phase creatures + + // other hatch eggs spells - currently it's unknown how to use them + SPELL_HATCH_EGGS_H = 59189, + SPELL_HATCH_EGGS = 58542, + SPELL_HATCH_EGGS_EFFECT_H = 59190, + SPELL_HATCH_EGGS_EFFECT = 58685, + + // Tenebron related npcs + NPC_TWILIGHT_EGG = 30882, // Twilight Egg - summoned during Tenebron event + NPC_SARTHARION_TWILIGHT_EGG = 31204, // Twilight Egg - summoned during Sartharion event + NPC_TWILIGHT_EGG_CONTROLLER = 31138, // not clear how to use this; used only to eject players to normal realm + + // Twilight eggs spells + SPELL_SUMMON_TWILIGHT_WHELP = 58035, // will spawn 30890 + SPELL_SUMMON_SARTHARION_TWILIGHT_WHELP = 58826, // will spawn 31214 + + NPC_TWILIGHT_WHELP = 30890, + NPC_SHARTHARION_TWILIGHT_WHELP = 31214, + + // 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 + + // Fire cyclone + // SPELL_LAVA_STRIKE = 57578, // triggers 57571 then trigger visual missile, then summon Lava Blaze on impact(spell 57572) + SPELL_LAVA_STRIKE_IMPACT = 57591, + // SPELL_CYCLONE_AURA = 57560, // in creature_template_addon + SPELL_CYCLONE_AURA_STRIKE = 57598, // triggers 57578 + + NPC_FLAME_TSUNAMI = 30616, // for the flame waves + NPC_LAVA_BLAZE = 30643, // adds spawning from flame strike + + // other + MAX_TWILIGHT_EGGS = 6, + PHASEMASK_TWILIGHT_REALM = 16, + + // 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, m_fO; +}; + +// each dragons special points. First where fly to before connect to connon, second where land point is. +Waypoint m_aTene[] = +{ + {3212.854f, 575.597f, 109.856f}, // init + {3246.425f, 565.367f, 61.249f} // end +}; + +Waypoint m_aShad[] = +{ + {3293.238f, 472.223f, 106.968f}, + {3271.669f, 526.907f, 61.931f} +}; + +Waypoint m_aVesp[] = +{ + {3193.310f, 472.861f, 102.697f}, + {3227.268f, 533.238f, 59.995f} +}; + +// points around raid "isle", counter clockwise. should probably be adjusted to be more alike +Waypoint m_aDragonCommon[] = +{ + {3214.012f, 468.932f, 98.652f}, + {3244.950f, 468.427f, 98.652f}, + {3283.520f, 496.869f, 98.652f}, + {3287.316f, 555.875f, 98.652f}, + {3250.479f, 585.827f, 98.652f}, + {3209.969f, 566.523f, 98.652f} +}; + +Waypoint m_aTsunamiLoc[] = +{ + // Note: this coords are guesswork, they might need to be updated. + {3201.0f, 481.82f, 58.6f, 6.23f}, // left to right + {3201.0f, 524.50f, 58.6f, 6.23f}, + {3201.0f, 566.64f, 58.6f, 6.23f}, + {3287.5f, 545.50f, 58.6f, 3.19f}, // right to left + {3287.5f, 503.00f, 58.6f, 3.19f}, +}; + +/*###### +## Boss Sartharion +######*/ + +struct boss_sartharionAI : public ScriptedAI +{ + boss_sartharionAI(Creature* pCreature) : ScriptedAI(pCreature) + { + m_pInstance = (instance_obsidian_sanctum*)pCreature->GetInstanceData(); + m_bIsRegularMode = pCreature->GetMap()->IsRegularDifficulty(); + Reset(); + } + + instance_obsidian_sanctum* m_pInstance; + bool m_bIsRegularMode; + + 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() override + { + m_bIsBerserk = false; + m_bIsSoftEnraged = false; + + m_uiEnrageTimer = 15 * MINUTE * IN_MILLISECONDS; + 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 = urand(20000, 30000); + + m_bHasCalledTenebron = false; + m_bHasCalledShadron = false; + m_bHasCalledVesperon = false; + } + + void Aggro(Unit* /*pWho*/) override + { + DoScriptText(SAY_SARTHARION_AGGRO, m_creature); + + if (m_pInstance) + { + m_pInstance->SetData(TYPE_SARTHARION_EVENT, IN_PROGRESS); + FetchDragons(); + } + } + + void JustDied(Unit* /*pKiller*/) override + { + DoScriptText(SAY_SARTHARION_DEATH, m_creature); + + if (m_pInstance) + m_pInstance->SetData(TYPE_SARTHARION_EVENT, DONE); + } + + void KilledUnit(Unit* /*pVictim*/) override + { + switch (urand(0, 2)) + { + case 0: DoScriptText(SAY_SARTHARION_SLAY_1, m_creature); break; + case 1: DoScriptText(SAY_SARTHARION_SLAY_2, m_creature); break; + case 2: DoScriptText(SAY_SARTHARION_SLAY_3, m_creature); break; + } + } + + void JustReachedHome() override + { + if (m_pInstance) + m_pInstance->SetData(TYPE_SARTHARION_EVENT, FAIL); + + // Despawn portal + if (GameObject* pPortal = GetClosestGameObjectWithEntry(m_creature, GO_TWILIGHT_PORTAL, 50.0f)) + pPortal->SetLootState(GO_JUST_DEACTIVATED); + } + + void FetchDragons() + { + Creature* pTene = m_pInstance->GetSingleCreatureFromStorage(NPC_TENEBRON); + Creature* pShad = m_pInstance->GetSingleCreatureFromStorage(NPC_SHADRON); + Creature* pVesp = m_pInstance->GetSingleCreatureFromStorage(NPC_VESPERON); + + // if at least one of the dragons are alive and are being called + uint8 uiCountFetchableDragons = 0; + + if (pTene && pTene->IsAlive() && !pTene->getVictim()) + { + ++uiCountFetchableDragons; + 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()) + { + ++uiCountFetchableDragons; + 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()) + { + ++uiCountFetchableDragons; + 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 (uiCountFetchableDragons) + DoCastSpellIfCan(m_creature, SPELL_WILL_OF_SARTHARION); + + m_pInstance->SetData(TYPE_ALIVE_DRAGONS, uiCountFetchableDragons); + } + + void CallDragon(uint32 uiEntry) + { + if (m_pInstance) + { + Creature* pTemp = m_pInstance->GetSingleCreatureFromStorage(uiEntry); + if (pTemp && pTemp->IsAlive()) + { + if (pTemp->HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE)) + pTemp->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE); + + if (pTemp->getVictim()) + return; + + pTemp->SetWalk(false); + + int32 iTextId = 0; + + switch (uiEntry) + { + case NPC_TENEBRON: + iTextId = SAY_SARTHARION_CALL_TENEBRON; + pTemp->GetMotionMaster()->MovePoint(POINT_ID_LAND, m_aTene[1].m_fX, m_aTene[1].m_fY, m_aTene[1].m_fZ); + break; + case NPC_SHADRON: + iTextId = SAY_SARTHARION_CALL_SHADRON; + pTemp->GetMotionMaster()->MovePoint(POINT_ID_LAND, m_aShad[1].m_fX, m_aShad[1].m_fY, m_aShad[1].m_fZ); + break; + case NPC_VESPERON: + iTextId = SAY_SARTHARION_CALL_VESPERON; + 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() + { + DoScriptText(EMOTE_LAVA_CHURN, m_creature); + + uint8 uiTsunamiStartLoc = 0; + uint8 uiTsunamiEndLoc = 3; + if (urand(0, 1)) + { + uiTsunamiStartLoc = 3; + uiTsunamiEndLoc = 5; + } + + for (uint8 i = uiTsunamiStartLoc; i < uiTsunamiEndLoc; ++i) + m_creature->SummonCreature(NPC_FLAME_TSUNAMI, m_aTsunamiLoc[i].m_fX, m_aTsunamiLoc[i].m_fY, m_aTsunamiLoc[i].m_fZ, m_aTsunamiLoc[i].m_fO, TEMPSUMMON_TIMED_DESPAWN, 15000); + } + + void UpdateAI(const uint32 uiDiff) override + { + // Return since we have no target + if (!m_creature->SelectHostileTarget() || !m_creature->getVictim()) + return; + + // spell will target dragons, if they are still alive at 35% + if (!m_bIsBerserk && m_creature->GetHealthPercent() < 35.0f) + { + if (DoCastSpellIfCan(m_creature, SPELL_BERSERK) == CAST_OK) + { + DoScriptText(SAY_SARTHARION_BERSERK, m_creature); + m_bIsBerserk = true; + } + } + + // soft enrage + if (!m_bIsSoftEnraged && m_creature->GetHealthPercent() <= 10.0f) + { + // TODO + m_bIsSoftEnraged = true; + } + + // hard enrage + if (!m_bIsHardEnraged) + { + if (m_uiEnrageTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature, SPELL_PYROBUFFET, CAST_TRIGGERED) == CAST_OK) + m_bIsHardEnraged = true; + } + else + m_uiEnrageTimer -= uiDiff; + } + + // flame tsunami + if (m_uiFlameTsunamiTimer < uiDiff) + { + SendFlameTsunami(); + m_uiFlameTsunamiTimer = 30000; + } + else + m_uiFlameTsunamiTimer -= uiDiff; + + // flame breath + if (m_uiFlameBreathTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature, m_bIsRegularMode ? SPELL_FLAME_BREATH : SPELL_FLAME_BREATH_H) == CAST_OK) + { + DoScriptText(SAY_SARTHARION_BREATH, m_creature); + m_uiFlameBreathTimer = urand(25000, 35000); + } + } + else + m_uiFlameBreathTimer -= uiDiff; + + // Tail Sweep + if (m_uiTailSweepTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature, m_bIsRegularMode ? SPELL_TAIL_LASH : SPELL_TAIL_LASH_H) == CAST_OK) + m_uiTailSweepTimer = urand(15000, 20000); + } + else + m_uiTailSweepTimer -= uiDiff; + + // Cleave + if (m_uiCleaveTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature->getVictim(), SPELL_CLEAVE) == CAST_OK) + m_uiCleaveTimer = urand(7000, 10000); + } + else + m_uiCleaveTimer -= uiDiff; + + // Lavas Strike + if (m_uiLavaStrikeTimer < uiDiff) + { + if (m_pInstance) + { + if (Creature* pCyclone = m_creature->GetMap()->GetCreature(m_pInstance->SelectRandomFireCycloneGuid())) + pCyclone->CastSpell(pCyclone, SPELL_CYCLONE_AURA_STRIKE, true); + + switch (urand(0, 5)) + { + case 0: DoScriptText(SAY_SARTHARION_SPECIAL_1, m_creature); break; + case 1: DoScriptText(SAY_SARTHARION_SPECIAL_2, m_creature); break; + case 2: DoScriptText(SAY_SARTHARION_SPECIAL_3, m_creature); break; + case 3: DoScriptText(SAY_SARTHARION_SPECIAL_4, m_creature); break; + } + } + m_uiLavaStrikeTimer = 30000; + } + else + m_uiLavaStrikeTimer -= uiDiff; + + // call tenebron + if (!m_bHasCalledTenebron) + { + if (m_uiTenebronTimer < uiDiff) + { + CallDragon(NPC_TENEBRON); + m_bHasCalledTenebron = true; + } + else + m_uiTenebronTimer -= uiDiff; + } + + // call shadron + if (!m_bHasCalledShadron) + { + if (m_uiShadronTimer < uiDiff) + { + CallDragon(NPC_SHADRON); + m_bHasCalledShadron = true; + } + else + m_uiShadronTimer -= uiDiff; + } + + // call vesperon + if (!m_bHasCalledVesperon) + { + if (m_uiVesperonTimer < uiDiff) + { + CallDragon(NPC_VESPERON); + m_bHasCalledVesperon = true; + } + else + m_uiVesperonTimer -= uiDiff; + } + + DoMeleeAttackIfReady(); + + EnterEvadeIfOutOfCombatArea(uiDiff); + } +}; + +CreatureAI* GetAI_boss_sartharion(Creature* pCreature) +{ + return new boss_sartharionAI(pCreature); +} + +enum TeneText +{ + SAY_TENEBRON_AGGRO = -1615009, + SAY_TENEBRON_SLAY_1 = -1615010, + SAY_TENEBRON_SLAY_2 = -1615011, + SAY_TENEBRON_DEATH = -1615012, + SAY_TENEBRON_BREATH = -1615013, + SAY_TENEBRON_RESPOND = -1615014, + SAY_TENEBRON_SPECIAL_1 = -1615015, + SAY_TENEBRON_SPECIAL_2 = -1615016 +}; + +enum ShadText +{ + SAY_SHADRON_AGGRO = -1615000, + SAY_SHADRON_SLAY_1 = -1615001, + SAY_SHADRON_SLAY_2 = -1615002, + SAY_SHADRON_DEATH = -1615003, + SAY_SHADRON_BREATH = -1615004, + SAY_SHADRON_RESPOND = -1615005, + SAY_SHADRON_SPECIAL_1 = -1615006, + SAY_SHADRON_SPECIAL_2 = -1615007 +}; + +enum VespText +{ + SAY_VESPERON_AGGRO = -1615033, + SAY_VESPERON_SLAY_1 = -1615034, + SAY_VESPERON_SLAY_2 = -1615035, + SAY_VESPERON_DEATH = -1615036, + SAY_VESPERON_BREATH = -1615037, + SAY_VESPERON_RESPOND = -1615038, + SAY_VESPERON_SPECIAL_1 = -1615039, + SAY_VESPERON_SPECIAL_2 = -1615040 +}; + +// to control each dragons common abilities +struct dummy_dragonAI : public ScriptedAI +{ + dummy_dragonAI(Creature* pCreature) : ScriptedAI(pCreature) + { + m_pInstance = (instance_obsidian_sanctum*)pCreature->GetInstanceData(); + m_bIsRegularMode = pCreature->GetMap()->IsRegularDifficulty(); + Reset(); + } + + instance_obsidian_sanctum* m_pInstance; + bool m_bIsRegularMode; + uint8 m_uiPortalId; + + uint32 m_uiWaypointId; + uint32 m_uiMoveNextTimer; + bool m_bCanMoveFree; + + uint32 m_uiPortalRespawnTimer; + uint32 m_uiShadowBreathTimer; + uint32 m_uiShadowFissureTimer; + + ObjectGuid m_portalOwnerGuid; + + void Reset() override + { + m_uiWaypointId = 0; + m_uiMoveNextTimer = 500; + m_bCanMoveFree = false; + + m_uiPortalRespawnTimer = 20000; + m_uiShadowBreathTimer = 20000; + m_uiShadowFissureTimer = 5000; + } + + void JustReachedHome() override + { + if (m_creature->HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE)) + m_creature->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE); + + // Despawn portal + if (GameObject* pPortal = GetClosestGameObjectWithEntry(m_creature, GO_TWILIGHT_PORTAL, 50.0f)) + pPortal->SetLootState(GO_JUST_DEACTIVATED); + + // reset portal events (in case some remain active); summons cleanup handled by creature linking + if (m_pInstance) + m_pInstance->SetPortalStatus(m_uiPortalId, false); + } + + void JustDied(Unit* /*pKiller*/) override + { + // despawn portal if Sartharion is not in combat + if (GameObject* pPortal = GetClosestGameObjectWithEntry(m_creature, GO_TWILIGHT_PORTAL, 50.0f)) + pPortal->SetLootState(GO_JUST_DEACTIVATED); + + // eject players and despawn portal owner + if (Creature* pTemp = m_creature->GetMap()->GetCreature(m_portalOwnerGuid)) + { + pTemp->CastSpell(pTemp, SPELL_TWILIGHT_SHIFT_REMOVAL_ALL, true); + pTemp->ForcedDespawn(1000); + } + } + + void MovementInform(uint32 uiType, uint32 uiPointId) override + { + 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 the end (!) + if (uiPointId == POINT_ID_LAND) + { + m_creature->GetMotionMaster()->Clear(); + m_bCanMoveFree = false; + m_creature->SetInCombatWithZone(); + return; + } + + // 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 >= countof(m_aDragonCommon)) + { + if (!m_bCanMoveFree) + m_bCanMoveFree = true; + + m_uiWaypointId = 0; + } + + m_uiMoveNextTimer = 500; + } + + //"opens" the portal and does the "opening" whisper + void DoOpenPortal() + { + // 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 = GetClosestGameObjectWithEntry(m_creature, GO_TWILIGHT_PORTAL, 50.0f); + DoScriptText(EMOTE_OPEN_PORTAL, m_creature); + + // 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(HOUR * IN_MILLISECONDS); + pPortal->Refresh(); + } + + // set portal status as active when Sartharion is in progress + if (m_pInstance && m_pInstance->GetData(TYPE_SARTHARION_EVENT) == IN_PROGRESS) + m_pInstance->SetPortalStatus(m_uiPortalId, true); + + // 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)) + i->getSource()->RemoveAurasDueToSpell(uiSpellId); + } + } + } + + // Eject players from Twilight realm if no other portal event is active + void DoEjectTwilightPlayersIfCan(Creature* pCreature) + { + if (!m_pInstance || !pCreature) + return; + + if (m_pInstance->GetData(TYPE_SARTHARION_EVENT) != IN_PROGRESS || !m_pInstance->IsActivePortal()) + { + pCreature->CastSpell(pCreature, SPELL_TWILIGHT_SHIFT_REMOVAL_ALL, true); + + if (GameObject* pPortal = GetClosestGameObjectWithEntry(m_creature, GO_TWILIGHT_PORTAL, 50.0f)) + pPortal->SetLootState(GO_JUST_DEACTIVATED); + } + } + + // Handle breath yell + virtual void DoHandleBreathYell() { } + + // Handle special events for each dragon + virtual void UpdateDragonAI(const uint32 /*uiDiff*/) { } + + void UpdateAI(const uint32 uiDiff) override + { + if (m_bCanMoveFree && m_uiMoveNextTimer) + { + if (m_uiMoveNextTimer <= uiDiff) + { + 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; + } + else + m_uiMoveNextTimer -= uiDiff; + } + + // if no target return + if (!m_creature->SelectHostileTarget() || !m_creature->getVictim()) + return; + + // Call dragon specific virtual function + UpdateDragonAI(uiDiff); + + // respawn portal + if (m_uiPortalRespawnTimer < uiDiff) + { + if (m_pInstance && m_pInstance->IsActivePortal()) + m_uiPortalRespawnTimer = 10000; + else + { + m_uiPortalRespawnTimer = 60000; + DoOpenPortal(); + } + } + else + m_uiPortalRespawnTimer -= uiDiff; + + // shadow fissure + if (m_uiShadowFissureTimer < uiDiff) + { + if (Unit* pTarget = m_creature->SelectAttackingTarget(ATTACKING_TARGET_RANDOM, 0)) + { + if (DoCastSpellIfCan(pTarget, m_bIsRegularMode ? SPELL_SHADOW_FISSURE : SPELL_SHADOW_FISSURE_H) == CAST_OK) + m_uiShadowFissureTimer = urand(15000, 20000); + } + } + else + m_uiShadowFissureTimer -= uiDiff; + + // shadow breath + if (m_uiShadowBreathTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature, m_bIsRegularMode ? SPELL_SHADOW_BREATH : SPELL_SHADOW_BREATH_H) == CAST_OK) + { + DoHandleBreathYell(); + m_uiShadowBreathTimer = urand(20000, 25000); + } + } + else + m_uiShadowBreathTimer -= uiDiff; + + DoMeleeAttackIfReady(); + } +}; + +/*###### +## Mob Tenebron +######*/ + +struct mob_tenebronAI : public dummy_dragonAI +{ + mob_tenebronAI(Creature* pCreature) : dummy_dragonAI(pCreature) + { + m_uiPortalId = TYPE_PORTAL_TENEBRON; + Reset(); + } + + uint32 m_uiSpawnEggsTimer; + + void Reset() override + { + m_uiSpawnEggsTimer = 20000; + + dummy_dragonAI::Reset(); + } + + void Aggro(Unit* /*pWho*/) override + { + DoScriptText(SAY_TENEBRON_AGGRO, m_creature); + DoCastSpellIfCan(m_creature, SPELL_POWER_OF_TENEBRON); + } + + void KilledUnit(Unit* /*pVictim*/) override + { + DoScriptText(urand(0, 1) ? SAY_TENEBRON_SLAY_1 : SAY_TENEBRON_SLAY_2, m_creature); + } + + void JustDied(Unit* pKiller) override + { + DoScriptText(SAY_TENEBRON_DEATH, m_creature); + + if (m_pInstance) + { + // Cast Twilight Revent - script target on Sartharion + if (m_pInstance->GetData(TYPE_SARTHARION_EVENT) == IN_PROGRESS) + DoCastSpellIfCan(m_creature, SPELL_TWILIGHT_REVENGE, CAST_TRIGGERED); + else + dummy_dragonAI::JustDied(pKiller); + } + } + + void JustSummoned(Creature* pSummoned) override + { + if (pSummoned->GetEntry() == NPC_TWILIGHT_EGG_CONTROLLER) + m_portalOwnerGuid = pSummoned->GetObjectGuid(); + + // update phasemask manually + pSummoned->SetPhaseMask(PHASEMASK_TWILIGHT_REALM, true); + } + + void ReceiveAIEvent(AIEventType eventType, Creature* /*pSender*/, Unit* pInvoker, uint32 /*uiMiscValue*/) override + { + if (eventType == AI_EVENT_CUSTOM_A && pInvoker->GetEntry() == NPC_TWILIGHT_EGG_CONTROLLER) + { + if (m_pInstance) + m_pInstance->SetPortalStatus(m_uiPortalId, false); + + DoEjectTwilightPlayersIfCan((Creature*)pInvoker); + } + } + + void DoHandleBreathYell() + { + DoScriptText(SAY_TENEBRON_BREATH, m_creature); + } + + void UpdateDragonAI(const uint32 uiDiff) + { + if (m_uiSpawnEggsTimer < uiDiff) + { + uint32 uiSpawnEntry = NPC_TWILIGHT_EGG; + if (m_pInstance) + { + if (m_pInstance->GetData(TYPE_SARTHARION_EVENT) == IN_PROGRESS) + uiSpawnEntry = NPC_SARTHARION_TWILIGHT_EGG; + } + + float fX, fY, fZ; + for (uint8 i = 0; i < MAX_TWILIGHT_EGGS; ++i) + { + m_creature->GetRandomPoint(m_creature->GetPositionX(), m_creature->GetPositionY(), m_creature->GetPositionZ(), 20.0f, fX, fY, fZ); + m_creature->SummonCreature(uiSpawnEntry, fX, fY, fZ, 0, TEMPSUMMON_DEAD_DESPAWN, 0); + } + + // spawn the controller as well in order to eject players from twilight realm + m_creature->GetRandomPoint(m_creature->GetPositionX(), m_creature->GetPositionY(), m_creature->GetPositionZ(), 20.0f, fX, fY, fZ); + m_creature->SummonCreature(NPC_TWILIGHT_EGG_CONTROLLER, fX, fY, fZ, 0, TEMPSUMMON_DEAD_DESPAWN, 0); + + // used only for visual - the result is handled by the Twilight eggs script + if (DoCastSpellIfCan(m_creature, SPELL_HATCH_EGGS_MAIN) == CAST_OK) + { + DoScriptText(EMOTE_HATCH_EGGS, m_creature); + if (urand(0, 1)) + DoScriptText(urand(0, 1) ? SAY_TENEBRON_SPECIAL_1 : SAY_TENEBRON_SPECIAL_2, m_creature); + } + + m_uiSpawnEggsTimer = 60000; + } + else + m_uiSpawnEggsTimer -= uiDiff; + } +}; + +CreatureAI* GetAI_mob_tenebron(Creature* pCreature) +{ + return new mob_tenebronAI(pCreature); +} + +/*###### +## Mob Shadron +######*/ + +struct mob_shadronAI : public dummy_dragonAI +{ + mob_shadronAI(Creature* pCreature) : dummy_dragonAI(pCreature) + { + m_uiPortalId = TYPE_PORTAL_SHADRON; + Reset(); + } + + uint32 m_uiAcolyteShadronTimer; + + void Reset() override + { + m_uiAcolyteShadronTimer = 25000; + + dummy_dragonAI::Reset(); + } + + void Aggro(Unit* /*pWho*/) override + { + DoScriptText(SAY_SHADRON_AGGRO, m_creature); + DoCastSpellIfCan(m_creature, SPELL_POWER_OF_SHADRON); + } + + void KilledUnit(Unit* /*pVictim*/) override + { + DoScriptText(urand(0, 1) ? SAY_SHADRON_SLAY_1 : SAY_SHADRON_SLAY_2, m_creature); + } + + void JustDied(Unit* pKiller) override + { + DoScriptText(SAY_SHADRON_DEATH, m_creature); + + if (m_pInstance) + { + // Cast Twilight Revent - script target on Sartharion + if (m_pInstance->GetData(TYPE_SARTHARION_EVENT) == IN_PROGRESS) + DoCastSpellIfCan(m_creature, SPELL_TWILIGHT_REVENGE, CAST_TRIGGERED); + else + dummy_dragonAI::JustDied(pKiller); + } + } + + void JustSummoned(Creature* pSummoned) override + { + if (pSummoned->GetEntry() == NPC_DISCIPLE_OF_SHADRON) + { + pSummoned->CastSpell(pSummoned, SPELL_GIFT_OF_TWILIGTH_SHA, true); + m_portalOwnerGuid = pSummoned->GetObjectGuid(); + } + else if (pSummoned->GetEntry() == NPC_ACOLYTE_OF_SHADRON) + pSummoned->CastSpell(pSummoned, SPELL_GIFT_OF_TWILIGTH_SAR, true); + + // update phasemask manually + pSummoned->SetPhaseMask(PHASEMASK_TWILIGHT_REALM, true); + } + + void SummonedCreatureJustDied(Creature* pSummoned) override + { + if (m_pInstance) + { + m_pInstance->SetPortalStatus(m_uiPortalId, false); + + if (m_pInstance->GetData(TYPE_SARTHARION_EVENT) == IN_PROGRESS) + { + if (Creature* pSartharion = m_pInstance->GetSingleCreatureFromStorage(NPC_SARTHARION)) + pSartharion->RemoveAurasDueToSpell(SPELL_GIFT_OF_TWILIGTH_SAR); + } + else + m_creature->RemoveAurasDueToSpell(SPELL_GIFT_OF_TWILIGTH_SHA); + } + + DoEjectTwilightPlayersIfCan(pSummoned); + m_uiAcolyteShadronTimer = m_uiPortalRespawnTimer + 5000; + } + + void DoHandleBreathYell() + { + DoScriptText(SAY_SHADRON_BREATH, m_creature); + } + + void UpdateDragonAI(const uint32 uiDiff) + { + if (m_uiAcolyteShadronTimer) + { + if (m_uiAcolyteShadronTimer <= uiDiff) + { + DoScriptText(EMOTE_SHADRON_DICIPLE, m_creature); + if (urand(0, 1)) + DoScriptText(urand(0, 1) ? SAY_SHADRON_SPECIAL_1 : SAY_SHADRON_SPECIAL_2, m_creature); + + uint32 uiSpawnEntry = NPC_DISCIPLE_OF_SHADRON; + if (m_pInstance) + { + if (m_pInstance->GetData(TYPE_SARTHARION_EVENT) == IN_PROGRESS) + uiSpawnEntry = NPC_ACOLYTE_OF_SHADRON; + } + + float fX, fY, fZ; + m_creature->GetRandomPoint(m_creature->GetPositionX(), m_creature->GetPositionY(), m_creature->GetPositionZ(), 20.0f, fX, fY, fZ); + m_creature->SummonCreature(uiSpawnEntry, fX, fY, fZ, 0, TEMPSUMMON_DEAD_DESPAWN, 0); + + m_uiAcolyteShadronTimer = 0; + } + else + m_uiAcolyteShadronTimer -= uiDiff; + } + } +}; + +CreatureAI* GetAI_mob_shadron(Creature* pCreature) +{ + return new mob_shadronAI(pCreature); +} + +/*###### +## Mob Vesperon +######*/ + +struct mob_vesperonAI : public dummy_dragonAI +{ + mob_vesperonAI(Creature* pCreature) : dummy_dragonAI(pCreature) + { + m_uiPortalId = TYPE_PORTAL_VESPERON; + Reset(); + } + + uint32 m_uiAcolyteVesperonTimer; + + void Reset() override + { + m_uiAcolyteVesperonTimer = 25000; + + dummy_dragonAI::Reset(); + } + + void Aggro(Unit* /*pWho*/) override + { + DoScriptText(SAY_VESPERON_AGGRO, m_creature); + DoCastSpellIfCan(m_creature, SPELL_POWER_OF_VESPERON); + } + + void KilledUnit(Unit* /*pVictim*/) override + { + DoScriptText(urand(0, 1) ? SAY_VESPERON_SLAY_1 : SAY_VESPERON_SLAY_2, m_creature); + } + + void JustDied(Unit* pKiller) override + { + DoScriptText(SAY_VESPERON_DEATH, m_creature); + + if (m_pInstance) + { + // Cast Twilight Revent - script target on Sartharion + if (m_pInstance->GetData(TYPE_SARTHARION_EVENT) == IN_PROGRESS) + DoCastSpellIfCan(m_creature, SPELL_TWILIGHT_REVENGE, CAST_TRIGGERED); + else + dummy_dragonAI::JustDied(pKiller); + } + } + + void JustSummoned(Creature* pSummoned) override + { + // ToDo: these spells may break the encounter and make it unplayable. More research is required!!! + if (pSummoned->GetEntry() == NPC_DISCIPLE_OF_VESPERON) + { + //pSummoned->CastSpell(pSummoned, SPELL_TWILIGHT_TORMENT_VESP, true); + m_portalOwnerGuid = pSummoned->GetObjectGuid(); + } + //else if (pSummoned->GetEntry() == NPC_ACOLYTE_OF_VESPERON) + // pSummoned->CastSpell(pSummoned, SPELL_TWILIGHT_TORMENT_VESP_ACO, true); + + // update phasemask manually + pSummoned->SetPhaseMask(PHASEMASK_TWILIGHT_REALM, true); + } + + void SummonedCreatureJustDied(Creature* pSummoned) override + { + // ToDo: remove Twilight Torment debuff + if (m_pInstance) + m_pInstance->SetPortalStatus(m_uiPortalId, false); + + DoEjectTwilightPlayersIfCan(pSummoned); + m_uiAcolyteVesperonTimer = m_uiPortalRespawnTimer + 5000; + } + + void DoHandleBreathYell() + { + DoScriptText(SAY_VESPERON_BREATH, m_creature); + } + + void UpdateDragonAI(const uint32 uiDiff) + { + if (m_uiAcolyteVesperonTimer) + { + if (m_uiAcolyteVesperonTimer <= uiDiff) + { + DoScriptText(EMOTE_VESPERON_DICIPLE, m_creature); + if (urand(0, 1)) + DoScriptText(urand(0, 1) ? SAY_VESPERON_SPECIAL_1 : SAY_VESPERON_SPECIAL_2, m_creature); + + uint32 uiSpawnEntry = NPC_DISCIPLE_OF_VESPERON; + if (m_pInstance) + { + if (m_pInstance->GetData(TYPE_SARTHARION_EVENT) == IN_PROGRESS) + uiSpawnEntry = NPC_ACOLYTE_OF_VESPERON; + } + + float fX, fY, fZ; + m_creature->GetRandomPoint(m_creature->GetPositionX(), m_creature->GetPositionY(), m_creature->GetPositionZ(), 20.0f, fX, fY, fZ); + m_creature->SummonCreature(uiSpawnEntry, fX, fY, fZ, 0, TEMPSUMMON_DEAD_DESPAWN, 0); + + m_uiAcolyteVesperonTimer = 0; + } + else + m_uiAcolyteVesperonTimer -= uiDiff; + } + } +}; + +CreatureAI* GetAI_mob_vesperon(Creature* pCreature) +{ + return new mob_vesperonAI(pCreature); +} + +/*###### +## Mob Twilight Eggs +######*/ + +struct mob_twilight_eggsAI : public Scripted_NoMovementAI +{ + mob_twilight_eggsAI(Creature* pCreature) : Scripted_NoMovementAI(pCreature) { Reset(); } + + uint32 m_uiHatchTimer; + + void Reset() override + { + m_uiHatchTimer = 20000; + } + + void AttackStart(Unit* /*pWho*/) override { } + void MoveInLineOfSight(Unit* /*pWho*/) override { } + + void JustSummoned(Creature* pSummoned) override + { + if (pSummoned->GetEntry() == NPC_TWILIGHT_WHELP || pSummoned->GetEntry() == NPC_SHARTHARION_TWILIGHT_WHELP) + pSummoned->SetInCombatWithZone(); + } + + void UpdateAI(const uint32 uiDiff) override + { + if (m_uiHatchTimer < uiDiff) + { + uint32 uiSpellEntry = 0; + switch (m_creature->GetEntry()) + { + case NPC_TWILIGHT_EGG: uiSpellEntry = SPELL_SUMMON_TWILIGHT_WHELP; break; + case NPC_SARTHARION_TWILIGHT_EGG: uiSpellEntry = SPELL_SUMMON_SARTHARION_TWILIGHT_WHELP; break; + } + + m_creature->SetPhaseMask(PHASEMASK_NORMAL, true); + DoCastSpellIfCan(m_creature, uiSpellEntry, CAST_TRIGGERED); + + m_creature->DealDamage(m_creature, m_creature->GetHealth(), NULL, DIRECT_DAMAGE, SPELL_SCHOOL_MASK_NONE, NULL, false); + m_uiHatchTimer = 0; + } + else + m_uiHatchTimer -= uiDiff; + } +}; + +CreatureAI* GetAI_mob_twilight_eggs(Creature* pCreature) +{ + return new mob_twilight_eggsAI(pCreature); +} + +/*###### +## npc_tenebron_egg_controller +######*/ + +struct npc_tenebron_egg_controllerAI : public Scripted_NoMovementAI +{ + npc_tenebron_egg_controllerAI(Creature* pCreature) : Scripted_NoMovementAI(pCreature) + { + m_pInstance = (ScriptedInstance*)pCreature->GetInstanceData(); + Reset(); + } + + ScriptedInstance* m_pInstance; + + uint32 m_uiHatchTimer; + + void Reset() override + { + m_uiHatchTimer = 20000; + } + + void AttackStart(Unit* /*pWho*/) override { } + void MoveInLineOfSight(Unit* /*pWho*/) override { } + + void UpdateAI(const uint32 uiDiff) override + { + if (m_uiHatchTimer) + { + if (m_uiHatchTimer < uiDiff) + { + if (m_pInstance) + { + // Inform Tenebron to hatch the eggs + if (Creature* pTenebron = m_pInstance->GetSingleCreatureFromStorage(NPC_TENEBRON)) + m_creature->AI()->SendAIEvent(AI_EVENT_CUSTOM_A, m_creature, pTenebron); + } + m_creature->ForcedDespawn(1000); + m_uiHatchTimer = 0; + } + else + m_uiHatchTimer -= uiDiff; + } + } +}; + +CreatureAI* GetAI_npc_tenebron_egg_controller(Creature* pCreature) +{ + return new npc_tenebron_egg_controllerAI(pCreature); +} + +/*###### +## npc_flame_tsunami +######*/ + +struct npc_flame_tsunamiAI : public ScriptedAI +{ + npc_flame_tsunamiAI(Creature* pCreature) : ScriptedAI(pCreature) { Reset(); } + + uint32 m_uiTsunamiTimer; + + void Reset() override + { + m_uiTsunamiTimer = 2000; + + DoCastSpellIfCan(m_creature, SPELL_FLAME_TSUNAMI, CAST_TRIGGERED); + DoCastSpellIfCan(m_creature, SPELL_FLAME_TSUNAMI_DMG_AURA, CAST_TRIGGERED); + } + + void AttackStart(Unit* /*pWho*/) override { } + void MoveInLineOfSight(Unit* /*pWho*/) override { } + + void MovementInform(uint32 uiType, uint32 uiPointId) override + { + if (uiType != POINT_MOTION_TYPE || !uiPointId) + return; + + m_creature->RemoveAllAurasOnEvade(); + } + + void UpdateAI(const uint32 uiDiff) override + { + if (m_uiTsunamiTimer) + { + if (m_uiTsunamiTimer <= uiDiff) + { + // Note: currently the way in which spell 60241 works is unk, so for the moment we'll use simple movement + m_creature->SetWalk(false); + m_creature->GetMotionMaster()->MovePoint(1, m_creature->GetPositionX() < 3250.0f ? m_creature->GetPositionX() + 86.5f : m_creature->GetPositionX() - 86.5f, + m_creature->GetPositionY(), m_creature->GetPositionZ()); + + m_uiTsunamiTimer = 0; + } + else + m_uiTsunamiTimer -= uiDiff; + } + } +}; + +CreatureAI* GetAI_npc_flame_tsunami(Creature* pCreature) +{ + return new npc_flame_tsunamiAI(pCreature); +} + +/*###### +## npc_fire_cyclone +######*/ + +struct npc_fire_cycloneAI : public ScriptedAI +{ + npc_fire_cycloneAI(Creature* pCreature) : ScriptedAI(pCreature) + { + m_pInstance = (ScriptedInstance*)pCreature->GetInstanceData(); + Reset(); + } + + ScriptedInstance* m_pInstance; + + void Reset() override { } + + void AttackStart(Unit* /*pWho*/) override { } + void MoveInLineOfSight(Unit* /*pWho*/) override { } + + void SpellHitTarget(Unit* pTarget, const SpellEntry* pSpell) override + { + // Mark the achiev failed for the hit target + if (pSpell->Id == SPELL_LAVA_STRIKE_IMPACT && pTarget->GetTypeId() == TYPEID_PLAYER && m_pInstance) + m_pInstance->SetData(TYPE_VOLCANO_BLOW_FAILED, pTarget->GetGUIDLow()); + } + + void JustSummoned(Creature* pSummoned) override + { + if (pSummoned->GetEntry() == NPC_LAVA_BLAZE) + pSummoned->SetInCombatWithZone(); + } +}; + +CreatureAI* GetAI_npc_fire_cyclone(Creature* pCreature) +{ + return new npc_fire_cycloneAI(pCreature); +} + +void AddSC_boss_sartharion() +{ + Script* pNewScript; + + pNewScript = new Script; + pNewScript->Name = "boss_sartharion"; + pNewScript->GetAI = &GetAI_boss_sartharion; + pNewScript->RegisterSelf(); + + pNewScript = new Script; + pNewScript->Name = "mob_vesperon"; + pNewScript->GetAI = &GetAI_mob_vesperon; + pNewScript->RegisterSelf(); + + pNewScript = new Script; + pNewScript->Name = "mob_shadron"; + pNewScript->GetAI = &GetAI_mob_shadron; + pNewScript->RegisterSelf(); + + pNewScript = new Script; + pNewScript->Name = "mob_tenebron"; + pNewScript->GetAI = &GetAI_mob_tenebron; + pNewScript->RegisterSelf(); + + pNewScript = new Script; + pNewScript->Name = "mob_twilight_eggs"; + pNewScript->GetAI = &GetAI_mob_twilight_eggs; + pNewScript->RegisterSelf(); + + pNewScript = new Script; + pNewScript->Name = "npc_tenebron_egg_controller"; + pNewScript->GetAI = &GetAI_npc_tenebron_egg_controller; + pNewScript->RegisterSelf(); + + pNewScript = new Script; + pNewScript->Name = "npc_flame_tsunami"; + pNewScript->GetAI = &GetAI_npc_flame_tsunami; + pNewScript->RegisterSelf(); + + pNewScript = new Script; + pNewScript->Name = "npc_fire_cyclone"; + pNewScript->GetAI = &GetAI_npc_fire_cyclone; + pNewScript->RegisterSelf(); +} diff --git a/src/modules/SD2/scripts/northrend/obsidian_sanctum/instance_obsidian_sanctum.cpp b/src/modules/SD2/scripts/northrend/obsidian_sanctum/instance_obsidian_sanctum.cpp new file mode 100644 index 000000000..9371b0790 --- /dev/null +++ b/src/modules/SD2/scripts/northrend/obsidian_sanctum/instance_obsidian_sanctum.cpp @@ -0,0 +1,153 @@ +/* Copyright (C) 2006 - 2013 ScriptDev2 + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along 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_Obsidian_Sanctum +SD%Complete: 80% +SDComment: +SDCategory: Obsidian Sanctum +EndScriptData */ + +#include "precompiled.h" +#include "obsidian_sanctum.h" + +/* Obsidian Sanctum encounters: +0 - Sartharion +*/ + +instance_obsidian_sanctum::instance_obsidian_sanctum(Map* pMap) : ScriptedInstance(pMap), + m_uiAliveDragons(0) +{ + Initialize(); +} + +void instance_obsidian_sanctum::Initialize() +{ + memset(&m_auiEncounter, 0, sizeof(m_auiEncounter)); + + for (uint8 i = 0; i < MAX_TWILIGHT_DRAGONS; ++i) + m_bPortalActive[i] = false; +} + +void instance_obsidian_sanctum::OnCreatureCreate(Creature* pCreature) +{ + switch (pCreature->GetEntry()) + { + // The three dragons below set to active state once created. + // We must expect bigger raid to encounter main boss, and then three dragons must be active due to grid differences + case NPC_TENEBRON: + case NPC_SHADRON: + case NPC_VESPERON: + pCreature->SetActiveObjectState(true); + case NPC_SARTHARION: + m_mNpcEntryGuidStore[pCreature->GetEntry()] = pCreature->GetObjectGuid(); + break; + case NPC_FIRE_CYCLONE: + m_lFireCycloneGuidList.push_back(pCreature->GetObjectGuid()); + break; + } +} + +void instance_obsidian_sanctum::SetData(uint32 uiType, uint32 uiData) +{ + if (uiType == TYPE_SARTHARION_EVENT) + { + m_auiEncounter[0] = uiData; + if (uiData == IN_PROGRESS) + m_sVolcanoBlowFailPlayers.clear(); + } + else if (uiType == TYPE_ALIVE_DRAGONS) + m_uiAliveDragons = uiData; + else if (uiType == TYPE_VOLCANO_BLOW_FAILED) + { + // Insert the players who fail the achiev and haven't been already inserted in the set + if (m_sVolcanoBlowFailPlayers.find(uiData) == m_sVolcanoBlowFailPlayers.end()) + m_sVolcanoBlowFailPlayers.insert(uiData); + } + + // No need to save anything here +} + +uint32 instance_obsidian_sanctum::GetData(uint32 uiType) const +{ + if (uiType == TYPE_SARTHARION_EVENT) + return m_auiEncounter[0]; + + return 0; +} + +ObjectGuid instance_obsidian_sanctum::SelectRandomFireCycloneGuid() +{ + if (m_lFireCycloneGuidList.empty()) + return ObjectGuid(); + + GuidList::iterator iter = m_lFireCycloneGuidList.begin(); + advance(iter, urand(0, m_lFireCycloneGuidList.size() - 1)); + + return *iter; +} + +bool instance_obsidian_sanctum::CheckAchievementCriteriaMeet(uint32 uiCriteriaId, Player const* pSource, Unit const* /*pTarget*/, uint32 /*uiMiscValue1 = 0*/) const +{ + switch (uiCriteriaId) + { + case ACHIEV_DRAGONS_ALIVE_1_N: + case ACHIEV_DRAGONS_ALIVE_1_H: + return m_uiAliveDragons >= 1; + case ACHIEV_DRAGONS_ALIVE_2_N: + case ACHIEV_DRAGONS_ALIVE_2_H: + return m_uiAliveDragons >= 2; + case ACHIEV_DRAGONS_ALIVE_3_N: + case ACHIEV_DRAGONS_ALIVE_3_H: + return m_uiAliveDragons >= 3; + case ACHIEV_CRIT_VOLCANO_BLOW_N: + case ACHIEV_CRIT_VOLCANO_BLOW_H: + // Return true if not found in the set + return m_sVolcanoBlowFailPlayers.find(pSource->GetGUIDLow()) == m_sVolcanoBlowFailPlayers.end(); + default: + return false; + } +} + +bool instance_obsidian_sanctum::CheckConditionCriteriaMeet(Player const* pPlayer, uint32 uiInstanceConditionId, WorldObject const* pConditionSource, uint32 conditionSourceType) const +{ + switch (uiInstanceConditionId) + { + case INSTANCE_CONDITION_ID_HARD_MODE: // Exactly one dragon alive on event start + case INSTANCE_CONDITION_ID_HARD_MODE_2: // Exactly two dragons alive on event start + case INSTANCE_CONDITION_ID_HARD_MODE_3: // All three dragons alive on event start + return m_uiAliveDragons == uiInstanceConditionId; + } + + script_error_log("instance_obsidian_sanctum::CheckConditionCriteriaMeet called with unsupported Id %u. Called with param plr %s, src %s, condition source type %u", + uiInstanceConditionId, pPlayer ? pPlayer->GetGuidStr().c_str() : "NULL", pConditionSource ? pConditionSource->GetGuidStr().c_str() : "NULL", conditionSourceType); + return false; +} + +InstanceData* GetInstanceData_instance_obsidian_sanctum(Map* pMap) +{ + return new instance_obsidian_sanctum(pMap); +} + +void AddSC_instance_obsidian_sanctum() +{ + Script* pNewScript; + + pNewScript = new Script; + pNewScript->Name = "instance_obsidian_sanctum"; + pNewScript->GetInstanceData = GetInstanceData_instance_obsidian_sanctum; + pNewScript->RegisterSelf(); +} diff --git a/src/modules/SD2/scripts/northrend/obsidian_sanctum/obsidian_sanctum.h b/src/modules/SD2/scripts/northrend/obsidian_sanctum/obsidian_sanctum.h new file mode 100644 index 000000000..2ba5d91db --- /dev/null +++ b/src/modules/SD2/scripts/northrend/obsidian_sanctum/obsidian_sanctum.h @@ -0,0 +1,85 @@ +/* Copyright (C) 2006 - 2013 ScriptDev2 + * This program is free software licensed under GPL version 2 + * Please see the included DOCS/LICENSE.TXT for more information */ + +#ifndef DEF_OBSIDIAN_SANCTUM_H +#define DEF_OBSIDIAN_SANCTUM_H + +enum +{ + MAX_ENCOUNTER = 1, + + TYPE_SARTHARION_EVENT = 1, + // internal used types for achievement + TYPE_ALIVE_DRAGONS = 2, + TYPE_VOLCANO_BLOW_FAILED = 3, + + MAX_TWILIGHT_DRAGONS = 3, + + TYPE_PORTAL_TENEBRON = 0, + TYPE_PORTAL_SHADRON = 1, + TYPE_PORTAL_VESPERON = 2, + + NPC_SARTHARION = 28860, + NPC_TENEBRON = 30452, + NPC_SHADRON = 30451, + NPC_VESPERON = 30449, + + NPC_FIRE_CYCLONE = 30648, + + GO_TWILIGHT_PORTAL = 193988, + + // Achievement related + ACHIEV_CRIT_VOLCANO_BLOW_N = 7326, // achievs 2047, 2048 (Go When the Volcano Blows) -- This is individual achievement! + ACHIEV_CRIT_VOLCANO_BLOW_H = 7327, + ACHIEV_DRAGONS_ALIVE_1_N = 7328, // achievs 2049, 2052 (Twilight Assist) + ACHIEV_DRAGONS_ALIVE_1_H = 7331, + ACHIEV_DRAGONS_ALIVE_2_N = 7329, // achievs 2050, 2053 (Twilight Duo) + ACHIEV_DRAGONS_ALIVE_2_H = 7332, + ACHIEV_DRAGONS_ALIVE_3_N = 7330, // achievs 2051, 2054 (The Twilight Zone) + ACHIEV_DRAGONS_ALIVE_3_H = 7333, +}; + +class instance_obsidian_sanctum : public ScriptedInstance +{ + public: + instance_obsidian_sanctum(Map* pMap); + + void Initialize() override; + + void OnCreatureCreate(Creature* pCreature) override; + + void SetData(uint32 uiType, uint32 uiData) override; + uint32 GetData(uint32 uiType) const override; + + ObjectGuid SelectRandomFireCycloneGuid(); + + bool IsActivePortal() + { + for (uint8 i = 0; i < MAX_TWILIGHT_DRAGONS; ++i) + { + if (m_bPortalActive[i]) + return true; + } + + return false; + } + + void SetPortalStatus(uint8 uiType, bool bStatus) { m_bPortalActive[uiType] = bStatus; } + bool GetPortaStatus(uint8 uiType) { return m_bPortalActive[uiType]; } + + bool CheckAchievementCriteriaMeet(uint32 uiCriteriaId, Player const* pSource, Unit const* pTarget, uint32 uiMiscValue1 /* = 0*/) const override; + bool CheckConditionCriteriaMeet(Player const* pPlayer, uint32 uiInstanceConditionId, WorldObject const* pConditionSource, uint32 conditionSourceType) const override; + + private: + uint32 m_auiEncounter[MAX_ENCOUNTER]; + bool m_bPortalActive[MAX_TWILIGHT_DRAGONS]; + + uint8 m_uiAliveDragons; + + std::set m_sVolcanoBlowFailPlayers; + + GuidList m_lFireCycloneGuidList; +}; + +#endif diff --git a/src/modules/SD2/scripts/northrend/ruby_sanctum/boss_baltharus.cpp b/src/modules/SD2/scripts/northrend/ruby_sanctum/boss_baltharus.cpp new file mode 100644 index 000000000..ddc352c92 --- /dev/null +++ b/src/modules/SD2/scripts/northrend/ruby_sanctum/boss_baltharus.cpp @@ -0,0 +1,318 @@ +/* Copyright (C) 2006 - 2013 ScriptDev2 + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +/* ScriptData +SDName: boss_baltharus +SD%Complete: 90 +SDComment: intro channeled spell NYI. +SDCategory: Ruby Sanctum +EndScriptData */ + +#include "precompiled.h" +#include "ruby_sanctum.h" + +enum +{ + // Xerestrasza intro and outro texts + SAY_HELP = -1724000, + SAY_INTRO = -1724001, + + SAY_THANKS = -1724002, + SAY_OUTRO_1 = -1724003, + SAY_OUTRO_2 = -1724004, + SAY_OUTRO_3 = -1724005, + SAY_OUTRO_4 = -1724006, + SAY_OUTRO_5 = -1724007, + SAY_OUTRO_6 = -1724008, + SAY_OUTRO_7 = -1724009, + + // Baltharus texts + SAY_AGGRO = -1724010, + SAY_SLAY_1 = -1724011, + SAY_SLAY_2 = -1724012, + SAY_DEATH = -1724013, + SAY_SPLIT = -1724014, + + SPELL_BARRIER_CHANNEL = 76221, // channeled on the tree + SPELL_BLADE_TEMPEST = 75125, + SPELL_CLEAVE = 40504, + SPELL_ENERVATING_BRAND = 74502, + SPELL_REPELLING_WAVE = 74509, + SPELL_ENERVATING_BRAND_PL = 74505, // spell triggerd on players by 74502 + SPELL_SIPHONED_MIGHT = 74507, // spell triggered on boss by 74505 + SPELL_SUMMON_CLONE = 74511, // summons 39899 + SPELL_SIMPLE_TELEPORT = 64195, // spell id not confirmed + + NPC_BALTHARUS_CLONE = 39899, +}; + +static const DialogueEntry aIntroDialogue[] = +{ + {SAY_HELP, NPC_XERESTRASZA, 7000}, + {SAY_INTRO, NPC_BALTHARUS, 0}, + {0, 0, 0}, +}; + +/*###### +## boss_baltharus +######*/ + +struct boss_baltharusAI : public ScriptedAI +{ + boss_baltharusAI(Creature* pCreature) : ScriptedAI(pCreature), + m_introDialogue(aIntroDialogue) + { + m_pInstance = (instance_ruby_sanctum*)pCreature->GetInstanceData(); + m_introDialogue.InitializeDialogueHelper(m_pInstance); + + // Health check percent depends on difficulty + if (m_pInstance) + m_fHealthPercentCheck = m_pInstance->Is25ManDifficulty() ? 33.3f : 50; + else + script_error_log("Instance Ruby Sanctum: ERROR Failed to load instance data for this instace."); + + m_bHasDoneIntro = false; + Reset(); + } + + instance_ruby_sanctum* m_pInstance; + DialogueHelper m_introDialogue; + + bool m_bHasDoneIntro; + + uint8 m_uiPhase; + float m_fHealthPercentCheck; + + uint32 m_uiBladeTempestTimer; + uint32 m_uiEnervatingBrandTimer; + uint32 m_uiCleaveTimer; + uint32 m_uiSummonCloneTimer; + + void Reset() override + { + m_uiPhase = 1; + m_uiSummonCloneTimer = 0; + m_uiBladeTempestTimer = 15000; + m_uiEnervatingBrandTimer = 14000; + m_uiCleaveTimer = urand(10000, 12000); + } + + void Aggro(Unit* /*pWho*/) override + { + DoScriptText(SAY_AGGRO, m_creature); + + if (m_pInstance) + m_pInstance->SetData(TYPE_BALTHARUS, IN_PROGRESS); + } + + void MoveInLineOfSight(Unit* pWho) override + { + if (!m_bHasDoneIntro && pWho->GetTypeId() == TYPEID_PLAYER && !((Player*)pWho)->isGameMaster()) + { + m_introDialogue.StartNextDialogueText(SAY_HELP); + m_bHasDoneIntro = true; + } + + ScriptedAI::MoveInLineOfSight(pWho); + } + + void KilledUnit(Unit* pVictim) override + { + if (pVictim->GetTypeId() != TYPEID_PLAYER) + return; + + if (urand(0, 1)) + DoScriptText(urand(0, 1) ? SAY_SLAY_1 : SAY_SLAY_2, m_creature); + } + + void JustDied(Unit* /*pKiller*/) override + { + DoScriptText(SAY_DEATH, m_creature); + + if (m_pInstance) + m_pInstance->SetData(TYPE_BALTHARUS, DONE); + } + + void JustReachedHome() override + { + if (m_pInstance) + m_pInstance->SetData(TYPE_BALTHARUS, FAIL); + } + + void JustSummoned(Creature* pSummoned) + { + if (pSummoned->GetEntry() == NPC_BALTHARUS_CLONE) + { + pSummoned->CastSpell(pSummoned, SPELL_SIMPLE_TELEPORT, true); + pSummoned->SetInCombatWithZone(); + } + } + + void SpellHitTarget(Unit* pTarget, SpellEntry const* pSpellEntry) override + { + if (pTarget->GetTypeId() == TYPEID_PLAYER && pSpellEntry->Id == SPELL_ENERVATING_BRAND_PL) + pTarget->CastSpell(m_creature, SPELL_SIPHONED_MIGHT, true); + } + + void UpdateAI(const uint32 uiDiff) override + { + m_introDialogue.DialogueUpdate(uiDiff); + + if (!m_creature->SelectHostileTarget() || !m_creature->getVictim()) + return; + + if (m_creature->GetHealthPercent() < 100 - m_fHealthPercentCheck * m_uiPhase) + { + if (DoCastSpellIfCan(m_creature, SPELL_REPELLING_WAVE, CAST_INTERRUPT_PREVIOUS) == CAST_OK) + { + m_uiSummonCloneTimer = 3000; + ++m_uiPhase; + } + } + + if (m_uiSummonCloneTimer) + { + if (m_uiSummonCloneTimer <= uiDiff) + { + if (DoCastSpellIfCan(m_creature, SPELL_SUMMON_CLONE) == CAST_OK) + { + DoScriptText(SAY_SPLIT, m_creature); + m_uiSummonCloneTimer = 0; + } + } + else + m_uiSummonCloneTimer -= uiDiff; + + // no other actions + return; + } + + if (m_uiBladeTempestTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature, SPELL_BLADE_TEMPEST) == CAST_OK) + m_uiBladeTempestTimer = 22000; + } + else + m_uiBladeTempestTimer -= uiDiff; + + if (m_uiEnervatingBrandTimer < uiDiff) + { + if (Unit* pTarget = m_creature->SelectAttackingTarget(ATTACKING_TARGET_RANDOM, 0)) + { + if (DoCastSpellIfCan(pTarget, SPELL_ENERVATING_BRAND) == CAST_OK) + m_uiEnervatingBrandTimer = 25000; + } + } + else + m_uiEnervatingBrandTimer -= uiDiff; + + if (m_uiCleaveTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature, SPELL_CLEAVE) == CAST_OK) + m_uiCleaveTimer = urand(17000, 20000); + } + else + m_uiCleaveTimer -= uiDiff; + + DoMeleeAttackIfReady(); + } +}; + +/*###### +## npc_baltharus_clone +######*/ + +struct npc_baltharus_cloneAI : public ScriptedAI +{ + npc_baltharus_cloneAI(Creature* pCreature) : ScriptedAI(pCreature) { Reset(); } + + uint32 m_uiBladeTempestTimer; + uint32 m_uiEnervatingBrandTimer; + uint32 m_uiCleaveTimer; + + void Reset() override + { + m_uiBladeTempestTimer = 15000; + m_uiEnervatingBrandTimer = 14000; + m_uiCleaveTimer = urand(10000, 12000); + } + + void SpellHitTarget(Unit* pTarget, SpellEntry const* pSpellEntry) override + { + if (pTarget->GetTypeId() == TYPEID_PLAYER && pSpellEntry->Id == SPELL_ENERVATING_BRAND_PL) + pTarget->CastSpell(m_creature, SPELL_SIPHONED_MIGHT, true); + } + + void UpdateAI(const uint32 uiDiff) override + { + if (!m_creature->SelectHostileTarget() || !m_creature->getVictim()) + return; + + if (m_uiBladeTempestTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature, SPELL_BLADE_TEMPEST) == CAST_OK) + m_uiBladeTempestTimer = 22000; + } + else + m_uiBladeTempestTimer -= uiDiff; + + if (m_uiEnervatingBrandTimer < uiDiff) + { + if (Unit* pTarget = m_creature->SelectAttackingTarget(ATTACKING_TARGET_RANDOM, 0)) + { + if (DoCastSpellIfCan(pTarget, SPELL_ENERVATING_BRAND) == CAST_OK) + m_uiEnervatingBrandTimer = 25000; + } + } + else + m_uiEnervatingBrandTimer -= uiDiff; + + if (m_uiCleaveTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature, SPELL_CLEAVE) == CAST_OK) + m_uiCleaveTimer = urand(17000, 20000); + } + else + m_uiCleaveTimer -= uiDiff; + + DoMeleeAttackIfReady(); + } +}; + +CreatureAI* GetAI_boss_baltharus(Creature* pCreature) +{ + return new boss_baltharusAI(pCreature); +} + +CreatureAI* GetAI_npc_baltharus_clone(Creature* pCreature) +{ + return new npc_baltharus_cloneAI(pCreature); +} + +void AddSC_boss_baltharus() +{ + Script* pNewScript; + + pNewScript = new Script; + pNewScript->Name = "boss_baltharus"; + pNewScript->GetAI = &GetAI_boss_baltharus; + pNewScript->RegisterSelf(); + + pNewScript = new Script; + pNewScript->Name = "npc_baltharus_clone"; + pNewScript->GetAI = &GetAI_npc_baltharus_clone; + pNewScript->RegisterSelf(); +} diff --git a/src/modules/SD2/scripts/northrend/ruby_sanctum/boss_halion.cpp b/src/modules/SD2/scripts/northrend/ruby_sanctum/boss_halion.cpp new file mode 100644 index 000000000..858ac49e6 --- /dev/null +++ b/src/modules/SD2/scripts/northrend/ruby_sanctum/boss_halion.cpp @@ -0,0 +1,543 @@ +/* Copyright (C) 2006 - 2013 ScriptDev2 + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +/* ScriptData +SDName: boss_halion +SD%Complete: 50 +SDComment: Phase 3 and related transition NYI; Twilight portals NYI; Shadow Orbs NYI; Meteor Strikes NYI; Heroic abilities NYI. +SDCategory: Ruby Sanctum +EndScriptData */ + +#include "precompiled.h" +#include "ruby_sanctum.h" + +enum +{ + SAY_AGGRO = -1724025, + SAY_SLAY_1 = -1724026, + SOUND_SLAY_2 = 17502, // not sure if this one has a text + SAY_DEATH = -1724027, + SAY_BERSERK = -1724028, + SAY_FIREBALL = -1724029, + SAY_SPHERES = -1724030, + SAY_PHASE_2 = -1724031, + SAY_PHASE_3 = -1724032, + + EMOTE_SPHERES = -1724033, + EMOTE_OUT_OF_TWILLIGHT = -1724034, + EMOTE_OUT_OF_PHYSICAL = -1724035, + EMOTE_INTO_TWILLIGHT = -1724036, + EMOTE_INTO_PHYSICAL = -1724037, + EMOTE_REGENERATE = -1724038, + + // *** Spells *** + // General + SPELL_TWILIGHT_PRECISION = 78243, + SPELL_CLEAVE = 74524, + SPELL_TAIL_LASH = 74531, + SPELL_BERSERK = 26662, + + // Transitions + SPELL_TWILIGHT_PHASING = 74808, // Start phase 2 + SPELL_SUMMON_PORTAL = 74809, + SPELL_TWILIGHT_DIVISION = 75063, + SPELL_TWILIGHT_REALM = 74807, + SPELL_TWILIGHT_MENDING = 75509, + SPELL_LEAVE_TWILIGHT_REALM = 74812, // handled by GO 202796 + //share damage spell: 74810 - serverside spell + + // Real + SPELL_FLAME_BREATH = 74525, + SPELL_METEOR_SUMMON = 74637, // summons 40029 + SPELL_FIERY_COMBUSTION = 74562, // curse - triggers 74567 on self (player); on dispell triggers 74607 and 74610 + + // Twilight + SPELL_DARK_BREATH = 74806, + SPELL_DUSK_SHROUD = 75476, + SPELL_SOUL_CONSUMPTION = 74792, // curse - triggers 74795 on self (player); on dispell triggers 74799 and 74800 + + // Corporeality + SPELL_CORPOREALITY_EVEN = 74826, // Deals & receives normal damage + SPELL_CORPOREALITY_20I = 74827, // Damage dealt increased by 10% - Damage taken increased by 15% + SPELL_CORPOREALITY_40I = 74828, // Damage dealt increased by 30% - Damage taken increased by 50% + SPELL_CORPOREALITY_60I = 74829, // Damage dealt increased by 60% - Damage taken increased by 100% + SPELL_CORPOREALITY_80I = 74830, // Damage dealt increased by 100% - Damage taken increased by 200% + SPELL_CORPOREALITY_100I = 74831, // Damage dealt increased by 200% - Damage taken increased by 400% + SPELL_CORPOREALITY_20D = 74832, // Damage dealt reduced by 10% - Damage taken reduced by 15% + SPELL_CORPOREALITY_40D = 74833, // Damage dealt reduced by 30% - Damage taken reduced by 50% + SPELL_CORPOREALITY_60D = 74834, // Damage dealt reduced by 60% - Damage taken reduced by 100% + SPELL_CORPOREALITY_80D = 74835, // Damage dealt reduced by 100% - Damage taken reduced by 200% + SPELL_CORPOREALITY_100D = 74836, // Damage dealt reduced by 200% - Damage taken reduced by 400% + + // *** Other spells *** + //Combustion + SPELL_COMBUSTION_PERIODIC = 74629, // cast by npc 40001 + + // Consumption + SPELL_CONSUMPTION_PERIODIC = 74803, // cast by npc 40135 + + // Meteor + SPELL_METEOR_VISUAL = 74641, // cast by npc 40029 (all meteor spells) + SPELL_METEOR_IMPACT = 74648, // cast on visual aura expire + SPELL_METEOR_FLAME = 74713, + SPELL_METEOR_FLAME2 = 74718, // cast by the secondary strike npcs + SPELL_BIRTH = 40031, // cast by the meteor strike npcs + + // Cutter + SPELL_TWILIGHT_CUTTER = 74768, + SEPLL_TWILIGHT_PULSE = 78861, + SPELL_TRACK_ROTATION = 74758, // cast by 40081 on 40091 + + // Living Inferno + SPELL_BLAZING_AURA = 75885, // cast by 40681 + + // Living Ember + SPELL_AWAKEN_FLAMES = 75889, // cast by 40683 + + // Npcs + NPC_COMBUSTION = 40001, + NPC_METEOR_STRIKE_MAIN = 40029, // summons the other meteor strikes using serverside spells like 74680, 74681, 74682, 74683 + NPC_CONSUMPTION = 40135, + NPC_ORB_CARRIER = 40081, // vehicle for shadow orbs + NPC_ORB_ROTATION_FOCUS = 40091, + + NPC_METEOR_STRIKE_1 = 40041, // Npc 40029 summons the first 4 secondary meteor strike npcs, then each of them summons one 40055 npc using serverside spells 74687, 74688 + NPC_METEOR_STRIKE_2 = 40042, + NPC_METEOR_STRIKE_3 = 40043, + NPC_METEOR_STRIKE_4 = 40044, + NPC_METEOR_STRIKE_FLAME = 40055, // Each npc 40055 summons other 10 40055 npcs resulting in a total spawns of 40 40055 npcs. + + // Heroic npcs + NPC_LIVING_INFERNO = 40681, // summoned by 75879 (heroic version spell) + NPC_LIVING_EMBER = 40683, + + // *** Phases *** + PHASE_PHISYCAL_REALM = 1, + PHASE_TWILIGHT_REALM = 2, + PHASE_BOTH_REALMS = 3, +}; + +static const uint32 aShadowOrbs[4] = { NPC_SHADOW_ORB_1, NPC_SHADOW_ORB_2, NPC_SHADOW_ORB_3, NPC_SHADOW_ORB_4 }; +static const uint32 aMeteorStrikes[4] = { NPC_METEOR_STRIKE_1, NPC_METEOR_STRIKE_2, NPC_METEOR_STRIKE_3, NPC_METEOR_STRIKE_4 }; + +static const float aRotationFocusPosition[4] = {3113.711f, 533.5382f, 72.96f, 1.93f}; +static const float aOrbCarrierPosition1[3] = {3153.75f, 579.1875f, 70.47f}; +static const float aOrbCarrierPosition2[3] = {3153.75f, 487.1875f, 70.47f}; + +/*###### +## boss_halion_real +######*/ + +struct boss_halion_realAI : public ScriptedAI +{ + boss_halion_realAI(Creature* pCreature) : ScriptedAI(pCreature) + { + m_pInstance = (instance_ruby_sanctum*)pCreature->GetInstanceData(); + Reset(); + } + + instance_ruby_sanctum* m_pInstance; + + uint8 m_uiPhase; + + uint32 m_uiTailLashTimer; + uint32 m_uiCleaveTimer; + uint32 m_uiFieryCombustionTimer; + uint32 m_uiMeteorTimer; + uint32 m_uiFlameBreathTimer; + uint32 m_uiBerserkTimer; + + void Reset() override + { + m_uiPhase = PHASE_PHISYCAL_REALM; + + m_uiTailLashTimer = 10000; + m_uiCleaveTimer = urand(5000, 10000); + m_uiFieryCombustionTimer = 15000; + m_uiMeteorTimer = 20000; + m_uiBerserkTimer = 8 * MINUTE * IN_MILLISECONDS; + } + + void Aggro(Unit* /*pWho*/) override + { + if (m_pInstance) + m_pInstance->SetData(TYPE_HALION, IN_PROGRESS); + + DoScriptText(SAY_AGGRO, m_creature); + DoCastSpellIfCan(m_creature, SPELL_TWILIGHT_PRECISION, CAST_TRIGGERED); + } + + void KilledUnit(Unit* pVictim) override + { + if (pVictim->GetTypeId() == TYPEID_PLAYER) + { + switch (urand(0, 1)) + { + case 0: DoScriptText(SAY_SLAY_1, m_creature); break; + case 1: DoPlaySoundToSet(m_creature, SOUND_SLAY_2); break; + } + } + } + + void JustDied(Unit* /*pKiller*/) override + { + if (m_pInstance) + m_pInstance->SetData(TYPE_HALION, DONE); + + DoScriptText(SAY_DEATH, m_creature); + } + + void JustReachedHome() override + { + if (m_pInstance) + m_pInstance->SetData(TYPE_HALION, FAIL); + } + + void JustSummoned(Creature* pSummoned) override + { + switch (pSummoned->GetEntry()) + { + case NPC_COMBUSTION: + pSummoned->CastSpell(pSummoned, SPELL_COMBUSTION_PERIODIC, true); + break; + case NPC_METEOR_STRIKE_MAIN: + // ToDo: summon the other meteor strikes around this one + pSummoned->CastSpell(pSummoned, SPELL_BIRTH, true); + pSummoned->CastSpell(pSummoned, SPELL_METEOR_VISUAL, true); + break; + } + } + + void DoPrepareTwilightPhase() + { + if (!m_pInstance) + return; + + // Spawn the orbs and the carriers. Use the twilight Halion version to preserve the phase + if (Creature* pHalion = m_pInstance->GetSingleCreatureFromStorage(NPC_HALION_TWILIGHT)) + { + // Set current Halion hp + pHalion->SetHealth(m_creature->GetHealth()); + + // NOTE: the spawn coords seem to be totally off, compared to the actual map layout - requires additional research!!! + + // Spawn the rotation focus first + // pHalion->SummonCreature(NPC_ORB_ROTATION_FOCUS, aRotationFocusPosition[0], aRotationFocusPosition[1], aRotationFocusPosition[2], aRotationFocusPosition[3], TEMPSUMMON_DEAD_DESPAWN, 0); + + // Then spawn the orb carriers and the shadow orbs. ToDo: research if it's possible to make this dynamic + // pHalion->SummonCreature(NPC_ORB_CARRIER, aOrbCarrierPosition1[0], aOrbCarrierPosition1[1], aOrbCarrierPosition1[2], 0, TEMPSUMMON_DEAD_DESPAWN, 0); + // pHalion->SummonCreature(NPC_ORB_CARRIER, aOrbCarrierPosition2[0], aOrbCarrierPosition2[1], aOrbCarrierPosition2[2], 0, TEMPSUMMON_DEAD_DESPAWN, 0); + // pHalion->SummonCreature(NPC_SHADOW_ORB_1, aOrbCarrierPosition1[0], aOrbCarrierPosition1[1], aOrbCarrierPosition1[2], 0, TEMPSUMMON_DEAD_DESPAWN, 0); + // pHalion->SummonCreature(NPC_SHADOW_ORB_2, aOrbCarrierPosition2[0], aOrbCarrierPosition2[1], aOrbCarrierPosition2[2], 0, TEMPSUMMON_DEAD_DESPAWN, 0); + } + } + + void UpdateAI(const uint32 uiDiff) override + { + if (!m_creature->SelectHostileTarget() || !m_creature->getVictim()) + return; + + if (m_uiBerserkTimer) + { + if (m_uiBerserkTimer <= uiDiff) + { + if (DoCastSpellIfCan(m_creature, SPELL_BERSERK) == CAST_OK) + { + // Do the same for the Twilight halion + if (m_pInstance) + { + if (Creature* pHalion = m_pInstance->GetSingleCreatureFromStorage(NPC_HALION_TWILIGHT, true)) + pHalion->CastSpell(pHalion, SPELL_BERSERK, true); + } + + DoScriptText(SAY_BERSERK, m_creature); + m_uiBerserkTimer = 0; + } + } + else + m_uiBerserkTimer -= uiDiff; + } + + switch (m_uiPhase) + { + case PHASE_BOTH_REALMS: + // ToDo: handle corporeality + // no break; + case PHASE_PHISYCAL_REALM: + + if (m_uiTailLashTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature, SPELL_TAIL_LASH) == CAST_OK) + m_uiTailLashTimer = urand(15000, 25000); + } + else + m_uiTailLashTimer -= uiDiff; + + if (m_uiCleaveTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature->getVictim(), SPELL_CLEAVE) == CAST_OK) + m_uiCleaveTimer = urand(10000, 15000); + } + else + m_uiCleaveTimer -= uiDiff; + + if (m_uiFlameBreathTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature, SPELL_FLAME_BREATH) == CAST_OK) + m_uiFlameBreathTimer = urand(15000, 20000); + } + else + m_uiFlameBreathTimer -= uiDiff; + + if (m_uiFieryCombustionTimer < uiDiff) + { + if (Unit* pTarget = m_creature->SelectAttackingTarget(ATTACKING_TARGET_RANDOM, 0, SPELL_FIERY_COMBUSTION, SELECT_FLAG_PLAYER)) + { + if (DoCastSpellIfCan(pTarget, SPELL_FIERY_COMBUSTION) == CAST_OK) + m_uiFieryCombustionTimer = 25000; + } + } + else + m_uiFieryCombustionTimer -= uiDiff; + + if (m_uiMeteorTimer < uiDiff) + { + if (Unit* pTarget = m_creature->SelectAttackingTarget(ATTACKING_TARGET_RANDOM, 1)) + { + if (DoCastSpellIfCan(pTarget, SPELL_METEOR_SUMMON) == CAST_OK) + { + DoScriptText(SAY_FIREBALL, m_creature); + m_uiMeteorTimer = 40000; + } + } + } + else + m_uiMeteorTimer -= uiDiff; + + // Switch to phase 2 + if (m_creature->GetHealthPercent() < 75.0f && m_uiPhase == PHASE_PHISYCAL_REALM) + { + if (DoCastSpellIfCan(m_creature, SPELL_TWILIGHT_PHASING, CAST_INTERRUPT_PREVIOUS) == CAST_OK) + { + DoCastSpellIfCan(m_creature, SPELL_SUMMON_PORTAL, CAST_TRIGGERED); + DoScriptText(SAY_PHASE_2, m_creature); + DoPrepareTwilightPhase(); + m_uiPhase = PHASE_TWILIGHT_REALM; + } + } + + break; + case PHASE_TWILIGHT_REALM: + + // Switch to phase 3 + if (m_creature->GetHealthPercent() < 50.0f) + { + m_creature->RemoveAurasDueToSpell(SPELL_TWILIGHT_PHASING); + DoScriptText(SAY_PHASE_3, m_creature); + m_uiPhase = PHASE_BOTH_REALMS; + } + + break; + } + + DoMeleeAttackIfReady(); + } +}; + +/*###### +## boss_halion_twilight +######*/ + +struct boss_halion_twilightAI : public ScriptedAI +{ + boss_halion_twilightAI(Creature* pCreature) : ScriptedAI(pCreature) + { + m_pInstance = (instance_ruby_sanctum*)pCreature->GetInstanceData(); + Reset(); + } + + instance_ruby_sanctum* m_pInstance; + + uint8 m_uiPhase; + uint32 m_uiTailLashTimer; + uint32 m_uiCleaveTimer; + uint32 m_uiDarkBreathTimer; + uint32 m_uiSoulConsumptionTimer; + + void Reset() override + { + m_uiPhase = PHASE_TWILIGHT_REALM; + m_uiTailLashTimer = 10000; + m_uiCleaveTimer = urand(5000, 10000); + m_uiDarkBreathTimer = 15000; + m_uiSoulConsumptionTimer = 20000; + } + + void Aggro(Unit* /*pWho*/) override + { + DoCastSpellIfCan(m_creature, SPELL_DUSK_SHROUD, CAST_TRIGGERED); + DoCastSpellIfCan(m_creature, SPELL_TWILIGHT_PRECISION, CAST_TRIGGERED); + } + + void JustReachedHome() override + { + // Allow real Halion to evade + if (m_pInstance) + { + if (Creature* pHalion = m_pInstance->GetSingleCreatureFromStorage(NPC_HALION_REAL)) + pHalion->AI()->EnterEvadeMode(); + } + } + + void JustDied(Unit* /*pKiller*/) override + { + // ToDo: handle the damage sharing! + + DoScriptText(SAY_DEATH, m_creature); + } + + void KilledUnit(Unit* pVictim) override + { + if (pVictim->GetTypeId() == TYPEID_PLAYER) + { + switch (urand(0, 1)) + { + case 0: DoScriptText(SAY_SLAY_1, m_creature); break; + case 1: DoPlaySoundToSet(m_creature, SOUND_SLAY_2); break; + } + } + } + + void JustSummoned(Creature* pSummoned) override + { + switch (pSummoned->GetEntry()) + { + case NPC_CONSUMPTION: + pSummoned->CastSpell(pSummoned, SPELL_CONSUMPTION_PERIODIC, true); + break; + case NPC_SHADOW_ORB_1: + case NPC_SHADOW_ORB_2: + case NPC_SHADOW_ORB_3: + case NPC_SHADOW_ORB_4: + if (Creature* pCarrier = GetClosestCreatureWithEntry(pSummoned, NPC_ORB_CARRIER, 5.0f)) + pSummoned->CastSpell(pCarrier, SPELL_RIDE_VEHICLE_HARDCODED, true); + break; + case NPC_ORB_CARRIER: + pSummoned->CastSpell(pSummoned, SPELL_TRACK_ROTATION, true); + break; + } + } + + void UpdateAI(const uint32 uiDiff) override + { + if (!m_creature->SelectHostileTarget() || !m_creature->getVictim()) + return; + + switch (m_uiPhase) + { + case PHASE_PHISYCAL_REALM: + // nothing here - phase not handled by this npc + break; + case PHASE_BOTH_REALMS: + // ToDo: handle corporeality + // no break; + case PHASE_TWILIGHT_REALM: + + if (m_uiTailLashTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature, SPELL_TAIL_LASH) == CAST_OK) + m_uiTailLashTimer = urand(15000, 25000); + } + else + m_uiTailLashTimer -= uiDiff; + + if (m_uiCleaveTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature->getVictim(), SPELL_CLEAVE) == CAST_OK) + m_uiCleaveTimer = urand(10000, 15000); + } + else + m_uiCleaveTimer -= uiDiff; + + if (m_uiDarkBreathTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature->getVictim(), SPELL_DARK_BREATH) == CAST_OK) + m_uiDarkBreathTimer = urand(15000, 20000); + } + else + m_uiDarkBreathTimer -= uiDiff; + + if (m_uiSoulConsumptionTimer < uiDiff) + { + if (Unit* pTarget = m_creature->SelectAttackingTarget(ATTACKING_TARGET_RANDOM, 0, SPELL_SOUL_CONSUMPTION, SELECT_FLAG_PLAYER)) + { + if (DoCastSpellIfCan(pTarget, SPELL_SOUL_CONSUMPTION) == CAST_OK) + m_uiSoulConsumptionTimer = 25000; + } + } + else + m_uiSoulConsumptionTimer -= uiDiff; + + // Switch to phase 3 + if (m_creature->GetHealthPercent() < 50.0f && m_uiPhase == PHASE_TWILIGHT_REALM) + { + if (DoCastSpellIfCan(m_creature, SPELL_TWILIGHT_DIVISION, CAST_INTERRUPT_PREVIOUS) == CAST_OK) + { + if (m_pInstance) + { + // ToDo: Update world states and spawn the exit portals + + // Set the real Halion health, so it can also begin phase 3 + if (Creature* pHalion = m_pInstance->GetSingleCreatureFromStorage(NPC_HALION_REAL)) + pHalion->SetHealth(m_creature->GetHealth()); + } + + DoScriptText(SAY_PHASE_3, m_creature); + m_uiPhase = PHASE_BOTH_REALMS; + } + } + + break; + } + + DoMeleeAttackIfReady(); + } +}; + +CreatureAI* GetAI_boss_halion_real(Creature* pCreature) +{ + return new boss_halion_realAI(pCreature); +}; + +CreatureAI* GetAI_boss_halion_twilight(Creature* pCreature) +{ + return new boss_halion_twilightAI(pCreature); +}; + +void AddSC_boss_halion() +{ + Script* pNewScript; + + pNewScript = new Script; + pNewScript->Name = "boss_halion_real"; + pNewScript->GetAI = &GetAI_boss_halion_real; + pNewScript->RegisterSelf(); + + pNewScript = new Script; + pNewScript->Name = "boss_halion_twilight"; + pNewScript->GetAI = &GetAI_boss_halion_twilight; + pNewScript->RegisterSelf(); +} diff --git a/src/modules/SD2/scripts/northrend/ruby_sanctum/boss_saviana.cpp b/src/modules/SD2/scripts/northrend/ruby_sanctum/boss_saviana.cpp new file mode 100644 index 000000000..82ae082f7 --- /dev/null +++ b/src/modules/SD2/scripts/northrend/ruby_sanctum/boss_saviana.cpp @@ -0,0 +1,222 @@ +/* Copyright (C) 2006 - 2013 ScriptDev2 + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +/* ScriptData +SDName: boss_saviana +SD%Complete: 100 +SDComment: +SDCategory: Ruby Sanctum +EndScriptData */ + +#include "precompiled.h" +#include "ruby_sanctum.h" + +enum +{ + SAY_AGGRO = -1724015, + SAY_SLAY_1 = -1724016, + SAY_SLAY_2 = -1724017, + SAY_SPECIAL = -1724018, + SOUND_DEATH = 17531, // On death it has only a screaming sound + EMOTE_ENRAGE = -1000003, + + SPELL_ENRAGE = 78722, + SPELL_FLAME_BREATH = 74403, + SPELL_CONFLAGRATION = 74452, // dummy targeting spell - effect handled in core + + PHASE_GROUND = 1, + PHASE_AIR = 2, + PHASE_TRANSITION = 3, + + POINT_AIR = 1, + POINT_GROUND = 2 +}; + +static const float aAirPositions[3] = {3155.51f, 683.844f, 90.50f}; + +struct boss_savianaAI : public ScriptedAI +{ + boss_savianaAI(Creature* pCreature) : ScriptedAI(pCreature) + { + m_pInstance = (instance_ruby_sanctum*)pCreature->GetInstanceData(); + Reset(); + } + + instance_ruby_sanctum* m_pInstance; + + uint8 m_uiPhase; + uint32 m_uiPhaseSwitchTimer; + uint32 m_uiFlameBreathTimer; + uint32 m_uiEnrageTimer; + + void Reset() override + { + m_uiPhase = PHASE_GROUND; + m_uiPhaseSwitchTimer = 28000; + m_uiEnrageTimer = urand(10000, 15000); + m_uiFlameBreathTimer = 10000; + } + + void Aggro(Unit* /*pWho*/) override + { + DoScriptText(SAY_AGGRO, m_creature); + + if (m_pInstance) + m_pInstance->SetData(TYPE_SAVIANA, IN_PROGRESS); + } + + void KilledUnit(Unit* pVictim) override + { + if (pVictim->GetTypeId() != TYPEID_PLAYER) + return; + + if (urand(0, 1)) + DoScriptText(urand(0, 1) ? SAY_SLAY_1 : SAY_SLAY_2, m_creature); + } + + void JustDied(Unit* /*pKiller*/) override + { + DoPlaySoundToSet(m_creature, SOUND_DEATH); + + if (m_pInstance) + m_pInstance->SetData(TYPE_SAVIANA, DONE); + } + + void JustReachedHome() override + { + SetCombatMovement(true); + m_creature->SetLevitate(false); + m_creature->SetByteFlag(UNIT_FIELD_BYTES_1, 3, 0); + + if (m_pInstance) + m_pInstance->SetData(TYPE_SAVIANA, FAIL); + } + + void MovementInform(uint32 uiMoveType, uint32 uiPointId) override + { + if (uiMoveType != POINT_MOTION_TYPE) + return; + + switch (uiPointId) + { + case POINT_AIR: + if (DoCastSpellIfCan(m_creature, SPELL_CONFLAGRATION) == CAST_OK) + { + DoScriptText(SAY_SPECIAL, m_creature); + m_uiPhaseSwitchTimer = 6000; + m_uiPhase = PHASE_AIR; + } + + break; + case POINT_GROUND: + m_uiPhase = PHASE_GROUND; + m_uiPhaseSwitchTimer = 38000; + + SetCombatMovement(true); + m_creature->SetLevitate(false); + m_creature->SetByteFlag(UNIT_FIELD_BYTES_1, 3, 0); + + if (m_creature->getVictim()) + m_creature->GetMotionMaster()->MoveChase(m_creature->getVictim()); + + break; + } + } + + void UpdateAI(const uint32 uiDiff) override + { + if (!m_creature->SelectHostileTarget() || !m_creature->getVictim()) + return; + + switch (m_uiPhase) + { + case PHASE_GROUND: + + if (m_uiFlameBreathTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature, SPELL_FLAME_BREATH) == CAST_OK) + m_uiFlameBreathTimer = urand(20000, 25000); + } + else + m_uiFlameBreathTimer -= uiDiff; + + if (m_uiEnrageTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature, SPELL_ENRAGE) == CAST_OK) + { + DoScriptText(EMOTE_ENRAGE, m_creature); + m_uiEnrageTimer = urand(20000, 25000); + } + } + else + m_uiEnrageTimer -= uiDiff; + + if (m_uiPhaseSwitchTimer < uiDiff) + { + m_uiPhaseSwitchTimer = 0; + m_uiPhase = PHASE_TRANSITION; + + SetCombatMovement(false); + m_creature->SetByteValue(UNIT_FIELD_BYTES_1, 3, UNIT_BYTE1_FLAG_ALWAYS_STAND | UNIT_BYTE1_FLAG_UNK_2); + m_creature->SetLevitate(true); + + m_creature->GetMotionMaster()->Clear(); + m_creature->GetMotionMaster()->MovePoint(POINT_AIR, aAirPositions[0], aAirPositions[1], aAirPositions[2]); + } + else + m_uiPhaseSwitchTimer -= uiDiff; + + DoMeleeAttackIfReady(); + + break; + case PHASE_AIR: + if (m_uiPhaseSwitchTimer) + { + if (m_uiPhaseSwitchTimer <= uiDiff) + { + m_uiPhase = PHASE_TRANSITION; + m_uiPhaseSwitchTimer = 0; + + float fX, fY, fZ; + m_creature->GetRespawnCoord(fX, fY, fZ); + m_creature->GetMotionMaster()->Clear(); + m_creature->GetMotionMaster()->MovePoint(POINT_GROUND, fX, fY, fZ); + } + else + m_uiPhaseSwitchTimer -= uiDiff; + } + break; + case PHASE_TRANSITION: + // nothing here + break; + } + } +}; + +CreatureAI* GetAI_boss_saviana(Creature* pCreature) +{ + return new boss_savianaAI(pCreature); +} + +void AddSC_boss_saviana() +{ + Script* pNewScript; + + pNewScript = new Script; + pNewScript->Name = "boss_saviana"; + pNewScript->GetAI = &GetAI_boss_saviana; + pNewScript->RegisterSelf(); +} diff --git a/src/modules/SD2/scripts/northrend/ruby_sanctum/boss_zarithrian.cpp b/src/modules/SD2/scripts/northrend/ruby_sanctum/boss_zarithrian.cpp new file mode 100644 index 000000000..e0a230588 --- /dev/null +++ b/src/modules/SD2/scripts/northrend/ruby_sanctum/boss_zarithrian.cpp @@ -0,0 +1,163 @@ +/* Copyright (C) 2006 - 2013 ScriptDev2 + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +/* ScriptData +SDName: boss_zarithrian +SD%Complete: 100 +SDComment: +SDCategory: Ruby Sanctum +EndScriptData */ + +#include "precompiled.h" +#include "ruby_sanctum.h" + +enum +{ + SAY_AGGRO = -1724019, + SAY_SLAY_1 = -1724020, + SAY_SLAY_2 = -1724021, + SAY_DEATH = -1724022, + SAY_SUMMON = -1724023, + + SPELL_SUMMON_FLAMECALLER = 74398, + SPELL_CLEAVE_ARMOR = 74367, + SPELL_INTIMIDATING_ROAR = 74384, + + NPC_ONYX_FLAMECALLER = 39814, // handled in ACID +}; + +struct boss_zarithrianAI : public ScriptedAI +{ + boss_zarithrianAI(Creature* pCreature) : ScriptedAI(pCreature) + { + m_pInstance = (instance_ruby_sanctum*)pCreature->GetInstanceData(); + Reset(); + } + + instance_ruby_sanctum* m_pInstance; + + uint32 m_uiCleaveArmorTimer; + uint32 m_uiIntimidatingRoarTimer; + uint32 m_uiCallFlamecallerTimer; + + void Reset() override + { + m_uiCleaveArmorTimer = 15000; + m_uiIntimidatingRoarTimer = 14000; + m_uiCallFlamecallerTimer = 15000; + } + + void Aggro(Unit* /*pWho*/) override + { + DoScriptText(SAY_AGGRO, m_creature); + + if (m_pInstance) + m_pInstance->SetData(TYPE_ZARITHRIAN, IN_PROGRESS); + } + + void KilledUnit(Unit* pVictim) override + { + if (pVictim->GetTypeId() != TYPEID_PLAYER) + return; + + if (urand(0, 1)) + DoScriptText(urand(0, 1) ? SAY_SLAY_1 : SAY_SLAY_2, m_creature); + } + + void JustDied(Unit* /*pKiller*/) override + { + DoScriptText(SAY_DEATH, m_creature); + + if (m_pInstance) + m_pInstance->SetData(TYPE_ZARITHRIAN, DONE); + } + + void JustReachedHome() override + { + if (m_pInstance) + m_pInstance->SetData(TYPE_ZARITHRIAN, FAIL); + } + + void JustSummoned(Creature* pSummoned) override + { + if (pSummoned->GetEntry() == NPC_ONYX_FLAMECALLER) + pSummoned->SetInCombatWithZone(); + } + + void UpdateAI(const uint32 uiDiff) override + { + if (!m_creature->SelectHostileTarget() || !m_creature->getVictim()) + return; + + if (m_uiCleaveArmorTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature->getVictim(), SPELL_CLEAVE_ARMOR) == CAST_OK) + m_uiCleaveArmorTimer = 15000; + } + else + m_uiCleaveArmorTimer -= uiDiff; + + if (m_uiIntimidatingRoarTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature, SPELL_INTIMIDATING_ROAR) == CAST_OK) + m_uiIntimidatingRoarTimer = 32000; + } + else + m_uiIntimidatingRoarTimer -= uiDiff; + + if (m_uiCallFlamecallerTimer < uiDiff) + { + if (!m_pInstance) + { + script_error_log("Instance Ruby Sanctum: ERROR Failed to load instance data for this instace."); + return; + } + + GuidList m_lStalkersGuidList; + m_pInstance->GetSpawnStalkersGuidList(m_lStalkersGuidList); + + for (GuidList::const_iterator itr = m_lStalkersGuidList.begin(); itr != m_lStalkersGuidList.end(); ++itr) + { + if (Creature* pStalker = m_creature->GetMap()->GetCreature(*itr)) + pStalker->CastSpell(pStalker, SPELL_SUMMON_FLAMECALLER, true, NULL, NULL, m_creature->GetObjectGuid()); + } + + DoScriptText(SAY_SUMMON, m_creature); + m_uiCallFlamecallerTimer = 45000; + } + else + m_uiCallFlamecallerTimer -= uiDiff; + + DoMeleeAttackIfReady(); + + EnterEvadeIfOutOfCombatArea(uiDiff); + } +}; + +CreatureAI* GetAI_boss_zarithrian(Creature* pCreature) +{ + return new boss_zarithrianAI(pCreature); +} + +void AddSC_boss_zarithrian() +{ + Script* pNewScript; + + pNewScript = new Script; + pNewScript->Name = "boss_zarithrian"; + pNewScript->GetAI = &GetAI_boss_zarithrian; + pNewScript->RegisterSelf(); +} diff --git a/src/modules/SD2/scripts/northrend/ruby_sanctum/instance_ruby_sanctum.cpp b/src/modules/SD2/scripts/northrend/ruby_sanctum/instance_ruby_sanctum.cpp new file mode 100644 index 000000000..8a8e2153e --- /dev/null +++ b/src/modules/SD2/scripts/northrend/ruby_sanctum/instance_ruby_sanctum.cpp @@ -0,0 +1,318 @@ +/* Copyright (C) 2006 - 2013 ScriptDev2 + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along 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_ruby_sanctum +SD%Complete: 30% +SDComment: Basic instance script +SDCategory: Ruby Sanctum +EndScriptData */ + +#include "precompiled.h" +#include "ruby_sanctum.h" + +instance_ruby_sanctum::instance_ruby_sanctum(Map* pMap) : ScriptedInstance(pMap), + m_uiHalionSummonTimer(0), + m_uiHalionSummonStage(0), + m_uiHalionResetTimer(0) +{ + Initialize(); +} + +void instance_ruby_sanctum::Initialize() +{ + memset(&m_auiEncounter, 0, sizeof(m_auiEncounter)); +} + +bool instance_ruby_sanctum::IsEncounterInProgress() const +{ + for (uint8 i = 1; i < MAX_ENCOUNTER ; ++i) + { + if (m_auiEncounter[i] == IN_PROGRESS) + return true; + } + + return false; +} + +void instance_ruby_sanctum::OnPlayerEnter(Player* /*pPlayer*/) +{ + // Return if Halion already dead, or Zarithrian alive + if (m_auiEncounter[TYPE_ZARITHRIAN] != DONE || m_auiEncounter[TYPE_HALION] == DONE) + return; + + // Return if already summoned + if (GetSingleCreatureFromStorage(NPC_HALION_REAL, true)) + return; + + if (Creature* pSummoner = GetSingleCreatureFromStorage(NPC_HALION_CONTROLLER)) + pSummoner->SummonCreature(NPC_HALION_REAL, pSummoner->GetPositionX(), pSummoner->GetPositionY(), pSummoner->GetPositionZ(), 3.159f, TEMPSUMMON_DEAD_DESPAWN, 0); +} + +void instance_ruby_sanctum::OnCreatureCreate(Creature* pCreature) +{ + switch (pCreature->GetEntry()) + { + case NPC_XERESTRASZA: + // Special case for Xerestrasza: she only needs to have questgiver flag if Baltharus is killed + if (m_auiEncounter[TYPE_BALTHARUS] != DONE) + pCreature->RemoveFlag(UNIT_NPC_FLAGS, UNIT_NPC_FLAG_QUESTGIVER); + m_mNpcEntryGuidStore[pCreature->GetEntry()] = pCreature->GetObjectGuid(); + break; + case NPC_ZARITHRIAN: + if (m_auiEncounter[TYPE_SAVIANA] == DONE && m_auiEncounter[TYPE_BALTHARUS] == DONE) + pCreature->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE); + // no break; + case NPC_BALTHARUS: + case NPC_HALION_REAL: + case NPC_HALION_TWILIGHT: + case NPC_HALION_CONTROLLER: + m_mNpcEntryGuidStore[pCreature->GetEntry()] = pCreature->GetObjectGuid(); + break; + case NPC_ZARITHRIAN_SPAWN_STALKER: + m_lSpawnStalkersGuidList.push_back(pCreature->GetObjectGuid()); + break; + } +} + +void instance_ruby_sanctum::OnObjectCreate(GameObject* pGo) +{ + switch (pGo->GetEntry()) + { + case GO_FLAME_WALLS: + if (m_auiEncounter[TYPE_SAVIANA] == DONE && m_auiEncounter[TYPE_BALTHARUS] == DONE) + pGo->SetGoState(GO_STATE_ACTIVE); + break; + case GO_FIRE_FIELD: + if (m_auiEncounter[TYPE_BALTHARUS] == DONE) + pGo->SetGoState(GO_STATE_ACTIVE); + break; + case GO_FLAME_RING: + break; + case GO_BURNING_TREE_1: + case GO_BURNING_TREE_2: + case GO_BURNING_TREE_3: + case GO_BURNING_TREE_4: + if (m_auiEncounter[TYPE_ZARITHRIAN] == DONE) + pGo->SetGoState(GO_STATE_ACTIVE); + break; + case GO_TWILIGHT_PORTAL_ENTER_1: + case GO_TWILIGHT_PORTAL_ENTER_2: + case GO_TWILIGHT_PORTAL_LEAVE: + break; + default: + return; + } + m_mGoEntryGuidStore[pGo->GetEntry()] = pGo->GetObjectGuid(); +} + +// Wrapper to unlock the flame wall in from of Zarithrian +void instance_ruby_sanctum::DoHandleZarithrianDoor() +{ + if (m_auiEncounter[TYPE_SAVIANA] == DONE && m_auiEncounter[TYPE_BALTHARUS] == DONE) + { + DoUseDoorOrButton(GO_FLAME_WALLS); + + // Also remove not_selectable unit flag + if (Creature* pZarithrian = GetSingleCreatureFromStorage(NPC_ZARITHRIAN)) + pZarithrian->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE); + } +} + +void instance_ruby_sanctum::SetData(uint32 uiType, uint32 uiData) +{ + switch (uiType) + { + case TYPE_SAVIANA: + m_auiEncounter[uiType] = uiData; + if (uiData == DONE) + DoHandleZarithrianDoor(); + break; + case TYPE_BALTHARUS: + m_auiEncounter[uiType] = uiData; + if (uiData == DONE) + { + DoHandleZarithrianDoor(); + DoUseDoorOrButton(GO_FIRE_FIELD); + + // Start outro event by DB script + if (Creature* pXerestrasza = GetSingleCreatureFromStorage(NPC_XERESTRASZA)) + pXerestrasza->GetMotionMaster()->MoveWaypoint(); + } + break; + case TYPE_ZARITHRIAN: + m_auiEncounter[uiType] = uiData; + DoUseDoorOrButton(GO_FLAME_WALLS); + if (uiData == DONE) + { + // Start Halion summoning process + if (Creature* pSummoner = GetSingleCreatureFromStorage(NPC_HALION_CONTROLLER)) + { + pSummoner->CastSpell(pSummoner, SPELL_FIRE_PILLAR, false); + m_uiHalionSummonTimer = 5000; + } + } + break; + case TYPE_HALION: + // Don't set the same data twice + if (m_auiEncounter[uiType] == uiData) + return; + m_auiEncounter[uiType] = uiData; + DoUseDoorOrButton(GO_FLAME_RING); + switch (uiData) + { + case FAIL: + // Despawn the boss + if (Creature* pHalion = GetSingleCreatureFromStorage(NPC_HALION_REAL)) + pHalion->ForcedDespawn(); + if (Creature* pHalion = GetSingleCreatureFromStorage(NPC_HALION_TWILIGHT)) + pHalion->ForcedDespawn(); + // Note: rest of the cleanup is handled by creature_linking + + m_uiHalionResetTimer = 30000; + // no break; + case DONE: + // clear debuffs + if (Creature* pController = GetSingleCreatureFromStorage(NPC_HALION_CONTROLLER)) + pController->CastSpell(pController, SPELL_CLEAR_DEBUFFS, true); + + // Despawn the portals + if (GameObject* pPortal = GetSingleGameObjectFromStorage(GO_TWILIGHT_PORTAL_ENTER_1)) + pPortal->SetLootState(GO_JUST_DEACTIVATED); + + // ToDo: despawn the other portals as well, and disable world state + break; + } + break; + } + + if (uiData == 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; + } +} + +uint32 instance_ruby_sanctum::GetData(uint32 uiType) const +{ + if (uiType < MAX_ENCOUNTER) + return m_auiEncounter[uiType]; + + return 0; +} + +void instance_ruby_sanctum::Update(uint32 uiDiff) +{ + if (m_uiHalionSummonTimer) + { + if (m_uiHalionSummonTimer <= uiDiff) + { + switch (m_uiHalionSummonStage) + { + case 0: + // Burn the first line of trees + DoUseDoorOrButton(GO_BURNING_TREE_1); + DoUseDoorOrButton(GO_BURNING_TREE_2); + m_uiHalionSummonTimer = 5000; + break; + case 1: + // Burn the second line of trees + DoUseDoorOrButton(GO_BURNING_TREE_3); + DoUseDoorOrButton(GO_BURNING_TREE_4); + m_uiHalionSummonTimer = 4000; + break; + case 2: + // Cast Fiery explosion + if (Creature* pSummoner = GetSingleCreatureFromStorage(NPC_HALION_CONTROLLER)) + pSummoner->CastSpell(pSummoner, SPELL_FIERY_EXPLOSION, true); + m_uiHalionSummonTimer = 2000; + case 3: + // Spawn Halion + if (Creature* pSummoner = GetSingleCreatureFromStorage(NPC_HALION_CONTROLLER)) + { + if (Creature* pHalion = pSummoner->SummonCreature(NPC_HALION_REAL, pSummoner->GetPositionX(), pSummoner->GetPositionY(), pSummoner->GetPositionZ(), 3.159f, TEMPSUMMON_DEAD_DESPAWN, 0)) + DoScriptText(SAY_HALION_SPAWN, pHalion); + } + m_uiHalionSummonTimer = 0; + break; + } + ++m_uiHalionSummonStage; + } + else + m_uiHalionSummonTimer -= uiDiff; + } + + // Resummon Halion if the encounter resets + if (m_uiHalionResetTimer) + { + if (m_uiHalionResetTimer <= uiDiff) + { + if (Creature* pSummoner = GetSingleCreatureFromStorage(NPC_HALION_CONTROLLER)) + pSummoner->SummonCreature(NPC_HALION_REAL, pSummoner->GetPositionX(), pSummoner->GetPositionY(), pSummoner->GetPositionZ(), 3.159f, TEMPSUMMON_DEAD_DESPAWN, 0); + + if (Creature* pHalion = GetSingleCreatureFromStorage(NPC_HALION_TWILIGHT)) + pHalion->Respawn(); + + m_uiHalionResetTimer = 0; + } + else + m_uiHalionResetTimer -= uiDiff; + } +} + +void instance_ruby_sanctum::Load(const char* chrIn) +{ + if (!chrIn) + { + 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 < MAX_ENCOUNTER; ++i) + { + if (m_auiEncounter[i] == IN_PROGRESS) + m_auiEncounter[i] = NOT_STARTED; + } + + OUT_LOAD_INST_DATA_COMPLETE; +} + +InstanceData* GetInstanceData_instance_ruby_sanctum(Map* pMap) +{ + return new instance_ruby_sanctum(pMap); +} + +void AddSC_instance_ruby_sanctum() +{ + Script* pNewScript; + + pNewScript = new Script; + pNewScript->Name = "instance_ruby_sanctum"; + pNewScript->GetInstanceData = &GetInstanceData_instance_ruby_sanctum; + pNewScript->RegisterSelf(); +} diff --git a/src/modules/SD2/scripts/northrend/ruby_sanctum/ruby_sanctum.h b/src/modules/SD2/scripts/northrend/ruby_sanctum/ruby_sanctum.h new file mode 100644 index 000000000..59babdfb9 --- /dev/null +++ b/src/modules/SD2/scripts/northrend/ruby_sanctum/ruby_sanctum.h @@ -0,0 +1,99 @@ +/* Copyright (C) 2006 - 2013 ScriptDev2 + * This program is free software licensed under GPL version 2 + * Please see the included DOCS/LICENSE.TXT for more information */ + +#ifndef DEF_RUBY_SANCTUM_H +#define DEF_RUBY_SANCTUM_H + +enum +{ + MAX_ENCOUNTER = 4, + + TYPE_SAVIANA = 0, + TYPE_BALTHARUS = 1, + TYPE_ZARITHRIAN = 2, + TYPE_HALION = 3, + + NPC_HALION_REAL = 39863, // Halion - Physical Realm NPC + NPC_HALION_TWILIGHT = 40142, // Halion - Twilight Realm NPC + NPC_HALION_CONTROLLER = 40146, + + NPC_SHADOW_ORB_1 = 40083, // shadow orbs for Halion encounter + NPC_SHADOW_ORB_2 = 40100, + NPC_SHADOW_ORB_3 = 40468, // heroic only + NPC_SHADOW_ORB_4 = 40469, // heroic only + + NPC_SAVIANA = 39747, // minibosses + NPC_BALTHARUS = 39751, + NPC_ZARITHRIAN = 39746, + + NPC_XERESTRASZA = 40429, // friendly npc, used for some cinematic and quest + NPC_ZARITHRIAN_SPAWN_STALKER = 39794, + + GO_TWILIGHT_PORTAL_ENTER_1 = 202794, // Portals used in the Halion encounter + GO_TWILIGHT_PORTAL_ENTER_2 = 202795, + GO_TWILIGHT_PORTAL_LEAVE = 202796, + + GO_FIRE_FIELD = 203005, // Xerestrasza flame door + GO_FLAME_WALLS = 203006, // Zarithrian flame walls + GO_FLAME_RING = 203007, // Halion flame ring + GO_TWILIGHT_FLAME_RING = 203624, // Halion flame ring - twilight version + + GO_BURNING_TREE_1 = 203036, // Trees which burn when Halion appears + GO_BURNING_TREE_2 = 203037, + GO_BURNING_TREE_3 = 203035, + GO_BURNING_TREE_4 = 203034, + + // Spells used to summon Halion + SPELL_FIRE_PILLAR = 76006, + SPELL_FIERY_EXPLOSION = 76010, + SPELL_CLEAR_DEBUFFS = 75396, // cast by the controller on encounter reset + + SAY_HALION_SPAWN = -1724024, + + // world state to show corporeality in Halion encounter - phase 3 + WORLD_STATE_CORP_PHYSICAL = 5049, + WORLD_STATE_CORP_TWILIGHT = 5050, + WORLD_STATE_CORPOREALITY = 5051, +}; + +class instance_ruby_sanctum : public ScriptedInstance +{ + public: + instance_ruby_sanctum(Map* pMap); + + void Initialize() override; + bool IsEncounterInProgress() const override; + + void OnPlayerEnter(Player* pPlayer) override; + void OnCreatureCreate(Creature* pCreature) override; + void OnObjectCreate(GameObject* pGo) override; + + void SetData(uint32 uiType, uint32 uiData) override; + uint32 GetData(uint32 uiType) const override; + + void Update(uint32 uiDiff) override; + + void GetSpawnStalkersGuidList(GuidList& lList) { lList = m_lSpawnStalkersGuidList; } + + const char* Save() const override { return strInstData.c_str(); } + void Load(const char* chrIn) override; + + // Difficulty wrappers + bool IsHeroicDifficulty() { return instance->GetDifficulty() == RAID_DIFFICULTY_10MAN_HEROIC || instance->GetDifficulty() == RAID_DIFFICULTY_25MAN_HEROIC; } + bool Is25ManDifficulty() { return instance->GetDifficulty() == RAID_DIFFICULTY_25MAN_NORMAL || instance->GetDifficulty() == RAID_DIFFICULTY_25MAN_HEROIC; } + + protected: + void DoHandleZarithrianDoor(); + + std::string strInstData; + uint32 m_auiEncounter[MAX_ENCOUNTER]; + + uint32 m_uiHalionSummonTimer; + uint32 m_uiHalionSummonStage; + uint32 m_uiHalionResetTimer; + + GuidList m_lSpawnStalkersGuidList; +}; + +#endif diff --git a/src/modules/SD2/scripts/northrend/sholazar_basin.cpp b/src/modules/SD2/scripts/northrend/sholazar_basin.cpp new file mode 100644 index 000000000..ea4585fc3 --- /dev/null +++ b/src/modules/SD2/scripts/northrend/sholazar_basin.cpp @@ -0,0 +1,434 @@ +/* Copyright (C) 2006 - 2013 ScriptDev2 + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along 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, 12580, 12688 +SDCategory: Sholazar Basin +EndScriptData */ + +/* ContentData +npc_helice +npc_injured_rainspeaker +npc_mosswalker_victim +EndContentData */ + +#include "precompiled.h" +#include "escort_ai.h" + +/*###### +## npc_helice +######*/ + +enum +{ + QUEST_ENGINEERING_DISASTER = 12688, + + SAY_HELICE_ACCEPT = -1000657, + SAY_HELICE_EXPLOSIVES_1 = -1000658, + SAY_HELICE_EXPLODE_1 = -1000659, + SAY_HELICE_MOVE_ON = -1000660, + SAY_HELICE_EXPLOSIVES_2 = -1000661, + SAY_HELICE_EXPLODE_2 = -1000662, + SAY_HELICE_COMPLETE = -1000663, + + SPELL_DETONATE_EXPLOSIVES_1 = 52369, // first "barrel" + SPELL_DETONATE_EXPLOSIVES_2 = 52371, // second "barrel" +}; + +struct npc_heliceAI : public npc_escortAI +{ + npc_heliceAI(Creature* pCreature) : npc_escortAI(pCreature) + { + m_uiExplodeTimer = 5000; + m_uiExplodePhase = 0; + m_bFirstBarrel = true; + Reset(); + } + + uint32 m_uiExplodeTimer; + uint32 m_uiExplodePhase; + bool m_bFirstBarrel; + + void Reset() override + { + } + + void WaypointReached(uint32 uiPointId) override + { + switch (uiPointId) + { + case 2: + { + if (Player* pPlayer = GetPlayerForEscort()) + { + DoScriptText(SAY_HELICE_EXPLOSIVES_1, m_creature, pPlayer); + SetEscortPaused(true); + } + break; + } + case 13: + { + if (Player* pPlayer = GetPlayerForEscort()) + { + DoScriptText(SAY_HELICE_EXPLOSIVES_2, m_creature, pPlayer); + SetEscortPaused(true); + } + break; + } + case 22: + { + if (Player* pPlayer = GetPlayerForEscort()) + { + DoScriptText(SAY_HELICE_COMPLETE, m_creature, pPlayer); + pPlayer->GroupEventHappens(QUEST_ENGINEERING_DISASTER, m_creature); + } + break; + } + } + } + + void UpdateEscortAI(const uint32 uiDiff) override + { + if (!m_creature->SelectHostileTarget() || !m_creature->getVictim()) + { + if (!HasEscortState(STATE_ESCORT_ESCORTING)) + return; + + if (HasEscortState(STATE_ESCORT_PAUSED)) + { + if (m_uiExplodeTimer < uiDiff) + { + if (m_bFirstBarrel) + { + switch (m_uiExplodePhase) + { + case 0: + DoCastSpellIfCan(m_creature, SPELL_DETONATE_EXPLOSIVES_1); + + if (Player* pPlayer = GetPlayerForEscort()) + DoScriptText(SAY_HELICE_EXPLODE_1, m_creature, pPlayer); + + m_uiExplodeTimer = 2500; + ++m_uiExplodePhase; + break; + case 1: + if (Player* pPlayer = GetPlayerForEscort()) + DoScriptText(SAY_HELICE_MOVE_ON, m_creature, pPlayer); + + m_uiExplodeTimer = 2500; + ++m_uiExplodePhase; + break; + case 2: + SetEscortPaused(false); + m_uiExplodePhase = 0; + m_uiExplodeTimer = 5000; + m_bFirstBarrel = false; + break; + } + } + else + { + switch (m_uiExplodePhase) + { + case 0: + DoCastSpellIfCan(m_creature, SPELL_DETONATE_EXPLOSIVES_2); + + if (Player* pPlayer = GetPlayerForEscort()) + DoScriptText(SAY_HELICE_EXPLODE_2, m_creature, pPlayer); + + m_uiExplodeTimer = 2500; + ++m_uiExplodePhase; + break; + case 1: + SetEscortPaused(false); + m_uiExplodePhase = 0; + m_uiExplodeTimer = 5000; + m_bFirstBarrel = true; + break; + } + } + } + else + m_uiExplodeTimer -= uiDiff; + } + + return; + } + + DoMeleeAttackIfReady(); + } +}; + +CreatureAI* GetAI_npc_helice(Creature* pCreature) +{ + return new npc_heliceAI(pCreature); +} + +bool QuestAccept_npc_helice(Player* pPlayer, Creature* pCreature, const Quest* pQuest) +{ + if (pQuest->GetQuestId() == QUEST_ENGINEERING_DISASTER) + { + DoScriptText(SAY_HELICE_ACCEPT, pCreature, pPlayer); + + if (npc_heliceAI* pEscortAI = dynamic_cast(pCreature->AI())) + { + pEscortAI->Start(false, pPlayer, pQuest); + pCreature->SetFactionTemporary(FACTION_ESCORT_N_NEUTRAL_PASSIVE, TEMPFACTION_RESTORE_RESPAWN); + } + } + + return false; +} + +/*###### +## npc_injured_rainspeaker +######*/ + +enum +{ + QUEST_FORTUNATE_MISUNDERSTAND = 12570, + + GOSSIP_ITEM_READY = -3000103, + + SAY_ACCEPT = -1000605, + SAY_START = -1000606, + SAY_END_1 = -1000607, + SAY_END_2 = -1000608, + SAY_TRACKER = -1000609, // not used in escort (aggro text for trackers? something for vekjik?) + + NPC_FRENZYHEART_TRACKER = 28077, + + SPELL_ORACLE_ESCORT_START = 51341, // unknown purpose + SPELL_FEIGN_DEATH = 51329, + SPELL_ORACLE_INTRO = 51448, +}; + +struct npc_injured_rainspeakerAI : public npc_escortAI +{ + npc_injured_rainspeakerAI(Creature* pCreature) : npc_escortAI(pCreature) { Reset(); } + + void Reset() override { } + + void JustStartedEscort() override + { + if (Player* pPlayer = GetPlayerForEscort()) + { + DoScriptText(SAY_START, m_creature, pPlayer); + DoCastSpellIfCan(m_creature, SPELL_ORACLE_ESCORT_START); + } + } + + void WaypointReached(uint32 uiPointId) override + { + switch (uiPointId) + { + case 22: + { + if (Player* pPlayer = GetPlayerForEscort()) + { + DoScriptText(SAY_END_1, m_creature, pPlayer); + DoCastSpellIfCan(m_creature, SPELL_ORACLE_INTRO); + } + break; + } + case 23: + { + DoScriptText(SAY_END_2, m_creature); + + // location behind + float fAngle = m_creature->GetOrientation(); + fAngle += M_PI_F; + + float fX, fY, fZ; + m_creature->GetNearPoint(m_creature, fX, fY, fZ, 0.0f, 15.0f, fAngle); + + m_creature->SummonCreature(NPC_FRENZYHEART_TRACKER, fX, fY, fZ, m_creature->GetOrientation(), TEMPSUMMON_TIMED_DESPAWN, 30000); + break; + } + } + } + + void UpdateEscortAI(const uint32 /*uiDiff*/) override + { + if (!m_creature->SelectHostileTarget() || !m_creature->getVictim()) + return; + + DoMeleeAttackIfReady(); + } +}; + +CreatureAI* GetAI_npc_injured_rainspeaker(Creature* pCreature) +{ + return new npc_injured_rainspeakerAI(pCreature); +} + +bool QuestAccept_npc_injured_rainspeaker(Player* pPlayer, Creature* pCreature, const Quest* pQuest) +{ + if (pQuest->GetQuestId() == QUEST_FORTUNATE_MISUNDERSTAND) + { + pCreature->RemoveAurasDueToSpell(SPELL_FEIGN_DEATH); + DoScriptText(SAY_ACCEPT, pCreature, pPlayer); + + // Workaround, GossipHello/GossipSelect doesn't work well when object already has gossip from database + if (npc_injured_rainspeakerAI* pEscortAI = dynamic_cast(pCreature->AI())) + { + pEscortAI->Start(true, pPlayer, pQuest); + pCreature->SetFactionTemporary(FACTION_ESCORT_N_NEUTRAL_PASSIVE, TEMPFACTION_RESTORE_RESPAWN); + } + } + + return false; +} + +/* +bool GossipHello_npc_injured_rainspeaker(Player* pPlayer, Creature* pCreature) +{ + if (pPlayer->GetQuestStatus(QUEST_FORTUNATE_MISUNDERSTAND) == QUEST_STATUS_INCOMPLETE) + { + pPlayer->ADD_GOSSIP_ITEM_ID(GOSSIP_ICON_CHAT, GOSSIP_ITEM_READY, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF); + pPlayer->SEND_GOSSIP_MENU(pPlayer->GetGossipTextId(pCreature), pCreature->GetObjectGuid()); + return true; + } + + return false; +} + +bool GossipSelect_npc_injured_rainspeaker(Player* pPlayer, Creature* pCreature, uint32 uiSender, uint32 uiAction) +{ + if (uiAction == GOSSIP_ACTION_INFO_DEF) + { + pPlayer->CLOSE_GOSSIP_MENU(); + + if (npc_injured_rainspeakerAI* pEscortAI = dynamic_cast(pCreature->AI())) + pEscortAI->Start(true, pPlayer); + } + + return false; +} +*/ + +/*###### +## npc_mosswalker_victim +######*/ + +enum +{ + QUEST_MOSSWALKER_SAVIOR = 12580, + SPELL_DEAD_SOLDIER = 45801, // not clear what this does, but looks like all have it + SPELL_MOSSWALKER_QUEST_CREDIT = 52157, + + GOSSIP_ITEM_PULSE = -3000104, + TEXT_ID_INJURED = 13318, + + EMOTE_PAIN = -1000610, + + SAY_RESCUE_1 = -1000611, + SAY_RESCUE_2 = -1000612, + SAY_RESCUE_3 = -1000613, + SAY_RESCUE_4 = -1000614, + + SAY_DIE_1 = -1000615, + SAY_DIE_2 = -1000616, + SAY_DIE_3 = -1000617, + SAY_DIE_4 = -1000618, + SAY_DIE_5 = -1000619, + SAY_DIE_6 = -1000620, +}; + +bool GossipHello_npc_mosswalker_victim(Player* pPlayer, Creature* pCreature) +{ + if (pPlayer->GetQuestStatus(QUEST_MOSSWALKER_SAVIOR) == QUEST_STATUS_INCOMPLETE) + { + // doesn't appear they always emote + if (urand(0, 3) == 0) + DoScriptText(EMOTE_PAIN, pCreature); + + pPlayer->ADD_GOSSIP_ITEM_ID(GOSSIP_ICON_CHAT, GOSSIP_ITEM_PULSE, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF); + } + + pPlayer->SEND_GOSSIP_MENU(TEXT_ID_INJURED, pCreature->GetObjectGuid()); + return true; +} + +bool GossipSelect_npc_mosswalker_victim(Player* pPlayer, Creature* pCreature, uint32 /*uiSender*/, uint32 uiAction) +{ + if (uiAction == GOSSIP_ACTION_INFO_DEF) + { + pPlayer->CLOSE_GOSSIP_MENU(); + + // just to prevent double credit + if (pCreature->GetLootRecipient()) + return true; + else + pCreature->SetLootRecipient(pPlayer); + + if (urand(0, 2)) // die + { + switch (urand(0, 5)) + { + case 0: DoScriptText(SAY_DIE_1, pCreature, pPlayer); break; + case 1: DoScriptText(SAY_DIE_2, pCreature, pPlayer); break; + case 2: DoScriptText(SAY_DIE_3, pCreature, pPlayer); break; + case 3: DoScriptText(SAY_DIE_4, pCreature, pPlayer); break; + case 4: DoScriptText(SAY_DIE_5, pCreature, pPlayer); break; + case 5: DoScriptText(SAY_DIE_6, pCreature, pPlayer); break; + } + } + else // survive + { + switch (urand(0, 3)) + { + case 0: DoScriptText(SAY_RESCUE_1, pCreature, pPlayer); break; + case 1: DoScriptText(SAY_RESCUE_2, pCreature, pPlayer); break; + case 2: DoScriptText(SAY_RESCUE_3, pCreature, pPlayer); break; + case 3: DoScriptText(SAY_RESCUE_4, pCreature, pPlayer); break; + } + + pCreature->CastSpell(pPlayer, SPELL_MOSSWALKER_QUEST_CREDIT, true); + } + + // more details may apply, instead of just despawn + pCreature->ForcedDespawn(5000); + } + return true; +} + +void AddSC_sholazar_basin() +{ + Script* pNewScript; + + pNewScript = new Script; + pNewScript->Name = "npc_helice"; + pNewScript->GetAI = &GetAI_npc_helice; + pNewScript->pQuestAcceptNPC = &QuestAccept_npc_helice; + pNewScript->RegisterSelf(); + + pNewScript = new Script; + pNewScript->Name = "npc_injured_rainspeaker"; + pNewScript->GetAI = &GetAI_npc_injured_rainspeaker; + pNewScript->pQuestAcceptNPC = &QuestAccept_npc_injured_rainspeaker; + // pNewScript->pGossipHello = &GossipHello_npc_injured_rainspeaker; + // pNewScript->pGossipSelect = &GossipSelect_npc_injured_rainspeaker; + pNewScript->RegisterSelf(); + + pNewScript = new Script; + pNewScript->Name = "npc_mosswalker_victim"; + pNewScript->pGossipHello = &GossipHello_npc_mosswalker_victim; + pNewScript->pGossipSelect = &GossipSelect_npc_mosswalker_victim; + pNewScript->RegisterSelf(); +} diff --git a/src/modules/SD2/scripts/northrend/storm_peaks.cpp b/src/modules/SD2/scripts/northrend/storm_peaks.cpp new file mode 100644 index 000000000..df23ed934 --- /dev/null +++ b/src/modules/SD2/scripts/northrend/storm_peaks.cpp @@ -0,0 +1,31 @@ +/* Copyright (C) 2006 - 2013 ScriptDev2 + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +/* ScriptData +SDName: Storm_Peaks +SD%Complete: 0 +SDComment: Placeholder +SDCategory: Storm Peaks +EndScriptData */ + +/* ContentData +EndContentData */ + +#include "precompiled.h" + +void AddSC_storm_peaks() +{ +} diff --git a/src/modules/SD2/scripts/northrend/ulduar/halls_of_lightning/boss_bjarngrim.cpp b/src/modules/SD2/scripts/northrend/ulduar/halls_of_lightning/boss_bjarngrim.cpp new file mode 100644 index 000000000..17566a597 --- /dev/null +++ b/src/modules/SD2/scripts/northrend/ulduar/halls_of_lightning/boss_bjarngrim.cpp @@ -0,0 +1,393 @@ +/* Copyright (C) 2006 - 2013 ScriptDev2 + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with 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: 90% +SDComment: Waypoint needed, we expect boss to always have 2x Stormforged Lieutenant following +SDCategory: Halls of Lightning +EndScriptData */ + +#include "precompiled.h" +#include "halls_of_lightning.h" + +enum +{ + // Yell + SAY_AGGRO = -1602000, + SAY_SLAY_1 = -1602001, + SAY_SLAY_2 = -1602002, + SAY_SLAY_3 = -1602003, + SAY_DEATH = -1602004, + SAY_BATTLE_STANCE = -1602005, + EMOTE_BATTLE_STANCE = -1602006, + SAY_BERSEKER_STANCE = -1602007, + EMOTE_BERSEKER_STANCE = -1602008, + SAY_DEFENSIVE_STANCE = -1602009, + EMOTE_DEFENSIVE_STANCE = -1602010, + + SPELL_DEFENSIVE_STANCE = 53790, + SPELL_SPELL_REFLECTION = 36096, + SPELL_PUMMEL = 12555, + SPELL_KNOCK_AWAY = 52029, + SPELL_IRONFORM = 52022, + + SPELL_BERSEKER_STANCE = 53791, + SPELL_INTERCEPT = 58769, + SPELL_WHIRLWIND = 52027, + SPELL_CLEAVE = 15284, + + SPELL_BATTLE_STANCE = 53792, + SPELL_MORTAL_STRIKE = 16856, + SPELL_SLAM = 52026, + + // Other spells - handled by DB wp movement script + // 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, + + STANCE_DEFENSIVE = 0, + STANCE_BERSERKER = 1, + STANCE_BATTLE = 2 +}; + +/*###### +## boss_bjarngrim +######*/ + +struct boss_bjarngrimAI : public ScriptedAI +{ + boss_bjarngrimAI(Creature* pCreature) : ScriptedAI(pCreature) + { + m_pInstance = (ScriptedInstance*)pCreature->GetInstanceData(); + m_bIsRegularMode = pCreature->GetMap()->IsRegularDifficulty(); + m_uiStance = STANCE_DEFENSIVE; + Reset(); + } + + ScriptedInstance* m_pInstance; + + bool m_bIsRegularMode; + bool m_bIsChangingStance; + + uint8 m_uiChargingStatus; + uint8 m_uiStance; + + uint32 m_uiChargeTimer; + uint32 m_uiChangeStanceTimer; + + uint32 m_uiReflectionTimer; + uint32 m_uiKnockAwayTimer; + uint32 m_uiPummelTimer; + uint32 m_uiIronformTimer; + + uint32 m_uiInterceptTimer; + uint32 m_uiWhirlwindTimer; + uint32 m_uiCleaveTimer; + + uint32 m_uiMortalStrikeTimer; + uint32 m_uiSlamTimer; + + void Reset() override + { + m_bIsChangingStance = false; + + m_uiChargingStatus = 0; + m_uiChargeTimer = 1000; + + m_uiChangeStanceTimer = urand(20000, 25000); + + m_uiReflectionTimer = 8000; + m_uiKnockAwayTimer = 20000; + m_uiPummelTimer = 10000; + m_uiIronformTimer = 25000; + + m_uiInterceptTimer = 5000; + m_uiWhirlwindTimer = 10000; + m_uiCleaveTimer = 8000; + + m_uiMortalStrikeTimer = 8000; + m_uiSlamTimer = 10000; + + if (m_uiStance != STANCE_DEFENSIVE) + { + DoCastSpellIfCan(m_creature, SPELL_DEFENSIVE_STANCE); + m_uiStance = STANCE_DEFENSIVE; + } + } + + void Aggro(Unit* /*pWho*/) override + { + DoScriptText(SAY_AGGRO, m_creature); + + if (m_pInstance) + { + // Set the achiev in progress + if (m_creature->HasAura(SPELL_TEMPORARY_ELECTRICAL_CHARGE)) + m_pInstance->SetData(TYPE_BJARNGRIM, SPECIAL); + + m_pInstance->SetData(TYPE_BJARNGRIM, IN_PROGRESS); + } + } + + void KilledUnit(Unit* /*pVictim*/) override + { + switch (urand(0, 2)) + { + case 0: DoScriptText(SAY_SLAY_1, m_creature); break; + case 1: DoScriptText(SAY_SLAY_2, m_creature); break; + case 2: DoScriptText(SAY_SLAY_3, m_creature); break; + } + } + + void JustDied(Unit* /*pKiller*/) override + { + DoScriptText(SAY_DEATH, m_creature); + + if (m_pInstance) + m_pInstance->SetData(TYPE_BJARNGRIM, DONE); + } + + void JustReachedHome() override + { + if (m_pInstance) + m_pInstance->SetData(TYPE_BJARNGRIM, FAIL); + } + + void UpdateAI(const uint32 uiDiff) override + { + // Return since we have no target + if (!m_creature->SelectHostileTarget() || !m_creature->getVictim()) + return; + + // Change stance + if (m_uiChangeStanceTimer < uiDiff) + { + // wait for current spell to finish before change stance + if (m_creature->IsNonMeleeSpellCasted(false)) + return; + + int uiTempStance = rand() % (3 - 1); + + if (uiTempStance >= m_uiStance) + ++uiTempStance; + + m_uiStance = uiTempStance; + + switch (m_uiStance) + { + case STANCE_DEFENSIVE: + DoScriptText(SAY_DEFENSIVE_STANCE, m_creature); + DoScriptText(EMOTE_DEFENSIVE_STANCE, m_creature); + DoCastSpellIfCan(m_creature, SPELL_DEFENSIVE_STANCE); + break; + case STANCE_BERSERKER: + DoScriptText(SAY_BERSEKER_STANCE, m_creature); + DoScriptText(EMOTE_BERSEKER_STANCE, m_creature); + DoCastSpellIfCan(m_creature, SPELL_BERSEKER_STANCE); + break; + case STANCE_BATTLE: + DoScriptText(SAY_BATTLE_STANCE, m_creature); + DoScriptText(EMOTE_BATTLE_STANCE, m_creature); + DoCastSpellIfCan(m_creature, SPELL_BATTLE_STANCE); + break; + } + + m_uiChangeStanceTimer = urand(20000, 25000); + return; + } + else + m_uiChangeStanceTimer -= uiDiff; + + switch (m_uiStance) + { + case STANCE_DEFENSIVE: + { + if (m_uiReflectionTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature, SPELL_SPELL_REFLECTION) == CAST_OK) + m_uiReflectionTimer = urand(8000, 9000); + } + else + m_uiReflectionTimer -= uiDiff; + + if (m_uiKnockAwayTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature, SPELL_KNOCK_AWAY) == CAST_OK) + m_uiKnockAwayTimer = urand(20000, 21000); + } + else + m_uiKnockAwayTimer -= uiDiff; + + if (m_uiPummelTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature->getVictim(), SPELL_PUMMEL) == CAST_OK) + m_uiPummelTimer = urand(10000, 11000); + } + else + m_uiPummelTimer -= uiDiff; + + if (m_uiIronformTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature, SPELL_IRONFORM) == CAST_OK) + m_uiIronformTimer = urand(25000, 26000); + } + else + m_uiIronformTimer -= uiDiff; + + break; + } + case STANCE_BERSERKER: + { + if (m_uiInterceptTimer < uiDiff) + { + // not much point is this, better random target and more often? + if (DoCastSpellIfCan(m_creature->getVictim(), SPELL_INTERCEPT) == CAST_OK) + m_uiInterceptTimer = urand(45000, 46000); + } + else + m_uiInterceptTimer -= uiDiff; + + if (m_uiWhirlwindTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature, SPELL_WHIRLWIND) == CAST_OK) + m_uiWhirlwindTimer = urand(10000, 11000); + } + else + m_uiWhirlwindTimer -= uiDiff; + + if (m_uiCleaveTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature->getVictim(), SPELL_CLEAVE) == CAST_OK) + m_uiCleaveTimer = urand(8000, 9000); + } + else + m_uiCleaveTimer -= uiDiff; + + break; + } + case STANCE_BATTLE: + { + if (m_uiMortalStrikeTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature->getVictim(), SPELL_MORTAL_STRIKE) == CAST_OK) + m_uiMortalStrikeTimer = urand(20000, 21000); + } + else + m_uiMortalStrikeTimer -= uiDiff; + + if (m_uiSlamTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature->getVictim(), SPELL_SLAM) == CAST_OK) + m_uiSlamTimer = urand(15000, 16000); + } + else + m_uiSlamTimer -= uiDiff; + + break; + } + } + + DoMeleeAttackIfReady(); + } +}; + +/*###### +## mob_stormforged_lieutenant +######*/ + +struct mob_stormforged_lieutenantAI : public ScriptedAI +{ + mob_stormforged_lieutenantAI(Creature* pCreature) : ScriptedAI(pCreature) + { + m_pInstance = (ScriptedInstance*)pCreature->GetInstanceData(); + m_bIsRegularMode = pCreature->GetMap()->IsRegularDifficulty(); + Reset(); + } + + ScriptedInstance* m_pInstance; + bool m_bIsRegularMode; + + uint32 m_uiArcWeldTimer; + uint32 m_uiRenewSteelTimer; + + void Reset() override + { + m_uiArcWeldTimer = urand(20000, 21000); + m_uiRenewSteelTimer = urand(10000, 11000); + } + + void UpdateAI(const uint32 uiDiff) override + { + // Return since we have no target + if (!m_creature->SelectHostileTarget() || !m_creature->getVictim()) + return; + + if (m_uiArcWeldTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature->getVictim(), SPELL_ARC_WELD) == CAST_OK) + m_uiArcWeldTimer = urand(20000, 21000); + } + else + m_uiArcWeldTimer -= uiDiff; + + if (m_uiRenewSteelTimer < uiDiff) + { + if (m_pInstance) + { + if (Creature* pBjarngrim = m_pInstance->GetSingleCreatureFromStorage(NPC_BJARNGRIM)) + { + if (pBjarngrim->IsAlive()) + DoCastSpellIfCan(pBjarngrim, m_bIsRegularMode ? SPELL_RENEW_STEEL_N : SPELL_RENEW_STEEL_H); + } + } + m_uiRenewSteelTimer = urand(10000, 14000); + } + else + m_uiRenewSteelTimer -= 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* pNewScript; + + pNewScript = new Script; + pNewScript->Name = "boss_bjarngrim"; + pNewScript->GetAI = &GetAI_boss_bjarngrim; + pNewScript->RegisterSelf(); + + pNewScript = new Script; + pNewScript->Name = "mob_stormforged_lieutenant"; + pNewScript->GetAI = &GetAI_mob_stormforged_lieutenant; + pNewScript->RegisterSelf(); +} diff --git a/src/modules/SD2/scripts/northrend/ulduar/halls_of_lightning/boss_ionar.cpp b/src/modules/SD2/scripts/northrend/ulduar/halls_of_lightning/boss_ionar.cpp new file mode 100644 index 000000000..4a51df712 --- /dev/null +++ b/src/modules/SD2/scripts/northrend/ulduar/halls_of_lightning/boss_ionar.cpp @@ -0,0 +1,388 @@ +/* Copyright (C) 2006 - 2013 ScriptDev2 + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with 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 "halls_of_lightning.h" + +enum +{ + SAY_AGGRO = -1602011, + SAY_SLAY_1 = -1602012, + SAY_SLAY_2 = -1602013, + SAY_SLAY_3 = -1602014, + 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 boss_ionarAI : public ScriptedAI +{ + boss_ionarAI(Creature* pCreature) : ScriptedAI(pCreature) + { + m_pInstance = (ScriptedInstance*)pCreature->GetInstanceData(); + m_bIsRegularMode = pCreature->GetMap()->IsRegularDifficulty(); + Reset(); + } + + ScriptedInstance* m_pInstance; + + GuidList m_lSparkGUIDList; + + bool m_bIsRegularMode; + + bool m_bIsDesperseCasting; + bool m_bIsSplitPhase; + uint32 m_uiSplitTimer; + uint32 m_uiSparkAtHomeCount; + + uint32 m_uiStaticOverloadTimer; + uint32 m_uiBallLightningTimer; + + uint32 m_uiHealthAmountModifier; + + void Reset() override + { + m_bIsSplitPhase = true; + m_bIsDesperseCasting = false; + m_uiSplitTimer = 25000; + m_uiSparkAtHomeCount = 0; + + m_uiStaticOverloadTimer = urand(5000, 6000); + m_uiBallLightningTimer = urand(10000, 11000); + + m_uiHealthAmountModifier = 1; + + if (m_creature->GetVisibility() == VISIBILITY_OFF) + m_creature->SetVisibility(VISIBILITY_ON); + } + + void AttackedBy(Unit* pAttacker) override + { + if (m_creature->getVictim()) + return; + + if (m_creature->GetVisibility() == VISIBILITY_OFF) + return; + + AttackStart(pAttacker); + } + + void Aggro(Unit* /*who*/) override + { + DoScriptText(SAY_AGGRO, m_creature); + + if (m_pInstance) + m_pInstance->SetData(TYPE_IONAR, IN_PROGRESS); + } + + void JustReachedHome() override + { + if (m_pInstance) + m_pInstance->SetData(TYPE_IONAR, FAIL); + + DespawnSpark(); + } + + void AttackStart(Unit* pWho) override + { + if (m_creature->Attack(pWho, true)) + { + m_creature->AddThreat(pWho); + m_creature->SetInCombatWith(pWho); + pWho->SetInCombatWith(m_creature); + + if (m_creature->GetVisibility() != VISIBILITY_OFF) + m_creature->GetMotionMaster()->MoveChase(pWho); + } + } + + void JustDied(Unit* /*killer*/) override + { + DoScriptText(SAY_DEATH, m_creature); + DespawnSpark(); + + if (m_pInstance) + m_pInstance->SetData(TYPE_IONAR, DONE); + } + + void KilledUnit(Unit* /*victim*/) override + { + switch (urand(0, 2)) + { + case 0: DoScriptText(SAY_SLAY_1, m_creature); break; + case 1: DoScriptText(SAY_SLAY_2, m_creature); break; + case 2: DoScriptText(SAY_SLAY_3, m_creature); break; + } + } + + void DespawnSpark() + { + for (GuidList::const_iterator itr = m_lSparkGUIDList.begin(); itr != m_lSparkGUIDList.end(); ++itr) + { + if (Creature* pTemp = m_creature->GetMap()->GetCreature(*itr)) + { + if (pTemp->IsAlive()) + pTemp->ForcedDespawn(); + } + } + + m_lSparkGUIDList.clear(); + } + + // make sparks come back + void CallBackSparks() + { + for (GuidList::const_iterator itr = m_lSparkGUIDList.begin(); itr != m_lSparkGUIDList.end(); ++itr) + { + if (Creature* pSpark = m_creature->GetMap()->GetCreature(*itr)) + { + if (pSpark->IsAlive()) + { + // Required to prevent combat movement, elsewise they might switch movement on aggro-change + if (ScriptedAI* pSparkAI = dynamic_cast(pSpark->AI())) + pSparkAI->SetCombatMovement(false); + + pSpark->GetMotionMaster()->MovePoint(POINT_CALLBACK, m_creature->GetPositionX(), m_creature->GetPositionY(), m_creature->GetPositionZ()); + } + } + } + } + + void RegisterSparkAtHome() + { + ++m_uiSparkAtHomeCount; + } + + void JustSummoned(Creature* pSummoned) override + { + if (pSummoned->GetEntry() == NPC_SPARK_OF_IONAR) + { + pSummoned->CastSpell(pSummoned, m_bIsRegularMode ? SPELL_SPARK_VISUAL_TRIGGER_N : SPELL_SPARK_VISUAL_TRIGGER_H, true); + + if (Unit* pTarget = m_creature->SelectAttackingTarget(ATTACKING_TARGET_RANDOM, 0)) + pSummoned->AI()->AttackStart(pTarget); + + m_lSparkGUIDList.push_back(pSummoned->GetObjectGuid()); + } + } + + void UpdateAI(const uint32 uiDiff) override + { + if (!m_creature->SelectHostileTarget() || !m_creature->getVictim()) + return; + + // Splitted + if (m_creature->GetVisibility() == VISIBILITY_OFF) + { + if (m_uiSplitTimer < uiDiff) + { + m_uiSplitTimer = 2500; + + // Return sparks to where Ionar splitted + if (m_bIsSplitPhase) + { + CallBackSparks(); + m_bIsSplitPhase = false; + } + // Lightning effect and restore Ionar + else if (m_uiSparkAtHomeCount == MAX_SPARKS) + { + m_creature->SetVisibility(VISIBILITY_ON); + DoCastSpellIfCan(m_creature, SPELL_SPARK_DESPAWN); + + m_uiSparkAtHomeCount = 0; + m_uiSplitTimer = 25000; + m_bIsSplitPhase = true; + m_bIsDesperseCasting = false; + + if (m_creature->GetMotionMaster()->GetCurrentMovementGeneratorType() != CHASE_MOTION_TYPE) + { + if (m_creature->getVictim()) + m_creature->GetMotionMaster()->MoveChase(m_creature->getVictim()); + } + } + } + else + m_uiSplitTimer -= uiDiff; + + return; + } + + if (m_uiStaticOverloadTimer < uiDiff) + { + if (Unit* pTarget = m_creature->SelectAttackingTarget(ATTACKING_TARGET_RANDOM, 0)) + { + if (DoCastSpellIfCan(pTarget, m_bIsRegularMode ? SPELL_STATIC_OVERLOAD_N : SPELL_STATIC_OVERLOAD_H) == CAST_OK) + m_uiStaticOverloadTimer = urand(5000, 6000); + } + } + else + m_uiStaticOverloadTimer -= uiDiff; + + if (m_uiBallLightningTimer < uiDiff) + { + if (Unit* pTarget = m_creature->SelectAttackingTarget(ATTACKING_TARGET_RANDOM, 0)) + { + if (DoCastSpellIfCan(pTarget, m_bIsRegularMode ? SPELL_BALL_LIGHTNING_N : SPELL_BALL_LIGHTNING_H) == CAST_OK) + m_uiBallLightningTimer = urand(10000, 11000); + } + } + else + m_uiBallLightningTimer -= uiDiff; + + // Health check + if (m_creature->GetHealthPercent() < float(100 - 20 * m_uiHealthAmountModifier)) + { + ++m_uiHealthAmountModifier; + + if (!m_bIsDesperseCasting && DoCastSpellIfCan(m_creature, SPELL_DISPERSE, CAST_INTERRUPT_PREVIOUS) == CAST_OK) + { + DoScriptText(urand(0, 1) ? SAY_SPLIT_1 : SAY_SPLIT_2, m_creature); + m_bIsDesperseCasting = true; + } + } + + DoMeleeAttackIfReady(); + } +}; + +CreatureAI* GetAI_boss_ionar(Creature* pCreature) +{ + return new boss_ionarAI(pCreature); +} + +bool EffectDummyCreature_boss_ionar(Unit* /*pCaster*/, uint32 uiSpellId, SpellEffectIndex uiEffIndex, Creature* pCreatureTarget, ObjectGuid /*originalCasterGuid*/) +{ + // always check spellid and effectindex + if (uiSpellId == SPELL_DISPERSE && uiEffIndex == EFFECT_INDEX_0) + { + 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() == CHASE_MOTION_TYPE) + pCreatureTarget->GetMotionMaster()->MovementExpired(); + + // always return true when we are handling this spell and effect + return true; + } + else if (uiSpellId == SPELL_SPARK_DESPAWN && uiEffIndex == EFFECT_INDEX_0) + { + if (pCreatureTarget->GetEntry() != NPC_IONAR) + return true; + + if (boss_ionarAI* pIonarAI = dynamic_cast(pCreatureTarget->AI())) + pIonarAI->DespawnSpark(); + + return true; + } + return false; +} + +/*###### +## mob_spark_of_ionar +######*/ + +struct mob_spark_of_ionarAI : public ScriptedAI +{ + mob_spark_of_ionarAI(Creature* pCreature) : ScriptedAI(pCreature) + { + m_pInstance = (ScriptedInstance*)pCreature->GetInstanceData(); + Reset(); + } + + ScriptedInstance* m_pInstance; + + void Reset() override { } + + void MovementInform(uint32 uiType, uint32 uiPointId) override + { + if (uiType != POINT_MOTION_TYPE || !m_pInstance) + return; + + if (uiPointId == POINT_CALLBACK) + { + if (Creature* pIonar = m_pInstance->GetSingleCreatureFromStorage(NPC_IONAR)) + { + if (!pIonar->IsAlive()) + { + m_creature->ForcedDespawn(); + return; + } + + if (boss_ionarAI* pIonarAI = dynamic_cast(pIonar->AI())) + pIonarAI->RegisterSparkAtHome(); + } + else + m_creature->ForcedDespawn(); + } + } +}; + +CreatureAI* GetAI_mob_spark_of_ionar(Creature* pCreature) +{ + return new mob_spark_of_ionarAI(pCreature); +} + +void AddSC_boss_ionar() +{ + Script* pNewScript; + + pNewScript = new Script; + pNewScript->Name = "boss_ionar"; + pNewScript->GetAI = &GetAI_boss_ionar; + pNewScript->pEffectDummyNPC = &EffectDummyCreature_boss_ionar; + pNewScript->RegisterSelf(); + + pNewScript = new Script; + pNewScript->Name = "mob_spark_of_ionar"; + pNewScript->GetAI = &GetAI_mob_spark_of_ionar; + pNewScript->RegisterSelf(); +} diff --git a/src/modules/SD2/scripts/northrend/ulduar/halls_of_lightning/boss_loken.cpp b/src/modules/SD2/scripts/northrend/ulduar/halls_of_lightning/boss_loken.cpp new file mode 100644 index 000000000..02d01a0b5 --- /dev/null +++ b/src/modules/SD2/scripts/northrend/ulduar/halls_of_lightning/boss_loken.cpp @@ -0,0 +1,182 @@ +/* Copyright (C) 2006 - 2013 ScriptDev2 + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with 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: 80% +SDComment: Missing intro. +SDCategory: Halls of Lightning +EndScriptData */ + +#include "precompiled.h" +#include "halls_of_lightning.h" + +enum +{ + SAY_AGGRO = -1602018, + SAY_INTRO_1 = -1602019, + SAY_INTRO_2 = -1602020, + SAY_SLAY_1 = -1602021, + SAY_SLAY_2 = -1602022, + SAY_SLAY_3 = -1602023, + SAY_DEATH = -1602024, + SAY_NOVA_1 = -1602025, + SAY_NOVA_2 = -1602026, + SAY_NOVA_3 = -1602027, + SAY_75HEALTH = -1602028, + SAY_50HEALTH = -1602029, + SAY_25HEALTH = -1602030, + EMOTE_NOVA = -1602031, + + SPELL_ARC_LIGHTNING = 52921, + SPELL_LIGHTNING_NOVA = 52960, + SPELL_LIGHTNING_NOVA_H = 59835, + + SPELL_PULSING_SHOCKWAVE = 52961, + SPELL_PULSING_SHOCKWAVE_H = 59836, + SPELL_PULSING_SHOCKWAVE_AURA = 59414 +}; + +/*###### +## Boss Loken +######*/ + +struct boss_lokenAI : public ScriptedAI +{ + boss_lokenAI(Creature* pCreature) : ScriptedAI(pCreature) + { + m_pInstance = (ScriptedInstance*)pCreature->GetInstanceData(); + m_bIsRegularMode = pCreature->GetMap()->IsRegularDifficulty(); + Reset(); + } + + ScriptedInstance* m_pInstance; + + bool m_bIsRegularMode; + + uint32 m_uiArcLightningTimer; + uint32 m_uiLightningNovaTimer; + + uint32 m_uiHealthAmountModifier; + + void Reset() override + { + m_uiArcLightningTimer = 15000; + m_uiLightningNovaTimer = 20000; + + m_uiHealthAmountModifier = 1; + } + + void Aggro(Unit* /*pWho*/) override + { + DoScriptText(SAY_AGGRO, m_creature); + + if (m_pInstance) + m_pInstance->SetData(TYPE_LOKEN, IN_PROGRESS); + + // Cast Pulsing Shockwave at aggro - ToDo: enable this when the core will properly support this spell + // DoCastSpellIfCan(m_creature, SPELL_PULSING_SHOCKWAVE_AURA, CAST_TRIGGERED | CAST_AURA_NOT_PRESENT); + // DoCastSpellIfCan(m_creature, m_bIsRegularMode ? SPELL_PULSING_SHOCKWAVE : SPELL_PULSING_SHOCKWAVE_H, CAST_TRIGGERED | CAST_AURA_NOT_PRESENT); + } + + void JustDied(Unit* /*pKiller*/) override + { + DoScriptText(SAY_DEATH, m_creature); + + if (m_pInstance) + m_pInstance->SetData(TYPE_LOKEN, DONE); + } + + void KilledUnit(Unit* /*pVictim*/) override + { + switch (urand(0, 2)) + { + case 0: DoScriptText(SAY_SLAY_1, m_creature); break; + case 1: DoScriptText(SAY_SLAY_2, m_creature); break; + case 2: DoScriptText(SAY_SLAY_3, m_creature); break; + } + } + + void JustReachedHome() override + { + if (m_pInstance) + m_pInstance->SetData(TYPE_LOKEN, FAIL); + } + + void UpdateAI(const uint32 uiDiff) override + { + // Return since we have no target + if (!m_creature->SelectHostileTarget() || !m_creature->getVictim()) + return; + + if (m_uiArcLightningTimer < uiDiff) + { + if (Unit* pTarget = m_creature->SelectAttackingTarget(ATTACKING_TARGET_RANDOM, 0)) + { + if (DoCastSpellIfCan(pTarget, SPELL_ARC_LIGHTNING) == CAST_OK) + m_uiArcLightningTimer = urand(15000, 16000); + } + } + else + m_uiArcLightningTimer -= uiDiff; + + if (m_uiLightningNovaTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature, m_bIsRegularMode ? SPELL_LIGHTNING_NOVA : SPELL_LIGHTNING_NOVA_H) == CAST_OK) + { + switch (urand(0, 2)) + { + case 0: DoScriptText(SAY_NOVA_1, m_creature); break; + case 1: DoScriptText(SAY_NOVA_2, m_creature); break; + case 2: DoScriptText(SAY_NOVA_3, m_creature); break; + } + m_uiLightningNovaTimer = urand(20000, 21000); + } + } + else + m_uiLightningNovaTimer -= uiDiff; + + // Health check + if (m_creature->GetHealthPercent() < float(100 - 25 * m_uiHealthAmountModifier)) + { + switch (m_uiHealthAmountModifier) + { + case 1: DoScriptText(SAY_75HEALTH, m_creature); break; + 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* pNewScript; + + pNewScript = new Script; + pNewScript->Name = "boss_loken"; + pNewScript->GetAI = &GetAI_boss_loken; + pNewScript->RegisterSelf(); +} diff --git a/src/modules/SD2/scripts/northrend/ulduar/halls_of_lightning/boss_volkhan.cpp b/src/modules/SD2/scripts/northrend/ulduar/halls_of_lightning/boss_volkhan.cpp new file mode 100644 index 000000000..7b93d28d6 --- /dev/null +++ b/src/modules/SD2/scripts/northrend/ulduar/halls_of_lightning/boss_volkhan.cpp @@ -0,0 +1,433 @@ +/* Copyright (C) 2006 - 2013 ScriptDev2 + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with 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: 80% +SDComment: The dummy spells need more research and should be handled in core +SDCategory: Halls of Lightning +EndScriptData */ + +#include "precompiled.h" +#include "halls_of_lightning.h" + +enum +{ + SAY_AGGRO = -1602032, + SAY_SLAY_1 = -1602033, + SAY_SLAY_2 = -1602034, + SAY_SLAY_3 = -1602035, + SAY_DEATH = -1602036, + SAY_STOMP_1 = -1602037, + SAY_STOMP_2 = -1602038, + SAY_FORGE_1 = -1602039, + SAY_FORGE_2 = -1602040, + EMOTE_TO_ANVIL = -1602041, + EMOTE_SHATTER = -1602042, + + SPELL_HEAT = 52387, + SPELL_HEAT_H = 59528, + SPELL_SHATTERING_STOMP = 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 = 52433, + SPELL_IMMOLATION_STRIKE_H = 59530, + SPELL_SHATTER = 52429, + SPELL_SHATTER_H = 59527, + + NPC_MOLTEN_GOLEM = 28695, + NPC_BRITTLE_GOLEM = 28681, + + MAX_GOLEM = 2, + MAX_ACHIEV_GOLEMS = 4 +}; + +/*###### +## Boss Volkhan +######*/ + +struct boss_volkhanAI : public ScriptedAI +{ + boss_volkhanAI(Creature* pCreature) : ScriptedAI(pCreature) + { + m_pInstance = (ScriptedInstance*)pCreature->GetInstanceData(); + m_bIsRegularMode = pCreature->GetMap()->IsRegularDifficulty(); + Reset(); + } + + ScriptedInstance* m_pInstance; + + GuidList m_lGolemGUIDList; + + bool m_bIsRegularMode; + bool m_bHasShattered; + + uint32 m_uiShatterTimer; + uint32 m_uiHeatTimer; + uint32 m_uiTemperTimer; + + void Reset() override + { + m_bHasShattered = false; + + m_uiShatterTimer = 3000; + m_uiHeatTimer = 30000; + m_uiTemperTimer = 10000; + } + + void Aggro(Unit* /*pWho*/) override + { + DoScriptText(SAY_AGGRO, m_creature); + + if (m_pInstance) + m_pInstance->SetData(TYPE_VOLKHAN, IN_PROGRESS); + } + + void JustDied(Unit* /*pKiller*/) override + { + DoScriptText(SAY_DEATH, m_creature); + DespawnGolems(); + + if (m_pInstance) + m_pInstance->SetData(TYPE_VOLKHAN, DONE); + } + + void JustReachedHome() override + { + DespawnGolems(); + + if (m_pInstance) + m_pInstance->SetData(TYPE_VOLKHAN, FAIL); + } + + void KilledUnit(Unit* /*pVictim*/) override + { + switch (urand(0, 2)) + { + case 0: DoScriptText(SAY_SLAY_1, m_creature); break; + case 1: DoScriptText(SAY_SLAY_2, m_creature); break; + case 2: DoScriptText(SAY_SLAY_3, m_creature); break; + } + } + + void DespawnGolems() + { + if (m_lGolemGUIDList.empty()) + return; + + for (GuidList::const_iterator itr = m_lGolemGUIDList.begin(); itr != m_lGolemGUIDList.end(); ++itr) + { + if (Creature* pTemp = m_creature->GetMap()->GetCreature(*itr)) + { + if (pTemp->IsAlive()) + pTemp->ForcedDespawn(); + } + } + } + + void ShatterGolems() + { + if (m_lGolemGUIDList.empty()) + return; + + uint8 m_uiBrittleGolemsCount = 0; + + for (GuidList::const_iterator itr = m_lGolemGUIDList.begin(); itr != m_lGolemGUIDList.end(); ++itr) + { + if (Creature* pTemp = m_creature->GetMap()->GetCreature(*itr)) + { + // only shatter brittle golems + if (pTemp->GetEntry() == NPC_BRITTLE_GOLEM) + { + pTemp->CastSpell(pTemp, m_bIsRegularMode ? SPELL_SHATTER : SPELL_SHATTER_H, true); + ++m_uiBrittleGolemsCount; + } + } + } + + // If shattered more than 4 golems mark achiev as failed + if (m_uiBrittleGolemsCount > MAX_ACHIEV_GOLEMS) + { + if (m_pInstance) + m_pInstance->SetData(TYPE_VOLKHAN, SPECIAL); + } + } + + void JustSummoned(Creature* pSummoned) override + { + if (pSummoned->GetEntry() == NPC_MOLTEN_GOLEM) + { + m_lGolemGUIDList.push_back(pSummoned->GetObjectGuid()); + + if (Unit* pTarget = m_creature->SelectAttackingTarget(ATTACKING_TARGET_RANDOM, 0)) + pSummoned->AI()->AttackStart(pTarget); + } + } + + void MovementInform(uint32 uiMoveType, uint32 uiPointId) override + { + if (uiMoveType != POINT_MOTION_TYPE || !uiPointId) + return; + + DoCastSpellIfCan(m_creature, SPELL_TEMPER); + SetCombatMovement(true); + } + + void UpdateAI(const uint32 uiDiff) override + { + // Return since we have no target + if (!m_creature->SelectHostileTarget() || !m_creature->getVictim()) + return; + + // he shatters only one time, at 25% + if (m_creature->GetHealthPercent() <= 25.0f && !m_bHasShattered) + { + // should he stomp even if he has no brittle golem to shatter? <-yes! + if (DoCastSpellIfCan(m_creature, m_bIsRegularMode ? SPELL_SHATTERING_STOMP : SPELL_SHATTERING_STOMP_H) == CAST_OK) + { + DoScriptText(urand(0, 1) ? SAY_STOMP_1 : SAY_STOMP_2, m_creature); + DoScriptText(EMOTE_SHATTER, m_creature); + m_uiShatterTimer = 3000; + m_bHasShattered = true; + } + } + + // Shatter Golems 3 seconds after Shattering Stomp + if (m_uiShatterTimer) + { + if (m_uiShatterTimer <= uiDiff) + { + ShatterGolems(); + m_uiShatterTimer = 0; + } + else + m_uiShatterTimer -= uiDiff; + } + + // Summon Golems only when over 25% hp + if (m_creature->GetHealthPercent() > 25.0f) + { + if (m_uiTemperTimer < uiDiff) + { + DoScriptText(EMOTE_TO_ANVIL, m_creature); + DoScriptText(urand(0, 1) ? SAY_FORGE_1 : SAY_FORGE_2, m_creature); + SetCombatMovement(false); + + if (m_pInstance) + { + if (Creature* pAnvil = m_pInstance->GetSingleCreatureFromStorage(NPC_VOLKHAN_ANVIL)) + { + float fX, fY, fZ; + pAnvil->GetContactPoint(m_creature, fX, fY, fZ, INTERACTION_DISTANCE); + m_creature->GetMotionMaster()->MovePoint(1, fX, fY, fZ); + } + else + script_error_log("Npc %u couldn't be found or something really bad happened.", NPC_VOLKHAN_ANVIL); + } + m_uiTemperTimer = 30000; + } + else + m_uiTemperTimer -= uiDiff; + } + + if (m_uiHeatTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature, m_bIsRegularMode ? SPELL_HEAT : SPELL_HEAT_H) == CAST_OK) + m_uiHeatTimer = urand(10000, 15000); + } + else + m_uiHeatTimer -= uiDiff; + + DoMeleeAttackIfReady(); + } +}; + +CreatureAI* GetAI_boss_volkhan(Creature* pCreature) +{ + return new boss_volkhanAI(pCreature); +} + +bool EffectDummyCreature_boss_volkhan(Unit* pCaster, uint32 uiSpellId, SpellEffectIndex uiEffIndex, Creature* pCreatureTarget, ObjectGuid /*originalCasterGuid*/) +{ + // always check spellid and effectindex + if (uiSpellId == SPELL_TEMPER_DUMMY && uiEffIndex == EFFECT_INDEX_0) + { + if (pCaster->GetEntry() != NPC_VOLKHAN_ANVIL || pCreatureTarget->GetEntry() != NPC_VOLKHAN) + return true; + + 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, SpellEffectIndex uiEffIndex, Creature* pCreatureTarget, ObjectGuid /*originalCasterGuid*/) +{ + // always check spellid and effectindex + if (uiSpellId == SPELL_TEMPER && uiEffIndex == EFFECT_INDEX_0) + { + if (pCaster->GetEntry() != NPC_VOLKHAN || pCreatureTarget->GetEntry() != NPC_VOLKHAN_ANVIL) + return true; + + pCreatureTarget->CastSpell(pCaster, SPELL_TEMPER_DUMMY, false); + // ToDo: research how the visual spell is used + + if (pCaster->getVictim()) + { + pCaster->GetMotionMaster()->Clear(); + pCaster->GetMotionMaster()->MoveChase(pCaster->getVictim()); + } + + // always return true when we are handling this spell and effect + return true; + } + + return false; +} + +/*###### +## mob_molten_golem +######*/ + +struct mob_molten_golemAI : public ScriptedAI +{ + mob_molten_golemAI(Creature* pCreature) : ScriptedAI(pCreature) + { + m_pInstance = (ScriptedInstance*)pCreature->GetInstanceData(); + m_bIsRegularMode = pCreature->GetMap()->IsRegularDifficulty(); + Reset(); + } + + ScriptedInstance* m_pInstance; + + bool m_bIsRegularMode; + + uint32 m_uiBlastTimer; + uint32 m_uiImmolationTimer; + + void Reset() override + { + m_uiBlastTimer = 20000; + m_uiImmolationTimer = 5000; + } + + void EnterEvadeMode() override + { + // Evade but keep the current location + m_creature->RemoveAllAurasOnEvade(); + m_creature->DeleteThreatList(); + m_creature->CombatStop(true); + m_creature->LoadCreatureAddon(true); + + m_creature->SetLootRecipient(NULL); + + // Update creature to Brittle Golem + // Note: the npc has the proper flags in DB and won't engate in combat anymore + m_creature->UpdateEntry(NPC_BRITTLE_GOLEM); + } + + void DamageTaken(Unit* /*pDoneBy*/, uint32& uiDamage) override + { + // Transform intro Brittle when damaged to 0 HP + if (uiDamage >= m_creature->GetHealth()) + { + uiDamage = 0; + + if (m_creature->IsNonMeleeSpellCasted(false)) + m_creature->InterruptNonMeleeSpells(false); + + EnterEvadeMode(); + } + } + + void SpellHit(Unit* /*pCaster*/, const SpellEntry* pSpell) override + { + // This is the dummy effect of the spells - Note: should be handled as a dummy effect in core + if (pSpell->Id == SPELL_SHATTER || pSpell->Id == SPELL_SHATTER_H) + { + if (m_creature->GetEntry() == NPC_BRITTLE_GOLEM) + m_creature->DealDamage(m_creature, m_creature->GetHealth(), NULL, DIRECT_DAMAGE, SPELL_SCHOOL_MASK_NORMAL, NULL, false); + } + } + + void UpdateAI(const uint32 uiDiff) override + { + // Return since we have no target or if we are frozen + if (!m_creature->SelectHostileTarget() || !m_creature->getVictim()) + return; + + if (m_uiBlastTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature, SPELL_BLAST_WAVE) == CAST_OK) + m_uiBlastTimer = 20000; + } + else + m_uiBlastTimer -= uiDiff; + + if (m_uiImmolationTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature->getVictim(), m_bIsRegularMode ? SPELL_IMMOLATION_STRIKE : SPELL_IMMOLATION_STRIKE_H) == CAST_OK) + m_uiImmolationTimer = 5000; + } + else + m_uiImmolationTimer -= uiDiff; + + DoMeleeAttackIfReady(); + } +}; + +CreatureAI* GetAI_mob_molten_golem(Creature* pCreature) +{ + return new mob_molten_golemAI(pCreature); +} + +void AddSC_boss_volkhan() +{ + Script* pNewScript; + + pNewScript = new Script; + pNewScript->Name = "boss_volkhan"; + pNewScript->GetAI = &GetAI_boss_volkhan; + pNewScript->pEffectDummyNPC = &EffectDummyCreature_boss_volkhan; + pNewScript->RegisterSelf(); + + pNewScript = new Script; + pNewScript->Name = "npc_volkhan_anvil"; + pNewScript->pEffectDummyNPC = &EffectDummyCreature_npc_volkhan_anvil; + pNewScript->RegisterSelf(); + + pNewScript = new Script; + pNewScript->Name = "mob_molten_golem"; + pNewScript->GetAI = &GetAI_mob_molten_golem; + pNewScript->RegisterSelf(); +} diff --git a/src/modules/SD2/scripts/northrend/ulduar/halls_of_lightning/halls_of_lightning.h b/src/modules/SD2/scripts/northrend/ulduar/halls_of_lightning/halls_of_lightning.h new file mode 100644 index 000000000..778dc7355 --- /dev/null +++ b/src/modules/SD2/scripts/northrend/ulduar/halls_of_lightning/halls_of_lightning.h @@ -0,0 +1,60 @@ +/* Copyright (C) 2006 - 2013 ScriptDev2 + * 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 +{ + MAX_ENCOUNTER = 4, + + TYPE_BJARNGRIM = 0, + TYPE_VOLKHAN = 1, + TYPE_IONAR = 2, + TYPE_LOKEN = 3, + + NPC_BJARNGRIM = 28586, + NPC_VOLKHAN = 28587, + NPC_IONAR = 28546, + NPC_LOKEN = 28923, + NPC_VOLKHAN_ANVIL = 28823, + + GO_VOLKHAN_DOOR = 191325, //_doors07 + GO_IONAR_DOOR = 191326, //_doors05 + // GO_LOKEN_DOOR = 191324, //_doors02 + GO_LOKEN_THRONE = 192654, + + ACHIEV_START_LOKEN_ID = 20384, + + ACHIEV_CRIT_LIGHTNING = 6835, // Bjarngrim, achiev 1834 + ACHIEV_CRIT_RESISTANT = 7321, // Volkhan, achiev 2042 +}; + +class instance_halls_of_lightning : public ScriptedInstance +{ + public: + instance_halls_of_lightning(Map* pMap); + + void Initialize() override; + + void OnCreatureCreate(Creature* pCreature) override; + void OnObjectCreate(GameObject* pGo) override; + + void SetData(uint32 uiType, uint32 uiData) override; + uint32 GetData(uint32 uiType) const override; + + bool CheckAchievementCriteriaMeet(uint32 uiCriteriaId, Player const* pSource, Unit const* pTarget, uint32 uiMiscValue1 /* = 0*/) const override; + + const char* Save() const override { return m_strInstData.c_str(); } + void Load(const char* chrIn) override; + + private: + uint32 m_auiEncounter[MAX_ENCOUNTER]; + std::string m_strInstData; + + bool m_bLightningStruck; + bool m_bIsShatterResistant; +}; + +#endif diff --git a/src/modules/SD2/scripts/northrend/ulduar/halls_of_lightning/instance_halls_of_lightning.cpp b/src/modules/SD2/scripts/northrend/ulduar/halls_of_lightning/instance_halls_of_lightning.cpp new file mode 100644 index 000000000..a008d6db2 --- /dev/null +++ b/src/modules/SD2/scripts/northrend/ulduar/halls_of_lightning/instance_halls_of_lightning.cpp @@ -0,0 +1,187 @@ +/* Copyright (C) 2006 - 2013 ScriptDev2 + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along 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 "halls_of_lightning.h" + +/* Halls of Lightning encounters: +0 - General Bjarngrim +1 - Volkhan +2 - Ionar +3 - Loken +*/ + +instance_halls_of_lightning::instance_halls_of_lightning(Map* pMap) : ScriptedInstance(pMap), + m_bLightningStruck(false), + m_bIsShatterResistant(false) +{ + Initialize(); +} + +void instance_halls_of_lightning::Initialize() +{ + memset(&m_auiEncounter, 0, sizeof(m_auiEncounter)); +} + +void instance_halls_of_lightning::OnCreatureCreate(Creature* pCreature) +{ + switch (pCreature->GetEntry()) + { + case NPC_BJARNGRIM: + case NPC_IONAR: + case NPC_VOLKHAN_ANVIL: + m_mNpcEntryGuidStore[pCreature->GetEntry()] = pCreature->GetObjectGuid(); + break; + } +} + +void instance_halls_of_lightning::OnObjectCreate(GameObject* pGo) +{ + switch (pGo->GetEntry()) + { + case GO_VOLKHAN_DOOR: + if (m_auiEncounter[TYPE_VOLKHAN] == DONE) + pGo->SetGoState(GO_STATE_ACTIVE); + break; + case GO_IONAR_DOOR: + if (m_auiEncounter[TYPE_IONAR] == DONE) + pGo->SetGoState(GO_STATE_ACTIVE); + break; + case GO_LOKEN_THRONE: + break; + + default: + return; + } + m_mGoEntryGuidStore[pGo->GetEntry()] = pGo->GetObjectGuid(); +} + +void instance_halls_of_lightning::SetData(uint32 uiType, uint32 uiData) +{ + switch (uiType) + { + case TYPE_BJARNGRIM: + if (uiData == SPECIAL) + m_bLightningStruck = true; + else if (uiData == FAIL) + m_bLightningStruck = false; + m_auiEncounter[uiType] = uiData; + break; + case TYPE_VOLKHAN: + if (uiData == DONE) + DoUseDoorOrButton(GO_VOLKHAN_DOOR); + else if (uiData == IN_PROGRESS) + m_bIsShatterResistant = true; + else if (uiData == SPECIAL) + m_bIsShatterResistant = false; + m_auiEncounter[uiType] = uiData; + break; + case TYPE_IONAR: + if (uiData == DONE) + DoUseDoorOrButton(GO_IONAR_DOOR); + m_auiEncounter[uiType] = uiData; + break; + case TYPE_LOKEN: + if (uiData == IN_PROGRESS) + DoStartTimedAchievement(ACHIEVEMENT_CRITERIA_TYPE_KILL_CREATURE, ACHIEV_START_LOKEN_ID); + else if (uiData == DONE) + { + // Appears to be type 5 GO with animation. Need to figure out how this work, code below only placeholder + if (GameObject* pGlobe = GetSingleGameObjectFromStorage(GO_LOKEN_THRONE)) + pGlobe->SetGoState(GO_STATE_ACTIVE); + } + m_auiEncounter[uiType] = uiData; + 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_strInstData = saveStream.str(); + + SaveToDB(); + OUT_SAVE_INST_DATA_COMPLETE; + } +} + +uint32 instance_halls_of_lightning::GetData(uint32 uiType) const +{ + if (uiType < MAX_ENCOUNTER) + return m_auiEncounter[uiType]; + + return 0; +} + +bool instance_halls_of_lightning::CheckAchievementCriteriaMeet(uint32 uiCriteriaId, Player const* /*pSource*/, Unit const* /*pTarget*/, uint32 /*uiMiscValue1 = 0*/) const +{ + switch (uiCriteriaId) + { + case ACHIEV_CRIT_LIGHTNING: + return m_bLightningStruck; + case ACHIEV_CRIT_RESISTANT: + return m_bIsShatterResistant; + } + + return false; +} + +void instance_halls_of_lightning::Load(const char* chrIn) +{ + if (!chrIn) + { + 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 < MAX_ENCOUNTER; ++i) + { + if (m_auiEncounter[i] == IN_PROGRESS) + m_auiEncounter[i] = NOT_STARTED; + } + + OUT_LOAD_INST_DATA_COMPLETE; +} + +InstanceData* GetInstanceData_instance_halls_of_lightning(Map* pMap) +{ + return new instance_halls_of_lightning(pMap); +} + +void AddSC_instance_halls_of_lightning() +{ + Script* pNewScript; + + pNewScript = new Script; + pNewScript->Name = "instance_halls_of_lightning"; + pNewScript->GetInstanceData = &GetInstanceData_instance_halls_of_lightning; + pNewScript->RegisterSelf(); +} diff --git a/src/modules/SD2/scripts/northrend/ulduar/halls_of_stone/boss_maiden_of_grief.cpp b/src/modules/SD2/scripts/northrend/ulduar/halls_of_stone/boss_maiden_of_grief.cpp new file mode 100644 index 000000000..4da253750 --- /dev/null +++ b/src/modules/SD2/scripts/northrend/ulduar/halls_of_stone/boss_maiden_of_grief.cpp @@ -0,0 +1,174 @@ +/* Copyright (C) 2006 - 2013 ScriptDev2 + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with 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_Grief +SD%Complete: 60% +SDComment: +SDCategory: Halls of Stone +EndScriptData */ + +#include "precompiled.h" +#include "halls_of_stone.h" + +enum +{ + SAY_AGGRO = -1599005, + SAY_SLAY_1 = -1599006, + SAY_SLAY_2 = -1599007, + SAY_SLAY_3 = -1599008, + SAY_SLAY_4 = -1599009, + SAY_STUN = -1599010, + SAY_DEATH = -1599011, + + SPELL_STORM_OF_GRIEF = 50752, + SPELL_STORM_OF_GRIEF_H = 59772, + + SPELL_SHOCK_OF_SORROW = 50760, + SPELL_SHOCK_OF_SORROW_H = 59726, + + SPELL_PILLAR_OF_WOE = 50761, + SPELL_PILLAR_OF_WOE_H = 59727, + + SPELL_PARTING_SORROW = 59723 +}; + +/*###### +## boss_maiden_of_grief +######*/ + +struct boss_maiden_of_griefAI : public ScriptedAI +{ + boss_maiden_of_griefAI(Creature* pCreature) : ScriptedAI(pCreature) + { + m_pInstance = (instance_halls_of_stone*)pCreature->GetInstanceData(); + m_bIsRegularMode = pCreature->GetMap()->IsRegularDifficulty(); + Reset(); + } + + instance_halls_of_stone* m_pInstance; + bool m_bIsRegularMode; + + uint32 m_uiStormTimer; + uint32 m_uiShockTimer; + uint32 m_uiPillarTimer; + uint32 m_uiPartingSorrowTimer; + + void Reset() override + { + m_uiStormTimer = 5000; + m_uiShockTimer = 10000; + m_uiPillarTimer = 15000; + m_uiPartingSorrowTimer = 12000; + } + + void Aggro(Unit* /*pWho*/) override + { + DoScriptText(SAY_AGGRO, m_creature); + + if (m_pInstance) + m_pInstance->SetData(TYPE_MAIDEN, IN_PROGRESS); + } + + void JustReachedHome() override + { + if (m_pInstance) + m_pInstance->SetData(TYPE_MAIDEN, FAIL); + } + + void KilledUnit(Unit* /*pVictim*/) override + { + switch (urand(0, 3)) + { + case 0: DoScriptText(SAY_SLAY_1, m_creature); break; + case 1: DoScriptText(SAY_SLAY_2, m_creature); break; + case 2: DoScriptText(SAY_SLAY_3, m_creature); break; + case 3: DoScriptText(SAY_SLAY_4, m_creature); break; + } + } + + void JustDied(Unit* /*pKiller*/) override + { + DoScriptText(SAY_DEATH, m_creature); + + if (m_pInstance) + m_pInstance->SetData(TYPE_MAIDEN, DONE); + } + + void UpdateAI(const uint32 uiDiff) override + { + if (!m_creature->SelectHostileTarget() || !m_creature->getVictim()) + return; + + if (m_uiPartingSorrowTimer < uiDiff) + { + if (Unit* pTarget = m_creature->SelectAttackingTarget(ATTACKING_TARGET_RANDOM, 0, SPELL_PARTING_SORROW, SELECT_FLAG_PLAYER | SELECT_FLAG_POWER_MANA)) + { + if (DoCastSpellIfCan(pTarget, SPELL_PARTING_SORROW) == CAST_OK) + m_uiPartingSorrowTimer = 12000 + rand() % 5000; + } + } + else + m_uiPartingSorrowTimer -= uiDiff; + + if (m_uiStormTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature, m_bIsRegularMode ? SPELL_STORM_OF_GRIEF : SPELL_STORM_OF_GRIEF_H) == CAST_OK) + m_uiStormTimer = 20000; + } + else + m_uiStormTimer -= uiDiff; + + if (m_uiPillarTimer < uiDiff) + { + if (Unit* pTarget = m_creature->SelectAttackingTarget(ATTACKING_TARGET_RANDOM, 0, m_bIsRegularMode ? SPELL_PILLAR_OF_WOE_H : SPELL_PILLAR_OF_WOE, SELECT_FLAG_PLAYER)) + { + if (DoCastSpellIfCan(pTarget, m_bIsRegularMode ? SPELL_PILLAR_OF_WOE : SPELL_PILLAR_OF_WOE_H) == CAST_OK) + m_uiPillarTimer = 10000; + } + } + else + m_uiPillarTimer -= uiDiff; + + if (m_uiShockTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature, m_bIsRegularMode ? SPELL_SHOCK_OF_SORROW : SPELL_SHOCK_OF_SORROW_H) == CAST_OK) + { + DoScriptText(SAY_STUN, m_creature); + m_uiShockTimer = 35000; + } + } + else + m_uiShockTimer -= uiDiff; + + DoMeleeAttackIfReady(); + } +}; + +CreatureAI* GetAI_boss_maiden_of_grief(Creature* pCreature) +{ + return new boss_maiden_of_griefAI(pCreature); +} + +void AddSC_boss_maiden_of_grief() +{ + Script* pNewScript; + + pNewScript = new Script; + pNewScript->Name = "boss_maiden_of_grief"; + pNewScript->GetAI = &GetAI_boss_maiden_of_grief; + pNewScript->RegisterSelf(); +} diff --git a/src/modules/SD2/scripts/northrend/ulduar/halls_of_stone/boss_sjonnir.cpp b/src/modules/SD2/scripts/northrend/ulduar/halls_of_stone/boss_sjonnir.cpp new file mode 100644 index 000000000..fd4fe4370 --- /dev/null +++ b/src/modules/SD2/scripts/northrend/ulduar/halls_of_stone/boss_sjonnir.cpp @@ -0,0 +1,285 @@ +/* Copyright (C) 2006 - 2013 ScriptDev2 + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +/* ScriptData +SDName: Boss_Sjonnir +SD%Complete: 60% +SDComment: Brann Event missing, no proper source for timers +SDCategory: Halls of Stone +EndScriptData */ + +#include "precompiled.h" +#include "halls_of_stone.h" + +enum +{ + SAY_AGGRO = -1599000, + SAY_SLAY_1 = -1599001, + SAY_SLAY_2 = -1599002, + SAY_SLAY_3 = -1599003, + SAY_DEATH = -1599004, + EMOTE_GENERIC_FRENZY = -1000002, + + SPELL_FRENZY = 28747, + + SPELL_CHAIN_LIGHTNING = 50830, + SPELL_CHAIN_LIGHTNING_H = 59844, + + SPELL_STATIC_CHARGE = 50834, + SPELL_STATIC_CHARGE_H = 59846, + + SPELL_LIGHTNING_SHIELD = 50831, + SPELL_LIGHTNING_SHIELD_H = 59845, + + SPELL_LIGHTNING_RING = 50840, + SPELL_LIGHTNING_RING_H = 59848, + + // Cast on aggro + SPELL_SUMMON_IRON_DWARF = 50789, // periodic dummy aura, tick each 30sec or each 20sec in heroic + SPELL_SUMMON_IRON_DWARF_H = 59860, // left/right 50790,50791 + + // Cast at 75% hp (also Brann has some yells at that point) + SPELL_SUMMON_IRON_TROGG = 50792, // periodic dummy aura, tick each 10sec or each 7sec in heroic + SPELL_SUMMON_IRON_TROGG_H = 59859, // left/right 50793,50794 + + // Cast at 50% hp + SPELL_SUMMON_MALFORMED_OOZE = 50801, // periodic dummy aura, tick each 5sec or each 3sec in heroic + SPELL_SUMMON_MALFORMED_OOZE_H = 59858, // left/right 50802,50803 + + // Cast at 15% hp when Bran repairs the machine + SPELL_SUMMON_EARTHEN_DWARF = 50824, // left/right 50825, 50826 + + // Ooze and Sludge spells + SPELL_OOZE_COMBINE = 50741, // periodic aura - cast by 27981 + // SPELL_SUMMON_IRON_SLUDGE = 50747, // instakill TARGET_SCRIPT + // SPELL_IRON_SLUDGE_SPAWN_VISUAL = 50777, + + NPC_IRON_TROGG = 27979, + NPC_IRON_DWARF = 27982, + NPC_MALFORMED_OOZE = 27981, + NPC_EARTHEN_DWARF = 27980, +}; + +/*###### +## boss_sjonnir +######*/ + +struct boss_sjonnirAI : public ScriptedAI +{ + boss_sjonnirAI(Creature* pCreature) : ScriptedAI(pCreature) + { + m_pInstance = (ScriptedInstance*)pCreature->GetInstanceData(); + m_bIsRegularMode = pCreature->GetMap()->IsRegularDifficulty(); + Reset(); + } + + ScriptedInstance* m_pInstance; + bool m_bIsRegularMode; + + uint32 m_uiChainLightningTimer; + uint32 m_uiLightningShieldTimer; + uint32 m_uiStaticChargeTimer; + uint32 m_uiLightningRingTimer; + uint32 m_uiFrenzyTimer; + + uint8 m_uiHpCheck; + + void Reset() override + { + m_uiChainLightningTimer = urand(3000, 8000); // TODO timers weak + m_uiLightningShieldTimer = urand(20000, 25000); + m_uiStaticChargeTimer = urand(20000, 25000); + m_uiLightningRingTimer = urand(30000, 35000); + m_uiFrenzyTimer = 4 * MINUTE * IN_MILLISECONDS; // TODO no proper source for this "long" + + m_uiHpCheck = 75; + } + + void Aggro(Unit* /*pWho*/) override + { + DoScriptText(SAY_AGGRO, m_creature); + + DoCastSpellIfCan(m_creature, m_bIsRegularMode ? SPELL_LIGHTNING_SHIELD : SPELL_LIGHTNING_SHIELD_H, CAST_TRIGGERED); + DoCastSpellIfCan(m_creature, m_bIsRegularMode ? SPELL_SUMMON_IRON_DWARF : SPELL_SUMMON_IRON_DWARF_H, CAST_TRIGGERED); + + if (m_pInstance) + m_pInstance->SetData(TYPE_SJONNIR, IN_PROGRESS); + } + + void JustDied(Unit* /*pKiller*/) override + { + DoScriptText(SAY_DEATH, m_creature); + + if (m_pInstance) + m_pInstance->SetData(TYPE_SJONNIR, DONE); + } + + void JustReachedHome() override + { + if (m_pInstance) + m_pInstance->SetData(TYPE_SJONNIR, FAIL); + } + + void JustSummoned(Creature* pSummoned) override + { + switch (pSummoned->GetEntry()) + { + case NPC_EARTHEN_DWARF: + pSummoned->AI()->AttackStart(m_creature); + break; + case NPC_MALFORMED_OOZE: + { + pSummoned->CastSpell(pSummoned, SPELL_OOZE_COMBINE, true); + + // Always move to the center of the room + float fX, fY, fZ; + m_creature->GetRespawnCoord(fX, fY, fZ); + + pSummoned->SetWalk(false); + pSummoned->GetMotionMaster()->MovePoint(1, fX, fY, fZ); + break; + } + case NPC_IRON_TROGG: + case NPC_IRON_DWARF: + { + // Move to a random point around the room in order to start the attack + float fX, fY, fZ; + pSummoned->GetRandomPoint(m_creature->GetPositionX(), m_creature->GetPositionY(), m_creature->GetPositionZ(), 10.0f, fX, fY, fZ); + + pSummoned->SetWalk(false); + pSummoned->GetMotionMaster()->MovePoint(0, fX, fY, fZ); + break; + } + } + } + + void SummonedMovementInform(Creature* pSummoned, uint32 uiType, uint32 uiPointId) override + { + if (uiType != POINT_MOTION_TYPE || pSummoned->GetEntry() != NPC_MALFORMED_OOZE || !uiPointId) + return; + + pSummoned->GetMotionMaster()->MoveRandomAroundPoint(pSummoned->GetPositionX(), pSummoned->GetPositionY(), pSummoned->GetPositionZ(), 10.0f); + } + + void KilledUnit(Unit* /*pVictim*/) override + { + switch (urand(0, 2)) + { + case 0: DoScriptText(SAY_SLAY_1, m_creature); break; + case 1: DoScriptText(SAY_SLAY_2, m_creature); break; + case 2: DoScriptText(SAY_SLAY_3, m_creature); break; + } + } + + bool DoFrenzyIfCan() + { + if (!m_uiFrenzyTimer) + return true; + + if (DoCastSpellIfCan(m_creature, SPELL_FRENZY) == CAST_OK) + { + DoCastSpellIfCan(m_creature, SPELL_SUMMON_EARTHEN_DWARF, CAST_TRIGGERED); + m_uiFrenzyTimer = 0; + + return true; + } + + return false; + } + + void UpdateAI(const uint32 uiDiff) override + { + if (!m_creature->SelectHostileTarget() || !m_creature->getVictim()) + return; + + if (m_creature->GetHealthPercent() <= (float)m_uiHpCheck) + { + switch (m_uiHpCheck) + { + case 75: + if (DoCastSpellIfCan(m_creature, m_bIsRegularMode ? SPELL_SUMMON_IRON_TROGG : SPELL_SUMMON_IRON_TROGG_H, CAST_TRIGGERED) == CAST_OK) + m_uiHpCheck = 50; + break; + case 50: + if (DoCastSpellIfCan(m_creature, m_bIsRegularMode ? SPELL_SUMMON_MALFORMED_OOZE : SPELL_SUMMON_MALFORMED_OOZE_H, CAST_TRIGGERED) == CAST_OK) + m_uiHpCheck = 15; + break; + case 15: + if (DoFrenzyIfCan()) + m_uiHpCheck = 0; + + break; + } + } + + if (m_uiChainLightningTimer < uiDiff) + { + if (Unit* pTarget = m_creature->SelectAttackingTarget(ATTACKING_TARGET_RANDOM, 0)) + { + if (DoCastSpellIfCan(pTarget, m_bIsRegularMode ? SPELL_CHAIN_LIGHTNING : SPELL_CHAIN_LIGHTNING_H) == CAST_OK) + m_uiChainLightningTimer = urand(10000, 15000); + } + } + else + m_uiChainLightningTimer -= uiDiff; + + if (m_uiLightningShieldTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature, m_bIsRegularMode ? SPELL_LIGHTNING_SHIELD : SPELL_LIGHTNING_SHIELD_H) == CAST_OK) + m_uiLightningShieldTimer = urand(20000, 25000); + } + else + m_uiLightningShieldTimer -= uiDiff; + + if (m_uiStaticChargeTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature->getVictim(), m_bIsRegularMode ? SPELL_STATIC_CHARGE : SPELL_STATIC_CHARGE_H) == CAST_OK) + m_uiStaticChargeTimer = urand(20000, 25000); + } + else + m_uiStaticChargeTimer -= uiDiff; + + if (m_uiLightningRingTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature, m_bIsRegularMode ? SPELL_LIGHTNING_RING : SPELL_LIGHTNING_RING_H) == CAST_OK) + m_uiLightningRingTimer = urand(30000, 35000); + } + else + m_uiLightningRingTimer -= uiDiff; + + if (m_uiFrenzyTimer <= uiDiff) + DoFrenzyIfCan(); + else + m_uiFrenzyTimer -= uiDiff; + + DoMeleeAttackIfReady(); + } +}; + +CreatureAI* GetAI_boss_sjonnir(Creature* pCreature) +{ + return new boss_sjonnirAI(pCreature); +} + +void AddSC_boss_sjonnir() +{ + Script* pNewScript; + + pNewScript = new Script; + pNewScript->Name = "boss_sjonnir"; + pNewScript->GetAI = &GetAI_boss_sjonnir; + pNewScript->RegisterSelf(); +} diff --git a/src/modules/SD2/scripts/northrend/ulduar/halls_of_stone/halls_of_stone.cpp b/src/modules/SD2/scripts/northrend/ulduar/halls_of_stone/halls_of_stone.cpp new file mode 100644 index 000000000..c0e25985d --- /dev/null +++ b/src/modules/SD2/scripts/northrend/ulduar/halls_of_stone/halls_of_stone.cpp @@ -0,0 +1,802 @@ +/* Copyright (C) 2006 - 2013 ScriptDev2 + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +/* ScriptData +SDName: Halls_of_Stone +SD%Complete: 50% +SDComment: Just base mechanics in script, timers and stuff is very uncertain, event-spells are not working +SDCategory: Halls of Stone +EndScriptData */ + +#include "precompiled.h" +#include "halls_of_stone.h" +#include "escort_ai.h" + +/* Notes + * The timers and handling of texts is not confirmed, but should also not be too far off + * The spells "of the statues" (handled in instance script), need quite much of core support + */ + +enum +{ + SAY_KILL_1 = -1599012, + SAY_KILL_2 = -1599013, + SAY_KILL_3 = -1599014, + SAY_LOW_HEALTH = -1599015, + SAY_DEATH = -1599016, + SAY_PLAYER_DEATH_1 = -1599017, + SAY_PLAYER_DEATH_2 = -1599018, + SAY_PLAYER_DEATH_3 = -1599019, + SAY_ESCORT_START = -1599020, + + SAY_SPAWN_DWARF = -1599021, + SAY_SPAWN_TROGG = -1599022, + SAY_SPAWN_OOZE = -1599023, + SAY_SPAWN_EARTHEN = -1599024, + + SAY_EVENT_INTRO_1 = -1599025, + SAY_EVENT_INTRO_2 = -1599026, + SAY_EVENT_INTRO_3_ABED = -1599027, + + SAY_EVENT_A_1 = -1599028, + SAY_EVENT_A_2_KADD = -1599029, + SAY_EVENT_A_3 = -1599030, + + SAY_EVENT_B_1 = -1599031, + SAY_EVENT_B_2_MARN = -1599032, + SAY_EVENT_B_3 = -1599033, + + SAY_EVENT_C_1 = -1599034, + SAY_EVENT_C_2_ABED = -1599035, + SAY_EVENT_C_3 = -1599036, + + SAY_EVENT_D_1 = -1599037, + SAY_EVENT_D_2_ABED = -1599038, + SAY_EVENT_D_3 = -1599039, + SAY_EVENT_D_4_ABED = -1599040, + + SAY_EVENT_END_01 = -1599041, + SAY_EVENT_END_02 = -1599042, + SAY_EVENT_END_03_ABED = -1599043, + SAY_EVENT_END_04 = -1599044, + SAY_EVENT_END_05_ABED = -1599045, + SAY_EVENT_END_06 = -1599046, + SAY_EVENT_END_07_ABED = -1599047, + SAY_EVENT_END_08 = -1599048, + SAY_EVENT_END_09_KADD = -1599049, + SAY_EVENT_END_10 = -1599050, + SAY_EVENT_END_11_KADD = -1599051, + SAY_EVENT_END_12 = -1599052, + SAY_EVENT_END_13_KADD = -1599053, + SAY_EVENT_END_14 = -1599054, + SAY_EVENT_END_15_MARN = -1599055, + SAY_EVENT_END_16 = -1599056, + SAY_EVENT_END_17_MARN = -1599057, + SAY_EVENT_END_18 = -1599058, + SAY_EVENT_END_19_MARN = -1599059, + SAY_EVENT_END_20 = -1599060, + SAY_EVENT_END_21_ABED = -1599061, + + SAY_VICTORY_SJONNIR_1 = -1599062, + SAY_VICTORY_SJONNIR_2 = -1599063, + + SAY_ENTRANCE_MEET = -1599064, + + GOSSIP_ITEM_ID_START = -3599000, + GOSSIP_ITEM_ID_PROGRESS = -3599001, + + TEXT_ID_START = 13100, + TEXT_ID_PROGRESS = 13101, + + SPELL_SUMMON_PROTECTOR = 51780, // all spells are casted by stalker npcs 28130 + SPELL_SUMMON_STORMCALLER = 51050, + SPELL_SUMMON_CUSTODIAN = 51051, + + SPELL_STEALTH = 58506, + + NPC_DARK_RUNE_PROTECTOR = 27983, + NPC_DARK_RUNE_STORMCALLER = 27984, + NPC_IRON_GOLEM_CUSTODIAN = 27985, + + QUEST_HALLS_OF_STONE = 13207, +}; + +/*###### +## npc_brann_hos +######*/ + +struct npc_brann_hosAI : public npc_escortAI +{ + npc_brann_hosAI(Creature* pCreature) : npc_escortAI(pCreature) + { + m_pInstance = (instance_halls_of_stone*)pCreature->GetInstanceData(); + m_bIsRegularMode = pCreature->GetMap()->IsRegularDifficulty(); + Reset(); + } + + instance_halls_of_stone* m_pInstance; + bool m_bIsRegularMode; + + bool m_bHasContinued; + bool m_bIsBattle; + bool m_bIsLowHP; + + uint32 m_uiStep; + uint32 m_uiPhaseTimer; + + GuidList m_luiDwarfGUIDs; + + void Reset() override + { + if (!HasEscortState(STATE_ESCORT_ESCORTING)) + { + m_bIsLowHP = false; + m_bIsBattle = false; + m_bHasContinued = false; + + m_uiStep = 0; + m_uiPhaseTimer = 0; + } + } + + void KilledUnit(Unit* /*pVictim*/) override // TODO - possible better as SummonedJustDied + { + switch (urand(0, 2)) + { + case 0: DoScriptText(SAY_KILL_1, m_creature); break; + case 1: DoScriptText(SAY_KILL_2, m_creature); break; + case 2: DoScriptText(SAY_KILL_3, m_creature); break; + } + } + + void JustDied(Unit* /*pKiller*/) override + { + DoScriptText(SAY_DEATH, m_creature); + + if (m_pInstance) + { + m_pInstance->SetData(TYPE_TRIBUNAL, FAIL); + // Continue at right state after respawn + if (m_bHasContinued) + m_pInstance->SetData(TYPE_TRIBUNAL, IN_PROGRESS); + } + + for (GuidList::const_iterator itr = m_luiDwarfGUIDs.begin(); itr != m_luiDwarfGUIDs.end(); ++itr) + { + if (Creature* pDwarf = m_creature->GetMap()->GetCreature(*itr)) + pDwarf->ForcedDespawn(); + } + m_luiDwarfGUIDs.clear(); + } + + void AttackStart(Unit* pWho) override + { + if (!pWho) + return; + + if (!m_bIsBattle) + return; + + npc_escortAI::AttackStart(pWho); + } + + void DamageTaken(Unit* /*pDealer*/, uint32& uiDamage) override + { + // If Brann takes damage, mark the achiev as failed + if (uiDamage && m_pInstance) + m_pInstance->SetBrannSpankin(false); + } + + void ContinueEvent() + { + if (!m_pInstance || m_pInstance->GetData(TYPE_TRIBUNAL) != IN_PROGRESS) + return; + + // Set the achiev in progress + m_pInstance->SetBrannSpankin(true); + + m_creature->RemoveFlag(UNIT_NPC_FLAGS, UNIT_NPC_FLAG_GOSSIP); + SetRun(true); + SetEscortPaused(false); + m_bHasContinued = true; + } + + void JustStartedEscort() override + { + if (m_pInstance) + m_pInstance->SetData(TYPE_TRIBUNAL, IN_PROGRESS); + + DoScriptText(SAY_ESCORT_START, m_creature); + } + + void WaypointReached(uint32 uiPointId) override + { + switch (uiPointId) + { + case 13: // Before Tribunal Event, Continue with Gossip Interaction + DoScriptText(SAY_EVENT_INTRO_1, m_creature); + SetEscortPaused(true); + m_creature->SetFlag(UNIT_NPC_FLAGS, UNIT_NPC_FLAG_GOSSIP); + break; + case 17: // Reach Tribunal + SetEscortPaused(true); + m_uiPhaseTimer = 500; + break; + case 18: // Reach Floor Event + SetEscortPaused(true); + if (m_pInstance) + { + if (GameObject* pKonsole = m_pInstance->GetSingleGameObjectFromStorage(GO_TRIBUNAL_CONSOLE)) + m_creature->SetFacingToObject(pKonsole); + m_pInstance->DoUseDoorOrButton(GO_TRIBUNAL_FLOOR); + } + m_uiPhaseTimer = 1000; + break; + } + } + + void SpawnDwarf(uint32 uEntry) + { + if (!m_pInstance) + return; + + // each case has an individual spawn stalker + switch (uEntry) + { + case NPC_DARK_RUNE_PROTECTOR: + { + Creature* pStalker = m_creature->GetMap()->GetCreature(m_pInstance->GetProtectorStalkerGuid()); + if (!pStalker) + return; + + uint32 uiSpawnNumber = (m_bIsRegularMode ? 2 : 3); + for (uint8 i = 0; i < uiSpawnNumber; ++i) + pStalker->CastSpell(pStalker, SPELL_SUMMON_PROTECTOR, true, NULL, NULL, m_creature->GetObjectGuid()); + pStalker->CastSpell(pStalker, SPELL_SUMMON_STORMCALLER, true, NULL, NULL, m_creature->GetObjectGuid()); + break; + } + case NPC_DARK_RUNE_STORMCALLER: + { + Creature* pStalker = m_creature->GetMap()->GetCreature(m_pInstance->GeStormcallerStalkerGuid()); + if (!pStalker) + return; + + for (uint8 i = 0; i < 2; ++i) + pStalker->CastSpell(pStalker, SPELL_SUMMON_STORMCALLER, true, NULL, NULL, m_creature->GetObjectGuid()); + break; + } + case NPC_IRON_GOLEM_CUSTODIAN: + { + Creature* pStalker = m_creature->GetMap()->GetCreature(m_pInstance->GetCustodianStalkerGuid()); + if (!pStalker) + return; + + pStalker->CastSpell(pStalker, SPELL_SUMMON_CUSTODIAN, true, NULL, NULL, m_creature->GetObjectGuid()); + break; + } + } + } + + void JustSummoned(Creature* pSummoned) override + { + m_luiDwarfGUIDs.push_back(pSummoned->GetObjectGuid()); + + pSummoned->AI()->AttackStart(m_creature); + } + + void UpdateEscortAI(const uint32 uiDiff) override + { + if (m_uiPhaseTimer && m_uiPhaseTimer <= uiDiff) + { + switch (m_uiStep) + { + // Begin Event + case 0: + // TODO, this is wrong, must be "using or similar" + m_creature->SetStandState(UNIT_STAND_STATE_KNEEL); + m_uiPhaseTimer = 1500; + break; + case 1: + DoScriptText(SAY_EVENT_INTRO_2, m_creature); + m_uiPhaseTimer = 2500; + break; + case 2: + if (m_pInstance) + m_pInstance->DoUseDoorOrButton(GO_TRIBUNAL_CONSOLE); + m_uiPhaseTimer = 6500; + break; + case 3: + if (m_pInstance) + m_pInstance->DoFaceSpeak(FACE_ABEDNEUM, SAY_EVENT_INTRO_3_ABED); + m_uiPhaseTimer = 8500; + break; + + // Activate Kaddrak + case 4: + DoScriptText(SAY_EVENT_A_1, m_creature); + m_uiPhaseTimer = 6500; + break; + case 5: + if (m_pInstance) + m_pInstance->DoFaceSpeak(FACE_KADDRAK, SAY_EVENT_A_2_KADD); + m_uiPhaseTimer = 12500; + break; + case 6: + DoScriptText(SAY_EVENT_A_3, m_creature); + m_uiPhaseTimer = 6000; + break; + case 7: + if (m_pInstance) + m_pInstance->ActivateFace(FACE_KADDRAK, false); + m_uiPhaseTimer = 5000; + break; + case 8: + SpawnDwarf(NPC_DARK_RUNE_PROTECTOR); + m_uiPhaseTimer = 20000; + break; + + // Activate Marnak + case 9: + DoScriptText(SAY_EVENT_B_1, m_creature); + m_uiPhaseTimer = 6000; + break; + case 10: + if (m_pInstance) + m_pInstance->DoFaceSpeak(FACE_MARNAK, SAY_EVENT_B_2_MARN); + SpawnDwarf(NPC_DARK_RUNE_PROTECTOR); + m_uiPhaseTimer = 20000; + break; + case 11: + DoScriptText(SAY_EVENT_B_3, m_creature); + m_uiPhaseTimer = 5000; + break; + case 12: + if (m_pInstance) + m_pInstance->ActivateFace(FACE_MARNAK, false); + m_uiPhaseTimer = 10000; + break; + case 13: + SpawnDwarf(NPC_DARK_RUNE_PROTECTOR); + m_uiPhaseTimer = 10000; + break; + case 14: + SpawnDwarf(NPC_DARK_RUNE_STORMCALLER); + m_uiPhaseTimer = (20000); + break; + case 15: + DoScriptText(SAY_EVENT_C_1, m_creature); + SpawnDwarf(NPC_DARK_RUNE_PROTECTOR); + m_uiPhaseTimer = 10000; + break; + case 16: + SpawnDwarf(NPC_DARK_RUNE_STORMCALLER); + m_uiPhaseTimer = 20000; + break; + + // Activate Abedneum + case 17: + if (m_pInstance) + m_pInstance->DoFaceSpeak(FACE_ABEDNEUM, SAY_EVENT_C_2_ABED); + SpawnDwarf(NPC_DARK_RUNE_PROTECTOR); + m_uiPhaseTimer = 20000; + break; + case 18: + DoScriptText(SAY_EVENT_C_3, m_creature); + m_uiPhaseTimer = 5000; + break; + case 19: + if (m_pInstance) + m_pInstance->ActivateFace(FACE_ABEDNEUM, false); + m_uiPhaseTimer = 5000; + break; + case 20: + SpawnDwarf(NPC_DARK_RUNE_STORMCALLER); + m_uiPhaseTimer = 10000; + break; + case 21: + SpawnDwarf(NPC_DARK_RUNE_PROTECTOR); + m_uiPhaseTimer = 15000; + break; + + case 22: + DoScriptText(SAY_EVENT_D_1, m_creature); + SpawnDwarf(NPC_IRON_GOLEM_CUSTODIAN); + m_uiPhaseTimer = 20000; + break; + case 23: + if (m_pInstance) + m_pInstance->DoFaceSpeak(FACE_ABEDNEUM, SAY_EVENT_D_2_ABED); + SpawnDwarf(NPC_DARK_RUNE_PROTECTOR); + m_uiPhaseTimer = 5000; + break; + case 24: + SpawnDwarf(NPC_DARK_RUNE_STORMCALLER); + m_uiPhaseTimer = 15000; + break; + case 25: + DoScriptText(SAY_EVENT_D_3, m_creature); + SpawnDwarf(NPC_IRON_GOLEM_CUSTODIAN); + m_uiPhaseTimer = 5000; + break; + case 26: + SpawnDwarf(NPC_DARK_RUNE_PROTECTOR); + m_uiPhaseTimer = 5000; + break; + case 27: + SpawnDwarf(NPC_DARK_RUNE_STORMCALLER); + m_uiPhaseTimer = 10000; + break; + case 28: + if (m_pInstance) + m_pInstance->DoFaceSpeak(FACE_ABEDNEUM, SAY_EVENT_D_4_ABED); + SpawnDwarf(NPC_DARK_RUNE_PROTECTOR); + m_uiPhaseTimer = 10000; + break; + + // End Event + case 29: + DoScriptText(SAY_EVENT_END_01, m_creature); + m_creature->SetStandState(UNIT_STAND_STATE_STAND);// TODO TODO + if (m_pInstance) + m_pInstance->SetData(TYPE_TRIBUNAL, SPECIAL); // Kill remaining npcs + + // ToDo: the loot and the achiev should be triggered at this point + // Brann should get the gossip option "There will be plenty of time for this later Brann, we need to get moving!" + // This will allow Brann to continue the escort to the last encounter + // When reaching the last door he has the gossip "We're with you Brann! Open it!" + + SetEscortPaused(false); + m_uiPhaseTimer = 3000; + // break; + // case 30: + if (m_pInstance) + m_pInstance->ActivateFace(FACE_ABEDNEUM, true); + m_uiPhaseTimer = 0; + break; + case 30: + DoScriptText(SAY_EVENT_END_02, m_creature); + m_uiPhaseTimer = 5500; + break; + case 31: + if (m_pInstance) + m_pInstance->DoFaceSpeak(FACE_ABEDNEUM, SAY_EVENT_END_03_ABED); + m_uiPhaseTimer = 8500; + break; + case 32: + DoScriptText(SAY_EVENT_END_04, m_creature); + m_uiPhaseTimer = 11500; + break; + case 33: + if (m_pInstance) + m_pInstance->DoFaceSpeak(FACE_ABEDNEUM, SAY_EVENT_END_05_ABED); + m_uiPhaseTimer = 11500; + break; + case 34: + DoScriptText(SAY_EVENT_END_06, m_creature); + m_uiPhaseTimer = 4500; + break; + case 35: + if (m_pInstance) + m_pInstance->DoFaceSpeak(FACE_ABEDNEUM, SAY_EVENT_END_07_ABED); + m_uiPhaseTimer = 22500; + break; + case 36: + DoScriptText(SAY_EVENT_END_08, m_creature); + m_uiPhaseTimer = 7500; + break; + case 37: + if (m_pInstance) + m_pInstance->DoFaceSpeak(FACE_KADDRAK, SAY_EVENT_END_09_KADD); + m_uiPhaseTimer = 18500; + break; + case 38: + DoScriptText(SAY_EVENT_END_10, m_creature); + m_uiPhaseTimer = 5500; + break; + case 39: + if (m_pInstance) + m_pInstance->DoFaceSpeak(FACE_KADDRAK, SAY_EVENT_END_11_KADD); + m_uiPhaseTimer = 20500; + break; + case 40: + DoScriptText(SAY_EVENT_END_12, m_creature); + m_uiPhaseTimer = 2500; + break; + case 41: + if (m_pInstance) + m_pInstance->DoFaceSpeak(FACE_KADDRAK, SAY_EVENT_END_13_KADD); + m_uiPhaseTimer = 19500; + break; + case 42: + DoScriptText(SAY_EVENT_END_14, m_creature); + m_uiPhaseTimer = 10500; + break; + case 43: + if (m_pInstance) + m_pInstance->DoFaceSpeak(FACE_MARNAK, SAY_EVENT_END_15_MARN); + m_uiPhaseTimer = 6500; + break; + case 44: + DoScriptText(SAY_EVENT_END_16, m_creature); + m_uiPhaseTimer = 6500; + break; + case 45: + if (m_pInstance) + m_pInstance->DoFaceSpeak(FACE_MARNAK, SAY_EVENT_END_17_MARN); + m_uiPhaseTimer = 25500; + break; + case 46: + DoScriptText(SAY_EVENT_END_18, m_creature); + m_uiPhaseTimer = 23500; + break; + case 47: + if (m_pInstance) + m_pInstance->DoFaceSpeak(FACE_MARNAK, SAY_EVENT_END_19_MARN); + m_uiPhaseTimer = 3500; + break; + case 48: + DoScriptText(SAY_EVENT_END_20, m_creature); + m_uiPhaseTimer = 8500; + break; + case 49: + if (m_pInstance) + m_pInstance->DoFaceSpeak(FACE_ABEDNEUM, SAY_EVENT_END_21_ABED); + m_uiPhaseTimer = 5500; + break; + case 50: + { + if (m_pInstance) + { + m_pInstance->DoUseDoorOrButton(GO_TRIBUNAL_FLOOR); + m_pInstance->SetData(TYPE_TRIBUNAL, DONE); + } + + Player* pPlayer = GetPlayerForEscort(); + if (pPlayer) + pPlayer->GroupEventHappens(QUEST_HALLS_OF_STONE, m_creature); + + m_creature->SetFlag(UNIT_NPC_FLAGS, UNIT_NPC_FLAG_GOSSIP); + m_creature->SetFlag(UNIT_NPC_FLAGS, UNIT_NPC_FLAG_QUESTGIVER); + + m_uiPhaseTimer = 180000; + break; + } + case 51: + SetEscortPaused(false); + break; + } + ++m_uiStep; + } + else if (m_uiPhaseTimer) + m_uiPhaseTimer -= uiDiff; + + if (!m_bIsLowHP && m_creature->GetHealthPercent() < 30) + { + DoScriptText(SAY_LOW_HEALTH, m_creature); + m_bIsLowHP = true; + } + else if (m_bIsLowHP && m_creature->GetHealthPercent() > 30) + m_bIsLowHP = false; + + // No Combat abilities needed here + if (!m_creature->SelectHostileTarget() || !m_creature->getVictim()) + return; + } + + // Respawn Handling: Relocate and Set Escort to WP 13 + void JustRespawned() override + { + if (!m_pInstance) + return; + + Reset(); + + if (m_pInstance->GetData(TYPE_TRIBUNAL) == IN_PROGRESS) + { + SetEscortPaused(true); + + m_uiStep = 0; + m_uiPhaseTimer = 0; + + m_creature->RemoveFlag(UNIT_NPC_FLAGS, UNIT_NPC_FLAG_QUESTGIVER); + + // Relocate to position of WP 13 + m_creature->GetMap()->CreatureRelocation(m_creature, 941.101563f, 377.373413f, 207.421f, 3.85f); + + SetCurrentWaypoint(13); + } + } +}; + +bool GossipHello_npc_brann_hos(Player* pPlayer, Creature* pCreature) +{ + if (pCreature->IsQuestGiver()) + pPlayer->PrepareQuestMenu(pCreature->GetObjectGuid()); + + if (instance_halls_of_stone* pInstance = (instance_halls_of_stone*)(pCreature->GetInstanceData())) + { + if (pInstance->GetData(TYPE_TRIBUNAL) == NOT_STARTED || pInstance->GetData(TYPE_TRIBUNAL) == FAIL) + { + pPlayer->ADD_GOSSIP_ITEM_ID(GOSSIP_ICON_CHAT, GOSSIP_ITEM_ID_START, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 1); + pPlayer->SEND_GOSSIP_MENU(TEXT_ID_START, pCreature->GetObjectGuid()); + } + else if (pInstance->GetData(TYPE_TRIBUNAL) == IN_PROGRESS) + { + pPlayer->ADD_GOSSIP_ITEM_ID(GOSSIP_ICON_CHAT, GOSSIP_ITEM_ID_PROGRESS, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 2); + pPlayer->SEND_GOSSIP_MENU(TEXT_ID_PROGRESS, pCreature->GetObjectGuid()); + } + } + + return true; +} + +bool GossipSelect_npc_brann_hos(Player* pPlayer, Creature* pCreature, uint32 /*uiSender*/, uint32 uiAction) +{ + switch (uiAction) + { + case GOSSIP_ACTION_INFO_DEF + 1: + if (npc_brann_hosAI* pBrannAi = dynamic_cast(pCreature->AI())) + pBrannAi->Start(false, pPlayer); + break; + case GOSSIP_ACTION_INFO_DEF + 2: + if (npc_brann_hosAI* pBrannAi = dynamic_cast(pCreature->AI())) + pBrannAi->ContinueEvent(); + break; + } + pPlayer->CLOSE_GOSSIP_MENU(); + + return true; +} + +CreatureAI* GetAI_npc_brann_hos(Creature* pCreature) +{ + return new npc_brann_hosAI(pCreature); +} + +enum +{ + SPELL_SUMMON_DARK_MATTER_TARGET = 51003, + SPELL_DARK_MATTER = 51012, + SPELL_DARK_MATTER_H = 59868, + NPC_DARK_MATTER_TARGET = 28237, + + SPELL_SEARING_GAZE = 51136, + SPELL_SEARING_GAZE_H = 59867, + // NPC_SEARING_GAZE_TARGET = 28265, +}; + +/*###### +## npc_dark_matter +######*/ + +struct npc_dark_matterAI : public ScriptedAI +{ + npc_dark_matterAI(Creature* pCreature) : ScriptedAI(pCreature) + { + m_bIsRegularMode = pCreature->GetMap()->IsRegularDifficulty(); + Reset(); + } + + bool m_bIsRegularMode; + + uint32 m_uiSummonTimer; + + void Reset() override + { + m_uiSummonTimer = 0; + } + + void AttackStart(Unit* /*pWho*/) override { } + void MoveInLineOfSight(Unit* /*pWho*/) override { } + + void SpellHit(Unit* /*pCaster*/, const SpellEntry* pSpell) override + { + if (pSpell->Id == SPELL_DARK_MATTER_START) + m_uiSummonTimer = 5000; + } + + void JustSummoned(Creature* pSummoned) override + { + if (pSummoned->GetEntry() == NPC_DARK_MATTER_TARGET) + m_creature->GetMotionMaster()->MovePoint(1, pSummoned->GetPositionX(), pSummoned->GetPositionY(), pSummoned->GetPositionZ()); + } + + void MovementInform(uint32 uiMoveType, uint32 uiPointId) override + { + if (uiMoveType != POINT_MOTION_TYPE || !uiPointId) + return; + + // Cast the Dark Matter spell and despawn for reset + if (DoCastSpellIfCan(m_creature, m_bIsRegularMode ? SPELL_DARK_MATTER : SPELL_DARK_MATTER_H) == CAST_OK) + { + m_creature->SetRespawnDelay(3); + m_creature->ForcedDespawn(1000); + } + } + + void UpdateAI(const uint32 uiDiff) override + { + if (m_uiSummonTimer) + { + if (m_uiSummonTimer <= uiDiff) + { + if (DoCastSpellIfCan(m_creature, SPELL_SUMMON_DARK_MATTER_TARGET) == CAST_OK) + m_uiSummonTimer = 0; + } + else + m_uiSummonTimer -= uiDiff; + } + } +}; + +CreatureAI* GetAI_npc_dark_matter(Creature* pCreature) +{ + return new npc_dark_matterAI(pCreature); +} + +/*###### +## npc_searing_gaze +######*/ + +// TODO Move this 'script' to eventAI when combat can be proper prevented from core-side +struct npc_searing_gazeAI : public Scripted_NoMovementAI +{ + npc_searing_gazeAI(Creature* pCreature) : Scripted_NoMovementAI(pCreature) + { + m_bIsRegularMode = pCreature->GetMap()->IsRegularDifficulty(); + Reset(); + } + + bool m_bIsRegularMode; + + void Reset() override + { + DoCastSpellIfCan(m_creature, m_bIsRegularMode ? SPELL_SEARING_GAZE : SPELL_SEARING_GAZE_H); + // despawn manually because of combat bug + m_creature->ForcedDespawn(30000); + } + + void AttackStart(Unit* /*pWho*/) override { } + void MoveInLineOfSight(Unit* /*pWho*/) override { } + void UpdateAI(const uint32 /*uiDiff*/) override { } +}; + +CreatureAI* GetAI_npc_searing_gaze(Creature* pCreature) +{ + return new npc_searing_gazeAI(pCreature); +} + +void AddSC_halls_of_stone() +{ + Script* pNewScript; + + pNewScript = new Script; + pNewScript->Name = "npc_brann_hos"; + pNewScript->GetAI = &GetAI_npc_brann_hos; + pNewScript->pGossipHello = &GossipHello_npc_brann_hos; + pNewScript->pGossipSelect = &GossipSelect_npc_brann_hos; + pNewScript->RegisterSelf(); + + pNewScript = new Script; + pNewScript->Name = "npc_dark_matter"; + pNewScript->GetAI = &GetAI_npc_dark_matter; + pNewScript->RegisterSelf(); + + pNewScript = new Script; + pNewScript->Name = "npc_searing_gaze"; + pNewScript->GetAI = &GetAI_npc_searing_gaze; + pNewScript->RegisterSelf(); +} diff --git a/src/modules/SD2/scripts/northrend/ulduar/halls_of_stone/halls_of_stone.h b/src/modules/SD2/scripts/northrend/ulduar/halls_of_stone/halls_of_stone.h new file mode 100644 index 000000000..4ef747b7d --- /dev/null +++ b/src/modules/SD2/scripts/northrend/ulduar/halls_of_stone/halls_of_stone.h @@ -0,0 +1,124 @@ +/* Copyright (C) 2006 - 2013 ScriptDev2 + * This program is free software licensed under GPL version 2 + * Please see the included DOCS/LICENSE.TXT for more information */ + +#ifndef DEF_HALLS_OF_STONE_H +#define DEF_HALLS_OF_STONE_H + +enum +{ + MAX_ENCOUNTER = 4, + + TYPE_TRIBUNAL = 0, + TYPE_MAIDEN = 1, + TYPE_KRYSTALLUS = 2, + TYPE_SJONNIR = 3, + + // NPC_BRANN = 28070, + + NPC_KADDRAK = 30898, + NPC_ABEDNEUM = 30899, + NPC_MARNAK = 30897, + NPC_TRIBUNAL_OF_AGES = 28234, + NPC_WORLDTRIGGER = 22515, + NPC_DARK_MATTER = 28235, // used by the Tribunal event + NPC_LIGHTNING_STALKER = 28130, // used by the Tribunal event as spawn point for the dwarfs + NPC_IRON_SLUDGE = 28165, // checked in the Sjonnir achiev + NPC_SJONNIR = 27978, + + GO_DOOR_MAIDEN = 191292, + GO_DOOR_TRIBUNAL = 191294, // possibly closed during event? + GO_DOOR_TO_TRIBUNAL = 191295, + GO_DOOR_SJONNIR = 191296, + + GO_TRIBUNAL_CHEST = 190586, + GO_TRIBUNAL_CHEST_H = 193996, + + GO_TRIBUNAL_HEAD_RIGHT = 191670, // marnak + GO_TRIBUNAL_HEAD_CENTER = 191669, // abedneum + GO_TRIBUNAL_HEAD_LEFT = 191671, // kaddrak + + GO_TRIBUNAL_CONSOLE = 193907, + GO_TRIBUNAL_FLOOR = 191527, + + GO_SJONNIR_CONSOLE = 193906, + + SPELL_DARK_MATTER_START = 51001, // Channeled spells used by the Tribunal event + + MAX_FACES = 3, + FACE_MARNAK = 0, + FACE_ABEDNEUM = 1, + FACE_KADDRAK = 2, + + MAX_ACHIEV_SLUDGES = 5, + + ACHIEV_START_MAIDEN_ID = 20383, + + ACHIEV_CRIT_BRANN = 7590, // Brann, achiev 2154 + ACHIEV_CRIT_ABUSE_OOZE = 7593, // Snonnir, achiev 2155 +}; + +struct Face +{ + Face() : m_bIsActive(false), m_uiTimer(1000) {} + + ObjectGuid m_leftEyeGuid; + ObjectGuid m_rightEyeGuid; + ObjectGuid m_goFaceGuid; + ObjectGuid m_speakerGuid; + bool m_bIsActive; + uint32 m_uiTimer; +}; + +class instance_halls_of_stone : public ScriptedInstance +{ + public: + instance_halls_of_stone(Map* pMap); + void Initialize() override; + + void OnCreatureCreate(Creature* pCreature) override; + void OnObjectCreate(GameObject* pGo) override; + + void OnCreatureDeath(Creature* pCreature) override; + + void SetData(uint32 uiType, uint32 uiData) override; + uint32 GetData(uint32 uiType) const override; + + const char* Save() const override { return m_strInstData.c_str(); } + void Load(const char* chrIn) override; + + void Update(uint32 uiDiff) override; + + bool CheckAchievementCriteriaMeet(uint32 uiCriteriaId, Player const* pSource, Unit const* pTarget, uint32 uiMiscValue1 /* = 0*/) const override; + + void ActivateFace(uint8 uiFace, bool bAfterEvent); + void DoFaceSpeak(uint8 uiFace, int32 iTextId); + void SetBrannSpankin(bool bIsMet) { m_bIsBrannSpankin = bIsMet; } + + ObjectGuid GetProtectorStalkerGuid() { return m_protectorStalkerGuid; } + ObjectGuid GeStormcallerStalkerGuid() { return m_stormcallerStalkerGuid; } + ObjectGuid GetCustodianStalkerGuid() { return m_custodianStalkerGuid; } + + private: + void SortFaces(); + void ProcessFace(uint8 uiFace); + + uint32 m_auiEncounter[MAX_ENCOUNTER]; + Face m_aFaces[MAX_FACES]; + std::string m_strInstData; + + uint8 m_uiIronSludgeKilled; + bool m_bIsBrannSpankin; + + ObjectGuid m_protectorStalkerGuid; + ObjectGuid m_stormcallerStalkerGuid; + ObjectGuid m_custodianStalkerGuid; + + GuidList m_lKaddrakGUIDs; + GuidList m_lAbedneumGUIDs; + GuidList m_lMarnakGUIDs; + GuidList m_lTribunalGUIDs; + GuidList m_lWorldtriggerGUIDs; +}; + +#endif diff --git a/src/modules/SD2/scripts/northrend/ulduar/halls_of_stone/instance_halls_of_stone.cpp b/src/modules/SD2/scripts/northrend/ulduar/halls_of_stone/instance_halls_of_stone.cpp new file mode 100644 index 000000000..0d325193f --- /dev/null +++ b/src/modules/SD2/scripts/northrend/ulduar/halls_of_stone/instance_halls_of_stone.cpp @@ -0,0 +1,430 @@ +/* Copyright (C) 2006 - 2013 ScriptDev2 + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along 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_Stone +SD%Complete: 50% +SDComment: +SDCategory: Halls of Stone +EndScriptData */ + +#include "precompiled.h" +#include "halls_of_stone.h" + +enum +{ + // KADDRAK + SPELL_GLARE_OF_THE_TRIBUNAL = 50988, + SPELL_GLARE_OF_THE_TRIBUNAL_H = 59870, + + // MARNAK + // Spells are handled in individual script + + // ABEDNEUM + SPELL_SUMMON_SEARING_GAZE_TARGET = 51146, // The other spells are handled in individual script + + SPELL_KILL_TRIBUNAL_ADD = 51288, // Cleanup event on finish + SPELL_ACHIEVEMENT_CHECK = 59046, // Doesn't exist in client dbc - added in spell_template +}; + +instance_halls_of_stone::instance_halls_of_stone(Map* pMap) : ScriptedInstance(pMap), + m_uiIronSludgeKilled(0), + m_bIsBrannSpankin(false) +{ + Initialize(); +} + +void instance_halls_of_stone::Initialize() +{ + memset(&m_auiEncounter, 0, sizeof(m_auiEncounter)); +} + +void instance_halls_of_stone::OnCreatureCreate(Creature* pCreature) +{ + switch (pCreature->GetEntry()) + { + case NPC_KADDRAK: m_lKaddrakGUIDs.push_back(pCreature->GetObjectGuid()); break; + case NPC_ABEDNEUM: m_lAbedneumGUIDs.push_back(pCreature->GetObjectGuid()); break; + case NPC_MARNAK: m_lMarnakGUIDs.push_back(pCreature->GetObjectGuid()); break; + case NPC_TRIBUNAL_OF_AGES: m_lTribunalGUIDs.push_back(pCreature->GetObjectGuid()); break; + case NPC_WORLDTRIGGER: m_lWorldtriggerGUIDs.push_back(pCreature->GetObjectGuid()); break; + case NPC_LIGHTNING_STALKER: + // Sort the dwarf summoning stalkers + if (pCreature->GetPositionY() > 400.0f) + m_protectorStalkerGuid = pCreature->GetObjectGuid(); + else if (pCreature->GetPositionY() > 380.0f) + m_stormcallerStalkerGuid = pCreature->GetObjectGuid(); + else + m_custodianStalkerGuid = pCreature->GetObjectGuid(); + break; + case NPC_DARK_MATTER: + case NPC_SJONNIR: + m_mNpcEntryGuidStore[pCreature->GetEntry()] = pCreature->GetObjectGuid(); + break; + } +} + +void instance_halls_of_stone::OnObjectCreate(GameObject* pGo) +{ + switch (pGo->GetEntry()) + { + case GO_TRIBUNAL_CHEST: + case GO_TRIBUNAL_CHEST_H: + if (m_auiEncounter[TYPE_TRIBUNAL] == DONE) + pGo->RemoveFlag(GAMEOBJECT_FLAGS, GO_FLAG_NO_INTERACT); + break; + case GO_TRIBUNAL_HEAD_RIGHT: + m_aFaces[FACE_MARNAK].m_goFaceGuid = pGo->GetObjectGuid(); + return; + case GO_TRIBUNAL_HEAD_CENTER: + m_aFaces[FACE_ABEDNEUM].m_goFaceGuid = pGo->GetObjectGuid(); + return; + case GO_TRIBUNAL_HEAD_LEFT: + m_aFaces[FACE_KADDRAK].m_goFaceGuid = pGo->GetObjectGuid(); + return; + case GO_DOOR_TO_TRIBUNAL: + case GO_DOOR_MAIDEN: + case GO_DOOR_SJONNIR: + case GO_DOOR_TRIBUNAL: + case GO_TRIBUNAL_CONSOLE: + case GO_TRIBUNAL_FLOOR: + case GO_SJONNIR_CONSOLE: + break; + + default: + return; + } + m_mGoEntryGuidStore[pGo->GetEntry()] = pGo->GetObjectGuid(); +} + +void instance_halls_of_stone::SetData(uint32 uiType, uint32 uiData) +{ + switch (uiType) + { + case TYPE_TRIBUNAL: + m_auiEncounter[uiType] = uiData; + switch (uiData) + { + case IN_PROGRESS: + SortFaces(); + break; + case DONE: + // Cast achiev check spell - Note: it's not clear who casts this spell, but for the moment we'll use Abedneum + if (Creature* pEye = instance->GetCreature(m_aFaces[1].m_leftEyeGuid)) + pEye->CastSpell(pEye, SPELL_ACHIEVEMENT_CHECK, true); + // Spawn the loot + DoRespawnGameObject(instance->IsRegularDifficulty() ? GO_TRIBUNAL_CHEST : GO_TRIBUNAL_CHEST_H, 30 * MINUTE); + DoToggleGameObjectFlags(instance->IsRegularDifficulty() ? GO_TRIBUNAL_CHEST : GO_TRIBUNAL_CHEST_H, GO_FLAG_NO_INTERACT, false); + // Door workaround because of the missing Bran event + DoUseDoorOrButton(GO_DOOR_SJONNIR); + break; + case FAIL: + for (uint8 i = 0; i < MAX_FACES; ++i) + { + // Shut down the faces + if (m_aFaces[i].m_bIsActive) + DoUseDoorOrButton(m_aFaces[i].m_goFaceGuid); + m_aFaces[i].m_bIsActive = false; + m_aFaces[i].m_uiTimer = 1000; + } + break; + case SPECIAL: + for (uint8 i = 0; i < MAX_FACES; ++i) + { + m_aFaces[i].m_bIsActive = false; + m_aFaces[i].m_uiTimer = 1000; + // TODO - Check which stay red and how long (also find out how they get red..) + + // Cleanup when finished + if (Creature* pEye = instance->GetCreature(m_aFaces[i].m_leftEyeGuid)) + pEye->CastSpell(pEye, SPELL_KILL_TRIBUNAL_ADD, true); + if (Creature* pEye = instance->GetCreature(m_aFaces[i].m_rightEyeGuid)) + pEye->CastSpell(pEye, SPELL_KILL_TRIBUNAL_ADD, true); + } + break; + } + break; + case TYPE_MAIDEN: + m_auiEncounter[uiType] = uiData; + if (uiData == IN_PROGRESS) + DoStartTimedAchievement(ACHIEVEMENT_CRITERIA_TYPE_KILL_CREATURE, ACHIEV_START_MAIDEN_ID); + break; + case TYPE_KRYSTALLUS: + m_auiEncounter[uiType] = uiData; + break; + case TYPE_SJONNIR: + m_auiEncounter[uiType] = uiData; + DoUseDoorOrButton(GO_DOOR_SJONNIR); + if (uiData == IN_PROGRESS) + m_uiIronSludgeKilled = 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_strInstData = saveStream.str(); + + SaveToDB(); + OUT_SAVE_INST_DATA_COMPLETE; + } +} + +uint32 instance_halls_of_stone::GetData(uint32 uiType) const +{ + if (uiType < MAX_ENCOUNTER) + return m_auiEncounter[uiType]; + + return 0; +} + +bool instance_halls_of_stone::CheckAchievementCriteriaMeet(uint32 uiCriteriaId, Player const* /*pSource*/, Unit const* /*pTarget*/, uint32 /*uiMiscValue1 = 0*/) const +{ + switch (uiCriteriaId) + { + case ACHIEV_CRIT_BRANN: + return m_bIsBrannSpankin; + case ACHIEV_CRIT_ABUSE_OOZE: + return m_uiIronSludgeKilled >= MAX_ACHIEV_SLUDGES; + + default: + return false; + } +} + +struct SortHelper +{ + SortHelper(WorldObject const* pRef): m_pRef(pRef) {} + bool operator()(WorldObject* pLeft, WorldObject* pRight) + { + return m_pRef->GetDistanceOrder(pLeft, pRight); + } + WorldObject const* m_pRef; +}; + +// Small Helper-function +static void GetValidNPCsOfList(Map* pMap, GuidList& lGUIDs, std::list& lNPCs) +{ + lNPCs.clear(); + for (GuidList::const_iterator itr = lGUIDs.begin(); itr != lGUIDs.end(); ++itr) + { + if (Creature* pMob = pMap->GetCreature(*itr)) + lNPCs.push_back(pMob); + } +} + +void instance_halls_of_stone::SortFaces() +{ + std::list lPossibleEyes; + GameObject* pFace = NULL; + + // FACE_MARNAK + if (pFace = instance->GetGameObject(m_aFaces[FACE_MARNAK].m_goFaceGuid)) + { + // Find Marnak NPCs + GetValidNPCsOfList(instance, m_lMarnakGUIDs, lPossibleEyes); + if (lPossibleEyes.size() > 1) + { + lPossibleEyes.sort(SortHelper(pFace)); + std::list::const_iterator itr = lPossibleEyes.begin(); + m_aFaces[FACE_MARNAK].m_leftEyeGuid = (*itr)->GetObjectGuid(); + ++itr; + m_aFaces[FACE_MARNAK].m_speakerGuid = (*itr)->GetObjectGuid(); + } + // Find Worldtrigger NPC + GetValidNPCsOfList(instance, m_lWorldtriggerGUIDs, lPossibleEyes); + if (!lPossibleEyes.empty()) + { + lPossibleEyes.sort(SortHelper(pFace)); + m_aFaces[FACE_MARNAK].m_rightEyeGuid = (*lPossibleEyes.begin())->GetObjectGuid(); + } + } + + // FACE_ABEDNEUM + if (pFace = instance->GetGameObject(m_aFaces[FACE_ABEDNEUM].m_goFaceGuid)) + { + // Find Abedneum NPCs + GetValidNPCsOfList(instance, m_lAbedneumGUIDs, lPossibleEyes); + if (lPossibleEyes.size() > 1) + { + lPossibleEyes.sort(SortHelper(pFace)); + std::list::const_iterator itr = lPossibleEyes.begin(); + m_aFaces[FACE_ABEDNEUM].m_leftEyeGuid = (*itr)->GetObjectGuid(); + ++itr; + m_aFaces[FACE_ABEDNEUM].m_speakerGuid = (*itr)->GetObjectGuid(); + } + // Find Worldtrigger NPC + GetValidNPCsOfList(instance, m_lWorldtriggerGUIDs, lPossibleEyes); + if (!lPossibleEyes.empty()) + { + lPossibleEyes.sort(SortHelper(pFace)); + m_aFaces[FACE_ABEDNEUM].m_rightEyeGuid = (*lPossibleEyes.begin())->GetObjectGuid(); + } + } + + // FACE_KADDRAK + if (pFace = instance->GetGameObject(m_aFaces[FACE_KADDRAK].m_goFaceGuid)) + { + // Find Marnak NPCs + GetValidNPCsOfList(instance, m_lKaddrakGUIDs, lPossibleEyes); + if (lPossibleEyes.size() > 1) + { + lPossibleEyes.sort(SortHelper(pFace)); + std::list::const_iterator itr = lPossibleEyes.begin(); + m_aFaces[FACE_KADDRAK].m_leftEyeGuid = (*itr)->GetObjectGuid(); + ++itr; + m_aFaces[FACE_KADDRAK].m_speakerGuid = (*itr)->GetObjectGuid(); + } + // Find Tribunal NPC + GetValidNPCsOfList(instance, m_lTribunalGUIDs, lPossibleEyes); + if (!lPossibleEyes.empty()) + { + lPossibleEyes.sort(SortHelper(pFace)); + m_aFaces[FACE_KADDRAK].m_rightEyeGuid = (*lPossibleEyes.begin())->GetObjectGuid(); + } + } + + // Clear GUIDs + m_lKaddrakGUIDs.clear(); + m_lAbedneumGUIDs.clear(); + m_lMarnakGUIDs.clear(); + m_lTribunalGUIDs.clear(); + m_lWorldtriggerGUIDs.clear(); +} + +void instance_halls_of_stone::ActivateFace(uint8 uiFace, bool bAfterEvent) +{ + if (uiFace >= MAX_FACES) + return; + + if (bAfterEvent) + DoUseDoorOrButton(m_aFaces[uiFace].m_goFaceGuid); + else + { + // TODO: How to get them red? + DoUseDoorOrButton(m_aFaces[uiFace].m_goFaceGuid); + m_aFaces[uiFace].m_bIsActive = true; + } +} + +void instance_halls_of_stone::DoFaceSpeak(uint8 uiFace, int32 iTextId) +{ + if (uiFace >= MAX_FACES) + return; + + if (Creature* pSpeaker = instance->GetCreature(m_aFaces[uiFace].m_speakerGuid)) + DoScriptText(iTextId, pSpeaker); +} + +void instance_halls_of_stone::OnCreatureDeath(Creature* pCreature) +{ + if (pCreature->GetEntry() == NPC_IRON_SLUDGE && GetData(TYPE_SJONNIR) == IN_PROGRESS) + ++m_uiIronSludgeKilled; +} + +void instance_halls_of_stone::Update(uint32 uiDiff) +{ + if (m_auiEncounter[TYPE_TRIBUNAL] == IN_PROGRESS) + { + for (uint8 i = 0; i < MAX_FACES; ++i) + { + if (!m_aFaces[i].m_bIsActive) + continue; + + if (m_aFaces[i].m_uiTimer < uiDiff) + ProcessFace(i); + else + m_aFaces[i].m_uiTimer -= uiDiff; + } + } +} + +void instance_halls_of_stone::Load(const char* chrIn) +{ + if (!chrIn) + { + 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 < MAX_ENCOUNTER; ++i) + { + if (m_auiEncounter[i] == IN_PROGRESS) + m_auiEncounter[i] = NOT_STARTED; + } + + OUT_LOAD_INST_DATA_COMPLETE; +} + +void instance_halls_of_stone::ProcessFace(uint8 uiFace) +{ + // Cast dmg spell from face eyes, and reset timer for face + switch (uiFace) + { + case FACE_KADDRAK: + if (Creature* pEye = instance->GetCreature(m_aFaces[uiFace].m_leftEyeGuid)) + pEye->CastSpell(pEye, instance->IsRegularDifficulty() ? SPELL_GLARE_OF_THE_TRIBUNAL : SPELL_GLARE_OF_THE_TRIBUNAL_H, true); + if (Creature* pEye = instance->GetCreature(m_aFaces[uiFace].m_rightEyeGuid)) + pEye->CastSpell(pEye, instance->IsRegularDifficulty() ? SPELL_GLARE_OF_THE_TRIBUNAL : SPELL_GLARE_OF_THE_TRIBUNAL_H, true, NULL, NULL, m_aFaces[uiFace].m_leftEyeGuid); + m_aFaces[uiFace].m_uiTimer = urand(1000, 2000); + break; + case FACE_MARNAK: + if (Creature* pDarkMatter = GetSingleCreatureFromStorage(NPC_DARK_MATTER)) + pDarkMatter->CastSpell(pDarkMatter, SPELL_DARK_MATTER_START, true); + // Note: Marnak doesn't cast anything directly. Keep this code for reference only. + // if (Creature* pEye = instance->GetCreature(m_aFaces[uiFace].m_leftEyeGuid)) + // pEye->CastSpell(pEye, SPELL_SUMMON_DARK_MATTER_TARGET, true); + // One should be enough.. + // if (Creature* pEye = instance->GetCreature(m_aFaces[uiFace].m_rightEyeGuid)) + // pEye->CastSpell(pEye, SPELL_SUMMON_DARK_MATTER_TARGET, true); + m_aFaces[uiFace].m_uiTimer = urand(21000, 30000); + break; + case FACE_ABEDNEUM: + if (Creature* pEye = instance->GetCreature(m_aFaces[uiFace].m_leftEyeGuid)) + pEye->CastSpell(pEye, SPELL_SUMMON_SEARING_GAZE_TARGET, true); + // One should be enough.. + // if (Creature* pEye = instance->GetCreature(m_aFaces[uiFace].m_rightEyeGuid)) + // pEye->CastSpell(pEye, SPELL_SUMMON_SEARING_GAZE_TARGET, true); + m_aFaces[uiFace].m_uiTimer = urand(15000, 20000); + break; + default: + return; + } +} + +InstanceData* GetInstanceData_instance_halls_of_stone(Map* pMap) +{ + return new instance_halls_of_stone(pMap); +} + +void AddSC_instance_halls_of_stone() +{ + Script* pNewScript; + + pNewScript = new Script; + pNewScript->Name = "instance_halls_of_stone"; + pNewScript->GetInstanceData = &GetInstanceData_instance_halls_of_stone; + pNewScript->RegisterSelf(); +} diff --git a/src/modules/SD2/scripts/northrend/ulduar/ulduar/assembly_of_iron.cpp b/src/modules/SD2/scripts/northrend/ulduar/ulduar/assembly_of_iron.cpp new file mode 100644 index 000000000..525fe4a1a --- /dev/null +++ b/src/modules/SD2/scripts/northrend/ulduar/ulduar/assembly_of_iron.cpp @@ -0,0 +1,778 @@ +/* Copyright (C) 2006 - 2013 ScriptDev2 + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along 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 +SD%Complete: 90% +SDComment: Lightning Tendrils target following could use some love from the core side +SDCategory: Ulduar +EndScriptData */ + +#include "precompiled.h" +#include "ulduar.h" + +enum +{ + SAY_BRUNDIR_AGGRO = -1603056, + SAY_BRUNDIR_WHIRL = -1603057, + SAY_BRUNDIR_DEATH_1 = -1603058, + SAY_BRUNDIR_DEATH_2 = -1603059, + SAY_BRUNDIR_SLAY_1 = -1603060, + SAY_BRUNDIR_SLAY_2 = -1603061, + SAY_BRUNDIR_BERSERK = -1603062, + SAY_BRUNDIR_FLY = -1603063, + + SAY_MOLGEIM_AGGRO = -1603064, + SAY_MOLGEIM_DEATH_1 = -1603065, + SAY_MOLGEIM_DEATH_2 = -1603066, + SAY_MOLGEIM_DEATH_RUNE = -1603067, + SAY_MOLGEIM_SURGE = -1603068, + SAY_MOLGEIM_SLAY_1 = -1603069, + SAY_MOLGEIM_SLAY_2 = -1603070, + SAY_MOLGEIM_BERSERK = -1603071, + + SAY_STEEL_AGGRO = -1603072, + SAY_STEEL_DEATH_1 = -1603073, + SAY_STEEL_DEATH_2 = -1603074, + SAY_STEEL_SLAY_1 = -1603075, + SAY_STEEL_SLAY_2 = -1603076, + SAY_STEEL_OVERWHELM = -1603077, + SAY_STEEL_BERSERK = -1603078, + + // Common spells + SPELL_BERSERK = 62535, // triggers 47008 after 15 min + SPELL_SUPERCHARGE = 61920, + SPELL_LIGHTNING_CHANNEL_PREFIGHT = 61942, // cast by Brundir on Steelbreaker + SPELL_RUNE_OF_POWER_PREFIGHT = 61975, // cast by Molgeim on Stellbreaker + SPELL_COUNCIL_KILL_CREDIT = 65195, // currently missing from DBC + + // Steelbreaker + SPELL_HIGH_VOLTAGE = 61890, // phase 1 spells + SPELL_HIGH_VOLTAGE_H = 63498, // probably related to 61892 - couldn't find any info regarding this one + SPELL_FUSION_PUNCH = 61903, + SPELL_FUSION_PUNCH_H = 63493, + SPELL_STATIC_DISRUPTION = 61911, // phase 2 spells + SPELL_STATIC_DISRUPTION_H = 63495, // should be triggered by 64641 + SPELL_OVERWHELMING_POWER = 61888, // phase 3 spells + SPELL_OVERWHELMING_POWER_H = 64637, + SPELL_ELECTRICAL_CHARGE = 61900, // triggers 61901 when target dies + + // Runemaster Molgeim + SPELL_SHIELD = 62274, // phase 1 spells + SPELL_SHIELD_H = 63489, + SPELL_RUNE_OF_POWER = 61973, + SPELL_RUNE_OF_DEATH = 62269, // phase 2 spells + SPELL_RUNE_OF_DEATH_H = 63490, + SPELL_RUNE_OF_SUMMONING = 62273, // phase 3 spells + + // Stormcaller Brundir + SPELL_CHAIN_LIGHTNING = 61879, // phase 1 spells + SPELL_CHAIN_LIGHTNING_H = 63479, + SPELL_OVERLOAD = 61869, + SPELL_LIGHTNING_WHIRL = 61915, // phase 2 spells + SPELL_LIGHTNING_WHIRL_H = 63483, + SPELL_LIGHTNING_WHIRL_DAMAGE = 61916, // used to check achiev criterias + SPELL_LIGHTNING_WHIRL_DAMAGE_H = 63482, + SPELL_STORMSHIELD = 64187, // phase 3 spells + SPELL_LIGHTNING_TENDRILS = 61887, + SPELL_LIGHTNING_TENDRILS_H = 63486, + SPELL_TENDRILS_VISUAL = 61883, + + // Summoned spells + SPELL_OVERLOAD_AURA = 61877, + SPELL_RUNE_OF_POWER_AURA = 61974, + SPELL_RUNE_OF_SUMMONING_AURA = 62019, // triggers 62020 which summons 32958 + SPELL_LIGHTNING_ELEMENTAL_PASSIVE = 62052, + SPELL_LIGHTNING_ELEMENTAL_PASSIVE_H = 63492, + + // summoned npcs + NPC_OVERLOAD_VISUAL = 32866, + NPC_RUNE_OF_POWER = 33705, + NPC_RUNE_OF_SUMMONING = 33051, + NPC_LIGHTNING_ELEMENTAL = 32958, + + PHASE_NO_CHARGE = 0, + PHASE_CHARGE_ONE = 1, + PHASE_CHARGE_TWO = 2, + + POINT_ID_LIFT_OFF = 1, + POINT_ID_LAND = 2, +}; + +struct boss_brundirAI : public ScriptedAI +{ + boss_brundirAI(Creature* pCreature) : ScriptedAI(pCreature) + { + m_pInstance = (instance_ulduar*)pCreature->GetInstanceData(); + m_bIsRegularMode = pCreature->GetMap()->IsRegularDifficulty(); + Reset(); + } + + instance_ulduar* m_pInstance; + bool m_bIsRegularMode; + + uint8 m_uiPhase; + uint32 m_uiVisualTimer; + uint32 m_uiChainLightningTimer; + uint32 m_uiOverloadTimer; + uint32 m_uiWhirlTimer; + uint32 m_uiTendrilsTimer; + uint32 m_uiTendrilsTargetTimer; + uint32 m_uiTendrilsEndTimer; + uint32 m_uiTendrilsFollowTimer; + + ObjectGuid m_followTargetGuid; + + void Reset() override + { + m_uiPhase = PHASE_NO_CHARGE; + m_uiVisualTimer = 5000; + m_uiChainLightningTimer = 0; + m_uiOverloadTimer = 35000; + m_uiWhirlTimer = 10000; + m_uiTendrilsTimer = 60000; + m_uiTendrilsEndTimer = 0; + m_uiTendrilsTargetTimer = 0; + m_uiTendrilsFollowTimer = 500; + + m_creature->SetLevitate(false); + } + + void JustDied(Unit* /*pKiller*/) override + { + if (!m_pInstance) + return; + + // If we are not on the last phase then cast Supercharge and set as unlootable + if (m_uiPhase != PHASE_CHARGE_TWO) + { + m_creature->RemoveFlag(UNIT_DYNAMIC_FLAGS, UNIT_DYNFLAG_LOOTABLE); + DoCastSpellIfCan(m_creature, SPELL_SUPERCHARGE, CAST_TRIGGERED); + m_pInstance->SetSpecialAchievementCriteria(TYPE_ACHIEV_BRUNDIR, false); + } + else + { + m_pInstance->SetData(TYPE_ASSEMBLY, DONE); + m_creature->CastSpell(m_creature, SPELL_COUNCIL_KILL_CREDIT, true); + } + + DoScriptText(urand(0, 1) ? SAY_BRUNDIR_DEATH_1 : SAY_BRUNDIR_DEATH_2, m_creature); + } + + void Aggro(Unit* /*pWho*/) override + { + DoScriptText(SAY_BRUNDIR_AGGRO, m_creature); + + if (m_pInstance) + m_pInstance->SetData(TYPE_ASSEMBLY, IN_PROGRESS); + + m_creature->InterruptNonMeleeSpells(false); + DoCastSpellIfCan(m_creature, SPELL_BERSERK, CAST_TRIGGERED); + } + + void KilledUnit(Unit* /*pVictim*/) override + { + DoScriptText(urand(0, 1) ? SAY_BRUNDIR_SLAY_1 : SAY_BRUNDIR_SLAY_2, m_creature); + } + + void JustReachedHome() override + { + if (m_pInstance) + m_pInstance->SetData(TYPE_ASSEMBLY, FAIL); + } + + void JustSummoned(Creature* pSummoned) override + { + if (pSummoned->GetEntry() == NPC_OVERLOAD_VISUAL) + { + pSummoned->CastSpell(pSummoned, SPELL_OVERLOAD_AURA, true); + // Visual npc- shouldn't move and should despawn in 6 sec + pSummoned->GetMotionMaster()->MoveIdle(); + pSummoned->ForcedDespawn(6000); + } + } + + void SpellHit(Unit* /*pCaster*/, const SpellEntry* pSpell) override + { + // Increase the phase when hit with the supercharge spell by his brothers + if (pSpell->Id == SPELL_SUPERCHARGE) + { + // Not sure if there is a spell for this, so we are doing it here + m_creature->SetHealth(m_creature->GetMaxHealth()); + ++m_uiPhase; + } + + if (m_uiPhase == PHASE_CHARGE_TWO) + { + // Cast stormshield in the last phase + DoCastSpellIfCan(m_creature, SPELL_STORMSHIELD, CAST_TRIGGERED); + + // set the instace data to special in order to mark the last phase - this is used to check the achiev criteria + if (m_pInstance) + m_pInstance->SetData(TYPE_ASSEMBLY, SPECIAL); + } + } + + void SpellHitTarget(Unit* pTarget, const SpellEntry* pSpell) override + { + if (pTarget->GetTypeId() != TYPEID_PLAYER) + return; + + if (!m_pInstance) + return; + + // Check achiev criterias + switch (pSpell->Id) + { + case SPELL_CHAIN_LIGHTNING: + case SPELL_CHAIN_LIGHTNING_H: + case SPELL_LIGHTNING_WHIRL_DAMAGE: + case SPELL_LIGHTNING_WHIRL_DAMAGE_H: + m_pInstance->SetSpecialAchievementCriteria(TYPE_ACHIEV_STUNNED, false); + break; + } + } + + void MovementInform(uint32 uiMoveType, uint32 uiPointId) override + { + if (uiMoveType != POINT_MOTION_TYPE || !uiPointId) + return; + + switch (uiPointId) + { + // After lift up follow a target and set the target change timer + case POINT_ID_LIFT_OFF: + // TODO: the boss should follow without changing his Z position - missing core feature + // Current implementation with move point is wrong + if (Unit* pTarget = m_creature->SelectAttackingTarget(ATTACKING_TARGET_RANDOM, 0, uint32(0), SELECT_FLAG_PLAYER | SELECT_FLAG_NOT_IN_MELEE_RANGE)) + { + DoMoveToTarget(pTarget); + m_followTargetGuid = pTarget->GetObjectGuid(); + } + m_uiTendrilsTargetTimer = 5000; + m_uiTendrilsFollowTimer = 500; + break; + // After reached the land remove all the auras and resume basic combat + case POINT_ID_LAND: + m_creature->SetLevitate(false); + SetCombatMovement(true); + if (m_creature->getVictim()) + m_creature->GetMotionMaster()->MoveChase(m_creature->getVictim()); + + m_creature->RemoveAurasDueToSpell(SPELL_TENDRILS_VISUAL); + m_creature->RemoveAurasDueToSpell(m_bIsRegularMode ? SPELL_LIGHTNING_TENDRILS : SPELL_LIGHTNING_TENDRILS_H); + break; + } + } + + // Wrapper for target movement + void DoMoveToTarget(Unit* pTarget) + { + if (pTarget) + { + m_creature->GetMotionMaster()->Clear(); + m_creature->GetMotionMaster()->MovePoint(0, pTarget->GetPositionX(), pTarget->GetPositionY(), m_creature->GetPositionZ()); + } + } + + void UpdateAI(const uint32 uiDiff) override + { + // Pre fight visual spell + if (m_uiVisualTimer) + { + if (m_uiVisualTimer <= uiDiff) + { + if (DoCastSpellIfCan(m_creature, SPELL_LIGHTNING_CHANNEL_PREFIGHT) == CAST_OK) + m_uiVisualTimer = 0; + } + else + m_uiVisualTimer -= uiDiff; + } + + if (!m_creature->SelectHostileTarget() || !m_creature->getVictim()) + return; + + switch (m_uiPhase) + { + case PHASE_CHARGE_TWO: + + if (m_uiTendrilsTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature, m_bIsRegularMode ? SPELL_LIGHTNING_TENDRILS : SPELL_LIGHTNING_TENDRILS_H) == CAST_OK) + { + DoCastSpellIfCan(m_creature, SPELL_TENDRILS_VISUAL, CAST_TRIGGERED); + DoScriptText(SAY_BRUNDIR_FLY, m_creature); + SetCombatMovement(false); + m_creature->SetLevitate(true); + m_creature->GetMotionMaster()->MovePoint(POINT_ID_LIFT_OFF, m_creature->GetPositionX(), m_creature->GetPositionY(), m_creature->GetPositionZ() + 15.0f); + m_uiTendrilsTimer = 90000; + m_uiTendrilsEndTimer = 25000; + } + } + else + m_uiTendrilsTimer -= uiDiff; + + if (m_uiTendrilsEndTimer) + { + if (m_uiTendrilsEndTimer <= uiDiff) + { + // Get proper Z position and land + float fZ = m_creature->GetTerrain()->GetWaterOrGroundLevel(m_creature->GetPositionX(), m_creature->GetPositionY(), m_creature->GetPositionZ()); + m_creature->GetMotionMaster()->MovePoint(POINT_ID_LAND, m_creature->GetPositionX(), m_creature->GetPositionY(), fZ); + m_uiOverloadTimer = 40000; + m_uiWhirlTimer = 15000; + m_uiChainLightningTimer = 3000; + m_uiTendrilsEndTimer = 0; + m_uiTendrilsTargetTimer = 0; + } + else + m_uiTendrilsEndTimer -= uiDiff; + + // Change follow target every 5 seconds + if (m_uiTendrilsTargetTimer) + { + if (m_uiTendrilsTargetTimer <= uiDiff) + { + // TODO: the boss should follow without changing his Z position - missing core feature + // Current implementation with move point is wrong + if (Unit* pTarget = m_creature->SelectAttackingTarget(ATTACKING_TARGET_RANDOM, 0, uint32(0), SELECT_FLAG_PLAYER | SELECT_FLAG_NOT_IN_MELEE_RANGE)) + { + DoMoveToTarget(pTarget); + m_followTargetGuid = pTarget->GetObjectGuid(); + } + m_uiTendrilsTargetTimer = 5000; + m_uiTendrilsFollowTimer = 500; + } + else + m_uiTendrilsTargetTimer -= uiDiff; + + // Workaround to follow the target + if (m_uiTendrilsFollowTimer < uiDiff) + { + if (Unit* pTarget = m_creature->GetMap()->GetUnit(m_followTargetGuid)) + DoMoveToTarget(pTarget); + m_uiTendrilsFollowTimer = 500; + } + else + m_uiTendrilsFollowTimer -= uiDiff; + } + + // no other spells during tendrils + return; + } + + // no break here; he uses the other spells as well + case PHASE_CHARGE_ONE: + + if (m_uiWhirlTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature, m_bIsRegularMode ? SPELL_LIGHTNING_WHIRL : SPELL_LIGHTNING_WHIRL_H) == CAST_OK) + { + DoScriptText(SAY_BRUNDIR_WHIRL, m_creature); + m_uiWhirlTimer = 30000; + } + } + else + m_uiWhirlTimer -= uiDiff; + + // no break here; he uses the other spells as well + case PHASE_NO_CHARGE: + + if (m_uiChainLightningTimer < uiDiff) + { + if (Unit* pTarget = m_creature->SelectAttackingTarget(ATTACKING_TARGET_RANDOM, 0)) + { + if (DoCastSpellIfCan(pTarget, m_bIsRegularMode ? SPELL_CHAIN_LIGHTNING : SPELL_CHAIN_LIGHTNING_H) == CAST_OK) + m_uiChainLightningTimer = 2000; + } + } + else + m_uiChainLightningTimer -= uiDiff; + + if (m_uiOverloadTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature, SPELL_OVERLOAD) == CAST_OK) + m_uiOverloadTimer = 80000; + } + else + m_uiOverloadTimer -= uiDiff; + + break; + } + + DoMeleeAttackIfReady(); + } +}; + +CreatureAI* GetAI_boss_brundir(Creature* pCreature) +{ + return new boss_brundirAI(pCreature); +} + +struct boss_molgeimAI : public ScriptedAI +{ + boss_molgeimAI(Creature* pCreature) : ScriptedAI(pCreature) + { + m_pInstance = (instance_ulduar*)pCreature->GetInstanceData(); + m_bIsRegularMode = pCreature->GetMap()->IsRegularDifficulty(); + Reset(); + } + + instance_ulduar* m_pInstance; + bool m_bIsRegularMode; + + uint8 m_uiPhase; + uint32 m_uiVisualTimer; + uint32 m_uiShieldTimer; + uint32 m_uiRunePowerTimer; + uint32 m_uiRuneDeathTimer; + uint32 m_uiRuneSummonTimer; + + void Reset() override + { + m_uiPhase = PHASE_NO_CHARGE; + m_uiVisualTimer = 5000; + m_uiShieldTimer = 25000; + m_uiRunePowerTimer = 15000; + m_uiRuneSummonTimer = 10000; + m_uiRuneDeathTimer = 30000; + } + + void JustDied(Unit* /*pKiller*/) override + { + if (!m_pInstance) + return; + + // If we are not on the last phase then cast Supercharge and set as unlootable + if (m_uiPhase != PHASE_CHARGE_TWO) + { + m_creature->RemoveFlag(UNIT_DYNAMIC_FLAGS, UNIT_DYNFLAG_LOOTABLE); + DoCastSpellIfCan(m_creature, SPELL_SUPERCHARGE, CAST_TRIGGERED); + m_pInstance->SetSpecialAchievementCriteria(TYPE_ACHIEV_MOLGEIM, false); + } + else + { + m_pInstance->SetData(TYPE_ASSEMBLY, DONE); + m_creature->CastSpell(m_creature, SPELL_COUNCIL_KILL_CREDIT, true); + } + + DoScriptText(urand(0, 1) ? SAY_MOLGEIM_DEATH_1 : SAY_MOLGEIM_DEATH_2, m_creature); + } + + void Aggro(Unit* /*pWho*/) override + { + DoScriptText(SAY_MOLGEIM_AGGRO, m_creature); + + if (m_pInstance) + m_pInstance->SetData(TYPE_ASSEMBLY, IN_PROGRESS); + + m_creature->InterruptNonMeleeSpells(false); + DoCastSpellIfCan(m_creature, SPELL_BERSERK, CAST_TRIGGERED); + } + + void KilledUnit(Unit* /*pVictim*/) override + { + DoScriptText(urand(0, 1) ? SAY_MOLGEIM_SLAY_1 : SAY_MOLGEIM_SLAY_2, m_creature); + } + + void JustReachedHome() override + { + if (m_pInstance) + m_pInstance->SetData(TYPE_ASSEMBLY, FAIL); + } + + void JustSummoned(Creature* pSummoned) override + { + if (pSummoned->GetEntry() == NPC_RUNE_OF_SUMMONING) + pSummoned->CastSpell(pSummoned, SPELL_RUNE_OF_SUMMONING_AURA, true, NULL, NULL, m_creature->GetObjectGuid()); + else if (pSummoned->GetEntry() == NPC_RUNE_OF_POWER) + pSummoned->CastSpell(pSummoned, SPELL_RUNE_OF_POWER_AURA, true); + else if (pSummoned->GetEntry() == NPC_LIGHTNING_ELEMENTAL) + { + pSummoned->CastSpell(pSummoned, m_bIsRegularMode ? SPELL_LIGHTNING_ELEMENTAL_PASSIVE : SPELL_LIGHTNING_ELEMENTAL_PASSIVE_H, true); + + if (Unit* pTarget = m_creature->SelectAttackingTarget(ATTACKING_TARGET_RANDOM, 0)) + pSummoned->AI()->AttackStart(pTarget); + } + } + + void SpellHit(Unit* /*pCaster*/, const SpellEntry* pSpell) override + { + // Increase the phase when hit with the supercharge spell by his brothers + if (pSpell->Id == SPELL_SUPERCHARGE) + { + // Not sure if there is a spell for this, so we are doing it here + m_creature->SetHealth(m_creature->GetMaxHealth()); + ++m_uiPhase; + } + + if (m_uiPhase == PHASE_CHARGE_TWO) + { + // set the instace data to special in order to mark the last phase - this is used to check the achiev criteria + if (m_pInstance) + m_pInstance->SetData(TYPE_ASSEMBLY, SPECIAL); + } + } + + void UpdateAI(const uint32 uiDiff) override + { + // Pre fight visual spell + if (m_uiVisualTimer) + { + if (m_uiVisualTimer <= uiDiff) + { + if (DoCastSpellIfCan(m_creature, SPELL_RUNE_OF_POWER_PREFIGHT) == CAST_OK) + m_uiVisualTimer = 0; + } + else + m_uiVisualTimer -= uiDiff; + } + + if (!m_creature->SelectHostileTarget() || !m_creature->getVictim()) + return; + + switch (m_uiPhase) + { + case PHASE_CHARGE_TWO: + + if (m_uiRuneSummonTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature, SPELL_RUNE_OF_SUMMONING) == CAST_OK) + { + DoScriptText(SAY_MOLGEIM_SURGE, m_creature); + m_uiRuneSummonTimer = 30000; + } + } + else + m_uiRuneSummonTimer -= uiDiff; + + // no break here; he uses the other spells as well + case PHASE_CHARGE_ONE: + + if (m_uiRuneDeathTimer < uiDiff) + { + if (Unit* pTarget = m_creature->SelectAttackingTarget(ATTACKING_TARGET_RANDOM, 0)) + { + if (DoCastSpellIfCan(pTarget, m_bIsRegularMode ? SPELL_RUNE_OF_DEATH : SPELL_RUNE_OF_DEATH_H) == CAST_OK) + { + DoScriptText(SAY_MOLGEIM_DEATH_RUNE, m_creature); + m_uiRuneDeathTimer = 30000; + } + } + } + else + m_uiRuneDeathTimer -= uiDiff; + + // no break here; he uses the other spells as well + case PHASE_NO_CHARGE: + + if (m_uiShieldTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature, m_bIsRegularMode ? SPELL_SHIELD : SPELL_SHIELD_H) == CAST_OK) + m_uiShieldTimer = 40000; + } + else + m_uiShieldTimer -= uiDiff; + + if (m_uiRunePowerTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature, SPELL_RUNE_OF_POWER) == CAST_OK) + m_uiRunePowerTimer = 45000; + } + else + m_uiRunePowerTimer -= uiDiff; + + break; + } + + DoMeleeAttackIfReady(); + } +}; + +CreatureAI* GetAI_boss_molgeim(Creature* pCreature) +{ + return new boss_molgeimAI(pCreature); +} + +struct boss_steelbreakerAI : public ScriptedAI +{ + boss_steelbreakerAI(Creature* pCreature) : ScriptedAI(pCreature) + { + m_pInstance = (instance_ulduar*)pCreature->GetInstanceData(); + m_bIsRegularMode = pCreature->GetMap()->IsRegularDifficulty(); + Reset(); + } + + instance_ulduar* m_pInstance; + bool m_bIsRegularMode; + + uint8 m_uiPhase; + uint32 m_uiFusionPunchTimer; + uint32 m_uiDisruptionTimer; + uint32 m_uiPowerTimer; + + void Reset() override + { + m_uiPhase = PHASE_NO_CHARGE; + m_uiFusionPunchTimer = 15000; + m_uiDisruptionTimer = 15000; + m_uiPowerTimer = 10000; + } + + void JustDied(Unit* /*pKiller*/) override + { + if (!m_pInstance) + return; + + // If we are not on the last phase then cast Supercharge and set as unlootable + if (m_uiPhase != PHASE_CHARGE_TWO) + { + m_creature->RemoveFlag(UNIT_DYNAMIC_FLAGS, UNIT_DYNFLAG_LOOTABLE); + DoCastSpellIfCan(m_creature, SPELL_SUPERCHARGE, CAST_TRIGGERED); + m_pInstance->SetSpecialAchievementCriteria(TYPE_ACHIEV_STEELBREAKER, false); + } + else + { + m_pInstance->SetData(TYPE_ASSEMBLY, DONE); + m_creature->CastSpell(m_creature, SPELL_COUNCIL_KILL_CREDIT, true); + } + + DoScriptText(urand(0, 1) ? SAY_STEEL_DEATH_1 : SAY_STEEL_DEATH_2, m_creature); + } + + void Aggro(Unit* /*pWho*/) override + { + DoScriptText(SAY_STEEL_AGGRO, m_creature); + + if (m_pInstance) + m_pInstance->SetData(TYPE_ASSEMBLY, IN_PROGRESS); + + DoCastSpellIfCan(m_creature, SPELL_BERSERK, CAST_TRIGGERED); + DoCastSpellIfCan(m_creature, m_bIsRegularMode ? SPELL_HIGH_VOLTAGE : SPELL_HIGH_VOLTAGE_H, CAST_TRIGGERED); + } + + void KilledUnit(Unit* /*pVictim*/) override + { + DoScriptText(urand(0, 1) ? SAY_STEEL_SLAY_1 : SAY_STEEL_SLAY_2, m_creature); + } + + void JustReachedHome() override + { + if (m_pInstance) + m_pInstance->SetData(TYPE_ASSEMBLY, FAIL); + } + + void SpellHit(Unit* /*pCaster*/, const SpellEntry* pSpell) override + { + // Increase the phase when hit with the supercharge spell by his brothers + if (pSpell->Id == SPELL_SUPERCHARGE) + { + // Not sure if there is a spell for this, so we are doing it here + m_creature->SetHealth(m_creature->GetMaxHealth()); + ++m_uiPhase; + } + + if (m_uiPhase == PHASE_CHARGE_TWO) + { + // Cast electrical charge aura on all players - this will proc when player dies + DoCastSpellIfCan(m_creature, SPELL_ELECTRICAL_CHARGE, CAST_TRIGGERED); + + // set the instace data to special in order to mark the last phase - this is used to check the achiev criteria + if (m_pInstance) + m_pInstance->SetData(TYPE_ASSEMBLY, SPECIAL); + } + } + + void UpdateAI(const uint32 uiDiff) override + { + if (!m_creature->SelectHostileTarget() || !m_creature->getVictim()) + return; + + switch (m_uiPhase) + { + case PHASE_CHARGE_TWO: + + if (m_uiPowerTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature->getVictim(), m_bIsRegularMode ? SPELL_OVERWHELMING_POWER : SPELL_OVERWHELMING_POWER_H) == CAST_OK) + { + DoScriptText(SAY_STEEL_OVERWHELM, m_creature); + m_uiPowerTimer = m_bIsRegularMode ? 60000 : 35000; + } + } + else + m_uiPowerTimer -= uiDiff; + + // no break here; he uses the other spells as well + case PHASE_CHARGE_ONE: + + if (m_uiDisruptionTimer < uiDiff) + { + // NOTE: This spell is not cast right: Normally it should be triggered by 64641 in core + // Because of the poor target selection in core we'll implement it here with select flag targeting + Unit* pTarget = m_creature->SelectAttackingTarget(ATTACKING_TARGET_RANDOM, 1, uint32(0), SELECT_FLAG_NOT_IN_MELEE_RANGE); + + if (!pTarget) + pTarget = m_creature->SelectAttackingTarget(ATTACKING_TARGET_RANDOM, 0); + + if (DoCastSpellIfCan(pTarget, m_bIsRegularMode ? SPELL_STATIC_DISRUPTION : SPELL_STATIC_DISRUPTION_H) == CAST_OK) + m_uiDisruptionTimer = urand(10000, 15000); + } + else + m_uiDisruptionTimer -= uiDiff; + + // no break here; he uses the other spells as well + case PHASE_NO_CHARGE: + + if (m_uiFusionPunchTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature->getVictim(), m_bIsRegularMode ? SPELL_FUSION_PUNCH : SPELL_FUSION_PUNCH_H) == CAST_OK) + m_uiFusionPunchTimer = 15000; + } + else + m_uiFusionPunchTimer -= uiDiff; + + break; + } + + DoMeleeAttackIfReady(); + } +}; + +CreatureAI* GetAI_boss_steelbreaker(Creature* pCreature) +{ + return new boss_steelbreakerAI(pCreature); +} + +void AddSC_boss_assembly_of_iron() +{ + Script* pNewScript; + + pNewScript = new Script; + pNewScript->Name = "boss_brundir"; + pNewScript->GetAI = GetAI_boss_brundir; + pNewScript->RegisterSelf(); + + pNewScript = new Script; + pNewScript->Name = "boss_molgeim"; + pNewScript->GetAI = GetAI_boss_molgeim; + pNewScript->RegisterSelf(); + + pNewScript = new Script; + pNewScript->Name = "boss_steelbreaker"; + pNewScript->GetAI = GetAI_boss_steelbreaker; + pNewScript->RegisterSelf(); +} diff --git a/src/modules/SD2/scripts/northrend/ulduar/ulduar/boss_algalon.cpp b/src/modules/SD2/scripts/northrend/ulduar/ulduar/boss_algalon.cpp new file mode 100644 index 000000000..bcd05996a --- /dev/null +++ b/src/modules/SD2/scripts/northrend/ulduar/ulduar/boss_algalon.cpp @@ -0,0 +1,58 @@ +/* Copyright (C) 2006 - 2013 ScriptDev2 + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +/* ScriptData +SDName: boss_algalon +SD%Complete: 0% +SDComment: +SDCategory: Ulduar +EndScriptData */ + +#include "precompiled.h" +#include "ulduar.h" + +enum +{ + SAY_INTRO_1 = -1603106, + SAY_INTRO_2 = -1603107, + SAY_INTRO_3 = -1603108, + + SAY_ENGAGE = -1603109, + SAY_AGGRO = -1603110, + SAY_SLAY_1 = -1603111, + SAY_SLAY_2 = -1603112, + SAY_SUMMON_STAR = -1603113, + SAY_BIG_BANG_1 = -1603114, + SAY_BIG_BANG_2 = -1603115, + SAY_PHASE_2 = -1603116, + SAY_BERSERK = -1603117, + + SAY_DESPAWN_1 = -1603118, + SAY_DESPAWN_2 = -1603119, + SAY_DESPAWN_3 = -1603120, + + SAY_OUTRO_1 = -1603121, + SAY_OUTRO_2 = -1603122, + SAY_OUTRO_3 = -1603123, + SAY_OUTRO_4 = -1603124, + SAY_OUTRO_5 = -1603125, + +}; + +void AddSC_boss_algalon() +{ + +} diff --git a/src/modules/SD2/scripts/northrend/ulduar/ulduar/boss_auriaya.cpp b/src/modules/SD2/scripts/northrend/ulduar/ulduar/boss_auriaya.cpp new file mode 100644 index 000000000..cf4005b52 --- /dev/null +++ b/src/modules/SD2/scripts/northrend/ulduar/ulduar/boss_auriaya.cpp @@ -0,0 +1,393 @@ +/* Copyright (C) 2006 - 2013 ScriptDev2 + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +/* ScriptData +SDName: boss_auriaya +SD%Complete: 100% +SDComment: +SDCategory: Ulduar +EndScriptData */ + +#include "precompiled.h" +#include "ulduar.h" + +enum +{ + SAY_AGGRO = -1603079, + SAY_SLAY_1 = -1603080, + SAY_SLAY_2 = -1603081, + SAY_BERSERK = -1603082, + SAY_DEATH = -1603083, + EMOTE_SCREECH = -1603084, + EMOTE_DEFENDER = -1603085, + + // Auriaya + SPELL_BERSERK = 47008, + SPELL_GUARDIAN_SWARM = 64396, // triggers 64397 + SPELL_SENTINEL_BLAST = 64389, // triggers 64392 + SPELL_SENTINEL_BLAST_H = 64678, // triggers 64679 + SPELL_SONIC_SCREECH = 64422, + SPELL_SONIC_SCREECH_H = 64688, + SPELL_TERRIFYING_SCREECH = 64386, + SPELL_ACTIVATE_FERAL_DEFENDER = 64449, // triggers 64447 + SPELL_ACTIVATE_FERAL_DEFENDER_TRIGG = 64448, + + // Feral Defender spells + SPELL_FERAL_ESSENCE = 64455, + SPELL_FERAL_ESSENCE_REMOVAL = 64456, // remove 1 stack of 64455 + SPELL_FERAL_POUNCE = 64478, + SPELL_FERAL_POUNCE_H = 64669, + SPELL_FERAL_RUSH = 64489, // triggers 64496 + SPELL_FERAL_RUSH_H = 64673, // triggers 64674 + SPELL_SEEPING_FERAL_ESSENCE_SUMMON = 64457, + SPELL_FEIGN_DEATH = 64461, // related to the feral defender feign death + SPELL_FULL_HEAL = 64460, // on feign death remove + + // Seeping Feral Essence + SPELL_SEEPING_FERAL_ESSENCE = 64458, + SPELL_SEEPING_FERAL_ESSENCE_H = 64676, + + NPC_SEEPING_FERAL_ESSENCE = 34098, // summoned by the feral defender on feign death + // NPC_GUARDIAN_SWARN = 34034, // summoned by spell + NPC_FERAL_DEFENDER_STALKER = 34096, +}; + +/*###### +## boss_auriaya +######*/ + +struct boss_auriayaAI : public ScriptedAI +{ + boss_auriayaAI(Creature* pCreature) : ScriptedAI(pCreature) + { + m_pInstance = (instance_ulduar*)pCreature->GetInstanceData(); + m_bIsRegularMode = pCreature->GetMap()->IsRegularDifficulty(); + Reset(); + } + + instance_ulduar* m_pInstance; + bool m_bIsRegularMode; + + uint32 m_uiEnrageTimer; + uint32 m_uiSwarmTimer; + uint32 m_uiSonicScreechTimer; + uint32 m_uiSentinelBlastTimer; + uint32 m_uiTerrifyingScreechTimer; + uint32 m_uiDefenderTimer; + + void Reset() override + { + m_uiEnrageTimer = 10 * MINUTE * IN_MILLISECONDS; + m_uiSwarmTimer = 50000; + m_uiSonicScreechTimer = 58000; + m_uiSentinelBlastTimer = 40000; + m_uiTerrifyingScreechTimer = 38000; + m_uiDefenderTimer = 1 * MINUTE * IN_MILLISECONDS; + } + + void JustDied(Unit* /*pKiller*/) override + { + DoScriptText(SAY_DEATH, m_creature); + + if (m_pInstance) + m_pInstance->SetData(TYPE_AURIAYA, DONE); + } + + void Aggro(Unit* /*pWho*/) override + { + if (m_pInstance) + m_pInstance->SetData(TYPE_AURIAYA, IN_PROGRESS); + + DoScriptText(SAY_AGGRO, m_creature); + } + + void KilledUnit(Unit* /*pVictim*/) override + { + DoScriptText(urand(0, 1) ? SAY_SLAY_1 : SAY_SLAY_2, m_creature); + } + + void JustReachedHome() override + { + if (m_pInstance) + m_pInstance->SetData(TYPE_AURIAYA, FAIL); + } + + void JustSummoned(Creature* pSummoned) override + { + // Summon the feral defender + if (pSummoned->GetEntry() == NPC_FERAL_DEFENDER_STALKER) + DoCastSpellIfCan(pSummoned, SPELL_ACTIVATE_FERAL_DEFENDER, CAST_INTERRUPT_PREVIOUS); + } + + void UpdateAI(const uint32 uiDiff) override + { + if (!m_creature->SelectHostileTarget() || !m_creature->getVictim()) + return; + + if (m_uiTerrifyingScreechTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature, SPELL_TERRIFYING_SCREECH) == CAST_OK) + { + DoScriptText(EMOTE_SCREECH, m_creature); + m_uiTerrifyingScreechTimer = 35000; + m_uiSentinelBlastTimer = 2000; + } + } + else + m_uiTerrifyingScreechTimer -= uiDiff; + + if (m_uiSentinelBlastTimer < uiDiff) + { + // Cast Sentinel blast right after terrifying screech + if (DoCastSpellIfCan(m_creature, m_bIsRegularMode ? SPELL_SENTINEL_BLAST : SPELL_SENTINEL_BLAST_H) == CAST_OK) + m_uiSentinelBlastTimer = 35000; + } + else + m_uiSentinelBlastTimer -= uiDiff; + + if (m_uiDefenderTimer) + { + if (m_uiDefenderTimer <= uiDiff) + { + // Summon the feral defender trigger + if (DoCastSpellIfCan(m_creature, SPELL_ACTIVATE_FERAL_DEFENDER_TRIGG) == CAST_OK) + m_uiDefenderTimer = 0; + } + else + m_uiDefenderTimer -= uiDiff; + } + + if (m_uiSonicScreechTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature, m_bIsRegularMode ? SPELL_SONIC_SCREECH : SPELL_SONIC_SCREECH_H) == CAST_OK) + m_uiSonicScreechTimer = 27000; + } + else + m_uiSonicScreechTimer -= uiDiff; + + if (m_uiSwarmTimer < uiDiff) + { + if (Unit* pTarget = m_creature->SelectAttackingTarget(ATTACKING_TARGET_RANDOM, 0)) + { + if (DoCastSpellIfCan(pTarget, SPELL_GUARDIAN_SWARM) == CAST_OK) + m_uiSwarmTimer = 37000; + } + } + else + m_uiSwarmTimer -= uiDiff; + + if (m_uiEnrageTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature, SPELL_BERSERK) == CAST_OK) + { + DoScriptText(SAY_BERSERK, m_creature); + m_uiEnrageTimer = 10 * MINUTE * IN_MILLISECONDS; + } + } + else + m_uiEnrageTimer -= uiDiff; + + DoMeleeAttackIfReady(); + } +}; + +CreatureAI* GetAI_boss_auriaya(Creature* pCreature) +{ + return new boss_auriayaAI(pCreature); +} + +/*###### +## boss_feral_defender +######*/ + +struct boss_feral_defenderAI : public ScriptedAI +{ + boss_feral_defenderAI(Creature* pCreature) : ScriptedAI(pCreature) + { + m_pInstance = (instance_ulduar*)pCreature->GetInstanceData(); + m_bIsRegularMode = pCreature->GetMap()->IsRegularDifficulty(); + m_uiMaxFeralRush = m_bIsRegularMode ? 6 : 10; + Reset(); + } + + instance_ulduar* m_pInstance; + bool m_bIsRegularMode; + + uint8 m_uiFeralRushCount; + uint8 m_uiMaxFeralRush; + uint32 m_uiPounceTimer; + uint32 m_uiFeralRushTimer; + uint32 m_uiReviveDelayTimer; + uint32 m_uiAttackDelayTimer; + uint32 m_uiKilledCount; + + void Reset() override + { + m_uiFeralRushCount = 0; + m_uiReviveDelayTimer = 0; + m_uiAttackDelayTimer = 0; + m_uiPounceTimer = urand(9000, 12000); + m_uiFeralRushTimer = urand(3000, 5000); + m_uiKilledCount = 0; + + DoCastSpellIfCan(m_creature, SPELL_FERAL_ESSENCE); + } + + void JustDied(Unit* /*pKiller*/) override + { + // Set achiev criteria to true + if (m_pInstance) + m_pInstance->SetSpecialAchievementCriteria(TYPE_ACHIEV_NINE_LIVES, true); + } + + void DamageTaken(Unit* /*pDoneBy*/, uint32& uiDamage) override + { + // If we don't have the feral essence anymore then ignore this + if (m_uiKilledCount >= 8) // 9-1 == 8 + return; + + if (m_uiReviveDelayTimer) // Already faking + { + uiDamage = 0; + return; + } + + if (uiDamage > m_creature->GetHealth()) + { + uiDamage = 0; + + // Set Feign death, remove one aura stack and summon a feral essence + DoCastSpellIfCan(m_creature, SPELL_FEIGN_DEATH, CAST_TRIGGERED); + DoCastSpellIfCan(m_creature, SPELL_FERAL_ESSENCE_REMOVAL, CAST_TRIGGERED); + DoCastSpellIfCan(m_creature, SPELL_SEEPING_FERAL_ESSENCE_SUMMON, CAST_TRIGGERED); + + // the feign death aura doesn't do everything, so keep the following here as well + m_creature->SetHealth(0); + m_creature->ClearComboPointHolders(); + m_creature->ModifyAuraState(AURA_STATE_HEALTHLESS_20_PERCENT, false); + m_creature->ModifyAuraState(AURA_STATE_HEALTHLESS_35_PERCENT, false); + m_creature->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE); + m_creature->ClearAllReactives(); + m_creature->GetMotionMaster()->Clear(); + m_creature->GetMotionMaster()->MoveIdle(); + m_creature->SetStandState(UNIT_STAND_STATE_DEAD); + + m_uiReviveDelayTimer = 30000; + ++m_uiKilledCount; + } + } + + void JustSummoned(Creature* pSummoned) override + { + // Cast seeping feral essence on the summoned + if (pSummoned->GetEntry() == NPC_SEEPING_FERAL_ESSENCE) + pSummoned->CastSpell(pSummoned, m_bIsRegularMode ? SPELL_SEEPING_FERAL_ESSENCE : SPELL_SEEPING_FERAL_ESSENCE_H, true, NULL, NULL, m_creature->GetObjectGuid()); + } + + void UpdateAI(const uint32 uiDiff) override + { + if (!m_creature->SelectHostileTarget() || !m_creature->getVictim()) + return; + + if (m_uiReviveDelayTimer) + { + if (m_uiReviveDelayTimer <= uiDiff) + { + // ToDo: figure out how to enable the ressurrect animation + DoCastSpellIfCan(m_creature, SPELL_FULL_HEAL, CAST_TRIGGERED); + m_uiReviveDelayTimer = 0; + m_uiAttackDelayTimer = 3000; + } + else + m_uiReviveDelayTimer -= uiDiff; + + // No Further action while faking + return; + } + + if (m_uiAttackDelayTimer) + { + if (m_uiAttackDelayTimer <= uiDiff) + { + DoResetThreat(); + m_creature->RemoveAurasDueToSpell(SPELL_FEIGN_DEATH); + m_creature->SetStandState(UNIT_STAND_STATE_STAND); + m_creature->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE); + + m_uiPounceTimer = urand(9000, 10000); + m_uiFeralRushCount = 0; + m_uiFeralRushTimer = 1000; + m_uiAttackDelayTimer = 0; + } + else + m_uiAttackDelayTimer -= uiDiff; + + // No Further action while faking + return; + } + + if (m_uiPounceTimer < uiDiff) + { + if (Unit* pTarget = m_creature->SelectAttackingTarget(ATTACKING_TARGET_RANDOM, 0)) + { + if (DoCastSpellIfCan(pTarget, m_bIsRegularMode ? SPELL_FERAL_POUNCE : SPELL_FERAL_POUNCE_H) == CAST_OK) + m_uiPounceTimer = urand(13000, 16000); + } + } + else + m_uiPounceTimer -= uiDiff; + + if (m_uiFeralRushTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature, m_bIsRegularMode ? SPELL_FERAL_RUSH : SPELL_FERAL_RUSH_H) == CAST_OK) + { + ++m_uiFeralRushCount; + + if (m_uiFeralRushCount == m_uiMaxFeralRush) + { + m_uiFeralRushCount = 0; + m_uiFeralRushTimer = 12000; + } + else + m_uiFeralRushTimer = 400; + } + } + else + m_uiFeralRushTimer -= uiDiff; + + DoMeleeAttackIfReady(); + } +}; + +CreatureAI* GetAI_boss_feral_defender(Creature* pCreature) +{ + return new boss_feral_defenderAI(pCreature); +} + +void AddSC_boss_auriaya() +{ + Script* pNewScript; + + pNewScript = new Script; + pNewScript->Name = "boss_auriaya"; + pNewScript->GetAI = GetAI_boss_auriaya; + pNewScript->RegisterSelf(); + + pNewScript = new Script; + pNewScript->Name = "boss_feral_defender"; + pNewScript->GetAI = &GetAI_boss_feral_defender; + pNewScript->RegisterSelf(); +} diff --git a/src/modules/SD2/scripts/northrend/ulduar/ulduar/boss_flame_leviathan.cpp b/src/modules/SD2/scripts/northrend/ulduar/ulduar/boss_flame_leviathan.cpp new file mode 100644 index 000000000..0bef1df4e --- /dev/null +++ b/src/modules/SD2/scripts/northrend/ulduar/ulduar/boss_flame_leviathan.cpp @@ -0,0 +1,55 @@ +/* Copyright (C) 2006 - 2013 ScriptDev2 + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +/* ScriptData +SDName: boss_flame_leviathan +SD%Complete: 0% +SDComment: +SDCategory: Ulduar +EndScriptData */ + +#include "precompiled.h" +#include "ulduar.h" + +enum +{ + SAY_AGGRO = -1603159, + SAY_SLAY = -1603160, + SAY_DEATH = -1603161, + + SAY_CHANGE_1 = -1603162, + SAY_CHANGE_2 = -1603163, + SAY_CHANGE_3 = -1603164, + SAY_PLAYER_RIDE = -1603165, + SAY_OVERLOAD_1 = -1603166, + SAY_OVERLOAD_2 = -1603167, + SAY_OVERLOAD_3 = -1603168, + + SAY_HARD_MODE = -1603169, + + SAY_TOWER_FROST = -1603170, + SAY_TOWER_FIRE = -1603171, + SAY_TOWER_ENERGY = -1603172, + SAY_TOWER_NATURE = -1603173, + SAY_TOWER_DOWN = -1603174, + + EMOTE_PURSUE = -1603175, +}; + +void AddSC_boss_flame_leviathan() +{ + +} diff --git a/src/modules/SD2/scripts/northrend/ulduar/ulduar/boss_freya.cpp b/src/modules/SD2/scripts/northrend/ulduar/ulduar/boss_freya.cpp new file mode 100644 index 000000000..ef7530e56 --- /dev/null +++ b/src/modules/SD2/scripts/northrend/ulduar/ulduar/boss_freya.cpp @@ -0,0 +1,64 @@ +/* Copyright (C) 2006 - 2013 ScriptDev2 + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +/* ScriptData +SDName: boss_freya +SD%Complete: 0% +SDComment: +SDCategory: Ulduar +EndScriptData */ + +#include "precompiled.h" +#include "ulduar.h" + +enum +{ + SAY_AGGRO = -1603000, + SAY_AGGRO_HARD = -1603001, + SAY_ADDS_CONSERVATOR = -1603002, + SAY_ADDS_TRIO = -1603003, + SAY_ADDS_LASHER = -1603004, + SAY_SLAY_1 = -1603005, + SAY_SLAY_2 = -1603006, + SAY_DEATH = -1603007, + SAY_BERSERK = -1603008, + SAY_HELP_YOGG = -1603009, + + EMOTE_ALLIES_NATURE = -1603010, + EMOTE_LIFEBINDER = -1603011, + EMOTE_TREMOR = -1603012, + EMOTE_IRON_ROOTS = -1603013, + + SAY_AGGRO_BRIGHT = -1603014, + SAY_SLAY_1_BRIGHT = -1603015, + SAY_SLAY_2_BRIGHT = -1603016, + SAY_DEATH_BRIGHT = -1603017, + + SAY_AGGRO_IRON = -1603018, + SAY_SLAY_1_IRON = -1603019, + SAY_SLAY_2_IRON = -1603020, + SAY_DEATH_IRON = -1603021, + + SAY_AGGRO_STONE = -1603022, + SAY_SLAY_1_STONE = -1603023, + SAY_SLAY_2_STONE = -1603024, + SAY_DEATH_STONE = -1603025, +}; + +void AddSC_boss_freya() +{ + +} diff --git a/src/modules/SD2/scripts/northrend/ulduar/ulduar/boss_general_vezax.cpp b/src/modules/SD2/scripts/northrend/ulduar/ulduar/boss_general_vezax.cpp new file mode 100644 index 000000000..bc6e7a1c4 --- /dev/null +++ b/src/modules/SD2/scripts/northrend/ulduar/ulduar/boss_general_vezax.cpp @@ -0,0 +1,326 @@ +/* Copyright (C) 2006 - 2013 ScriptDev2 + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with 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_vezax +SD%Complete: 90% +SDComment: Some details may need some small adjustments +SDCategory: Ulduar +EndScriptData */ + +#include "precompiled.h" +#include "ulduar.h" + +enum +{ + SAY_AGGRO = -1603096, + SAY_SLAY_1 = -1603097, + SAY_SLAY_2 = -1603098, + SAY_SURGE = -1603099, + SAY_DEATH = -1603100, + SAY_ENRAGE = -1603101, + SAY_HARD_MODE = -1603102, + + EMOTE_VAPOR = -1603103, + EMOTE_SURGE = -1603104, + EMOTE_ANIMUS = -1603105, + + SPELL_AURA_OF_DESPAIR = 62692, + SPELL_SHADOW_CRASH = 62660, + SPELL_MARK_OF_FACELESS = 63276, // triggers 63278 + SPELL_SEARING_FLAMES = 62661, + SPELL_SURGE_OF_DARKNESS = 62662, + SPELL_BERSERK = 26662, + SPELL_SARONITE_BARRIER = 63364, // also sends event 9735 + SPELL_SUMMON_ANIMUS = 63145, // the animus should spam 63420 on target - to be done in acid + SPELL_SUMMON_VAPORS = 63081, + SPELL_ANIMUS_FORMATION = 63319, // visual aura on the vapors + SPELL_SARONITE_VAPORS = 63323, // cast by vapor on death + SPELL_CORRUPTED_RAGE = 68415, // Unused + SPELL_CORRUPTED_WISDOM = 64646, + + MAX_HARD_MODE_VAPORS = 6, + + NPC_SARONITE_VAPOR = 33488, +}; + +/*###### +## boss_general_vezax +######*/ + +struct boss_general_vezaxAI : public ScriptedAI +{ + boss_general_vezaxAI(Creature* pCreature) : ScriptedAI(pCreature) + { + m_pInstance = (instance_ulduar*)pCreature->GetInstanceData(); + Reset(); + } + + instance_ulduar* m_pInstance; + + uint32 m_uiEnrageTimer; + uint32 m_uiCrashTimer; + uint32 m_uiMarkTimer; + uint32 m_uiFlamesTimer; + uint32 m_uiSurgeTimer; + uint32 m_uiSaroniteVaporTimer; + uint32 m_uiSaroniteBarrierTimer; + + uint8 m_uiVaporsGathered; + + GuidList m_lVaporsGuids; + + void Reset() override + { + m_uiEnrageTimer = 10 * MINUTE * IN_MILLISECONDS; + m_uiFlamesTimer = 8000; + m_uiSaroniteVaporTimer = 30000; + m_uiSurgeTimer = 60000; + m_uiMarkTimer = 20000; + m_uiCrashTimer = 15000; + m_uiSaroniteBarrierTimer = 0; + m_uiVaporsGathered = 0; + } + + void Aggro(Unit* /*pWho*/) override + { + if (m_pInstance) + { + m_pInstance->SetData(TYPE_VEZAX, IN_PROGRESS); + m_pInstance->SetData(TYPE_VEZAX_HARD, NOT_STARTED); + } + + DoCastSpellIfCan(m_creature, SPELL_AURA_OF_DESPAIR); + + DoScriptText(SAY_AGGRO, m_creature); + } + + void JustReachedHome() override + { + if (m_pInstance) + m_pInstance->SetData(TYPE_VEZAX, FAIL); + } + + void JustDied(Unit* /*pKiller*/) override + { + if (m_pInstance) + m_pInstance->SetData(TYPE_VEZAX, DONE); + + DoScriptText(SAY_DEATH, m_creature); + } + + void KilledUnit(Unit* /*pVictim*/) override + { + DoScriptText(urand(0, 1) ? SAY_SLAY_1 : SAY_SLAY_2, m_creature); + } + + void JustSummoned(Creature* pSummoned) override + { + if (pSummoned->GetEntry() == NPC_SARONITE_VAPOR) + { + m_lVaporsGuids.push_back(pSummoned->GetObjectGuid()); + + // if vapors have reached the max number for hard mode then summon animus + if (m_lVaporsGuids.size() == MAX_HARD_MODE_VAPORS) + DoPrepareAnimusIfCan(); + } + else if (pSummoned->GetEntry() == NPC_SARONITE_ANIMUS) + pSummoned->SetInCombatWithZone(); + } + + void SummonedCreatureJustDied(Creature* pSummoned) override + { + // decrease the number of vapors when they die + if (pSummoned->GetEntry() == NPC_SARONITE_VAPOR) + { + pSummoned->CastSpell(pSummoned, SPELL_SARONITE_VAPORS, true); + m_lVaporsGuids.remove(pSummoned->GetObjectGuid()); + } + // remove saronite barrier when animus dies + else if (pSummoned->GetEntry() == NPC_SARONITE_ANIMUS) + { + m_creature->RemoveAurasDueToSpell(SPELL_SARONITE_BARRIER); + + if (m_creature->IsAlive() && m_pInstance) + m_pInstance->SetData(TYPE_VEZAX_HARD, DONE); + } + } + + void SummonedMovementInform(Creature* pSummoned, uint32 uiMoveType, uint32 uiPointId) override + { + if (uiMoveType != POINT_MOTION_TYPE || uiPointId != 0 || pSummoned->GetEntry() != NPC_SARONITE_VAPOR) + return; + + ++m_uiVaporsGathered; + + // Cast the saronite formation aura when all vapors arive + if (m_uiVaporsGathered == MAX_HARD_MODE_VAPORS) + { + pSummoned->CastSpell(pSummoned, SPELL_ANIMUS_FORMATION, true); + + // Despawn the vapors + for (GuidList::const_iterator itr = m_lVaporsGuids.begin(); itr != m_lVaporsGuids.end(); ++itr) + { + if (Creature* pVapor = m_creature->GetMap()->GetCreature(*itr)) + pVapor->ForcedDespawn(4000); + } + + DoScriptText(EMOTE_ANIMUS, m_creature); + m_uiSaroniteBarrierTimer = 4000; + } + } + + // Merge vapors + void DoPrepareAnimusIfCan() + { + // Gather the vapors to the boss - NOTE: not sure if position is ok + for (GuidList::const_iterator itr = m_lVaporsGuids.begin(); itr != m_lVaporsGuids.end(); ++itr) + { + // Better place: near him or some fixed position? + if (Creature* pVapor = m_creature->GetMap()->GetCreature(*itr)) + pVapor->GetMotionMaster()->MovePoint(0, m_creature->GetPositionX(), m_creature->GetPositionY(), m_creature->GetPositionZ()); + } + } + + void UpdateAI(const uint32 uiDiff) override + { + if (!m_creature->SelectHostileTarget() || !m_creature->getVictim()) + return; + + if (m_uiSaroniteBarrierTimer) + { + if (m_uiSaroniteBarrierTimer <= uiDiff) + { + if (DoCastSpellIfCan(m_creature, SPELL_SARONITE_BARRIER) == CAST_OK) + m_uiSaroniteBarrierTimer = 0; + } + else + m_uiSaroniteBarrierTimer -= uiDiff; + } + + // summon saronite vapors before the hard mode + if (m_pInstance && m_pInstance->GetData(TYPE_VEZAX_HARD) == NOT_STARTED) + { + if (m_uiSaroniteVaporTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature, SPELL_SUMMON_VAPORS) == CAST_OK) + { + DoScriptText(EMOTE_VAPOR, m_creature); + m_uiSaroniteVaporTimer = 30000; + } + } + else + m_uiSaroniteVaporTimer -= uiDiff; + } + + // Searing flames only while animus is not around + if (m_pInstance && m_pInstance->GetData(TYPE_VEZAX_HARD) != IN_PROGRESS) + { + if (m_uiFlamesTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature, SPELL_SEARING_FLAMES) == CAST_OK) + m_uiFlamesTimer = urand(9000, 16000); + } + else + m_uiFlamesTimer -= uiDiff; + } + + if (m_uiSurgeTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature, SPELL_SURGE_OF_DARKNESS) == CAST_OK) + { + DoScriptText(SAY_SURGE, m_creature); + DoScriptText(EMOTE_SURGE, m_creature); + m_uiSurgeTimer = 62000; + } + } + else + m_uiSurgeTimer -= uiDiff; + + if (m_uiMarkTimer < uiDiff) + { + if (Unit* pTarget = m_creature->SelectAttackingTarget(ATTACKING_TARGET_RANDOM, 1)) + { + if (DoCastSpellIfCan(pTarget, SPELL_MARK_OF_FACELESS) == CAST_OK) + m_uiMarkTimer = urand(25000, 30000); + } + } + else + m_uiMarkTimer -= uiDiff; + + if (m_uiCrashTimer < uiDiff) + { + if (Unit* pTarget = m_creature->SelectAttackingTarget(ATTACKING_TARGET_RANDOM, 1)) + { + if (DoCastSpellIfCan(pTarget, SPELL_SHADOW_CRASH) == CAST_OK) + m_uiCrashTimer = 15000; + } + } + else + m_uiCrashTimer -= uiDiff; + + if (m_uiEnrageTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature, SPELL_BERSERK) == CAST_OK) + { + DoScriptText(SAY_ENRAGE, m_creature); + m_uiEnrageTimer = 30000; + } + } + else + m_uiEnrageTimer -= uiDiff; + + DoMeleeAttackIfReady(); + } +}; + +CreatureAI* GetAI_boss_general_vezax(Creature* pCreature) +{ + return new boss_general_vezaxAI(pCreature); +} + +bool ProcessEventId_event_spell_saronite_barrier(uint32 /*uiEventId*/, Object* pSource, Object* /*pTarget*/, bool /*bIsStart*/) +{ + if (pSource->GetTypeId() == TYPEID_UNIT && ((Creature*)pSource)->GetEntry() == NPC_VEZAX) + { + if (instance_ulduar* pInstance = (instance_ulduar*)((Creature*)pSource)->GetInstanceData()) + { + // Start hard mode for Vezax and summon the Animus + pInstance->SetData(TYPE_VEZAX_HARD, IN_PROGRESS); + + ((Creature*)pSource)->CastSpell((Creature*)pSource, SPELL_SUMMON_ANIMUS, true); + DoScriptText(SAY_HARD_MODE, (Creature*)pSource); + + return true; + } + } + return false; +} + +void AddSC_boss_general_vezax() +{ + Script* pNewScript; + + pNewScript = new Script; + pNewScript->Name = "boss_general_vezax"; + pNewScript->GetAI = &GetAI_boss_general_vezax; + pNewScript->RegisterSelf(); + + pNewScript = new Script; + pNewScript->Name = "event_spell_saronite_barrier"; + pNewScript->pProcessEventId = &ProcessEventId_event_spell_saronite_barrier; + pNewScript->RegisterSelf(); +} diff --git a/src/modules/SD2/scripts/northrend/ulduar/ulduar/boss_hodir.cpp b/src/modules/SD2/scripts/northrend/ulduar/ulduar/boss_hodir.cpp new file mode 100644 index 000000000..2c25a41dd --- /dev/null +++ b/src/modules/SD2/scripts/northrend/ulduar/ulduar/boss_hodir.cpp @@ -0,0 +1,45 @@ +/* Copyright (C) 2006 - 2013 ScriptDev2 + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +/* ScriptData +SDName: boss_hodir +SD%Complete: 0% +SDComment: +SDCategory: Ulduar +EndScriptData */ + +#include "precompiled.h" +#include "ulduar.h" + +enum +{ + SAY_AGGRO = -1603086, + SAY_SLAY_1 = -1603087, + SAY_SLAY_2 = -1603088, + SAY_FLASH_FREEZE = -1603089, + SAY_FROZEN_BLOWS = -1603090, + SAY_DEATH = -1603091, + SAY_BERSERK = -1603092, + SAY_HELP_YOGG = -1603093, + + EMOTE_FLASH_FREEZE = -1603094, + EMOTE_FROZEN_BLOWS = -1603095, +}; + +void AddSC_boss_hodir() +{ + +} diff --git a/src/modules/SD2/scripts/northrend/ulduar/ulduar/boss_ignis.cpp b/src/modules/SD2/scripts/northrend/ulduar/ulduar/boss_ignis.cpp new file mode 100644 index 000000000..655548633 --- /dev/null +++ b/src/modules/SD2/scripts/northrend/ulduar/ulduar/boss_ignis.cpp @@ -0,0 +1,353 @@ +/* Copyright (C) 2006 - 2013 ScriptDev2 + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +/* ScriptData +SDName: boss_ignis +SD%Complete: 100% +SDComment: +SDCategory: Ulduar +EndScriptData */ + +#include "precompiled.h" +#include "ulduar.h" + +enum +{ + SAY_AGGRO = -1603026, + SAY_SLAGPOT_1 = -1603027, + SAY_SLAGPOT_2 = -1603028, + SAY_SLAGPOT_3 = -1603029, + SAY_ADDS = -1603030, + SAY_SLAY_1 = -1603031, + SAY_SLAY_2 = -1603032, + SAY_BERSERK = -1603033, + SAY_DEATH = -1603034, + + EMOTE_FLAME_JETS = -1603035, + + // spells + SPELL_FLAME_JETS = 62680, + SPELL_FLAME_JETS_H = 63472, + SPELL_SLAG_POT = 62717, // damage aura applied when passenger is switched to second seat + // SPELL_SLAG_IMBUED = 63536, // buff received if target survives the slag pot + SPELL_GRAB = 62707, // charge spells for Slag pot - triggers 62708 which will load the player into Ingis' hand (seat 1) + SPELL_GRAB_POT = 62711, // aura triggered after 1,5 sec after the first grab; switches the seats from hand to pot (seat 2) + SPELL_SCORCH = 62546, + SPELL_SCORCH_H = 63474, + SPELL_SCORCH_SUMMON = 62551, // summons npc 33221 + SPELL_ACTIVATE_CONSTRUCT = 62488, // activates constructs and set them in combat (handled in core) + SPELL_KILL_ALL_CONSTRUCTS = 65109, // on death + SPELL_BERSERK = 26662, + + // iron construct + SPELL_CONSTRUCT_HITTING_YA = 65110, // procs on melee damage; purpose unk + SPELL_STONED = 62468, + SPELL_HEAT = 65667, // stackable aura which heats the construct + SPELL_MOLTEN = 62373, + SPELL_BRITTLE = 62382, + SPELL_BRITTLE_H = 67114, + SPELL_SHATTER = 62383, // sends event 21620 for the achiev check + SPELL_STRENGTH_REMOVE = 64475, + // SPELL_WATER = 64502, // cast by world triggers, in order to check when the constructs reach the water (handled in core) + + // scorch target + SPELL_SCORCH_AURA = 62548, + SPELL_SCORCH_AURA_H = 63476, + + // NPC ids + NPC_SCORCH = 33221, + + MAX_HEAT_STACKS = 10, +}; + +/*###### +## boss_ignis +######*/ + +struct boss_ignisAI : public ScriptedAI +{ + boss_ignisAI(Creature* pCreature) : ScriptedAI(pCreature) + { + m_pInstance = (instance_ulduar*)pCreature->GetInstanceData(); + m_bIsRegularMode = pCreature->GetMap()->IsRegularDifficulty(); + Reset(); + } + + instance_ulduar* m_pInstance; + bool m_bIsRegularMode; + + uint32 m_uiBerserkTimer; + uint32 m_uiFlameJetsTimer; + uint32 m_uiSlagPotTimer; + uint32 m_uiScorchTimer; + uint32 m_uiConstructTimer; + + void Reset() override + { + m_uiBerserkTimer = 10 * MINUTE * IN_MILLISECONDS; + m_uiFlameJetsTimer = 20000; + m_uiSlagPotTimer = 25000; + m_uiScorchTimer = 13000; + m_uiConstructTimer = 10000; + } + + void JustDied(Unit* pKiller) override + { + if (m_pInstance) + m_pInstance->SetData(TYPE_IGNIS, DONE); + + DoCastSpellIfCan(m_creature, SPELL_KILL_ALL_CONSTRUCTS, CAST_TRIGGERED); + DoScriptText(SAY_DEATH, m_creature); + } + + void KilledUnit(Unit* pVictim) override + { + if (pVictim->GetTypeId() != TYPEID_PLAYER) + return; + + DoScriptText(urand(0, 1) ? SAY_SLAY_1 : SAY_SLAY_2, m_creature); + } + + void Aggro(Unit* pWho) override + { + if (m_pInstance) + m_pInstance->SetData(TYPE_IGNIS, IN_PROGRESS); + + DoScriptText(SAY_AGGRO, m_creature); + } + + void JustReachedHome() override + { + if (m_pInstance) + m_pInstance->SetData(TYPE_IGNIS, FAIL); + } + + void JustSummoned(Creature* pSummoned) override + { + if (pSummoned->GetEntry() == NPC_SCORCH) + pSummoned->CastSpell(pSummoned, m_bIsRegularMode ? SPELL_SCORCH_AURA : SPELL_SCORCH_AURA_H, true); + } + + // TODO: Use the vehicle boarding wrappers when they are implemented in core + void SpellHit(Unit* pCaster, const SpellEntry* pSpell) override + { + if (pCaster->GetTypeId() != TYPEID_PLAYER) + return; + + // Handle the case when passenger is loaded to the second seat + if (pSpell->Id == SPELL_GRAB_POT) + DoCastSpellIfCan(pCaster, SPELL_SLAG_POT, CAST_TRIGGERED); + } + + void UpdateAI(const uint32 uiDiff) override + { + if (!m_creature->SelectHostileTarget() || !m_creature->getVictim()) + return; + + if (m_uiBerserkTimer) + { + if (m_uiBerserkTimer <= uiDiff) + { + if (DoCastSpellIfCan(m_creature, SPELL_BERSERK) == CAST_OK) + { + DoScriptText(SAY_BERSERK, m_creature); + m_uiBerserkTimer = 0; + } + } + else + m_uiBerserkTimer -= uiDiff; + } + + if (m_uiFlameJetsTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature, m_bIsRegularMode ? SPELL_FLAME_JETS : SPELL_FLAME_JETS_H) == CAST_OK) + { + DoScriptText(EMOTE_FLAME_JETS, m_creature); + m_uiFlameJetsTimer = 35000; + } + } + else + m_uiFlameJetsTimer -= uiDiff; + + if (m_uiSlagPotTimer < uiDiff) + { + if (Unit* pTarget = m_creature->SelectAttackingTarget(ATTACKING_TARGET_RANDOM, 1, SPELL_GRAB, SELECT_FLAG_PLAYER)) + { + if (DoCastSpellIfCan(pTarget, SPELL_GRAB) == CAST_OK) + { + switch (urand(0, 2)) + { + case 0: DoScriptText(SAY_SLAGPOT_1, m_creature); break; + case 1: DoScriptText(SAY_SLAGPOT_2, m_creature); break; + case 2: DoScriptText(SAY_SLAGPOT_3, m_creature); break; + } + m_uiSlagPotTimer = 30000; + } + } + } + else + m_uiSlagPotTimer -= uiDiff; + + if (m_uiConstructTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature, SPELL_ACTIVATE_CONSTRUCT) == CAST_OK) + { + DoScriptText(SAY_ADDS, m_creature); + m_uiConstructTimer = 40000; + } + } + else + m_uiConstructTimer -= uiDiff; + + if (m_uiScorchTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature, m_bIsRegularMode ? SPELL_SCORCH : SPELL_SCORCH_H) == CAST_OK) + { + DoCastSpellIfCan(m_creature, SPELL_SCORCH_SUMMON, CAST_TRIGGERED); + m_uiScorchTimer = 25000; + } + } + else + m_uiScorchTimer -= uiDiff; + + DoMeleeAttackIfReady(); + } +}; + +CreatureAI* GetAI_boss_ignis(Creature* pCreature) +{ + return new boss_ignisAI(pCreature); +} + +/*###### +## npc_iron_construct +######*/ + +struct npc_iron_constructAI : public ScriptedAI +{ + npc_iron_constructAI(Creature* pCreature) : ScriptedAI(pCreature) + { + m_bIsRegularMode = pCreature->GetMap()->IsRegularDifficulty(); + Reset(); + } + + bool m_bIsRegularMode; + bool m_bHasShattered; + + void Reset() override + { + m_bHasShattered = false; + + DoCastSpellIfCan(m_creature, SPELL_STONED, CAST_TRIGGERED); + DoCastSpellIfCan(m_creature, SPELL_CONSTRUCT_HITTING_YA, CAST_TRIGGERED); + } + + void JustReachedHome() override + { + // reset flags if necessary + if (m_creature->HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE)) + m_creature->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE); + + DoCastSpellIfCan(m_creature, SPELL_STONED, CAST_TRIGGERED); + DoCastSpellIfCan(m_creature, SPELL_CONSTRUCT_HITTING_YA, CAST_TRIGGERED); + } + + void DamageTaken(Unit* pDoneBy, uint32 &uiDamage) override + { + // ToDo: This may need more research related to spell proc + if (m_creature->HasAura(m_bIsRegularMode ? SPELL_BRITTLE : SPELL_BRITTLE_H) && !m_bHasShattered) + { + if (uiDamage > 5000) + { + DoCastSpellIfCan(m_creature, SPELL_SHATTER, CAST_TRIGGERED); + DoCastSpellIfCan(m_creature, SPELL_STRENGTH_REMOVE, CAST_TRIGGERED); + + m_creature->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE); + m_creature->GetMotionMaster()->MoveIdle(); + m_bHasShattered = true; + } + } + } + + void SpellHit(Unit* pCaster, const SpellEntry* pSpell) override + { + if (pSpell->Id == SPELL_HEAT) + { + if (SpellAuraHolder* pHeatAura = m_creature->GetSpellAuraHolder(SPELL_HEAT)) + { + if (pHeatAura && pHeatAura->GetStackAmount() == MAX_HEAT_STACKS) + DoCastSpellIfCan(m_creature, SPELL_MOLTEN); + } + } + } + + void UpdateAI(const uint32 /*uiDiff*/) override + { + if (!m_creature->SelectHostileTarget() || !m_creature->getVictim()) + return; + + // stop attacking after shattered + if (m_bHasShattered) + return; + + DoMeleeAttackIfReady(); + } +}; + +CreatureAI* GetAI_npc_iron_construct(Creature* pCreature) +{ + return new npc_iron_constructAI(pCreature); +} + +/*###### +## npc_scorch +######*/ + +// TODO Remove this 'script' when combat can be proper prevented from core-side +struct npc_scorchAI : public Scripted_NoMovementAI +{ + npc_scorchAI(Creature* pCreature) : Scripted_NoMovementAI(pCreature) { Reset(); } + + void Reset() override { } + void AttackStart(Unit* /*pWho*/) override { } + void MoveInLineOfSight(Unit* /*pWho*/) override { } + void UpdateAI(const uint32 /*uiDiff*/) override { } +}; + +CreatureAI* GetAI_npc_scorch(Creature* pCreature) +{ + return new npc_scorchAI(pCreature); +} + +void AddSC_boss_ignis() +{ + Script* pNewScript; + + pNewScript = new Script; + pNewScript->Name = "boss_ignis"; + pNewScript->GetAI = GetAI_boss_ignis; + pNewScript->RegisterSelf(); + + pNewScript = new Script; + pNewScript->Name = "npc_iron_construct"; + pNewScript->GetAI = &GetAI_npc_iron_construct; + pNewScript->RegisterSelf(); + + pNewScript = new Script; + pNewScript->Name = "npc_scorch"; + pNewScript->GetAI = &GetAI_npc_scorch; + pNewScript->RegisterSelf(); +} diff --git a/src/modules/SD2/scripts/northrend/ulduar/ulduar/boss_kologarn.cpp b/src/modules/SD2/scripts/northrend/ulduar/ulduar/boss_kologarn.cpp new file mode 100644 index 000000000..7dff7b709 --- /dev/null +++ b/src/modules/SD2/scripts/northrend/ulduar/ulduar/boss_kologarn.cpp @@ -0,0 +1,47 @@ +/* Copyright (C) 2006 - 2013 ScriptDev2 + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +/* ScriptData +SDName: boss_kologarn +SD%Complete: 0% +SDComment: +SDCategory: Ulduar +EndScriptData */ + +#include "precompiled.h" +#include "ulduar.h" + +enum +{ + SAY_AGGRO = -1603126, + SAY_SHOCKWAVE = -1603127, + SAY_GRAB = -1603128, + SAY_ARM_LOST_LEFT = -1603129, + SAY_ARM_LOST_RIGHT = -1603130, + SAY_SLAY_1 = -1603131, + SAY_SLAY_2 = -1603132, + SAY_BERSERK = -1603133, + SAY_DEATH = -1603134, + + EMOTE_ARM_RIGHT = -1603135, + EMOTE_ARM_LEFT = -1603136, + EMOTE_STONE_GRIP = -1603137, +}; + +void AddSC_boss_kologarn() +{ + +} diff --git a/src/modules/SD2/scripts/northrend/ulduar/ulduar/boss_mimiron.cpp b/src/modules/SD2/scripts/northrend/ulduar/ulduar/boss_mimiron.cpp new file mode 100644 index 000000000..80def03e4 --- /dev/null +++ b/src/modules/SD2/scripts/northrend/ulduar/ulduar/boss_mimiron.cpp @@ -0,0 +1,61 @@ +/* Copyright (C) 2006 - 2013 ScriptDev2 + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +/* ScriptData +SDName: boss_mimiron +SD%Complete: 0% +SDComment: +SDCategory: Ulduar +EndScriptData */ + +#include "precompiled.h" +#include "ulduar.h" + +enum +{ + SAY_AGGRO = -1603176, + SAY_HARD_MODE = -1603177, + SAY_BERSERK = -1603178, + + SAY_TANK_ACTIVE = -1603179, + SAY_TANK_SLAY_1 = -1603180, + SAY_TANK_SLAY_2 = -1603181, + SAY_TANK_DEATH = -1603182, + + SAY_TORSO_ACTIVE = -1603183, + SAY_TORSO_SLAY_1 = -1603184, + SAY_TORSO_SLAY_2 = -1603185, + SAY_TORSO_DEATH = -1603186, + + SAY_HEAD_ACTIVE = -1603187, + SAY_HEAD_SLAY_1 = -1603188, + SAY_HEAD_SLAY_2 = -1603189, + SAY_HEAD_DEATH = -1603190, + + SAY_ROBOT_ACTIVE = -1603191, + SAY_ROBOT_SLAY_1 = -1603192, + SAY_ROBOT_SLAY_2 = -1603193, + SAY_ROBOT_DEATH = -1603194, + + SAY_HELP_YOGG = -1603195, + + EMOTE_PLASMA_BLAST = -1603196, +}; + +void AddSC_boss_mimiron() +{ + +} diff --git a/src/modules/SD2/scripts/northrend/ulduar/ulduar/boss_razorscale.cpp b/src/modules/SD2/scripts/northrend/ulduar/ulduar/boss_razorscale.cpp new file mode 100644 index 000000000..0829694e1 --- /dev/null +++ b/src/modules/SD2/scripts/northrend/ulduar/ulduar/boss_razorscale.cpp @@ -0,0 +1,44 @@ +/* Copyright (C) 2006 - 2013 ScriptDev2 + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +/* ScriptData +SDName: boss_razorscale +SD%Complete: 0% +SDComment: +SDCategory: Ulduar +EndScriptData */ + +#include "precompiled.h" +#include "ulduar.h" + +enum +{ + SAY_INTRO_WELCOME = -1603036, + SAY_INTRO_1 = -1603037, + SAY_INTRO_2 = -1603038, + SAY_INTRO_3 = -1603039, + SAY_GROUNDED = -1603040, + SAY_EXTINGUISH_FIRE = -1603041, + + EMOTE_BREATH = -1603042, + EMOTE_HARPOON_READY = -1603043, + EMOTE_GROUNDED = -1603044, +}; + +void AddSC_boss_razorscale() +{ + +} diff --git a/src/modules/SD2/scripts/northrend/ulduar/ulduar/boss_thorim.cpp b/src/modules/SD2/scripts/northrend/ulduar/ulduar/boss_thorim.cpp new file mode 100644 index 000000000..c6567bd7c --- /dev/null +++ b/src/modules/SD2/scripts/northrend/ulduar/ulduar/boss_thorim.cpp @@ -0,0 +1,61 @@ +/* Copyright (C) 2006 - 2013 ScriptDev2 + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +/* ScriptData +SDName: boss_thorim +SD%Complete: 0% +SDComment: +SDCategory: Ulduar +EndScriptData */ + +#include "precompiled.h" +#include "ulduar.h" + +enum +{ + SAY_AGGRO_1 = -1603138, + SAY_AGGRO_2 = -1603139, + SAY_SPECIAL_1 = -1603140, + SAY_SPECIAL_2 = -1603141, + SAY_SPECIAL_3 = -1603142, + SAY_JUMP = -1603143, + + SAY_SLAY_1 = -1603144, + SAY_SLAY_2 = -1603145, + SAY_BERSERK = -1603146, + + SAY_ARENA_WIPE = -1603147, + SAY_DEFEATED = -1603148, + + SAY_OUTRO_1 = -1603149, + SAY_OUTRO_2 = -1603150, + SAY_OUTRO_3 = -1603151, + + SAY_OUTRO_HARD_1 = -1603152, + SAY_OUTRO_HARD_2 = -1603153, + SAY_OUTRO_HARD_3 = -1603154, + + SAY_HELP_YOGG = -1603155, + + SAY_SIF_BEGIN = -1603156, + SAY_SIF_EVENT = -1603157, + SAY_SIF_DESPAWN = -1603158, +}; + +void AddSC_boss_thorim() +{ + +} diff --git a/src/modules/SD2/scripts/northrend/ulduar/ulduar/boss_xt_002.cpp b/src/modules/SD2/scripts/northrend/ulduar/ulduar/boss_xt_002.cpp new file mode 100644 index 000000000..0b378a332 --- /dev/null +++ b/src/modules/SD2/scripts/northrend/ulduar/ulduar/boss_xt_002.cpp @@ -0,0 +1,197 @@ +/* Copyright (C) 2006 - 2013 ScriptDev2 + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +/* ScriptData +SDName: boss_xt_002 +SD%Complete: 20% +SDComment: +SDCategory: Ulduar +EndScriptData */ + +#include "precompiled.h" +#include "ulduar.h" + +enum +{ + SAY_AGGRO = -1603045, + SAY_SLAY_1 = -1603046, + SAY_SLAY_2 = -1603047, + SAY_BERSERK = -1603048, + SAY_ADDS = -1603049, + SAY_DEATH = -1603050, + SAY_HEART_OPEN = -1603051, + SAY_HEART_CLOSE = -1603052, + SAY_TANCTRUM = -1603053, + + EMOTE_HEART = -1603054, + EMOTE_REPAIR = -1603055, + + // spells + SPELL_TANCTRUM = 62776, + SPELL_SEARING_LIGHT = 63018, + SPELL_SEARING_LIGHT_H = 65121, + SPELL_GRAVITY_BOMB = 63024, + SPELL_GRAVITY_BOMB_H = 64234, + SPELL_BERSERK = 26662, + + // hard mode spells + SPELL_HEARTBREAK = 65737, + SPELL_HEARTBREAK_H = 64193, + SPELL_VOIDZONE = 64203, + SPELL_VOIDZONE_H = 64235, + SPELL_LIFE_SPARK = 64210, + + // heart of XT002 spells + SPELL_HEART_RIDE_VEHICLE = 63852, // ride seat 1 - procs on damage (probably spell 17683) + SPELL_RIDE_VEHICLE = 63313, // ride seat 2 + SPELL_LIGHTNING_TETHER = 64799, // dummy, triggers 62789 and 63849 + SPELL_HEART_OVERLOAD = 62789, // triggers missing spell 62791 - used to spawn adds + SPELL_EXPOSED_HEART = 63849, // procs on damage + SPELL_ENERGY_ORB = 62790, // targets 33337, in order to start spawning robots + + // robot summoning spells + SPELL_RECHARGE_ROBOT_1 = 62828, // summons 33343 + SPELL_RECHARGE_ROBOT_2 = 62835, // summons 33346 + SPELL_RECHARGE_ROBOT_3 = 62831, // summons 33344 + + // summoned spells + SPELL_CONSUMPTION = 64208, // cast by the void zone + SPELL_ARCANE_POWER_STATE = 49411, // cast by the life spark + SPELL_STATIC_CHARGED = 64227, // cast by the life spark (needs to be confirmed) + SPELL_STATIC_CHARGED_H = 64236, + + // NPC ids + NPC_SCRAPBOT = 33343, + NPC_BOOMBOT = 33346, + NPC_PUMMELLER = 33344, + NPC_VOIDZONE = 34001, + NPC_LIFE_SPARK = 34004, + NPC_XT_TOY_PILE = 33337, +}; + +/*###### +## boss_xt_002 +######*/ + +struct boss_xt_002AI : public ScriptedAI +{ + boss_xt_002AI(Creature* pCreature) : ScriptedAI(pCreature) + { + m_pInstance = (instance_ulduar*)pCreature->GetInstanceData(); + m_bIsRegularMode = pCreature->GetMap()->IsRegularDifficulty(); + m_uiMountTimer = 1000; + Reset(); + } + + instance_ulduar* m_pInstance; + bool m_bIsRegularMode; + + uint32 m_uiBerserkTimer; + uint32 m_uiMountTimer; + + void Reset() override + { + m_uiBerserkTimer = 10 * MINUTE * IN_MILLISECONDS; + } + + void JustDied(Unit* pKiller) override + { + if (m_pInstance) + m_pInstance->SetData(TYPE_XT002, DONE); + + DoScriptText(SAY_DEATH, m_creature); + } + + void KilledUnit(Unit* pVictim) override + { + if (pVictim->GetTypeId() != TYPEID_PLAYER) + return; + + DoScriptText(urand(0, 1) ? SAY_SLAY_1 : SAY_SLAY_2, m_creature); + } + + void Aggro(Unit* pWho) override + { + if (m_pInstance) + m_pInstance->SetData(TYPE_XT002, IN_PROGRESS); + + DoScriptText(SAY_AGGRO, m_creature); + } + + void JustReachedHome() override + { + if (m_pInstance) + m_pInstance->SetData(TYPE_XT002, FAIL); + } + + void JustSummoned(Creature* pSummoned) override + { + } + + void UpdateAI(const uint32 uiDiff) override + { + if (!m_pInstance) + return; + + // The heart needs to be mounted manually, not by vehicle_accessories + if (m_uiMountTimer) + { + if (m_uiMountTimer <= uiDiff) + { + if (Creature* pHeart = m_pInstance->GetSingleCreatureFromStorage(NPC_HEART_DECONSTRUCTOR)) + pHeart->CastSpell(m_creature, SPELL_HEART_RIDE_VEHICLE, true); + + m_uiMountTimer = 0; + } + else + m_uiMountTimer -= uiDiff; + } + + if (!m_creature->SelectHostileTarget() || !m_creature->getVictim()) + return; + + if (m_uiBerserkTimer) + { + if (m_uiBerserkTimer <= uiDiff) + { + if (DoCastSpellIfCan(m_creature, SPELL_BERSERK) == CAST_OK) + { + DoScriptText(SAY_BERSERK, m_creature); + m_uiBerserkTimer = 0; + } + } + else + m_uiBerserkTimer -= uiDiff; + } + + DoMeleeAttackIfReady(); + } +}; + +CreatureAI* GetAI_boss_xt_002(Creature* pCreature) +{ + return new boss_xt_002AI(pCreature); +} + +void AddSC_boss_xt_002() +{ + Script* pNewScript; + + pNewScript = new Script; + pNewScript->Name = "boss_xt_002"; + pNewScript->GetAI = GetAI_boss_xt_002; + pNewScript->RegisterSelf(); +} diff --git a/src/modules/SD2/scripts/northrend/ulduar/ulduar/boss_yogg_saron.cpp b/src/modules/SD2/scripts/northrend/ulduar/ulduar/boss_yogg_saron.cpp new file mode 100644 index 000000000..ef25dca69 --- /dev/null +++ b/src/modules/SD2/scripts/northrend/ulduar/ulduar/boss_yogg_saron.cpp @@ -0,0 +1,78 @@ +/* Copyright (C) 2006 - 2013 ScriptDev2 + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +/* ScriptData +SDName: boss_yogg_saron +SD%Complete: 0% +SDComment: +SDCategory: Ulduar +EndScriptData */ + +#include "precompiled.h" +#include "ulduar.h" + +enum +{ + SAY_SARA_INTRO_1 = -1603197, + SAY_SARA_INTRO_2 = -1603198, + SAY_SARA_AGGRO = -1603199, + SAY_SARA_HELP_1 = -1603200, + SAY_SARA_HELP_2 = -1603201, + SAY_SARA_SLAY_1 = -1603202, + SAY_SARA_SLAY_2 = -1603203, + SAY_WIPE_PHASE_1 = -1603204, + + SAY_PHASE_2_INTRO = -1603205, + SAY_SARA_PHASE_2_INTRO_A = -1603206, + SAY_SARA_PHASE_2_INTRO_B = -1603207, + + SAY_MADNESS = -1603209, + SAY_PHASE_3 = -1603210, + SAY_SLAY_1 = -1603211, + SAY_SLAY_2 = -1603212, + SAY_DEATH = -1603213, + SAY_TO_INSANE_1 = -1603214, + SAY_TO_INSANE_2 = -1603215, + + SAY_LICH_KING_1 = -1603216, + SAY_CHAMPION_1 = -1603217, + SAY_CHAMPION_2 = -1603218, + SAY_LICH_KING_2 = -1603219, + SAY_YOGG_V3_1 = -1603220, + SAY_YOGG_V3_2 = -1603221, + + SAY_NELTHARION_1 = -1603222, + SAY_YSERA = -1603223, + SAY_NELTHARION_2 = -1603224, + SAY_MALYGOS = -1603225, + SAY_YOGG_V2 = -1603226, + + SAY_GARONA_1 = -1603227, + SAY_GARONA_2 = -1603228, + SAY_YOGG_V1_1 = -1603229, + SAY_YOGG_V1_2 = -1603230, + SAY_GARONA_3 = -1603231, + SAY_GARONA_4 = -1603232, + SAY_YOGG_V1_3 = -1603233, + + EMOTE_VISION_BLAST = -1603234, + EMOTE_SHATTER_BLAST = -1603235, +}; + +void AddSC_boss_yogg_saron() +{ + +} diff --git a/src/modules/SD2/scripts/northrend/ulduar/ulduar/instance_ulduar.cpp b/src/modules/SD2/scripts/northrend/ulduar/ulduar/instance_ulduar.cpp new file mode 100644 index 000000000..d02f32956 --- /dev/null +++ b/src/modules/SD2/scripts/northrend/ulduar/ulduar/instance_ulduar.cpp @@ -0,0 +1,704 @@ +/* Copyright (C) 2006 - 2013 ScriptDev2 +* This program is free software; you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation; either version 2 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along 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_ulduar +SD%Complete: +SDComment: +SDCategory: Ulduar +EndScriptData */ + +#include "precompiled.h" +#include "ulduar.h" + +struct sSpawnLocation +{ + float m_fX, m_fY, m_fZ, m_fO; +}; + +static sSpawnLocation m_aKeepersSpawnLocs[] = +{ + {2036.892f, 25.621f, 411.358f, 3.83f}, // Freya + {1939.215f, 42.677f, 411.355f, 5.31f}, // Mimiron + {1939.195f, -90.662f, 411.357f, 1.06f}, // Hodir + {2036.674f, -73.814f, 411.355f, 2.51f}, // Thorim +}; + +instance_ulduar::instance_ulduar(Map* pMap) : ScriptedInstance(pMap) +{ + Initialize(); +} + +void instance_ulduar::Initialize() +{ + memset(&m_auiEncounter, 0, sizeof(m_auiEncounter)); + memset(&m_auiHardBoss, 0, sizeof(m_auiHardBoss)); + memset(&m_auiUlduarKeepers, 0, sizeof(m_auiUlduarKeepers)); + + for (uint8 i = 0; i < MAX_SPECIAL_ACHIEV_CRITS; ++i) + m_abAchievCriteria[i] = false; +} + +bool instance_ulduar::IsEncounterInProgress() const +{ + for (uint8 i = 0; i < MAX_ENCOUNTER; ++i) + { + if (m_auiEncounter[i] == IN_PROGRESS) + return true; + } + return false; +} + +void instance_ulduar::OnCreatureCreate(Creature* pCreature) +{ + switch (pCreature->GetEntry()) + { + case NPC_LEVIATHAN: + case NPC_IGNIS: + case NPC_RAZORSCALE: + case NPC_COMMANDER: + case NPC_XT002: + case NPC_HEART_DECONSTRUCTOR: + case NPC_STEELBREAKER: + case NPC_MOLGEIM: + case NPC_BRUNDIR: + case NPC_KOLOGARN: + case NPC_RIGHT_ARM: + case NPC_LEFT_ARM: + case NPC_AURIAYA: + case NPC_FERAL_DEFENDER: + case NPC_LEVIATHAN_MK: + case NPC_RUNIC_COLOSSUS: + case NPC_RUNE_GIANT: + case NPC_JORMUNGAR_BEHEMOTH: + case NPC_ELDER_BRIGHTLEAF: + case NPC_ELDER_IRONBRACH: + case NPC_ELDER_STONEBARK: + case NPC_VEZAX: + case NPC_SARONITE_ANIMUS: + case NPC_YOGGSARON: + case NPC_SARA: + case NPC_YOGG_BRAIN: + case NPC_ALGALON: + break; + + case NPC_MIMIRON: + if (m_auiEncounter[TYPE_MIMIRON] == DONE) + SpawnFriendlyKeeper(NPC_MIMIRON_IMAGE); + break; + case NPC_HODIR: + if (m_auiEncounter[TYPE_HODIR] == DONE) + SpawnFriendlyKeeper(NPC_HODIR_IMAGE); + break; + case NPC_THORIM: + if (m_auiEncounter[TYPE_THORIM] == DONE) + SpawnFriendlyKeeper(NPC_THORIM_IMAGE); + break; + case NPC_FREYA: + if (m_auiEncounter[TYPE_FREYA] == DONE) + SpawnFriendlyKeeper(NPC_FREYA_IMAGE); + break; + + default: + return; + } + m_mNpcEntryGuidStore[pCreature->GetEntry()] = pCreature->GetObjectGuid(); +} + +void instance_ulduar::OnObjectCreate(GameObject* pGo) +{ + switch (pGo->GetEntry()) + { + // ----------------- Doors & Other ----------------- + // The siege + case GO_SHIELD_WALL: + break; + case GO_LEVIATHAN_GATE: + if (m_auiEncounter[TYPE_LEVIATHAN] == DONE) + pGo->SetGoState(GO_STATE_ACTIVE); + break; + case GO_XT002_GATE: + pGo->SetGoState(GO_STATE_READY); + if (m_auiEncounter[TYPE_XT002] == DONE) + pGo->SetGoState(GO_STATE_ACTIVE); + if (m_auiEncounter[TYPE_LEVIATHAN] == DONE) + pGo->SetGoState(GO_STATE_ACTIVE); + break; + case GO_BROKEN_HARPOON: + pGo->SetFlag(GAMEOBJECT_FLAGS, GO_FLAG_NO_INTERACT); + break; + + // Archivum + case GO_IRON_ENTRANCE_DOOR: + break; + case GO_ARCHIVUM_DOOR: + if (m_auiEncounter[TYPE_ASSEMBLY]) + pGo->SetGoState(GO_STATE_ACTIVE); + break; + case GO_ARCHIVUM_CONSOLE: + case GO_UNIVERSE_FLOOR_ARCHIVUM: + // Celestial Planetarium + case GO_CELESTIAL_ACCES: + case GO_CELESTIAL_DOOR: + case GO_UNIVERSE_FLOOR_CELESTIAL: + case GO_AZEROTH_GLOBE: + break; + // Shattered Hallway + case GO_KOLOGARN_BRIDGE: + pGo->SetGoState(GO_STATE_ACTIVE); + if (m_auiEncounter[TYPE_KOLOGARN] == DONE) + pGo->SetGoState(GO_STATE_READY); + break; + case GO_SHATTERED_DOOR: + break; + + // ----------------- The Keepers ----------------- + // Hodir + case GO_HODIR_EXIT: + if (m_auiEncounter[TYPE_HODIR]) + pGo->SetGoState(GO_STATE_ACTIVE); + break; + case GO_HODIR_ICE_WALL: + if (m_auiEncounter[TYPE_HODIR]) + pGo->SetGoState(GO_STATE_ACTIVE); + break; + case GO_HODIR_ENTER: + break; + // Mimiron + case G0_MIMIRON_BUTTON: + if (m_auiEncounter[TYPE_MIMIRON] == NOT_STARTED) + pGo->RemoveFlag(GAMEOBJECT_FLAGS, GO_FLAG_NO_INTERACT); + break; + case GO_MIMIRON_DOOR_1: + case GO_MIMIRON_DOOR_2: + case GO_MIMIRON_DOOR_3: + case GO_MIMIRON_ELEVATOR: + case GO_MIMIRON_TEL1: + case GO_MIMIRON_TEL2: + case GO_MIMIRON_TEL3: + case GO_MIMIRON_TEL4: + case GO_MIMIRON_TEL5: + case GO_MIMIRON_TEL6: + case GO_MIMIRON_TEL7: + case GO_MIMIRON_TEL8: + case GO_MIMIRON_TEL9: + // Thorim + case GO_DARK_IRON_PORTCULIS: + case GO_RUNED_STONE_DOOR: + case GO_THORIM_STONE_DOOR: + case GO_LIGHTNING_FIELD: + break; + case GO_DOOR_LEVER: + pGo->SetFlag(GAMEOBJECT_FLAGS, GO_FLAG_NO_INTERACT); + break; + + // Prison + case GO_ANCIENT_GATE: + DoOpenMadnessDoorIfCan(); + break; + case GO_VEZAX_GATE: + pGo->SetGoState(GO_STATE_READY); + if (m_auiEncounter[TYPE_VEZAX]) + pGo->SetGoState(GO_STATE_ACTIVE); + break; + case GO_YOGG_GATE: + case GO_BRAIN_DOOR1: + case GO_BRAIN_DOOR2: + case GO_BRAIN_DOOR3: + break; + + // ----------------- Chests ----------------- + // Kologarn + case GO_CACHE_OF_LIVING_STONE_10: + case GO_CACHE_OF_LIVING_STONE_25: + + // Hodir + case GO_CACHE_OF_WINTER_10: + case GO_CACHE_OF_WINTER_25: + case GO_CACHE_OF_RARE_WINTER_10: + case GO_CACHE_OF_RARE_WINTER_25: + + // Thorim + case GO_CACHE_OF_STORMS_10: + case GO_CACHE_OF_STORMS_25: + case GO_CACHE_OF_STORMS_10_H: + case GO_CACHE_OF_STORMS_25_H: + + // Mimiron + case GO_CACHE_OF_INOV_10: + case GO_CACHE_OF_INOV_25: + case GO_CACHE_OF_INOV_10_H: + case GO_CACHE_OF_INOV_25_H: + + // Alagon + case GO_GIFT_OF_OBSERVER_10: + case GO_GIFT_OF_OBSERVER_25: + break; + + default: + return; + } + m_mGoEntryGuidStore[pGo->GetEntry()] = pGo->GetObjectGuid(); +} + +// Used in order to unlock the door to Vezax +void instance_ulduar::DoOpenMadnessDoorIfCan() +{ + if (m_auiEncounter[TYPE_MIMIRON] == DONE && m_auiEncounter[TYPE_HODIR] == DONE && m_auiEncounter[TYPE_THORIM] == DONE && m_auiEncounter[TYPE_FREYA] == DONE) + { + if (GameObject* pDoor = GetSingleGameObjectFromStorage(GO_ANCIENT_GATE)) + pDoor->SetGoState(GO_STATE_ACTIVE); + } +} + +void instance_ulduar::SetData(uint32 uiType, uint32 uiData) +{ + switch (uiType) + { + case TYPE_LEVIATHAN: + m_auiEncounter[uiType] = uiData; + DoUseDoorOrButton(GO_SHIELD_WALL); + if (uiData == DONE) + { + DoUseDoorOrButton(GO_XT002_GATE); + DoUseDoorOrButton(GO_LIGHTNING_FIELD); + } + break; + case TYPE_IGNIS: + m_auiEncounter[uiType] = uiData; + if (uiData == IN_PROGRESS) + { + DoStartTimedAchievement(ACHIEVEMENT_CRITERIA_TYPE_KILL_CREATURE, ACHIEV_START_IGNIS_ID); + SetSpecialAchievementCriteria(TYPE_ACHIEV_SHATTERED, false); + } + break; + case TYPE_RAZORSCALE: + m_auiEncounter[uiType] = uiData; + break; + case TYPE_XT002: + m_auiEncounter[uiType] = uiData; + DoUseDoorOrButton(GO_XT002_GATE); + if (uiData == IN_PROGRESS) + DoStartTimedAchievement(ACHIEVEMENT_CRITERIA_TYPE_KILL_CREATURE, ACHIEV_START_XT002_ID); + break; + case TYPE_ASSEMBLY: + // Don't set the same encounter data twice + if (uiData == m_auiEncounter[uiType]) + return; + m_auiEncounter[uiType] = uiData; + // don't continue for encounter = special + if (uiData == SPECIAL) + return; + DoUseDoorOrButton(GO_IRON_ENTRANCE_DOOR); + if (uiData == DONE) + DoUseDoorOrButton(GO_ARCHIVUM_DOOR); + else if (uiData == IN_PROGRESS) + { + SetSpecialAchievementCriteria(TYPE_ACHIEV_BRUNDIR, true); + SetSpecialAchievementCriteria(TYPE_ACHIEV_MOLGEIM, true); + SetSpecialAchievementCriteria(TYPE_ACHIEV_STEELBREAKER, true); + SetSpecialAchievementCriteria(TYPE_ACHIEV_STUNNED, true); + } + break; + case TYPE_KOLOGARN: + m_auiEncounter[uiType] = uiData; + DoUseDoorOrButton(GO_SHATTERED_DOOR); + if (uiData == DONE) + { + DoRespawnGameObject(instance->IsRegularDifficulty() ? GO_CACHE_OF_LIVING_STONE_10 : GO_CACHE_OF_LIVING_STONE_25, 30 * MINUTE); + if (GameObject* pBridge = GetSingleGameObjectFromStorage(GO_KOLOGARN_BRIDGE)) + pBridge->SetGoState(GO_STATE_READY); + } + break; + case TYPE_AURIAYA: + m_auiEncounter[uiType] = uiData; + if (uiData == IN_PROGRESS) + { + SetSpecialAchievementCriteria(TYPE_ACHIEV_CAT_LADY, true); + SetSpecialAchievementCriteria(TYPE_ACHIEV_NINE_LIVES, false); + } + break; + // Keepers + case TYPE_MIMIRON: + m_auiEncounter[uiType] = uiData; + DoUseDoorOrButton(GO_MIMIRON_DOOR_1); + DoUseDoorOrButton(GO_MIMIRON_DOOR_2); + DoUseDoorOrButton(GO_MIMIRON_DOOR_3); + if (uiData == DONE) + { + if (GetData(TYPE_MIMIRON_HARD) != DONE) + DoRespawnGameObject(instance->IsRegularDifficulty() ? GO_CACHE_OF_INOV_10 : GO_CACHE_OF_INOV_25, 30 * MINUTE); + SpawnFriendlyKeeper(NPC_MIMIRON_IMAGE); + } + break; + case TYPE_HODIR: + m_auiEncounter[uiType] = uiData; + DoUseDoorOrButton(GO_HODIR_ENTER); + if (uiData == DONE) + { + DoUseDoorOrButton(GO_HODIR_ICE_WALL); + DoUseDoorOrButton(GO_HODIR_EXIT); + DoRespawnGameObject(instance->IsRegularDifficulty() ? GO_CACHE_OF_WINTER_10 : GO_CACHE_OF_WINTER_25, 30 * MINUTE); + SpawnFriendlyKeeper(NPC_HODIR_IMAGE); + } + break; + case TYPE_THORIM: + m_auiEncounter[uiType] = uiData; + DoUseDoorOrButton(GO_LIGHTNING_FIELD); + if (uiData == IN_PROGRESS) + DoUseDoorOrButton(GO_DARK_IRON_PORTCULIS); + if (uiData == DONE) + { + if (GetData(TYPE_THORIM_HARD) != DONE) + DoRespawnGameObject(instance->IsRegularDifficulty() ? GO_CACHE_OF_STORMS_10 : GO_CACHE_OF_STORMS_25, 30 * MINUTE); + SpawnFriendlyKeeper(NPC_THORIM_IMAGE); + } + break; + case TYPE_FREYA: + m_auiEncounter[uiType] = uiData; + if (uiData == DONE) + SpawnFriendlyKeeper(NPC_FREYA_IMAGE); + break; + // Prison + case TYPE_VEZAX: + m_auiEncounter[uiType] = uiData; + if (uiData == DONE) + DoUseDoorOrButton(GO_VEZAX_GATE); + break; + case TYPE_YOGGSARON: + m_auiEncounter[uiType] = uiData; + DoUseDoorOrButton(GO_YOGG_GATE); + break; + + // Celestial Planetarium + case TYPE_ALGALON: + m_auiEncounter[uiType] = uiData; + // TODO: need to find the proper way to use these + DoUseDoorOrButton(GO_CELESTIAL_DOOR); + DoUseDoorOrButton(GO_UNIVERSE_FLOOR_CELESTIAL); + if (uiData == DONE) + DoRespawnGameObject(instance->IsRegularDifficulty() ? GO_GIFT_OF_OBSERVER_10 : GO_GIFT_OF_OBSERVER_25, 30 * MINUTE); + break; + + // Hard modes + case TYPE_LEVIATHAN_HARD: + m_auiHardBoss[0] = uiData; // TODO: add extra loot + break; + case TYPE_XT002_HARD: + m_auiHardBoss[1] = uiData; // TODO: add extra loot + break; + case TYPE_HODIR_HARD: + m_auiHardBoss[2] = uiData; + if (uiData == DONE) + DoRespawnGameObject(instance->IsRegularDifficulty() ? GO_CACHE_OF_RARE_WINTER_10 : GO_CACHE_OF_RARE_WINTER_25, 30 * MINUTE); + break; + case TYPE_THORIM_HARD: + m_auiHardBoss[3] = uiData; + if (uiData == DONE) + DoRespawnGameObject(instance->IsRegularDifficulty() ? GO_CACHE_OF_STORMS_10_H : GO_CACHE_OF_STORMS_25_H, 30 * MINUTE); + break; + case TYPE_MIMIRON_HARD: + m_auiHardBoss[4] = uiData; + if (uiData == DONE) + DoRespawnGameObject(instance->IsRegularDifficulty() ? GO_CACHE_OF_INOV_10_H : GO_CACHE_OF_INOV_25_H, 30 * MINUTE); + break; + case TYPE_VEZAX_HARD: + m_auiHardBoss[5] = uiData; // TODO: add extra loot + break; + case TYPE_YOGGSARON_HARD: + m_auiHardBoss[6] = uiData; // TODO: add extra loot + break; + + // Ulduar keepers + case TYPE_KEEPER_HODIR: + m_auiUlduarKeepers[0] = uiData; + break; + case TYPE_KEEPER_THORIM: + m_auiUlduarKeepers[1] = uiData; + break; + case TYPE_KEEPER_FREYA: + m_auiUlduarKeepers[2] = uiData; + break; + case TYPE_KEEPER_MIMIRON: + m_auiUlduarKeepers[3] = uiData; + break; + } + + DoOpenMadnessDoorIfCan(); + + if (uiData == DONE || uiData == FAIL) + { + OUT_SAVE_INST_DATA; + + // Save all encounters, hard bosses, keepers and teleporters + 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] << " " + << m_auiEncounter[12] << " " << m_auiEncounter[13] << " " << m_auiHardBoss[0] << " " + << m_auiHardBoss[1] << " " << m_auiHardBoss[2] << " " << m_auiHardBoss[2] << " " + << m_auiHardBoss[4] << " " << m_auiHardBoss[5] << " " << m_auiHardBoss[6] << " " + << m_auiUlduarKeepers[0] << " " << m_auiUlduarKeepers[1] << " " << m_auiUlduarKeepers[2] << " " << m_auiUlduarKeepers[3]; + + m_strInstData = saveStream.str(); + + SaveToDB(); + OUT_SAVE_INST_DATA_COMPLETE; + } +} + +// TODO: implement all hard mode loot here! +bool instance_ulduar::CheckConditionCriteriaMeet(Player const* pPlayer, uint32 uiInstanceConditionId, WorldObject const* pConditionSource, uint32 conditionSourceType) const +{ + switch (uiInstanceConditionId) + { + case TYPE_XT002_HARD: + break; + } + + script_error_log("instance_ulduar::CheckConditionCriteriaMeet called with unsupported Id %u. Called with param plr %s, src %s, condition source type %u", + uiInstanceConditionId, pPlayer ? pPlayer->GetGuidStr().c_str() : "NULL", pConditionSource ? pConditionSource->GetGuidStr().c_str() : "NULL", conditionSourceType); + return false; +} + +uint32 instance_ulduar::GetData(uint32 uiType) const +{ + switch (uiType) + { + case TYPE_LEVIATHAN: + return m_auiEncounter[0]; + case TYPE_IGNIS: + return m_auiEncounter[1]; + case TYPE_RAZORSCALE: + return m_auiEncounter[2]; + case TYPE_XT002: + return m_auiEncounter[3]; + case TYPE_ASSEMBLY: + return m_auiEncounter[4]; + case TYPE_KOLOGARN: + return m_auiEncounter[5]; + case TYPE_AURIAYA: + return m_auiEncounter[6]; + case TYPE_MIMIRON: + return m_auiEncounter[7]; + case TYPE_HODIR: + return m_auiEncounter[8]; + case TYPE_THORIM: + return m_auiEncounter[9]; + case TYPE_FREYA: + return m_auiEncounter[10]; + case TYPE_VEZAX: + return m_auiEncounter[11]; + case TYPE_YOGGSARON: + return m_auiEncounter[12]; + case TYPE_ALGALON: + return m_auiEncounter[13]; + + // Hard modes + case TYPE_LEVIATHAN_HARD: + return m_auiHardBoss[0]; + case TYPE_XT002_HARD: + return m_auiHardBoss[1]; + case TYPE_HODIR_HARD: + return m_auiHardBoss[2]; + case TYPE_THORIM_HARD: + return m_auiHardBoss[3]; + case TYPE_MIMIRON_HARD: + return m_auiHardBoss[4]; + case TYPE_VEZAX_HARD: + return m_auiHardBoss[5]; + case TYPE_YOGGSARON_HARD: + return m_auiHardBoss[6]; + + // Ulduar Keepers + case TYPE_KEEPER_HODIR: + return m_auiUlduarKeepers[0]; + case TYPE_KEEPER_THORIM: + return m_auiUlduarKeepers[1]; + case TYPE_KEEPER_FREYA: + return m_auiUlduarKeepers[2]; + case TYPE_KEEPER_MIMIRON: + return m_auiUlduarKeepers[3]; + } + + return 0; +} + +// Spawn the friendly keepers in the central chamber +void instance_ulduar::SpawnFriendlyKeeper(uint32 uiWho) +{ + Player* pPlayer = GetPlayerInMap(); + if (!pPlayer) + return; + + switch (uiWho) + { + case NPC_MIMIRON_IMAGE: pPlayer->SummonCreature(NPC_MIMIRON_IMAGE, m_aKeepersSpawnLocs[1].m_fX, m_aKeepersSpawnLocs[1].m_fY, m_aKeepersSpawnLocs[1].m_fZ, m_aKeepersSpawnLocs[1].m_fO, TEMPSUMMON_CORPSE_TIMED_DESPAWN, 10000); break; + case NPC_HODIR_IMAGE: pPlayer->SummonCreature(NPC_HODIR_IMAGE, m_aKeepersSpawnLocs[2].m_fX, m_aKeepersSpawnLocs[2].m_fY, m_aKeepersSpawnLocs[2].m_fZ, m_aKeepersSpawnLocs[2].m_fO, TEMPSUMMON_CORPSE_TIMED_DESPAWN, 10000); break; + case NPC_THORIM_IMAGE: pPlayer->SummonCreature(NPC_THORIM_IMAGE, m_aKeepersSpawnLocs[3].m_fX, m_aKeepersSpawnLocs[3].m_fY, m_aKeepersSpawnLocs[3].m_fZ, m_aKeepersSpawnLocs[3].m_fO, TEMPSUMMON_CORPSE_TIMED_DESPAWN, 10000); break; + case NPC_FREYA_IMAGE: pPlayer->SummonCreature(NPC_FREYA_IMAGE, m_aKeepersSpawnLocs[0].m_fX, m_aKeepersSpawnLocs[0].m_fY, m_aKeepersSpawnLocs[0].m_fZ, m_aKeepersSpawnLocs[0].m_fO, TEMPSUMMON_CORPSE_TIMED_DESPAWN, 10000); break; + } +} + +void instance_ulduar::OnCreatureDeath(Creature* pCreature) +{ + switch (pCreature->GetEntry()) + { + case NPC_SANCTUM_SENTRY: + if (GetData(TYPE_AURIAYA) == IN_PROGRESS) + SetSpecialAchievementCriteria(TYPE_ACHIEV_CAT_LADY, false); + break; + } +} + +void instance_ulduar::Load(const char* strIn) +{ + if (!strIn) + { + OUT_LOAD_INST_DATA_FAIL; + return; + } + + OUT_LOAD_INST_DATA(strIn); + + std::istringstream loadStream(strIn); + 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] >> m_auiEncounter[12] >> m_auiEncounter[13] + >> m_auiHardBoss[0] >> m_auiHardBoss[1] >> m_auiHardBoss[2] >> m_auiHardBoss[3] >> m_auiHardBoss[4] >> m_auiHardBoss[5] >> m_auiHardBoss[6] + >> m_auiUlduarKeepers[0] >> m_auiUlduarKeepers[1] >> m_auiUlduarKeepers[2] >> m_auiUlduarKeepers[3]; + + for (uint8 i = 0; i < MAX_ENCOUNTER; ++i) + { + if (m_auiEncounter[i] == IN_PROGRESS) + m_auiEncounter[i] = NOT_STARTED; + } + + OUT_LOAD_INST_DATA_COMPLETE; +} + +void instance_ulduar::SetSpecialAchievementCriteria(uint32 uiType, bool bIsMet) +{ + if (uiType < MAX_SPECIAL_ACHIEV_CRITS) + m_abAchievCriteria[uiType] = bIsMet; +} + +bool instance_ulduar::CheckAchievementCriteriaMeet(uint32 uiCriteriaId, Player const* /*pSource*/, Unit const* /*pTarget*/, uint32 /*uiMiscValue1 = 0*/) const +{ + switch (uiCriteriaId) + { + case ACHIEV_CRIT_SARONITE_N: + case ACHIEV_CRIT_SARONITE_H: + return GetData(TYPE_VEZAX_HARD) == DONE; + case ACHIEV_CRIT_CAT_LADY_N: + case ACHIEV_CRIT_CAT_LADY_H: + return m_abAchievCriteria[TYPE_ACHIEV_CAT_LADY]; + case ACHIEV_CRIT_NINE_LIVES_N: + case ACHIEV_CRIT_NINE_LIVES_H: + return m_abAchievCriteria[TYPE_ACHIEV_NINE_LIVES]; + case ACHIEV_CRIT_BRUNDIR_N: + case ACHIEV_CRIT_BRUNDIR_H: + if (GetData(TYPE_ASSEMBLY) == SPECIAL) + return m_abAchievCriteria[TYPE_ACHIEV_BRUNDIR]; + case ACHIEV_CRIT_MOLGEIM_N: + case ACHIEV_CRIT_MOLGEIM_H: + if (GetData(TYPE_ASSEMBLY) == SPECIAL) + return m_abAchievCriteria[TYPE_ACHIEV_MOLGEIM]; + case ACHIEV_CRIT_STEELBREAKER_N: + case ACHIEV_CRIT_STEELBREAKER_H: + if (GetData(TYPE_ASSEMBLY) == SPECIAL) + return m_abAchievCriteria[TYPE_ACHIEV_STEELBREAKER]; + case ACHIEV_CRIT_STUNNED_BRUND_N: + case ACHIEV_CRIT_STUNNED_STEEL_N: + case ACHIEV_CRIT_STUNNED_MOLG_N: + case ACHIEV_CRIT_STUNNED_BRUND_H: + case ACHIEV_CRIT_STUNNED_STEEL_H: + case ACHIEV_CRIT_STUNNED_MOLG_H: + if (GetData(TYPE_ASSEMBLY) == SPECIAL) + return m_abAchievCriteria[TYPE_ACHIEV_STUNNED]; + case ACHIEV_CRIT_SHATTERED_N: + case ACHIEV_CRIT_SHATTERED_H: + return m_abAchievCriteria[TYPE_ACHIEV_SHATTERED]; + case ACHIEV_CRIT_HEARTBREAKER_N: + case ACHIEV_CRIT_HEARTBREAKER_H: + return GetData(TYPE_XT002_HARD) == DONE; + + default: + return false; + } +} + +void instance_ulduar::DoProcessShatteredEvent() +{ + // If timer is already running set achiev criteria to true, else start the timer + if (m_uiShatterAchievTimer) + SetSpecialAchievementCriteria(TYPE_ACHIEV_SHATTERED, true); + else + m_uiShatterAchievTimer = 5000; +} + +void instance_ulduar::Update(uint32 uiDiff) +{ + if (GetData(TYPE_IGNIS) == IN_PROGRESS) + { + if (m_uiShatterAchievTimer) + { + // Just set the timer to 0 when it expires + if (m_uiShatterAchievTimer <= uiDiff) + m_uiShatterAchievTimer = 0; + else + m_uiShatterAchievTimer -= uiDiff; + } + } +} + +InstanceData* GetInstanceData_instance_ulduar(Map* pMap) +{ + return new instance_ulduar(pMap); +} + +bool ProcessEventId_event_ulduar(uint32 uiEventId, Object* pSource, Object* pTarget, bool bIsStart) +{ + if (uiEventId == EVENT_ID_SPELL_SHATTER) + { + if (pSource->GetTypeId() == TYPEID_UNIT) + { + if (instance_ulduar* pInstance = (instance_ulduar*)((Creature*)pSource)->GetInstanceData()) + { + pInstance->DoProcessShatteredEvent(); + return true; + } + } + } + + return false; +} + +void AddSC_instance_ulduar() +{ + Script* pNewScript; + + pNewScript = new Script; + pNewScript->Name = "instance_ulduar"; + pNewScript->GetInstanceData = &GetInstanceData_instance_ulduar; + pNewScript->RegisterSelf(); + + pNewScript = new Script; + pNewScript->Name = "event_ulduar"; + pNewScript->pProcessEventId = &ProcessEventId_event_ulduar; + pNewScript->RegisterSelf(); +} diff --git a/src/modules/SD2/scripts/northrend/ulduar/ulduar/ulduar.cpp b/src/modules/SD2/scripts/northrend/ulduar/ulduar/ulduar.cpp new file mode 100644 index 000000000..982ef8dc6 --- /dev/null +++ b/src/modules/SD2/scripts/northrend/ulduar/ulduar/ulduar.cpp @@ -0,0 +1,59 @@ +/* Copyright (C) 2006 - 2013 ScriptDev2 + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +/* ScriptData +SDName: ulduar +SD%Complete: 0% +SDComment: +SDCategory: Ulduar +EndScriptData */ + +#include "precompiled.h" +#include "ulduar.h" + +/*##### +## Teleporters +#####*/ +enum TeleporterSpells +{ + SPELL_TELE_EXPEDITION_BASE_CAMP = 64014, + SPELL_TELE_FORMATION_GROUNDS = 64032, + SPELL_TELE_COLOSSAL_FORGE = 64028, + SPELL_TELE_SCRAPYARD = 64031, + SPELL_TELE_ANTECHAMBER_OF_ULDUAR = 64030, + SPELL_TELE_SHATTERED_WALKWAY = 64029, + SPELL_TELE_CONSERVATORY_OF_LIFE = 64024, + SPELL_TELE_SPARK_OF_IMAGINATION = 65061, + SPELL_TELE_PRISON_OF_YOGG = 65042, +}; + +// Teleporter Gossip handled by SD2 because depending on Instance Data +enum TeleporterGossipItems +{ + GOSSIP_ITEM_TELE_BASE_CAMP = -3603000, + GOSSIP_ITEM_TELE_FORMATION_GROUNDS = -3603001, + GOSSIP_ITEM_TELE_COLOSSAR_FORGE = -3603002, + GOSSIP_ITEM_TELE_SCRAPYARD = -3603003, + GOSSIP_ITEM_TELE_ANTECHAMBER = -3603004, + GOSSIP_ITEM_TELE_WALKWAY = -3603005, + GOSSIP_ITEM_TELE_CONSERVATORY = -3603006, + GOSSIP_ITEM_TELE_SPARK_IMAGINATION = -3603007, + GOSSIP_ITEM_TELE_YOGG_SARON = -3603008, +}; + +void AddSC_ulduar() +{ +} diff --git a/src/modules/SD2/scripts/northrend/ulduar/ulduar/ulduar.h b/src/modules/SD2/scripts/northrend/ulduar/ulduar/ulduar.h new file mode 100644 index 000000000..c074e7906 --- /dev/null +++ b/src/modules/SD2/scripts/northrend/ulduar/ulduar/ulduar.h @@ -0,0 +1,281 @@ +/* Copyright (C) 2006 - 2013 ScriptDev2 +* This program is free software licensed under GPL version 2 +* Please see the included DOCS/LICENSE.TXT for more information */ + +#ifndef DEF_ULDUAR_H +#define DEF_ULDUAR_H + +enum +{ + MAX_ENCOUNTER = 14, + HARD_MODE_ENCOUNTER = 7, + KEEPER_ENCOUNTER = 4, + + // Main boss types + TYPE_LEVIATHAN = 0, + TYPE_IGNIS = 1, + TYPE_RAZORSCALE = 2, + TYPE_XT002 = 3, + TYPE_ASSEMBLY = 4, + TYPE_KOLOGARN = 5, + TYPE_AURIAYA = 6, + TYPE_MIMIRON = 7, + TYPE_HODIR = 8, + TYPE_THORIM = 9, + TYPE_FREYA = 10, + TYPE_VEZAX = 11, + TYPE_YOGGSARON = 12, + TYPE_ALGALON = 13, + + // Hard mode boss types + // Used for hard mode bosses only + TYPE_LEVIATHAN_HARD = 14, + TYPE_XT002_HARD = 15, + TYPE_MIMIRON_HARD = 17, + TYPE_HODIR_HARD = 18, + TYPE_THORIM_HARD = 19, + TYPE_VEZAX_HARD = 21, + TYPE_YOGGSARON_HARD = 22, + + // Keeper types + // Used to store the keepers which will be used at yogg + TYPE_KEEPER_HODIR = 23, + TYPE_KEEPER_FREYA = 24, + TYPE_KEEPER_THORIM = 25, + TYPE_KEEPER_MIMIRON = 26, + + // The siege of ulduar + NPC_LEVIATHAN = 33113, + NPC_IGNIS = 33118, + NPC_RAZORSCALE = 33186, + NPC_COMMANDER = 33210, + NPC_XT002 = 33293, + NPC_HEART_DECONSTRUCTOR = 33329, + + // The antechamber of ulduar + NPC_STEELBREAKER = 32867, + NPC_MOLGEIM = 32927, + NPC_BRUNDIR = 32857, + NPC_KOLOGARN = 32930, + NPC_RIGHT_ARM = 32934, + NPC_LEFT_ARM = 32933, + NPC_AURIAYA = 33515, + NPC_SANCTUM_SENTRY = 34014, + NPC_FERAL_DEFENDER = 34035, + + // The keepers of ulduar + NPC_MIMIRON = 33350, + NPC_LEVIATHAN_MK = 33432, + NPC_VX001 = 33651, + NPC_AERIAL_UNIT = 33670, + NPC_HODIR = 32845, + NPC_THORIM = 32865, + NPC_RUNIC_COLOSSUS = 32872, + NPC_RUNE_GIANT = 32873, + NPC_JORMUNGAR_BEHEMOTH = 32882, + NPC_FREYA = 32906, + NPC_ELDER_BRIGHTLEAF = 32915, + NPC_ELDER_IRONBRACH = 32913, + NPC_ELDER_STONEBARK = 32914, + + // The descent into madness + NPC_VEZAX = 33271, + NPC_SARONITE_ANIMUS = 33524, + NPC_YOGGSARON = 33288, + NPC_SARA = 33134, + NPC_YOGG_BRAIN = 33890, + + // Celestial planetarium + NPC_ALGALON = 32871, + + // Keepers images + // They spawn in the central room after they are released from Yogg's enslavement + // You may talk to them and ask them to help you fight Yogg-Saron + NPC_THORIM_IMAGE = 33413, + NPC_MIMIRON_IMAGE = 33412, + NPC_HODIR_IMAGE = 33411, + NPC_FREYA_IMAGE = 33410, + + // Keepers used to fight Yogg-Saron + NPC_KEEPER_FREYA = 33241, + NPC_KEEPER_HODIR = 33213, + NPC_KEEPER_MIMIRON = 33244, + NPC_KEEPER_THORIM = 33242, + + MAX_SPECIAL_ACHIEV_CRITS = 7, + + TYPE_ACHIEV_CAT_LADY = 0, + TYPE_ACHIEV_NINE_LIVES = 1, + TYPE_ACHIEV_STEELBREAKER = 2, + TYPE_ACHIEV_BRUNDIR = 3, + TYPE_ACHIEV_MOLGEIM = 4, + TYPE_ACHIEV_STUNNED = 5, + TYPE_ACHIEV_SHATTERED = 6, + + // Loot chests + // Kologarn + GO_CACHE_OF_LIVING_STONE_10 = 195046, + GO_CACHE_OF_LIVING_STONE_25 = 195047, + + // Hodir + GO_CACHE_OF_WINTER_10 = 194307, + GO_CACHE_OF_WINTER_25 = 194308, + GO_CACHE_OF_RARE_WINTER_10 = 194200, + GO_CACHE_OF_RARE_WINTER_25 = 194201, + + // Mimiron + GO_CACHE_OF_INOV_10 = 194789, + GO_CACHE_OF_INOV_25 = 194956, + GO_CACHE_OF_INOV_10_H = 194957, + GO_CACHE_OF_INOV_25_H = 194958, + + // Thorim + GO_CACHE_OF_STORMS_10 = 194312, + GO_CACHE_OF_STORMS_25 = 194315, + GO_CACHE_OF_STORMS_10_H = 194313, + GO_CACHE_OF_STORMS_25_H = 194314, + + // Alagon + GO_GIFT_OF_OBSERVER_10 = 194821, + GO_GIFT_OF_OBSERVER_25 = 194822, + + // Doors and other Objects + // The siege + GO_SHIELD_WALL = 194416, // Gate before Leviathan + GO_LIGHTNING_FIELD = 194559, // Lightning gate after the Leviathan. It closes after the boss enters the arena + GO_LEVIATHAN_GATE = 194630, // Gate after Leviathan -> this will be broken when the boss enters the arena + GO_XT002_GATE = 194631, // Gate before Xt002 + GO_BROKEN_HARPOON = 194565, // Broken harpoon from Razorscale + + // Antechamber + GO_KOLOGARN_BRIDGE = 194232, + GO_SHATTERED_DOOR = 194553, // Door before kologarn + GO_IRON_ENTRANCE_DOOR = 194554, // Door before iron council + GO_ARCHIVUM_DOOR = 194556, // Entrance door to the archivum + GO_ARCHIVUM_CONSOLE = 194555, // Used at some sort of cinematic + GO_UNIVERSE_FLOOR_ARCHIVUM = 194715, // Used for animation + + // Planetarium + GO_CELESTIAL_ACCES = 194628, // Acces console for 10 man mode + GO_CELESTIAL_ACCES_H = 194752, // Acces console for 25 man mode + GO_CELESTIAL_DOOR = 194767, // Entrance door to the planetarium + GO_UNIVERSE_FLOOR_CELESTIAL = 194716, // For animation + GO_AZEROTH_GLOBE = 194148, // For animation + + // The keepers + // Hodir + GO_HODIR_EXIT = 194634, + GO_HODIR_ICE_WALL = 194441, + GO_HODIR_ENTER = 194442, + // Mimiron + G0_MIMIRON_BUTTON = 194739, // Used to start hard mode + GO_MIMIRON_DOOR_1 = 194774, + GO_MIMIRON_DOOR_2 = 194775, + GO_MIMIRON_DOOR_3 = 194776, + GO_MIMIRON_TEL1 = 194741, // Used to summon mobs in phase 3 + GO_MIMIRON_TEL2 = 194742, + GO_MIMIRON_TEL3 = 194743, + GO_MIMIRON_TEL4 = 194744, + GO_MIMIRON_TEL5 = 194740, + GO_MIMIRON_TEL6 = 194746, + GO_MIMIRON_TEL7 = 194747, + GO_MIMIRON_TEL8 = 194748, + GO_MIMIRON_TEL9 = 194745, + GO_MIMIRON_ELEVATOR = 194749, // Central elevator + // Thorim + GO_DARK_IRON_PORTCULIS = 194560, // Door from the arena to the hallway + GO_RUNED_STONE_DOOR = 194557, // Door after the runic colossus + GO_THORIM_STONE_DOOR = 194558, // Door after the ancient rune giant + GO_LIGHTNING_DOOR = 194905, // Arena exit door + GO_DOOR_LEVER = 194264, // In front of the door + + // Descent to madness + GO_ANCIENT_GATE = 194255, // Door upstairs before vezax, opens when all keepers are freed + GO_VEZAX_GATE = 194750, // Door after vezax + GO_YOGG_GATE = 194773, // Yogg-Saron chamber door + GO_BRAIN_DOOR1 = 194635, // Brain chamber doors + GO_BRAIN_DOOR2 = 194636, + GO_BRAIN_DOOR3 = 194637, + + // World state used for algalon timer + WORLD_STATE_TIMER = 4132, + WORLD_STATE_TIMER_COUNT = 4131, + + // events + EVENT_ID_SPELL_SHATTER = 21620, + + // Achievement related + ACHIEV_START_IGNIS_ID = 20951, // Ignis timed achievs 2930, 2929 + ACHIEV_START_XT002_ID = 21027, // XT-002 timed achievs 2937, 2938 + + ACHIEV_CRIT_SARONITE_N = 10451, // General Vezax, achievs 3181, 3188 + ACHIEV_CRIT_SARONITE_H = 10462, + ACHIEV_CRIT_CAT_LADY_N = 10400, // Auriaya, achievs 3006, 3007 + ACHIEV_CRIT_CAT_LADY_H = 10184, + ACHIEV_CRIT_NINE_LIVES_N = 10399, // Auriaya, achievs 3076, 3077 + ACHIEV_CRIT_NINE_LIVES_H = 10243, + ACHIEV_CRIT_STEELBREAKER_N = 10084, // Iron council, achievs 2941, 2944 + ACHIEV_CRIT_STEELBREAKER_H = 10087, + ACHIEV_CRIT_MOLGEIM_N = 10082, // Iron council, achievs 2939, 2942 + ACHIEV_CRIT_MOLGEIM_H = 10085, + ACHIEV_CRIT_BRUNDIR_N = 10083, // Iron council, achievs 2940, 2943 + ACHIEV_CRIT_BRUNDIR_H = 10086, + ACHIEV_CRIT_STUNNED_BRUND_N = 10090, // Iron council, achiev 2947 + ACHIEV_CRIT_STUNNED_STEEL_N = 10422, + ACHIEV_CRIT_STUNNED_MOLG_N = 10423, + ACHIEV_CRIT_STUNNED_BRUND_H = 10091, // Iron council, achiev 2948 + ACHIEV_CRIT_STUNNED_STEEL_H = 10424, + ACHIEV_CRIT_STUNNED_MOLG_H = 10425, + ACHIEV_CRIT_SHATTERED_N = 10068, // Ignis, achievs 2925, 2926 + ACHIEV_CRIT_SHATTERED_H = 10069, + ACHIEV_CRIT_HEARTBREAKER_N = 10221, // XT-002, achievs 3058, 3059 + ACHIEV_CRIT_HEARTBREAKER_H = 10220, + ACHIEV_CRIT_NERF_ENG_N = 10074, // XT-002, achievs 2931, 2932 + ACHIEV_CRIT_NERF_ENG_H = 10075, + ACHIEV_CRIT_NERF_GRAVITY_N = 10077, // XT-002, achievs 2934, 2936 + ACHIEV_CRIT_NERF_GRAVITY_H = 10079, +}; + +class instance_ulduar : public ScriptedInstance +{ + public: + instance_ulduar(Map* pMap); + ~instance_ulduar() {} + + void Initialize() override; + bool IsEncounterInProgress() const override; + + void OnCreatureCreate(Creature* pCreature) override; + void OnCreatureDeath(Creature* pCreature) override; + void OnObjectCreate(GameObject* pGo) override; + + void SetData(uint32 uiType, uint32 uiData) override; + uint32 GetData(uint32 uiType) const override; + + const char* Save() const override { return m_strInstData.c_str(); } + void Load(const char* chrIn) override; + + void SetSpecialAchievementCriteria(uint32 uiType, bool bIsMet); + bool CheckAchievementCriteriaMeet(uint32 uiCriteriaId, Player const* pSource, Unit const* pTarget, uint32 uiMiscValue1 /* = 0*/) const override; + // Dummy, leave till correct solution for hardmode found + bool CheckConditionCriteriaMeet(Player const* pPlayer, uint32 uiInstanceConditionId, WorldObject const* pConditionSource, uint32 conditionSourceType) const override; + + void DoOpenMadnessDoorIfCan(); + + void SpawnFriendlyKeeper(uint32 uiWho); + + void DoProcessShatteredEvent(); + + void Update(uint32 uiDiff); + + protected: + std::string m_strInstData; + uint32 m_auiEncounter[MAX_ENCOUNTER]; + uint32 m_auiHardBoss[HARD_MODE_ENCOUNTER]; + uint32 m_auiUlduarKeepers[KEEPER_ENCOUNTER]; + bool m_abAchievCriteria[MAX_SPECIAL_ACHIEV_CRITS]; + + uint32 m_uiShatterAchievTimer; +}; + +#endif diff --git a/src/modules/SD2/scripts/northrend/utgarde_keep/utgarde_keep/boss_ingvar.cpp b/src/modules/SD2/scripts/northrend/utgarde_keep/utgarde_keep/boss_ingvar.cpp new file mode 100644 index 000000000..46ed858b0 --- /dev/null +++ b/src/modules/SD2/scripts/northrend/utgarde_keep/utgarde_keep/boss_ingvar.cpp @@ -0,0 +1,407 @@ +/* Copyright (C) 2006 - 2013 ScriptDev2 + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with 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 +SD%Complete: 70% +SDComment: TODO: correct timers, spell 42912 requires proper position fix in core +SDCategory: Utgarde Keep +EndScriptData */ + +#include "precompiled.h" +#include "utgarde_keep.h" + +enum +{ + SAY_AGGRO_FIRST = -1574005, + SAY_AGGRO_SECOND = -1574006, + SAY_DEATH_FIRST = -1574007, + SAY_DEATH_SECOND = -1574008, + SAY_KILL_FIRST = -1574009, + SAY_KILL_SECOND = -1574010, + EMOTE_ROAR = -1574022, + SAY_ANNHYLDE_REZ = -1574023, + + NPC_ANNHYLDE = 24068, + NPC_THROW_TARGET = 23996, // the target, casting spell and target of moving dummy + NPC_THROW_DUMMY = 23997, // the axe, moving to target + NPC_GROUND_VISUAL = 24012, // has SPELL_SCOURGE_RES_BUBBLE aura + + // phase 1 + SPELL_CLEAVE = 42724, + + SPELL_SMASH = 42669, + SPELL_SMASH_H = 59706, + + SPELL_ENRAGE = 42705, + SPELL_ENRAGE_H = 59707, + + SPELL_STAGGERING_ROAR = 42708, + SPELL_STAGGERING_ROAR_H = 59708, + + // phase 2 + SPELL_DARK_SMASH_H = 42723, + + SPELL_DREADFUL_ROAR = 42729, + SPELL_DREADFUL_ROAR_H = 59734, + + SPELL_WOE_STRIKE = 42730, + SPELL_WOE_STRIKE_H = 59735, + + SPELL_SHADOW_AXE = 42748, + SPELL_SHADOW_AXE_PROC = 42750, // triggers 42751 + SPELL_SHADOW_AXE_PROC_H = 59719, // triggers 59720 + + // ressurection sequenze + SPELL_ASTRAL_TELEPORT = 34427, // aura cast by Annhylde on spawn + SPELL_SUMMON_BANSHEE = 42912, // summons Annhylde and sets a glow aura + SPELL_FEIGN_DEATH = 42795, + SPELL_TRANSFORM = 42796, + SPELL_SCOURGE_RES_SUMMON = 42863, // summones a dummy target + SPELL_SCOURGE_RES_HEAL = 42704, // heals max HP + SPELL_SCOURGE_RES_BUBBLE = 42862, // black bubble + SPELL_SCOURGE_RES_CHANNEL = 42857, // the whirl from annhylde + + POINT_ID_ANNHYLDE = 1 +}; + +/*###### +## boss_ingvar +######*/ + +struct boss_ingvarAI : public ScriptedAI +{ + boss_ingvarAI(Creature* pCreature) : ScriptedAI(pCreature) + { + m_pInstance = (ScriptedInstance*)pCreature->GetInstanceData(); + m_bIsRegularMode = pCreature->GetMap()->IsRegularDifficulty(); + Reset(); + } + + ScriptedInstance* m_pInstance; + bool m_bIsRegularMode; + + bool m_bIsResurrected; + bool m_bIsFakingDeath; + + uint32 m_uiCleaveTimer; + uint32 m_uiSmashTimer; + uint32 m_uiStaggeringRoarTimer; + uint32 m_uiEnrageTimer; + + void Reset() override + { + m_bIsResurrected = false; + m_bIsFakingDeath = false; + + m_uiCleaveTimer = urand(5000, 7000); + m_uiSmashTimer = urand(8000, 15000); + m_uiStaggeringRoarTimer = urand(10000, 25000); + m_uiEnrageTimer = 30000; + } + + void Aggro(Unit* pWho) override + { + // don't yell for her + if (pWho->GetEntry() == NPC_ANNHYLDE) + return; + + // ToDo: it shouldn't yell this aggro text after removing the feign death aura + DoScriptText(SAY_AGGRO_FIRST, m_creature); + + if (m_pInstance) + m_pInstance->SetData(TYPE_INGVAR, IN_PROGRESS); + } + + void DamageTaken(Unit* /*pDealer*/, uint32& uiDamage) override + { + if (m_bIsResurrected) + return; + + if (m_bIsFakingDeath) + { + uiDamage = 0; + return; + } + + if (uiDamage >= m_creature->GetHealth()) + { + uiDamage = 0; + + DoScriptText(SAY_DEATH_FIRST, m_creature); + + DoCastSpellIfCan(m_creature, SPELL_SUMMON_BANSHEE, CAST_TRIGGERED); + DoCastSpellIfCan(m_creature, SPELL_FEIGN_DEATH, CAST_TRIGGERED); + m_creature->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE); + + m_bIsFakingDeath = true; + } + } + + void SpellHit(Unit* /*pCaster*/, const SpellEntry* pSpell) override + { + if (pSpell->Id == SPELL_TRANSFORM) + { + DoScriptText(SAY_AGGRO_SECOND, m_creature); + m_creature->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE); + m_creature->UpdateEntry(pSpell->GetEffectMiscValue(EFFECT_INDEX_0)); + m_bIsResurrected = true; + m_bIsFakingDeath = false; + } + } + + void JustSummoned(Creature* pSummoned) override + { + switch (pSummoned->GetEntry()) + { + case NPC_THROW_DUMMY: + // ToDo: should this move to the target? + pSummoned->CastSpell(pSummoned, m_bIsRegularMode ? SPELL_SHADOW_AXE_PROC : SPELL_SHADOW_AXE_PROC_H, true); + break; + + case NPC_ANNHYLDE: + // This is not blizzlike - npc should be summoned above the boss and should move slower + pSummoned->CastSpell(pSummoned, SPELL_ASTRAL_TELEPORT, false); + pSummoned->SetLevitate(true); + pSummoned->GetMotionMaster()->MovePoint(POINT_ID_ANNHYLDE, pSummoned->GetPositionX(), pSummoned->GetPositionY(), pSummoned->GetPositionZ() + 15.0f); + break; + + case NPC_GROUND_VISUAL: + pSummoned->CastSpell(pSummoned, SPELL_SCOURGE_RES_BUBBLE, false); + // npc doesn't despawn on time + pSummoned->ForcedDespawn(8000); + break; + } + } + + void JustDied(Unit* /*pKiller*/) override + { + DoScriptText(SAY_DEATH_SECOND, m_creature); + + if (m_pInstance) + m_pInstance->SetData(TYPE_INGVAR, DONE); + } + + void KilledUnit(Unit* /*pVictim*/) override + { + if (urand(0, 1)) + DoScriptText(m_bIsResurrected ? SAY_KILL_SECOND : SAY_KILL_FIRST, m_creature); + } + + void JustReachedHome() override + { + if (m_pInstance) + m_pInstance->SetData(TYPE_INGVAR, FAIL); + + m_creature->UpdateEntry(NPC_INGVAR); + } + + void UpdateAI(const uint32 uiDiff) override + { + if (!m_creature->SelectHostileTarget() || !m_creature->getVictim() || m_bIsFakingDeath) + return; + + if (!m_bIsResurrected) // First phase + { + if (m_uiCleaveTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature->getVictim(), SPELL_CLEAVE) == CAST_OK) + m_uiCleaveTimer = urand(2500, 7000); + } + else + m_uiCleaveTimer -= uiDiff; + + if (m_uiSmashTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature, m_bIsRegularMode ? SPELL_SMASH : SPELL_SMASH_H) == CAST_OK) + m_uiSmashTimer = urand(8000, 15000); + } + else + m_uiSmashTimer -= uiDiff; + + if (m_uiStaggeringRoarTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature, m_bIsRegularMode ? SPELL_STAGGERING_ROAR : SPELL_STAGGERING_ROAR_H) == CAST_OK) + { + DoScriptText(EMOTE_ROAR, m_creature); + m_uiStaggeringRoarTimer = urand(15000, 30000); + } + } + else + m_uiStaggeringRoarTimer -= uiDiff; + + if (m_uiEnrageTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature, m_bIsRegularMode ? SPELL_ENRAGE : SPELL_ENRAGE_H) == CAST_OK) + m_uiEnrageTimer = urand(10000, 20000); + } + else + m_uiEnrageTimer -= uiDiff; + } + else // Second phase + { + if (m_uiCleaveTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature->getVictim(), m_bIsRegularMode ? SPELL_WOE_STRIKE : SPELL_WOE_STRIKE_H) == CAST_OK) + m_uiCleaveTimer = urand(2500, 7000); + } + else + m_uiCleaveTimer -= uiDiff; + + if (m_uiSmashTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature, SPELL_DARK_SMASH_H) == CAST_OK) + m_uiSmashTimer = urand(8000, 15000); + } + else + m_uiSmashTimer -= uiDiff; + + if (m_uiStaggeringRoarTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature, m_bIsRegularMode ? SPELL_DREADFUL_ROAR : SPELL_DREADFUL_ROAR_H) == CAST_OK) + { + DoScriptText(EMOTE_ROAR, m_creature); + m_uiStaggeringRoarTimer = urand(15000, 30000); + } + } + else + m_uiStaggeringRoarTimer -= uiDiff; + + if (m_uiEnrageTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature, SPELL_SHADOW_AXE) == CAST_OK) + m_uiEnrageTimer = urand(10000, 20000); + } + else + m_uiEnrageTimer -= uiDiff; + } + + DoMeleeAttackIfReady(); + } +}; + +CreatureAI* GetAI_boss_ingvar(Creature* pCreature) +{ + return new boss_ingvarAI(pCreature); +} + +/*###### +## npc_annhylde +######*/ + +struct npc_annhyldeAI : public ScriptedAI +{ + npc_annhyldeAI(Creature* pCreature) : ScriptedAI(pCreature) + { + m_pInstance = (ScriptedInstance*)pCreature->GetInstanceData(); + Reset(); + } + + ScriptedInstance* m_pInstance; + + uint32 m_uiResurrectTimer; + uint8 m_uiResurrectPhase; + + void Reset() override + { + m_uiResurrectTimer = 0; + m_uiResurrectPhase = 0; + } + + // No attacking + void MoveInLineOfSight(Unit*) override {} + void AttackStart(Unit*) override {} + + void MovementInform(uint32 uiMotionType, uint32 uiPointId) override + { + if (uiMotionType != POINT_MOTION_TYPE || uiPointId != POINT_ID_ANNHYLDE) + return; + + DoScriptText(SAY_ANNHYLDE_REZ, m_creature); + m_uiResurrectTimer = 3000; + } + + void UpdateAI(const uint32 uiDiff) override + { + if (m_uiResurrectTimer) + { + if (m_uiResurrectTimer <= uiDiff) + { + if (!m_pInstance) + return; + + switch (m_uiResurrectPhase) + { + case 0: + DoCastSpellIfCan(m_creature, SPELL_SCOURGE_RES_CHANNEL); + if (Creature* pIngvar = m_pInstance->GetSingleCreatureFromStorage(NPC_INGVAR)) + { + if (pIngvar->HasAura(SPELL_SUMMON_BANSHEE)) + pIngvar->RemoveAurasDueToSpell(SPELL_SUMMON_BANSHEE); + } + m_uiResurrectTimer = 3000; + break; + case 1: + if (Creature* pIngvar = m_pInstance->GetSingleCreatureFromStorage(NPC_INGVAR)) + { + pIngvar->CastSpell(pIngvar, SPELL_SCOURGE_RES_SUMMON, true); + // Workaround - set Feign death again because it's removed by the previous casted spell + pIngvar->CastSpell(pIngvar, SPELL_FEIGN_DEATH, true); + } + m_uiResurrectTimer = 5000; + break; + case 2: + if (Creature* pIngvar = m_pInstance->GetSingleCreatureFromStorage(NPC_INGVAR)) + pIngvar->CastSpell(pIngvar, SPELL_SCOURGE_RES_HEAL, false); + m_uiResurrectTimer = 3000; + break; + case 3: + if (Creature* pIngvar = m_pInstance->GetSingleCreatureFromStorage(NPC_INGVAR)) + pIngvar->CastSpell(pIngvar, SPELL_TRANSFORM, false); + // despawn the creature + m_creature->GetMotionMaster()->MovePoint(2, m_creature->GetPositionX(), m_creature->GetPositionY(), m_creature->GetPositionZ() + 50); + m_creature->ForcedDespawn(5000); + m_uiResurrectTimer = 0; + break; + } + + ++m_uiResurrectPhase; + } + else + m_uiResurrectTimer -= uiDiff; + } + } +}; + +CreatureAI* GetAI_npc_annhylde(Creature* pCreature) +{ + return new npc_annhyldeAI(pCreature); +} + +void AddSC_boss_ingvar() +{ + Script* pNewScript; + + pNewScript = new Script; + pNewScript->Name = "boss_ingvar"; + pNewScript->GetAI = &GetAI_boss_ingvar; + pNewScript->RegisterSelf(); + + pNewScript = new Script; + pNewScript->Name = "npc_annhylde"; + pNewScript->GetAI = &GetAI_npc_annhylde; + pNewScript->RegisterSelf(); +} diff --git a/src/modules/SD2/scripts/northrend/utgarde_keep/utgarde_keep/boss_keleseth.cpp b/src/modules/SD2/scripts/northrend/utgarde_keep/utgarde_keep/boss_keleseth.cpp new file mode 100644 index 000000000..b7c9b945c --- /dev/null +++ b/src/modules/SD2/scripts/northrend/utgarde_keep/utgarde_keep/boss_keleseth.cpp @@ -0,0 +1,354 @@ +/* Copyright (C) 2006 - 2013 ScriptDev2 + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +/* ScriptData +SDName: Boss_Keleseth +SD%Complete: 60% +SDComment: +SDCategory: Utgarde Keep +EndScriptData */ + +#include "precompiled.h" +#include "utgarde_keep.h" + +enum +{ + SAY_AGGRO = -1574000, + SAY_FROSTTOMB = -1574001, + SAY_SKELETONS = -1574002, + SAY_KILL = -1574003, + SAY_DEATH = -1574004, + EMOTE_FROST_TOMB = -1574021, + + // Boss Spells + SPELL_SHADOWBOLT = 43667, + SPELL_SHADOWBOLT_H = 59389, + + SPELL_SUMMON_FROST_TOMB = 42714, + SPELL_FROST_TOMB = 48400, // stun and deal damage + + // Skeleton Spells + SPELL_DECREPIFY = 42702, + SPELL_DECREPIFY_H = 59397, + SPELL_BONE_ARMOR = 59386, // casted on boss, heroic only + + NPC_VRYKUL_SKELETON = 23970 +}; + +const float RUN_DISTANCE = 20.0; + +static float fAddPosition[4] = {163.5727f, 252.1900f, 42.8684f, 5.57052f}; + +/*###### +## mob_vrykul_skeleton +######*/ + +struct mob_vrykul_skeletonAI : public ScriptedAI +{ + mob_vrykul_skeletonAI(Creature* pCreature) : ScriptedAI(pCreature) + { + m_pInstance = (ScriptedInstance*)pCreature->GetInstanceData(); + m_bIsRegularMode = m_creature->GetMap()->IsRegularDifficulty(); + Reset(); + } + + ScriptedInstance* m_pInstance; + bool m_bIsRegularMode; + + uint32 m_uiCastTimer; + uint32 m_uiReviveTimer; + + void Reset() override + { + m_uiReviveTimer = 0; + m_uiCastTimer = urand(5000, 10000); // taken out of thin air + } + + void MoveInLineOfSight(Unit* pWho) override + { + if (!pWho || m_uiReviveTimer) + return; + + ScriptedAI::MoveInLineOfSight(pWho); + } + + void AttackStart(Unit* pWho) override + { + if (!pWho || m_uiReviveTimer) + return; + + ScriptedAI::AttackStart(pWho); + } + + void Revive() + { + m_creature->SetHealth(m_creature->GetMaxHealth()); + m_creature->RemoveFlag(UNIT_DYNAMIC_FLAGS, UNIT_DYNFLAG_DEAD); + m_creature->SetStandState(UNIT_STAND_STATE_STAND); + + if (Unit* pTarget = m_creature->SelectAttackingTarget(ATTACKING_TARGET_RANDOM, 0)) + m_creature->GetMotionMaster()->MoveChase(pTarget); + + DoResetThreat(); + m_uiReviveTimer = 0; + } + + void DamageTaken(Unit* /*pDoneBy*/, uint32& uiDamage) override + { + if (m_uiReviveTimer) + { + uiDamage = 0; + return; + } + + if (m_creature->GetHealth() < uiDamage) + { + // start faking death + uiDamage = 0; + m_uiReviveTimer = 6000; + m_creature->SetHealth(0); + m_creature->RemoveAllAurasOnDeath(); + m_creature->GetMotionMaster()->Clear(); + m_creature->SetFlag(UNIT_DYNAMIC_FLAGS, UNIT_DYNFLAG_DEAD); + m_creature->SetStandState(UNIT_STAND_STATE_DEAD); + return; + } + } + + void UpdateAI(const uint32 uiDiff) override + { + if (!m_creature->SelectHostileTarget() || !m_creature->getVictim()) + return; + + if (m_uiReviveTimer) + { + if (m_uiReviveTimer <= uiDiff) + Revive(); + else + m_uiReviveTimer -= uiDiff; + + return; + } + + if (m_uiCastTimer < uiDiff) + { + if (m_bIsRegularMode) + DoCastSpellIfCan(m_creature->getVictim(), SPELL_DECREPIFY); + else + { + if (urand(0, 3)) + DoCastSpellIfCan(m_creature->getVictim(), SPELL_DECREPIFY_H); + else if (m_pInstance && m_pInstance->GetData(TYPE_KELESETH) == IN_PROGRESS) + { + if (Creature* pKeleseth = m_pInstance->GetSingleCreatureFromStorage(NPC_KELESETH)) + DoCastSpellIfCan(pKeleseth, SPELL_BONE_ARMOR); + } + } + + m_uiCastTimer = urand(5000, 15000); + } + else + m_uiCastTimer -= uiDiff; + + DoMeleeAttackIfReady(); + } +}; + +CreatureAI* GetAI_mob_vrykul_skeleton(Creature* pCreature) +{ + return new mob_vrykul_skeletonAI(pCreature); +} + +/*###### +## boss_keleseth +######*/ + +struct boss_kelesethAI : public ScriptedAI +{ + boss_kelesethAI(Creature* pCreature) : ScriptedAI(pCreature) + { + m_pInstance = (ScriptedInstance*)pCreature->GetInstanceData(); + m_bIsRegularMode = pCreature->GetMap()->IsRegularDifficulty(); + Reset(); + } + + ScriptedInstance* m_pInstance; + bool m_bIsRegularMode; + + uint32 m_uiFrostTombTimer; + uint32 m_uiSummonTimer; + uint32 m_uiShadowboltTimer; + + GuidList m_lSummonedAddGuids; + + void Reset() override + { + // timers need confirmation + m_uiFrostTombTimer = 20000; + m_uiSummonTimer = 5000 ; + m_uiShadowboltTimer = 0; + + DespawnOrKillAdds(true); + } + + void AttackStart(Unit* pWho) override + { + if (m_creature->Attack(pWho, true)) + { + m_creature->AddThreat(pWho); + m_creature->SetInCombatWith(pWho); + pWho->SetInCombatWith(m_creature); + + m_creature->GetMotionMaster()->MoveChase(pWho, RUN_DISTANCE); + } + } + + void Aggro(Unit* /*pWho*/) override + { + DoScriptText(SAY_AGGRO, m_creature); + + if (m_pInstance) + m_pInstance->SetData(TYPE_KELESETH, IN_PROGRESS); + } + + void SummonAdds() + { + for (uint8 i = 0; i < 4; ++i) + m_creature->SummonCreature(NPC_VRYKUL_SKELETON, fAddPosition[0] + rand() % 7, fAddPosition[1] + rand() % 7, fAddPosition[2], fAddPosition[3], TEMPSUMMON_DEAD_DESPAWN, 0); + } + + void DespawnOrKillAdds(bool bDespawn) + { + for (GuidList::const_iterator itr = m_lSummonedAddGuids.begin(); itr != m_lSummonedAddGuids.end(); ++itr) + { + if (Creature* pAdd = m_creature->GetMap()->GetCreature(*itr)) + { + if (bDespawn) + pAdd->ForcedDespawn(); + else + { + pAdd->SetDeathState(JUST_DIED); + pAdd->SetHealth(0); + } + } + } + + m_lSummonedAddGuids.clear(); + } + + void JustSummoned(Creature* pSummoned) override + { + if (pSummoned->GetEntry() == NPC_VRYKUL_SKELETON) + { + pSummoned->AI()->AttackStart(m_creature->getVictim()); + m_lSummonedAddGuids.push_back(pSummoned->GetObjectGuid()); + } + + if (pSummoned->GetEntry() == NPC_FROST_TOMB) + pSummoned->ApplySpellImmune(0, IMMUNITY_DAMAGE, SPELL_SCHOOL_MASK_FROST, true); + } + + void JustDied(Unit* /*pKiller*/) override + { + DoScriptText(SAY_DEATH, m_creature); + + if (m_pInstance) + m_pInstance->SetData(TYPE_KELESETH, DONE); + + DespawnOrKillAdds(false); + } + + void JustReachedHome() override + { + if (m_pInstance) + m_pInstance->SetData(TYPE_KELESETH, FAIL); + } + + void KilledUnit(Unit* /*pVictim*/) override + { + DoScriptText(SAY_KILL, m_creature); + } + + void UpdateAI(const uint32 uiDiff) override + { + if (!m_creature->SelectHostileTarget() || !m_creature->getVictim()) + return; + + if (m_uiSummonTimer) + { + if (m_uiSummonTimer <= uiDiff) + { + SummonAdds(); + m_uiSummonTimer = 0; + } + else + m_uiSummonTimer -= uiDiff; + } + + if (m_uiShadowboltTimer < uiDiff) + { + DoCastSpellIfCan(m_creature->getVictim(), m_bIsRegularMode ? SPELL_SHADOWBOLT : SPELL_SHADOWBOLT_H); + m_uiShadowboltTimer = 3000; + } + else + m_uiShadowboltTimer -= uiDiff; + + if (m_uiFrostTombTimer < uiDiff) + { + if (Unit* pTombTarget = m_creature->SelectAttackingTarget(ATTACKING_TARGET_RANDOM, 0)) + { + // DoCastSpellIfCan(pTombTarget, SPELL_SUMMON_FROST_TOMB); + float fPosX, fPosY, fPosZ; + pTombTarget->GetPosition(fPosX, fPosY, fPosZ); + + if (Creature* pFrostTomb = m_creature->SummonCreature(NPC_FROST_TOMB, fPosX, fPosY, fPosZ, 0, TEMPSUMMON_TIMED_DESPAWN, 20000)) + { + pFrostTomb->AddThreat(pTombTarget); + pFrostTomb->CastSpell(pTombTarget, SPELL_FROST_TOMB, false); + } + + DoScriptText(SAY_FROSTTOMB, m_creature); + DoScriptText(EMOTE_FROST_TOMB, m_creature, pTombTarget); + } + + m_uiFrostTombTimer = 25000; + } + else + m_uiFrostTombTimer -= uiDiff; + + DoMeleeAttackIfReady(); + } +}; + +CreatureAI* GetAI_boss_keleseth(Creature* pCreature) +{ + return new boss_kelesethAI(pCreature); +} + +void AddSC_boss_keleseth() +{ + Script* pNewScript; + + pNewScript = new Script; + pNewScript->Name = "boss_keleseth"; + pNewScript->GetAI = &GetAI_boss_keleseth; + pNewScript->RegisterSelf(); + + pNewScript = new Script; + pNewScript->Name = "mob_vrykul_skeleton"; + pNewScript->GetAI = &GetAI_mob_vrykul_skeleton; + pNewScript->RegisterSelf(); +} diff --git a/src/modules/SD2/scripts/northrend/utgarde_keep/utgarde_keep/boss_skarvald_and_dalronn.cpp b/src/modules/SD2/scripts/northrend/utgarde_keep/utgarde_keep/boss_skarvald_and_dalronn.cpp new file mode 100644 index 000000000..bc3a79358 --- /dev/null +++ b/src/modules/SD2/scripts/northrend/utgarde_keep/utgarde_keep/boss_skarvald_and_dalronn.cpp @@ -0,0 +1,334 @@ +/* Copyright (C) 2006 - 2013 ScriptDev2 + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with 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_and_Dalronn +SD%Complete: 60% +SDComment: TODO: correct timers +SDCategory: Utgarde Keep +EndScriptData */ + +#include "precompiled.h" +#include "utgarde_keep.h" + +enum +{ + SAY_SKA_AGGRO = -1574011, + SAY_SKA_DEATH = -1574012, + SAY_SKA_DEATH_REAL = -1574013, + SAY_SKA_KILL = -1574014, + SAY_SKA_DAL_DIES_REPLY = -1574015, + + SAY_DAL_AGGRO_REPLY = -1574016, + SAY_DAL_DEATH = -1574017, + SAY_DAL_DEATH_REAL = -1574018, + SAY_DAL_KILL = -1574019, + SAY_DAL_SKA_DIES_REPLY = -1574020, + + SPELL_SUMMON_DAL_GHOST = 48612, + SPELL_SUMMON_SKA_GHOST = 48613, + + NPC_DAL_GHOST = 27389, + NPC_SKA_GHOST = 27390, + + NPC_SKELETAL = 28878, // summoned guardian in heroic + + // skarvald + SPELL_CHARGE = 43651, + SPELL_STONE_STRIKE = 48583, + SPELL_ENRAGE = 48193, + + // dalronn + SPELL_SHADOW_BOLT = 43649, + SPELL_SHADOW_BOLT_H = 59575, + + SPELL_DEBILITATE = 43650, + SPELL_DEBILITATE_H = 59577, + + SPELL_SUMMON_SKELETONS = 52611 +}; + +struct Yell +{ + int32 m_iTextId; + int32 m_iTextReplyId; +}; + +Yell m_aYell[] = +{ + {SAY_SKA_AGGRO, SAY_DAL_AGGRO_REPLY}, + {SAY_SKA_DEATH, SAY_DAL_SKA_DIES_REPLY}, + {SAY_DAL_DEATH, SAY_SKA_DAL_DIES_REPLY} +}; + +struct boss_s_and_d_dummyAI : public ScriptedAI +{ + boss_s_and_d_dummyAI(Creature* pCreature) : ScriptedAI(pCreature) + { + m_pInstance = (ScriptedInstance*)pCreature->GetInstanceData(); + m_bIsRegularMode = pCreature->GetMap()->IsRegularDifficulty(); + Reset(); + } + + ScriptedInstance* m_pInstance; + bool m_bIsRegularMode; + ObjectGuid m_ghostGuid; + + Creature* GetBuddy() + { + if (!m_pInstance) + return NULL; + + return m_pInstance->GetSingleCreatureFromStorage(m_creature->GetEntry() == NPC_DALRONN ? NPC_SKARVALD : NPC_DALRONN); + } + + void Reset() override { } + + void JustReachedHome() override + { + if (Creature* pBuddy = GetBuddy()) + { + if (pBuddy->IsDead()) + pBuddy->Respawn(); + } + + if (Creature* pGhost = m_creature->GetMap()->GetCreature(m_ghostGuid)) + { + if (pGhost->IsAlive()) + pGhost->ForcedDespawn(); + } + } + + void EnterCombat(Unit* pWho) override + { + if (!pWho) + return; + + if (Creature* pBuddy = GetBuddy()) + { + if (!pBuddy->getVictim()) + pBuddy->AI()->AttackStart(pWho); + } + + Aggro(pWho); + } + + void JustSummoned(Creature* pSummoned) override + { + // EventAI can probably handle ghosts + if (pSummoned->GetEntry() == NPC_DAL_GHOST || pSummoned->GetEntry() == NPC_SKA_GHOST) + m_ghostGuid = pSummoned->GetObjectGuid(); + + Unit* pTarget = m_creature->SelectAttackingTarget(ATTACKING_TARGET_TOPAGGRO, 1); + + if (m_creature->getVictim()) + pSummoned->AI()->AttackStart(pTarget ? pTarget : m_creature->getVictim()); + } + + void JustDied(Unit* /*pKiller*/) override + { + if (Creature* pBuddy = GetBuddy()) + { + if (pBuddy->IsAlive()) + { + DoScriptText(m_creature->GetEntry() == NPC_SKARVALD ? m_aYell[1].m_iTextId : m_aYell[2].m_iTextId, m_creature); + DoScriptText(m_creature->GetEntry() == NPC_SKARVALD ? m_aYell[1].m_iTextReplyId : m_aYell[2].m_iTextReplyId, pBuddy); + + pBuddy->CastSpell(m_creature, m_creature->GetEntry() == NPC_SKARVALD ? SPELL_SUMMON_SKA_GHOST : SPELL_SUMMON_DAL_GHOST, true); + + m_creature->RemoveFlag(UNIT_DYNAMIC_FLAGS, UNIT_DYNFLAG_LOOTABLE); + } + else + { + if (Creature* pGhost = m_creature->GetMap()->GetCreature(m_ghostGuid)) + pGhost->ForcedDespawn(); + + pBuddy->SetFlag(UNIT_DYNAMIC_FLAGS, UNIT_DYNFLAG_LOOTABLE); + } + } + } +}; + +/*###### +## boss_skarvald +######*/ + +struct boss_skarvaldAI : public boss_s_and_d_dummyAI +{ + boss_skarvaldAI(Creature* pCreature) : boss_s_and_d_dummyAI(pCreature) { Reset(); } + + uint32 m_uiYellDelayTimer; + uint32 m_uiChargeTimer; + uint32 m_uiEnrageTimer; + uint32 m_uiStoneStrikeTimer; + + void Reset() override + { + m_uiYellDelayTimer = 0; + m_uiChargeTimer = urand(2000, 6000); + m_uiEnrageTimer = 15000; + m_uiStoneStrikeTimer = 8000; + } + + void Aggro(Unit* /*pWho*/) override + { + DoScriptText(m_aYell[0].m_iTextId, m_creature); + m_uiYellDelayTimer = 5000; + } + + void KilledUnit(Unit* /*pVictim*/) override + { + DoScriptText(SAY_SKA_KILL, m_creature); + } + + void UpdateAI(const uint32 uiDiff) override + { + if (!m_creature->SelectHostileTarget() || !m_creature->getVictim()) + return; + + if (m_uiYellDelayTimer) + { + if (m_uiYellDelayTimer <= uiDiff) + { + if (Creature* pBuddy = GetBuddy()) + DoScriptText(m_aYell[0].m_iTextReplyId, pBuddy); + + m_uiYellDelayTimer = 0; + } + else + m_uiYellDelayTimer -= uiDiff; + } + + if (m_uiChargeTimer < uiDiff) + { + if (Unit* pTarget = m_creature->SelectAttackingTarget(ATTACKING_TARGET_RANDOM, 1)) + DoCastSpellIfCan(pTarget, SPELL_CHARGE); + + m_uiChargeTimer = urand(8000, 16000); + } + else + m_uiChargeTimer -= uiDiff; + + if (m_uiEnrageTimer < uiDiff) + { + DoCastSpellIfCan(m_creature, SPELL_ENRAGE); + m_uiEnrageTimer = 20000; + } + else + m_uiEnrageTimer -= uiDiff; + + if (m_uiStoneStrikeTimer < uiDiff) + { + DoCastSpellIfCan(m_creature->getVictim(), SPELL_STONE_STRIKE); + m_uiStoneStrikeTimer = urand(5000, 15000); + } + else + m_uiStoneStrikeTimer -= uiDiff; + + DoMeleeAttackIfReady(); + } +}; + +CreatureAI* GetAI_boss_skarvald(Creature* pCreature) +{ + return new boss_skarvaldAI(pCreature); +} + +/*###### +## boss_dalronn +######*/ + +struct boss_dalronnAI : public boss_s_and_d_dummyAI +{ + boss_dalronnAI(Creature* pCreature) : boss_s_and_d_dummyAI(pCreature) { Reset(); } + + uint32 m_uiDebilitateTimer; + uint32 m_uiShadowBoltTimer; + uint32 m_uiSkeletonTimer; + + void Reset() override + { + m_uiDebilitateTimer = urand(5000, 10000); + m_uiShadowBoltTimer = urand(2500, 6000); + m_uiSkeletonTimer = urand(25000, 35000); + } + + void KilledUnit(Unit* /*pVictim*/) override + { + DoScriptText(SAY_DAL_KILL, m_creature); + } + + void UpdateAI(const uint32 uiDiff) override + { + if (!m_creature->SelectHostileTarget() || !m_creature->getVictim()) + return; + + if (m_uiDebilitateTimer < uiDiff) + { + if (Unit* pTarget = m_creature->SelectAttackingTarget(ATTACKING_TARGET_RANDOM, 0)) + DoCastSpellIfCan(pTarget, m_bIsRegularMode ? SPELL_DEBILITATE : SPELL_DEBILITATE_H); + + m_uiDebilitateTimer = urand(12000, 20000); + } + else + m_uiDebilitateTimer -= uiDiff; + + if (m_uiShadowBoltTimer < uiDiff) + { + if (Unit* pTarget = m_creature->SelectAttackingTarget(ATTACKING_TARGET_RANDOM, 0)) + DoCastSpellIfCan(pTarget, m_bIsRegularMode ? SPELL_SHADOW_BOLT : SPELL_SHADOW_BOLT_H); + + m_uiShadowBoltTimer = urand(3000, 6000); + } + else + m_uiShadowBoltTimer -= uiDiff; + + if (!m_bIsRegularMode) + { + if (m_uiSkeletonTimer < uiDiff) + { + if (!m_creature->FindGuardianWithEntry(NPC_SKELETAL)) + DoCastSpellIfCan(m_creature, SPELL_SUMMON_SKELETONS); + + m_uiSkeletonTimer = 30000; + } + else + m_uiSkeletonTimer -= uiDiff; + } + + DoMeleeAttackIfReady(); + } +}; + +CreatureAI* GetAI_boss_dalronn(Creature* pCreature) +{ + return new boss_dalronnAI(pCreature); +} + +void AddSC_boss_skarvald_and_dalronn() +{ + Script* pNewScript; + + pNewScript = new Script; + pNewScript->Name = "boss_skarvald"; + pNewScript->GetAI = &GetAI_boss_skarvald; + pNewScript->RegisterSelf(); + + pNewScript = new Script; + pNewScript->Name = "boss_dalronn"; + pNewScript->GetAI = &GetAI_boss_dalronn; + pNewScript->RegisterSelf(); +} diff --git a/src/modules/SD2/scripts/northrend/utgarde_keep/utgarde_keep/instance_utgarde_keep.cpp b/src/modules/SD2/scripts/northrend/utgarde_keep/utgarde_keep/instance_utgarde_keep.cpp new file mode 100644 index 000000000..ccab4d375 --- /dev/null +++ b/src/modules/SD2/scripts/northrend/utgarde_keep/utgarde_keep/instance_utgarde_keep.cpp @@ -0,0 +1,199 @@ +/* Copyright (C) 2006 - 2013 ScriptDev2 + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along 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: 20% +SDComment: +SDCategory: Utgarde Keep +EndScriptData */ + +#include "precompiled.h" +#include "utgarde_keep.h" + +instance_utgarde_keep::instance_utgarde_keep(Map* pMap) : ScriptedInstance(pMap), + m_bKelesethAchievFailed(false) +{ + Initialize(); +} + +void instance_utgarde_keep::Initialize() +{ + memset(&m_auiEncounter, 0, sizeof(m_auiEncounter)); +} + +void instance_utgarde_keep::OnCreatureCreate(Creature* pCreature) +{ + switch (pCreature->GetEntry()) + { + case NPC_KELESETH: + case NPC_SKARVALD: + case NPC_DALRONN: + case NPC_INGVAR: + m_mNpcEntryGuidStore[pCreature->GetEntry()] = pCreature->GetObjectGuid(); + break; + } +} + +void instance_utgarde_keep::OnObjectCreate(GameObject* pGo) +{ + switch (pGo->GetEntry()) + { + case GO_BELLOW_1: + if (m_auiEncounter[TYPE_BELLOW_1] == DONE) + pGo->SetGoState(GO_STATE_ACTIVE); + break; + case GO_BELLOW_2: + if (m_auiEncounter[TYPE_BELLOW_2] == DONE) + pGo->SetGoState(GO_STATE_ACTIVE); + break; + case GO_BELLOW_3: + if (m_auiEncounter[TYPE_BELLOW_3] == DONE) + pGo->SetGoState(GO_STATE_ACTIVE); + break; + case GO_FORGEFIRE_1: + if (m_auiEncounter[TYPE_BELLOW_1] == DONE) + pGo->SetGoState(GO_STATE_ACTIVE); + break; + case GO_FORGEFIRE_2: + if (m_auiEncounter[TYPE_BELLOW_2] == DONE) + pGo->SetGoState(GO_STATE_ACTIVE); + break; + case GO_FORGEFIRE_3: + if (m_auiEncounter[TYPE_BELLOW_3] == DONE) + pGo->SetGoState(GO_STATE_ACTIVE); + break; + case GO_PORTCULLIS_EXIT_1: + case GO_PORTCULLIS_EXIT_2: + if (m_auiEncounter[TYPE_INGVAR] == DONE) + pGo->SetGoState(GO_STATE_ACTIVE); + break; + case GO_PORTCULLIS_COMBAT: + break; + + default: + return; + } + m_mGoEntryGuidStore[pGo->GetEntry()] = pGo->GetObjectGuid(); +} + +void instance_utgarde_keep::OnCreatureDeath(Creature* pCreature) +{ + if (pCreature->GetEntry() == NPC_FROST_TOMB) + m_bKelesethAchievFailed = true; +} + +void instance_utgarde_keep::SetData(uint32 uiType, uint32 uiData) +{ + switch (uiType) + { + case TYPE_KELESETH: + if (uiData == IN_PROGRESS) + m_bKelesethAchievFailed = false; + m_auiEncounter[uiType] = uiData; + break; + case TYPE_SKARVALD_DALRONN: + m_auiEncounter[uiType] = uiData; + break; + case TYPE_INGVAR: + if (m_auiEncounter[uiType] == uiData) + return; + DoUseDoorOrButton(GO_PORTCULLIS_COMBAT); + if (uiData == DONE) + { + DoUseDoorOrButton(GO_PORTCULLIS_EXIT_1); + DoUseDoorOrButton(GO_PORTCULLIS_EXIT_2); + } + m_auiEncounter[uiType] = uiData; + break; + case TYPE_BELLOW_1: + m_auiEncounter[uiType] = uiData; + break; + case TYPE_BELLOW_2: + m_auiEncounter[uiType] = uiData; + break; + case TYPE_BELLOW_3: + m_auiEncounter[uiType] = uiData; + 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_strInstData = saveStream.str(); + + SaveToDB(); + OUT_SAVE_INST_DATA_COMPLETE; + } +} + +uint32 instance_utgarde_keep::GetData(uint32 uiType) const +{ + if (uiType < MAX_ENCOUNTER) + return m_auiEncounter[uiType]; + + return 0; +} + +void instance_utgarde_keep::Load(const char* chrIn) +{ + if (!chrIn) + { + 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]; + + for (uint8 i = 0; i < MAX_ENCOUNTER; ++i) + { + if (m_auiEncounter[i] == IN_PROGRESS) + m_auiEncounter[i] = NOT_STARTED; + } + + OUT_LOAD_INST_DATA_COMPLETE; +} + +bool instance_utgarde_keep::CheckAchievementCriteriaMeet(uint32 uiCriteriaId, Player const* /*pSource*/, Unit const* /*pTarget*/, uint32 /*uiMiscValue1 = 0*/) const +{ + if (uiCriteriaId == ACHIEV_CRIT_ON_THE_ROCKS) + return !m_bKelesethAchievFailed; + + return false; +} + +InstanceData* GetInstanceData_instance_utgarde_keep(Map* pMap) +{ + return new instance_utgarde_keep(pMap); +} + +void AddSC_instance_utgarde_keep() +{ + Script* pNewScript; + + pNewScript = new Script; + pNewScript->Name = "instance_utgarde_keep"; + pNewScript->GetInstanceData = GetInstanceData_instance_utgarde_keep; + pNewScript->RegisterSelf(); +} diff --git a/src/modules/SD2/scripts/northrend/utgarde_keep/utgarde_keep/utgarde_keep.cpp b/src/modules/SD2/scripts/northrend/utgarde_keep/utgarde_keep/utgarde_keep.cpp new file mode 100644 index 000000000..b1ddbe358 --- /dev/null +++ b/src/modules/SD2/scripts/northrend/utgarde_keep/utgarde_keep/utgarde_keep.cpp @@ -0,0 +1,158 @@ +/* Copyright (C) 2006 - 2013 ScriptDev2 + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +/* ScriptData +SDName: Utgarde_Keep +SD%Complete: 75 +SDComment: +SDCategory: Utgarde Keep +EndScriptData */ + +/* ContentData +mob_dragonflayer_forge_master +EndContentData */ + +#include "precompiled.h" +#include "utgarde_keep.h" + +/*###### +## mob_dragonflayer_forge_master +######*/ + +enum +{ + SPELL_BURNING_BRAND = 43757, + SPELL_BURNING_BRAND_H = 59601, + SPELL_CAUTERIZE = 60211, + + MAX_FORGE = 3 +}; + +struct mob_dragonflayer_forge_masterAI : public ScriptedAI +{ + mob_dragonflayer_forge_masterAI(Creature* pCreature) : ScriptedAI(pCreature) + { + m_pInstance = (ScriptedInstance*)pCreature->GetInstanceData(); + m_bIsRegularMode = pCreature->GetMap()->IsRegularDifficulty(); + m_uiForgeEncounterId = 0; + Reset(); + } + + ScriptedInstance* m_pInstance; + bool m_bIsRegularMode; + + uint32 m_uiForgeEncounterId; + uint32 m_uiBurningBrandTimer; + + void Reset() override + { + m_uiBurningBrandTimer = 2000; + } + + void SetMyForge() + { + std::list lGOList; + uint32 uiGOBellow = 0; + uint32 uiGOFire = 0; + + for (uint8 i = 0; i < MAX_FORGE; ++i) + { + switch (i) + { + case 0: uiGOBellow = GO_BELLOW_1; break; + case 1: uiGOBellow = GO_BELLOW_2; break; + case 2: uiGOBellow = GO_BELLOW_3; break; + } + + if (GameObject* pGOTemp = m_pInstance->GetSingleGameObjectFromStorage(uiGOBellow)) + lGOList.push_back(pGOTemp); + } + + if (!lGOList.empty()) + { + if (lGOList.size() != MAX_FORGE) + script_error_log("mob_dragonflayer_forge_master expected %u in lGOList, but does not match.", MAX_FORGE); + + lGOList.sort(ObjectDistanceOrder(m_creature)); + + if (lGOList.front()->getLootState() == GO_READY) + lGOList.front()->UseDoorOrButton(DAY); + else if (lGOList.front()->getLootState() == GO_ACTIVATED) + lGOList.front()->ResetDoorOrButton(); + + switch (lGOList.front()->GetEntry()) + { + case GO_BELLOW_1: uiGOFire = GO_FORGEFIRE_1; m_uiForgeEncounterId = TYPE_BELLOW_1; break; + case GO_BELLOW_2: uiGOFire = GO_FORGEFIRE_2; m_uiForgeEncounterId = TYPE_BELLOW_2; break; + case GO_BELLOW_3: uiGOFire = GO_FORGEFIRE_3; m_uiForgeEncounterId = TYPE_BELLOW_3; break; + } + + if (GameObject* pGOTemp = m_pInstance->GetSingleGameObjectFromStorage(uiGOFire)) + { + if (pGOTemp->getLootState() == GO_READY) + pGOTemp->UseDoorOrButton(DAY); + else if (pGOTemp->getLootState() == GO_ACTIVATED) + pGOTemp->ResetDoorOrButton(); + } + } + } + + void Aggro(Unit* /*pWho*/) override + { + SetMyForge(); + } + + void JustReachedHome() override + { + SetMyForge(); + } + + void JustDied(Unit* /*pKiller*/) override + { + if (m_pInstance) + m_pInstance->SetData(m_uiForgeEncounterId, DONE); + } + + void UpdateAI(const uint32 uiDiff) override + { + if (!m_creature->SelectHostileTarget() || !m_creature->getVictim()) + return; + + if (m_uiBurningBrandTimer < uiDiff) + { + DoCastSpellIfCan(m_creature->getVictim(), m_bIsRegularMode ? SPELL_BURNING_BRAND : SPELL_BURNING_BRAND_H); + m_uiBurningBrandTimer = 15000; + } + else m_uiBurningBrandTimer -= uiDiff; + + DoMeleeAttackIfReady(); + } +}; + +CreatureAI* GetAI_mob_dragonflayer_forge_master(Creature* pCreature) +{ + return new mob_dragonflayer_forge_masterAI(pCreature); +} + +void AddSC_utgarde_keep() +{ + Script* pNewScript; + + pNewScript = new Script; + pNewScript->Name = "mob_dragonflayer_forge_master"; + pNewScript->GetAI = &GetAI_mob_dragonflayer_forge_master; + pNewScript->RegisterSelf(); +} diff --git a/src/modules/SD2/scripts/northrend/utgarde_keep/utgarde_keep/utgarde_keep.h b/src/modules/SD2/scripts/northrend/utgarde_keep/utgarde_keep/utgarde_keep.h new file mode 100644 index 000000000..fa85b9651 --- /dev/null +++ b/src/modules/SD2/scripts/northrend/utgarde_keep/utgarde_keep/utgarde_keep.h @@ -0,0 +1,67 @@ +/* Copyright (C) 2006 - 2013 ScriptDev2 + * This program is free software licensed under GPL version 2 + * Please see the included DOCS/LICENSE.TXT for more information */ + +#ifndef DEF_UTG_KEEP_H +#define DEF_UTG_KEEP_H + +enum +{ + MAX_ENCOUNTER = 6, + + TYPE_KELESETH = 0, + TYPE_SKARVALD_DALRONN = 1, + TYPE_INGVAR = 2, + TYPE_BELLOW_1 = 3, + TYPE_BELLOW_2 = 4, + TYPE_BELLOW_3 = 5, + + NPC_KELESETH = 23953, + NPC_SKARVALD = 24200, + NPC_DALRONN = 24201, + NPC_INGVAR = 23954, + + NPC_FROST_TOMB = 23965, + + GO_BELLOW_1 = 186688, + GO_BELLOW_2 = 186689, + GO_BELLOW_3 = 186690, + GO_FORGEFIRE_1 = 186692, + GO_FORGEFIRE_2 = 186693, + GO_FORGEFIRE_3 = 186691, + GO_PORTCULLIS_COMBAT = 186612, + GO_PORTCULLIS_EXIT_1 = 186694, + GO_PORTCULLIS_EXIT_2 = 186756, + + ACHIEV_CRIT_ON_THE_ROCKS = 7231, +}; + +class instance_utgarde_keep : public ScriptedInstance +{ + public: + instance_utgarde_keep(Map* pMap); + ~instance_utgarde_keep() {} + + void Initialize() override; + + void OnCreatureCreate(Creature* pCreature) override; + void OnObjectCreate(GameObject* pGo) override; + + void OnCreatureDeath(Creature* pCreature) override; + + void SetData(uint32 uiType, uint32 uiData) override; + uint32 GetData(uint32 uiType) const override; + + const char* Save() const override { return m_strInstData.c_str(); } + void Load(const char* chrIn) override; + + bool CheckAchievementCriteriaMeet(uint32 uiCriteriaId, Player const* pSource, Unit const* pTarget, uint32 uiMiscValue1 /* = 0*/) const override; + + protected: + uint32 m_auiEncounter[MAX_ENCOUNTER]; + std::string m_strInstData; + + bool m_bKelesethAchievFailed; +}; + +#endif diff --git a/src/modules/SD2/scripts/northrend/utgarde_keep/utgarde_pinnacle/boss_gortok.cpp b/src/modules/SD2/scripts/northrend/utgarde_keep/utgarde_pinnacle/boss_gortok.cpp new file mode 100644 index 000000000..73d50efa5 --- /dev/null +++ b/src/modules/SD2/scripts/northrend/utgarde_keep/utgarde_pinnacle/boss_gortok.cpp @@ -0,0 +1,216 @@ +/* Copyright (C) 2006 - 2013 ScriptDev2 + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +/* ScriptData +SDName: Boss_Gortok +SD%Complete: 90% +SDComment: Timers; The subbosses and Gortok should be activated on aura remove +SDCategory: Utgarde Pinnacle +EndScriptData */ + +#include "precompiled.h" +#include "utgarde_pinnacle.h" + +enum +{ + SAY_AGGRO = -1575015, + SAY_SLAY_1 = -1575016, + SAY_SLAY_2 = -1575017, + SAY_DEATH = -1575018, + + SPELL_FREEZE_ANIM = 16245, + + SPELL_IMPALE = 48261, + SPELL_IMPALE_H = 59268, + + SPELL_WITHERING_ROAR = 48256, + SPELL_WITHERING_ROAR_H = 59267, + + SPELL_ARCING_SMASH = 48260 +}; + +/*###### +## boss_gortok +######*/ + +struct boss_gortokAI : public ScriptedAI +{ + boss_gortokAI(Creature* pCreature) : ScriptedAI(pCreature) + { + m_pInstance = (ScriptedInstance*)pCreature->GetInstanceData(); + m_bIsRegularMode = pCreature->GetMap()->IsRegularDifficulty(); + Reset(); + } + + ScriptedInstance* m_pInstance; + bool m_bIsRegularMode; + + uint32 m_uiRoarTimer; + uint32 m_uiImpaleTimer; + uint32 m_uiArcingSmashTimer; + + void Reset() override + { + m_uiRoarTimer = 10000; + m_uiImpaleTimer = 15000; + m_uiArcingSmashTimer = urand(5000, 8000); + + // This needs to be reset in case the event fails + m_creature->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE); + } + + void Aggro(Unit* /*pWho*/) override + { + DoScriptText(SAY_AGGRO, m_creature); + } + + void KilledUnit(Unit* /*pVictim*/) override + { + DoScriptText(urand(0, 1) ? SAY_SLAY_1 : SAY_SLAY_2, m_creature); + } + + void JustDied(Unit* /*pKiller*/) override + { + DoScriptText(SAY_DEATH, m_creature); + + if (m_pInstance) + m_pInstance->SetData(TYPE_GORTOK, DONE); + } + + void JustReachedHome() override + { + if (m_pInstance) + m_pInstance->SetData(TYPE_GORTOK, FAIL); + } + + void UpdateAI(const uint32 uiDiff) override + { + if (!m_creature->SelectHostileTarget() || !m_creature->getVictim()) + return; + + if (m_uiRoarTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature, m_bIsRegularMode ? SPELL_WITHERING_ROAR : SPELL_WITHERING_ROAR_H) == CAST_OK) + m_uiRoarTimer = 10000; + } + else + m_uiRoarTimer -= uiDiff; + + if (m_uiImpaleTimer < uiDiff) + { + if (Unit* pTarget = m_creature->SelectAttackingTarget(ATTACKING_TARGET_RANDOM, 0)) + { + if (DoCastSpellIfCan(pTarget, m_bIsRegularMode ? SPELL_IMPALE : SPELL_IMPALE_H) == CAST_OK) + m_uiImpaleTimer = urand(8000, 15000); + } + } + else + m_uiImpaleTimer -= uiDiff; + + if (m_uiArcingSmashTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature->getVictim(), SPELL_ARCING_SMASH) == CAST_OK) + m_uiArcingSmashTimer = urand(5000, 13000); + } + else + m_uiArcingSmashTimer -= uiDiff; + + DoMeleeAttackIfReady(); + } +}; + +CreatureAI* GetAI_boss_gortok(Creature* pCreature) +{ + return new boss_gortokAI(pCreature); +} + +bool EffectDummyCreature_spell_awaken_gortok(Unit* /*pCaster*/, uint32 uiSpellId, SpellEffectIndex uiEffIndex, Creature* pCreatureTarget, ObjectGuid /*originalCasterGuid*/) +{ + // always check spellid and effectindex + if (uiSpellId == SPELL_AWAKEN_GORTOK && uiEffIndex == EFFECT_INDEX_0) + { + pCreatureTarget->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE); + pCreatureTarget->RemoveAurasDueToSpell(SPELL_FREEZE_ANIM); + + // Start attacking the players + if (instance_pinnacle* pInstance = (instance_pinnacle*)pCreatureTarget->GetInstanceData()) + { + if (Unit* pStarter = pCreatureTarget->GetMap()->GetUnit(pInstance->GetGortokEventStarter())) + pCreatureTarget->AI()->AttackStart(pStarter); + } + + // always return true when we are handling this spell and effect + return true; + } + + return false; +} + +bool EffectAuraDummy_spell_aura_dummy_awaken_subboss(const Aura* pAura, bool bApply) +{ + // Note: this should be handled on aura remove, but this can't be done because there are some core issues with areaeffect spells + if (pAura->GetId() == SPELL_AWAKEN_SUBBOSS && pAura->GetEffIndex() == EFFECT_INDEX_0 && bApply) + { + if (Creature* pTarget = (Creature*)pAura->GetTarget()) + { + pTarget->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE); + pTarget->RemoveAurasDueToSpell(SPELL_FREEZE_ANIM); + + // Start attacking the players + if (instance_pinnacle* pInstance = (instance_pinnacle*)pTarget->GetInstanceData()) + { + if (Unit* pStarter = pTarget->GetMap()->GetUnit(pInstance->GetGortokEventStarter())) + pTarget->AI()->AttackStart(pStarter); + } + } + } + return true; +} + +bool ProcessEventId_event_spell_gortok_event(uint32 /*uiEventId*/, Object* pSource, Object* /*pTarget*/, bool /*bIsStart*/) +{ + if (instance_pinnacle* pInstance = (instance_pinnacle*)((Creature*)pSource)->GetInstanceData()) + { + if (pInstance->GetData(TYPE_GORTOK) == IN_PROGRESS || pInstance->GetData(TYPE_GORTOK) == DONE) + return false; + + pInstance->SetData(TYPE_GORTOK, IN_PROGRESS); + pInstance->SetGortokEventStarter(pSource->GetObjectGuid()); + return true; + } + return false; +} + +void AddSC_boss_gortok() +{ + Script* pNewScript; + + pNewScript = new Script; + pNewScript->Name = "boss_gortok"; + pNewScript->GetAI = &GetAI_boss_gortok; + pNewScript->pEffectDummyNPC = &EffectDummyCreature_spell_awaken_gortok; + pNewScript->RegisterSelf(); + + pNewScript = new Script; + pNewScript->Name = "npc_gortok_subboss"; + pNewScript->pEffectAuraDummy = &EffectAuraDummy_spell_aura_dummy_awaken_subboss; + pNewScript->RegisterSelf(); + + pNewScript = new Script; + pNewScript->Name = "event_spell_gortok_event"; + pNewScript->pProcessEventId = &ProcessEventId_event_spell_gortok_event; + pNewScript->RegisterSelf(); +} diff --git a/src/modules/SD2/scripts/northrend/utgarde_keep/utgarde_pinnacle/boss_skadi.cpp b/src/modules/SD2/scripts/northrend/utgarde_keep/utgarde_pinnacle/boss_skadi.cpp new file mode 100644 index 000000000..e61ea5e86 --- /dev/null +++ b/src/modules/SD2/scripts/northrend/utgarde_keep/utgarde_pinnacle/boss_skadi.cpp @@ -0,0 +1,517 @@ +/* Copyright (C) 2006 - 2013 ScriptDev2 + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +/* ScriptData +SDName: Boss_Skadi +SD%Complete: 80% +SDComment: The gauntlet movement needs to be random choosed for left and right. Event reset not implemented using the proper spell +SDCategory: Utgarde Pinnacle +EndScriptData */ + +#include "precompiled.h" +#include "utgarde_pinnacle.h" + +enum +{ + SAY_AGGRO = -1575019, + SAY_DRAKEBREATH_1 = -1575020, + SAY_DRAKEBREATH_2 = -1575021, + SAY_DRAKEBREATH_3 = -1575022, + SAY_DRAKE_HARPOON_1 = -1575023, + SAY_DRAKE_HARPOON_2 = -1575024, + SAY_KILL_1 = -1575025, + SAY_KILL_2 = -1575026, + SAY_KILL_3 = -1575027, + SAY_DEATH = -1575028, + SAY_DRAKE_DEATH = -1575029, + EMOTE_HARPOON_RANGE = -1575030, + EMOTE_DEEP_BREATH = -1575041, + + // phase 1 spells + SPELL_RIDE_VEHICLE = 61791, + SPELL_FREEZING_CLOUD_LEFT = 47590, + SPELL_FREEZING_CLOUD_RIGHT = 47592, + SPELL_SKADI_TELEPORT = 61790, // teleport when Grauf is killed + SPELL_GAUNTLET_PERIODIC = 47546, // what is this? Unknown use/effect, but probably related - cast by each player + SPELL_SUMMON_GAUNTLET_MOBS = 48630, // tick every 30 sec + SPELL_SUMMON_GAUNTLET_MOBS_H = 59275, // tick every 25 sec + SPELL_LAUNCH_HARPOON = 48642, // this spell hit drake to reduce HP (force triggered from 48641) + + // phase 2 spells + SPELL_CRUSH = 50234, + SPELL_CRUSH_H = 59330, + SPELL_WHIRLWIND = 50228, + SPELL_WHIRLWIND_H = 59322, + SPELL_POISONED_SPEAR = 50255, + SPELL_POISONED_SPEAR_H = 59331, + + MAX_INTRO_MOBS = 13, + + PHASE_GAUNTLET = 1, + PHASE_NORMAL_COMBAT = 2, +}; + +struct GauntletIntroData +{ + uint32 uiCreatureId; + float fX, fY, fZ; +}; + +static const GauntletIntroData aSkadiIntroData[MAX_INTRO_MOBS] = +{ + {NPC_YMIRJAR_WITCH_DOCTOR, 478.31f, -511.049f, 104.7242f}, + {NPC_YMIRJAR_HARPOONER, 482.25f, -514.1273f, 104.7234f}, + {NPC_YMIRJAR_HARPOONER, 481.3883f, -507.1089f, 104.7241f}, + {NPC_YMIRJAR_WARRIOR, 458.5323f, -516.2537f, 104.617f}, + {NPC_YMIRJAR_WARRIOR, 429.4242f, -517.5624f, 104.8936f}, + {NPC_YMIRJAR_WARRIOR, 427.4026f, -510.7716f, 104.8802f}, + {NPC_YMIRJAR_WARRIOR, 458.5323f, -510.2537f, 104.617f}, + {NPC_YMIRJAR_WARRIOR, 397.036f, -515.158f, 104.725f}, // the rest are guesswork but follow the same pattern + {NPC_YMIRJAR_WARRIOR, 397.036f, -507.158f, 104.725f}, + {NPC_YMIRJAR_WARRIOR, 360.297f, -508.927f, 104.662f}, + {NPC_YMIRJAR_WARRIOR, 360.297f, -516.927f, 104.662f}, + {NPC_YMIRJAR_WARRIOR, 328.324f, -513.387f, 104.577f}, + {NPC_YMIRJAR_WARRIOR, 328.324f, -504.387f, 104.577f}, +}; + +/*###### +## boss_skadi +######*/ + +struct boss_skadiAI : public ScriptedAI +{ + boss_skadiAI(Creature* pCreature) : ScriptedAI(pCreature) + { + m_pInstance = (instance_pinnacle*)pCreature->GetInstanceData(); + m_bIsRegularMode = pCreature->GetMap()->IsRegularDifficulty(); + Reset(); + } + + instance_pinnacle* m_pInstance; + bool m_bIsRegularMode; + + uint32 m_uiCrush; + uint32 m_uiWhirlwind; + uint32 m_uiPoisonedSpear; + uint32 m_uiMountTimer; + uint8 m_uiPhase; + bool m_IntroMobs; + + void Reset() override + { + m_uiMountTimer = 0; + m_uiCrush = 15000; + m_uiWhirlwind = 23000; + m_uiPoisonedSpear = 10000; + m_uiPhase = PHASE_GAUNTLET; + m_IntroMobs = false; + + // Set immune during phase 1 + m_creature->ApplySpellImmune(0, IMMUNITY_DAMAGE, SPELL_SCHOOL_MASK_ALL, true); + m_creature->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE | UNIT_FLAG_OOC_NOT_ATTACKABLE); + } + + void AttackStart(Unit* pWho) override + { + if (m_uiPhase == PHASE_GAUNTLET) + return; + + ScriptedAI::AttackStart(pWho); + } + + void MoveInLineOfSight(Unit* pWho) override + { + if (m_uiPhase == PHASE_GAUNTLET) + return; + + ScriptedAI::MoveInLineOfSight(pWho); + } + + void JustReachedHome() override + { + if (m_pInstance) + m_pInstance->SetData(TYPE_SKADI, NOT_STARTED); + } + + void Aggro(Unit* /*pWho*/) override + { + if (m_pInstance) + m_pInstance->SetData(TYPE_SKADI, IN_PROGRESS); + } + + void KilledUnit(Unit* /*pVictim*/) override + { + switch (urand(0, 2)) + { + case 0: DoScriptText(SAY_KILL_1, m_creature); break; + case 1: DoScriptText(SAY_KILL_2, m_creature); break; + case 2: DoScriptText(SAY_KILL_3, m_creature); break; + } + } + + void JustDied(Unit* /*pKiller*/) override + { + DoScriptText(SAY_DEATH, m_creature); + + if (m_pInstance) + m_pInstance->SetData(TYPE_SKADI, DONE); + } + + void SpellHit(Unit* /*pCaster*/, const SpellEntry* pSpell) override + { + if (pSpell->Id == SPELL_SKADI_TELEPORT) + { + m_uiPhase = PHASE_NORMAL_COMBAT; + m_creature->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE | UNIT_FLAG_OOC_NOT_ATTACKABLE); + m_creature->ApplySpellImmune(0, IMMUNITY_DAMAGE, SPELL_SCHOOL_MASK_ALL, false); + } + } + + void JustSummoned(Creature* pSummon) override + { + // the intro mobs have predefined positions + if (m_IntroMobs) + return; + + // Move all the way to the entrance - the exact location is unk so use waypoint movement + switch (pSummon->GetEntry()) + { + case NPC_YMIRJAR_HARPOONER: + case NPC_YMIRJAR_WARRIOR: + case NPC_YMIRJAR_WITCH_DOCTOR: + pSummon->SetWalk(false); + pSummon->GetMotionMaster()->MoveWaypoint(); + break; + } + } + + void SummonedMovementInform(Creature* pSummoned, uint32 uiMotionType, uint32 uiPointId) override + { + if (uiMotionType != POINT_MOTION_TYPE || !uiPointId) + return; + + // called only for the intro mobs which are summoned directly + pSummoned->SetFacingTo(3.15f); + if (pSummoned->GetEntry() == NPC_YMIRJAR_WARRIOR) + pSummoned->HandleEmote(EMOTE_STATE_READY1H); + else + pSummoned->HandleEmote(EMOTE_STATE_READYTHROWN); + } + + void DoPrepareForGauntlet() + { + DoScriptText(SAY_AGGRO, m_creature); + m_uiMountTimer = 3000; + + if (!m_pInstance) + return; + + // Prepare to periodic summon the mobs + if (Creature* pTrigger = m_creature->GetMap()->GetCreature(m_pInstance->GetSkadiMobsTrigger())) + { + pTrigger->CastSpell(pTrigger, m_bIsRegularMode ? SPELL_SUMMON_GAUNTLET_MOBS : SPELL_SUMMON_GAUNTLET_MOBS_H, true, NULL, NULL, m_creature->GetObjectGuid()); + + // Spawn the intro mobs + m_IntroMobs = true; + for (uint8 i = 0; i < MAX_INTRO_MOBS; ++i) + { + if (Creature* pYmirjar = m_creature->SummonCreature(aSkadiIntroData[i].uiCreatureId, pTrigger->GetPositionX(), pTrigger->GetPositionY(), pTrigger->GetPositionZ(), 0, TEMPSUMMON_DEAD_DESPAWN, 0)) + { + pYmirjar->SetWalk(false); + pYmirjar->GetMotionMaster()->MovePoint(1, aSkadiIntroData[i].fX, aSkadiIntroData[i].fY, aSkadiIntroData[i].fZ); + } + } + + m_IntroMobs = false; + } + } + + void UpdateAI(const uint32 uiDiff) override + { + if (m_uiMountTimer) + { + if (m_uiMountTimer <= uiDiff) + { + if (!m_pInstance) + return; + + if (Creature* pGrauf = m_pInstance->GetSingleCreatureFromStorage(NPC_GRAUF)) + { + if (DoCastSpellIfCan(pGrauf, SPELL_RIDE_VEHICLE) == CAST_OK) + { + // Maybe this flag should be set by the vehicle flags - requires research + m_creature->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE | UNIT_FLAG_OOC_NOT_ATTACKABLE); + m_uiMountTimer = 0; + } + } + } + else + m_uiMountTimer -= uiDiff; + } + + if (!m_creature->SelectHostileTarget() || !m_creature->getVictim()) + return; + + if (m_uiCrush < uiDiff) + { + if (DoCastSpellIfCan(m_creature->getVictim(), m_bIsRegularMode ? SPELL_CRUSH : SPELL_CRUSH_H) == CAST_OK) + m_uiCrush = urand(10000, 15000); + } + else + m_uiCrush -= uiDiff; + + if (m_uiWhirlwind < uiDiff) + { + if (DoCastSpellIfCan(m_creature, m_bIsRegularMode ? SPELL_WHIRLWIND : SPELL_WHIRLWIND_H) == CAST_OK) + m_uiWhirlwind = 23000; + } + else + m_uiWhirlwind -= uiDiff; + + if (m_uiPoisonedSpear < uiDiff) + { + if (Unit* pTarget = m_creature->SelectAttackingTarget(ATTACKING_TARGET_RANDOM, 0)) + { + if (DoCastSpellIfCan(pTarget, m_bIsRegularMode ? SPELL_POISONED_SPEAR : SPELL_POISONED_SPEAR_H) == CAST_OK) + m_uiPoisonedSpear = urand(10000, 15000); + } + } + else + m_uiPoisonedSpear -= uiDiff; + + DoMeleeAttackIfReady(); + } +}; + +CreatureAI* GetAI_boss_skadi(Creature* pCreature) +{ + return new boss_skadiAI(pCreature); +} + +/*###### +## npc_grauf +######*/ + +struct npc_graufAI : public ScriptedAI +{ + npc_graufAI(Creature* pCreature) : ScriptedAI(pCreature) + { + m_pInstance = (instance_pinnacle*)pCreature->GetInstanceData(); + SetCombatMovement(false); + Reset(); + } + + instance_pinnacle* m_pInstance; + + uint32 m_uiFlightDelayTimer; + + void Reset() override + { + m_uiFlightDelayTimer = 0; + } + + void AttackStart(Unit* /*pWho*/) override { } + void MoveInLineOfSight(Unit* /*pWho*/) override { } + + void JustReachedHome() override + { + if (!m_pInstance) + return; + + // Handle the auras only when reached home in order to avoid vehicle complications + m_creature->RemoveAllAuras(); + + // Allow Skadi to evade + if (Creature* pSkadi = m_pInstance->GetSingleCreatureFromStorage(NPC_SKADI)) + pSkadi->AI()->EnterEvadeMode(); + + m_creature->SetLevitate(false); + m_creature->SetByteFlag(UNIT_FIELD_BYTES_1, 3, 0); + } + + void SpellHit(Unit* pCaster, const SpellEntry* pSpell) override + { + if (!m_pInstance) + return; + + if (pSpell->Id == SPELL_LAUNCH_HARPOON) + { + if (m_creature->GetHealth() < m_creature->GetMaxHealth() * 0.35f) + { + // Prepare phase 2 here, because the JustDied is called too late + if (Creature* pSkadi = m_pInstance->GetSingleCreatureFromStorage(NPC_SKADI)) + { + DoScriptText(SAY_DRAKE_DEATH, pSkadi); + // Exit vehicle before teleporting + m_creature->RemoveAllAuras(); + pSkadi->CastSpell(pSkadi, SPELL_SKADI_TELEPORT, true); + } + } + else if (urand(0, 1)) + { + if (Creature* pSkadi = m_pInstance->GetSingleCreatureFromStorage(NPC_SKADI)) + DoScriptText(urand(0, 1) ? SAY_DRAKE_HARPOON_1 : SAY_DRAKE_HARPOON_2, pSkadi); + } + + // Deal 35% damage on each harpoon hit + m_creature->DealDamage(m_creature, m_creature->GetMaxHealth() * 0.35f, NULL, DIRECT_DAMAGE, SPELL_SCHOOL_MASK_NONE, NULL, false); + } + // TODO: Temporary workaround - please remove when the boarding wrappers are implemented in core + else if (pSpell->Id == SPELL_RIDE_VEHICLE && pCaster->GetEntry() == NPC_SKADI) + m_uiFlightDelayTimer = 2000; + } + + void MovementInform(uint32 uiType, uint32 uiPointId) override + { + if (uiType != WAYPOINT_MOTION_TYPE || !m_pInstance) + return; + + // Note: On blizz the left and right sides are randomly choosen. + // However because of the lack of waypoint movement scripting we'll use them alternatively + // Another note: the pointId in script = pointId - 1 from DB + switch (uiPointId) + { + case 8: + case 21: + // TODO: choose the left / right patch random when core will support this + DoScriptText(EMOTE_HARPOON_RANGE, m_creature); + + break; + case 10: // left breath + if (DoCastSpellIfCan(m_creature, SPELL_FREEZING_CLOUD_LEFT) == CAST_OK) + { + DoHandleBreathYell(); + DoScriptText(EMOTE_DEEP_BREATH, m_creature); + } + + // Set the achiev as failed once we get to breath area + m_pInstance->SetSpecialAchievementCriteria(TYPE_ACHIEV_LOVE_SKADI, false); + break; + case 13: // left breath end + m_creature->RemoveAurasDueToSpell(SPELL_FREEZING_CLOUD_LEFT); + break; + case 23: // right breath + if (DoCastSpellIfCan(m_creature, SPELL_FREEZING_CLOUD_RIGHT) == CAST_OK) + { + DoHandleBreathYell(); + DoScriptText(EMOTE_DEEP_BREATH, m_creature); + } + + // Set the achiev as failed once we get to breath area + m_pInstance->SetSpecialAchievementCriteria(TYPE_ACHIEV_LOVE_SKADI, false); + break; + case 26: // right breath end + m_creature->RemoveAurasDueToSpell(SPELL_FREEZING_CLOUD_RIGHT); + break; + } + } + + void DoHandleBreathYell() + { + if (!m_pInstance || !roll_chance_i(25)) + return; + + // Yell on drake breath + if (Creature* pSkadi = m_pInstance->GetSingleCreatureFromStorage(NPC_SKADI)) + { + switch (urand(0, 2)) + { + case 0: DoScriptText(SAY_DRAKEBREATH_1, pSkadi); break; + case 1: DoScriptText(SAY_DRAKEBREATH_2, pSkadi); break; + case 2: DoScriptText(SAY_DRAKEBREATH_3, pSkadi); break; + } + } + } + + // TODO: Enable the wrappers below, when they will be properly supported by the core + /* + void PassengerBoarded(Unit* pPassenger, uint8 uiSeat) override + { + if (pPassenger->GetEntry() == NPC_SKADI) + m_uiFlightDelayTimer = 2000; + } + */ + + void UpdateAI(const uint32 uiDiff) override + { + // Start the gauntlet flight + if (m_uiFlightDelayTimer) + { + if (m_uiFlightDelayTimer <= uiDiff) + { + m_creature->SetLevitate(true); + m_creature->SetWalk(false); + m_creature->SetByteValue(UNIT_FIELD_BYTES_1, 3, UNIT_BYTE1_FLAG_ALWAYS_STAND | UNIT_BYTE1_FLAG_UNK_2); + m_creature->GetMotionMaster()->MoveWaypoint(); + m_uiFlightDelayTimer = 0; + } + else + m_uiFlightDelayTimer -= uiDiff; + } + } +}; + +CreatureAI* GetAI_npc_grauf(Creature* pCreature) +{ + return new npc_graufAI(pCreature); +} + +/*###### +## at_skadi +######*/ + +bool AreaTrigger_at_skadi(Player* pPlayer, AreaTriggerEntry const* /*pAt*/) +{ + if (pPlayer->isGameMaster()) + return false; + + if (ScriptedInstance* pInstance = (ScriptedInstance*)pPlayer->GetInstanceData()) + { + if (pInstance->GetData(TYPE_SKADI) == NOT_STARTED) + { + pInstance->SetData(TYPE_SKADI, SPECIAL); + + // Start the gauntlet + if (Creature* pSkadi = pInstance->GetSingleCreatureFromStorage(NPC_SKADI)) + { + if (boss_skadiAI* pBossAI = dynamic_cast(pSkadi->AI())) + pBossAI->DoPrepareForGauntlet(); + } + } + } + + return false; +} + +void AddSC_boss_skadi() +{ + Script* pNewScript; + + pNewScript = new Script; + pNewScript->Name = "boss_skadi"; + pNewScript->GetAI = &GetAI_boss_skadi; + pNewScript->RegisterSelf(); + + pNewScript = new Script; + pNewScript->Name = "npc_grauf"; + pNewScript->GetAI = &GetAI_npc_grauf; + pNewScript->RegisterSelf(); + + pNewScript = new Script; + pNewScript->Name = "at_skadi"; + pNewScript->pAreaTrigger = &AreaTrigger_at_skadi; + pNewScript->RegisterSelf(); +} diff --git a/src/modules/SD2/scripts/northrend/utgarde_keep/utgarde_pinnacle/boss_svala.cpp b/src/modules/SD2/scripts/northrend/utgarde_keep/utgarde_pinnacle/boss_svala.cpp new file mode 100644 index 000000000..dbb7696d6 --- /dev/null +++ b/src/modules/SD2/scripts/northrend/utgarde_keep/utgarde_pinnacle/boss_svala.cpp @@ -0,0 +1,371 @@ +/* Copyright (C) 2006 - 2013 ScriptDev2 + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +/* ScriptData +SDName: Boss_Svala +SD%Complete: 80% +SDComment: The way spells for intro work could use more research. +SDCategory: Utgarde Pinnacle +EndScriptData */ + +#include "precompiled.h" +#include "utgarde_pinnacle.h" + +enum +{ + SAY_INTRO_1 = -1575000, + SAY_INTRO_2_ARTHAS = -1575001, + SAY_INTRO_3 = -1575002, + SAY_INTRO_4_ARTHAS = -1575003, + SAY_INTRO_5 = -1575004, + + SAY_AGGRO = -1575005, + SAY_SLAY_1 = -1575006, + SAY_SLAY_2 = -1575007, + SAY_SLAY_3 = -1575008, + SAY_SACRIFICE_1 = -1575009, + SAY_SACRIFICE_2 = -1575010, + SAY_SACRIFICE_3 = -1575011, + SAY_SACRIFICE_4 = -1575012, + SAY_SACRIFICE_5 = -1575013, + SAY_DEATH = -1575014, + + NPC_SVALA_SORROW = 26668, + NPC_ARTHAS_IMAGE = 29280, + NPC_CHANNELER = 27281, + NPC_SCOURGE_HULK = 26555, // used to check the achiev + + SPELL_ARTHAS_VISUAL = 54134, + + SPELL_TRANSFORMING = 54205, // should also remove aura 54140 (script effect) + SPELL_TRANSFORMING_FLOATING = 54140, // triggers 54142 + SPELL_TRANSFORMING_CHANNEL = 54142, + + SPELL_RITUAL_OF_SWORD = 48276, // teleports the boss + SPELL_RITUAL_STRIKE = 48331, + SPELL_RITUAL_DISARM = 54159, + SPELL_CALL_FLAMES = 48258, // sends event 17841 - this makes npc 27273 cast 48246 + SPELL_SINISTER_STRIKE = 15667, + SPELL_SINISTER_STRIKE_H = 59409, + + SPELL_SUMMON_CHANNELER_1 = 48271, + SPELL_SUMMON_CHANNELER_2 = 48274, + SPELL_SUMMON_CHANNELER_3 = 48275, + + // spells used by channelers + SPELL_PARALIZE = 48278, // should apply effect 48267 on target + SPELL_SHADOWS_IN_THE_DARK = 59407, +}; + +/*###### +## boss_svala +######*/ + +struct boss_svalaAI : public ScriptedAI +{ + boss_svalaAI(Creature* pCreature) : ScriptedAI(pCreature) + { + m_pInstance = (instance_pinnacle*)pCreature->GetInstanceData(); + m_bIsRegularMode = pCreature->GetMap()->IsRegularDifficulty(); + m_bIsIntroDone = false; + Reset(); + } + + instance_pinnacle* m_pInstance; + bool m_bIsRegularMode; + + Creature* pArthas; + + bool m_bIsIntroDone; + uint32 m_uiIntroTimer; + uint32 m_uiIntroCount; + + uint32 m_uiSinisterStrikeTimer; + uint32 m_uiCallFlamesTimer; + uint32 m_uiRitualStrikeTimer; + bool m_bHasDoneRitual; + + ObjectGuid m_ritualTargetGuid; + + void Reset() override + { + pArthas = NULL; + + m_uiIntroTimer = 2500; + m_uiIntroCount = 0; + + m_uiSinisterStrikeTimer = 10000; + m_uiCallFlamesTimer = urand(10000, 20000); + m_uiRitualStrikeTimer = 0; + m_bHasDoneRitual = false; + + if (m_creature->IsAlive() && m_pInstance && m_pInstance->GetData(TYPE_SVALA) > IN_PROGRESS) + { + if (m_creature->GetEntry() != NPC_SVALA_SORROW) + m_creature->UpdateEntry(NPC_SVALA_SORROW); + + m_creature->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE); + + m_bIsIntroDone = true; + } + } + + void JustReachedHome() override + { + DoMoveToPosition(); + + if (m_pInstance) + m_pInstance->SetData(TYPE_SVALA, FAIL); + } + + void MoveInLineOfSight(Unit* pWho) override + { + if (!m_bIsIntroDone) + { + if (m_pInstance && m_pInstance->GetData(TYPE_SVALA) == IN_PROGRESS) + { + m_pInstance->SetData(TYPE_SVALA, SPECIAL); + + float fX, fY, fZ; + m_creature->GetClosePoint(fX, fY, fZ, m_creature->GetObjectBoundingRadius(), 16.0f, 0.0f); + + // we assume m_creature is spawned in proper location + m_creature->SummonCreature(NPC_ARTHAS_IMAGE, fX, fY, fZ, 0.0f, TEMPSUMMON_TIMED_DESPAWN, 60000); + } + + return; + } + + ScriptedAI::MoveInLineOfSight(pWho); + } + + void Aggro(Unit* /*pWho*/) override + { + m_creature->SetLevitate(false); + DoScriptText(SAY_AGGRO, m_creature); + } + + void JustSummoned(Creature* pSummoned) override + { + if (pSummoned->GetEntry() == NPC_ARTHAS_IMAGE) + { + pSummoned->CastSpell(pSummoned, SPELL_ARTHAS_VISUAL, true); + pArthas = pSummoned; + pSummoned->SetFacingToObject(m_creature); + } + else if (pSummoned->GetEntry() == NPC_CHANNELER) + { + if (!m_bIsRegularMode) + pSummoned->CastSpell(pSummoned, SPELL_SHADOWS_IN_THE_DARK, true); + + if (Unit* pTarget = m_creature->GetMap()->GetUnit(m_ritualTargetGuid)) + pSummoned->CastSpell(pTarget, SPELL_PARALIZE, true); + } + } + + void SummonedCreatureDespawn(Creature* pDespawned) override + { + if (pDespawned->GetEntry() == NPC_ARTHAS_IMAGE) + pArthas = NULL; + } + + void SpellHit(Unit* /*pCaster*/, const SpellEntry* pSpell) override + { + if (pSpell->Id == SPELL_TRANSFORMING) + { + if (pArthas) + pArthas->InterruptNonMeleeSpells(true); + + m_creature->RemoveAurasDueToSpell(SPELL_TRANSFORMING_FLOATING); + m_creature->UpdateEntry(NPC_SVALA_SORROW); + } + } + + void KilledUnit(Unit* pVictim) override + { + // set achiev to true if boss kills a hulk + if (pVictim->GetEntry() == NPC_SCOURGE_HULK && m_pInstance) + m_pInstance->SetSpecialAchievementCriteria(TYPE_ACHIEV_INCREDIBLE_HULK, true); + + switch (urand(0, 2)) + { + case 0: DoScriptText(SAY_SLAY_1, m_creature); break; + case 1: DoScriptText(SAY_SLAY_2, m_creature); break; + case 2: DoScriptText(SAY_SLAY_3, m_creature); break; + } + } + + void JustDied(Unit* /*pKiller*/) override + { + DoScriptText(SAY_DEATH, m_creature); + + if (m_pInstance) + m_pInstance->SetData(TYPE_SVALA, DONE); + } + + void DoMoveToPosition() + { + float fX, fZ, fY; + m_creature->GetRespawnCoord(fX, fY, fZ); + + m_creature->SetLevitate(true); + m_creature->GetMotionMaster()->MovePoint(0, fX, fY, fZ + 5.0f); + } + + void UpdateAI(const uint32 uiDiff) override + { + if (!m_creature->SelectHostileTarget() || !m_creature->getVictim()) + { + if (m_bIsIntroDone) + return; + + if (pArthas && pArthas->IsAlive()) + { + if (m_uiIntroTimer < uiDiff) + { + m_uiIntroTimer = 10000; + + switch (m_uiIntroCount) + { + case 0: + DoScriptText(SAY_INTRO_1, m_creature); + break; + case 1: + DoScriptText(SAY_INTRO_2_ARTHAS, pArthas); + break; + case 2: + DoCastSpellIfCan(m_creature, SPELL_TRANSFORMING_FLOATING); + pArthas->CastSpell(m_creature, SPELL_TRANSFORMING_CHANNEL, false); + DoMoveToPosition(); + break; + case 3: + DoCastSpellIfCan(m_creature, SPELL_TRANSFORMING); + DoScriptText(SAY_INTRO_3, m_creature); + break; + case 4: + DoScriptText(SAY_INTRO_4_ARTHAS, pArthas); + break; + case 5: + DoScriptText(SAY_INTRO_5, m_creature); + break; + case 6: + m_creature->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE); + m_bIsIntroDone = true; + break; + } + + ++m_uiIntroCount; + } + else + m_uiIntroTimer -= uiDiff; + } + + return; + } + + if (m_uiSinisterStrikeTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature->getVictim(), m_bIsRegularMode ? SPELL_SINISTER_STRIKE : SPELL_SINISTER_STRIKE_H) == CAST_OK) + m_uiSinisterStrikeTimer = 10000; + } + else + m_uiSinisterStrikeTimer -= uiDiff; + + if (m_uiCallFlamesTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature, SPELL_CALL_FLAMES) == CAST_OK) + m_uiCallFlamesTimer = urand(10000, 20000); + } + else + m_uiCallFlamesTimer -= uiDiff; + + if (m_uiRitualStrikeTimer) + { + if (m_uiRitualStrikeTimer <= uiDiff) + { + DoCastSpellIfCan(m_creature, SPELL_RITUAL_STRIKE, CAST_INTERRUPT_PREVIOUS); + DoCastSpellIfCan(m_creature, SPELL_RITUAL_DISARM, CAST_TRIGGERED); + m_uiRitualStrikeTimer = 0; + } + else + m_uiRitualStrikeTimer -= uiDiff; + } + + // As from patch notes: Svala Sorrowgrave now casts Ritual of the Sword 1 time during the encounter, down from 3. + if (m_creature->GetHealthPercent() < 50.0f && !m_bHasDoneRitual) + { + if (Unit* pTarget = m_creature->SelectAttackingTarget(ATTACKING_TARGET_RANDOM, 0, SPELL_RITUAL_OF_SWORD, SELECT_FLAG_PLAYER)) + { + if (DoCastSpellIfCan(pTarget, SPELL_RITUAL_OF_SWORD) == CAST_OK) + { + m_ritualTargetGuid = pTarget->GetObjectGuid(); + + // summon channelers + DoCastSpellIfCan(m_creature, SPELL_SUMMON_CHANNELER_1, CAST_TRIGGERED); + DoCastSpellIfCan(m_creature, SPELL_SUMMON_CHANNELER_2, CAST_TRIGGERED); + DoCastSpellIfCan(m_creature, SPELL_SUMMON_CHANNELER_3, CAST_TRIGGERED); + + switch (urand(0, 3)) + { + case 0: DoScriptText(SAY_SACRIFICE_1, m_creature); break; + case 1: DoScriptText(SAY_SACRIFICE_2, m_creature); break; + case 2: DoScriptText(SAY_SACRIFICE_3, m_creature); break; + case 3: DoScriptText(SAY_SACRIFICE_4, m_creature); break; + } + + m_uiRitualStrikeTimer = 1000; + m_bHasDoneRitual = true; + } + } + } + + DoMeleeAttackIfReady(); + } +}; + +CreatureAI* GetAI_boss_svala(Creature* pCreature) +{ + return new boss_svalaAI(pCreature); +} + +bool AreaTrigger_at_svala_intro(Player* pPlayer, AreaTriggerEntry const* /*pAt*/) +{ + if (pPlayer->isGameMaster()) + return false; + + if (ScriptedInstance* pInstance = (ScriptedInstance*)pPlayer->GetInstanceData()) + { + if (pInstance->GetData(TYPE_SVALA) == NOT_STARTED) + pInstance->SetData(TYPE_SVALA, IN_PROGRESS); + } + + return false; +} + +void AddSC_boss_svala() +{ + Script* pNewScript; + + pNewScript = new Script; + pNewScript->Name = "boss_svala"; + pNewScript->GetAI = &GetAI_boss_svala; + pNewScript->RegisterSelf(); + + pNewScript = new Script; + pNewScript->Name = "at_svala_intro"; + pNewScript->pAreaTrigger = &AreaTrigger_at_svala_intro; + pNewScript->RegisterSelf(); +} diff --git a/src/modules/SD2/scripts/northrend/utgarde_keep/utgarde_pinnacle/boss_ymiron.cpp b/src/modules/SD2/scripts/northrend/utgarde_keep/utgarde_pinnacle/boss_ymiron.cpp new file mode 100644 index 000000000..503b9388a --- /dev/null +++ b/src/modules/SD2/scripts/northrend/utgarde_keep/utgarde_pinnacle/boss_ymiron.cpp @@ -0,0 +1,435 @@ +/* Copyright (C) 2006 - 2013 ScriptDev2 + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +/* ScriptData +SDName: Boss_Ymiron +SD%Complete: 90% +SDComment: Timers +SDCategory: Utgarde Pinnacle +EndScriptData */ + +#include "precompiled.h" +#include "utgarde_pinnacle.h" + +enum +{ + SAY_AGGRO = -1575031, + SAY_SUMMON_BJORN = -1575032, + SAY_SUMMON_HALDOR = -1575033, + SAY_SUMMON_RANULF = -1575034, + SAY_SUMMON_TORGYN = -1575035, + SAY_SLAY_1 = -1575036, + SAY_SLAY_2 = -1575037, + SAY_SLAY_3 = -1575038, + SAY_SLAY_4 = -1575039, + SAY_DEATH = -1575040, + + SPELL_BANE = 48294, // sends script event 20651 when target is hit - set achiev to false + SPELL_BANE_H = 59301, + SPELL_DARK_SLASH = 48292, + SPELL_FETID_ROT = 48291, + SPELL_FETID_ROT_H = 59300, + SPELL_SCREAMS_OF_THE_DEAD = 51750, // knockback players to summon boat + // SPELL_CHOOSE_SPIRIT = 48306, // boss chooses spirit + + // blessings + SPELL_SPIRIT_BURST = 48529, // by Ranulf + SPELL_SPIRIT_BURST_H = 59305, + SPELL_SPIRIT_STRIKE = 48423, // by Haldor + SPELL_SPIRIT_STRIKE_H = 59304, + SPELL_SUMMON_SPIRIT_FOUNT = 48386, // by Bjorn + SPELL_SPIRIT_FOUNT_BEAM = 48385, // channeled beam on the spirit fount - triggers 48380 : 59320 on aura expire + SPELL_AVENGING_SPIRITS = 48590, // by Torgyn + + // visuals + SPELL_CHANNEL_YMIRON_SPIRIT = 48307, + SPELL_CHANNEL_SPIRIT_YMIRON = 48316, + SPELL_EMERGE_STATE = 56864, + SPELL_SPIRIT_DIES = 48596, // cast by a boat spirit + + // by summoned creatures + // SPELL_SPIRIT_VISUAL = 48593, // avenging spirit summon visual - handled in eventAI + // SPELL_WITHER_TRIGG = 48584, // aura for avenging spirits - triggers 48585 on melee - handled in eventAI + + // spirit transforms + SPELL_BJORN_TRANSFORM = 48308, + SPELL_HALDOR_TRANSFORM = 48311, + SPELL_RANULF_TRANSFORM = 48312, + SPELL_TORGYN_TRANSFORM = 48313, + + NPC_SPIRIT_FOUNT = 27339, + // NPC_AVENGING_SPIRIT = 27386, + // NPC_SPIRIT_SUMMONER = 27392, // summoned around the boss - triggers 48592 + + MAX_BOATS = 4, + + PHASE_NO_BOAT = 0, + PHASE_BJORN = 1, + PHASE_HALDOR = 2, + PHASE_RANULF = 3, + PHASE_TORGYN = 4 +}; + +struct BoatSpirits +{ + uint32 uiSpiritSpell, uiSpiritTarget; + int32 iYellId; + uint8 uiBoatPhase; +}; + +static const BoatSpirits aYmironBoatsSpirits[MAX_BOATS] = +{ + {SPELL_BJORN_TRANSFORM, NPC_BJORN, SAY_SUMMON_BJORN, PHASE_BJORN}, + {SPELL_HALDOR_TRANSFORM, NPC_HALDOR, SAY_SUMMON_HALDOR, PHASE_HALDOR}, + {SPELL_RANULF_TRANSFORM, NPC_RANULF, SAY_SUMMON_RANULF, PHASE_RANULF}, + {SPELL_TORGYN_TRANSFORM, NPC_TORGYN, SAY_SUMMON_TORGYN, PHASE_TORGYN} +}; + +/*###### +## boss_ymiron +######*/ + +struct boss_ymironAI : public ScriptedAI +{ + boss_ymironAI(Creature* pCreature) : ScriptedAI(pCreature) + { + m_pInstance = (ScriptedInstance*)pCreature->GetInstanceData(); + m_bIsRegularMode = pCreature->GetMap()->IsRegularDifficulty(); + + for (uint8 i = 0; i < MAX_BOATS; ++i) + m_vuiBoatPhases.push_back(i); + + Reset(); + } + + ScriptedInstance* m_pInstance; + bool m_bIsRegularMode; + + uint32 m_uiFetidRotTimer; + uint32 m_uiBaneTimer; + uint32 m_uiDarkSlashTimer; + uint32 m_uiSpiritTransformTimer; + uint32 m_uiCombatResumeTimer; + uint8 m_uiPhase; + uint8 m_uiBoats; + float m_fHealthCheck; + + uint32 m_uiSpiritBurstTimer; + uint32 m_uiSpiritStrikeTimer; + uint32 m_uiSpiritFountTimer; + uint32 m_uiAvengingSpiritsTimer; + + bool m_bIsChannelingSpirit; + + ObjectGuid m_uiCurrentSpiritGuid; + + std::vector m_vuiBoatPhases; + + void Reset() override + { + m_uiFetidRotTimer = urand(8000, 13000); + m_uiBaneTimer = urand(18000, 23000); + m_uiDarkSlashTimer = urand(28000, 33000); + m_uiSpiritTransformTimer = 0; + m_uiCombatResumeTimer = 0; + m_uiPhase = PHASE_NO_BOAT; + m_uiBoats = 0; + m_fHealthCheck = m_bIsRegularMode ? 33.3f : 20.0f; + + m_uiSpiritBurstTimer = 10000; + m_uiSpiritStrikeTimer = 10000; + m_uiSpiritFountTimer = 10000; + m_uiAvengingSpiritsTimer = 10000; + + m_bIsChannelingSpirit = false; + + m_uiCurrentSpiritGuid.Clear(); + + // Randomize spirit order + std::random_shuffle(m_vuiBoatPhases.begin(), m_vuiBoatPhases.end()); + } + + void Aggro(Unit* /*pWho*/) override + { + DoScriptText(SAY_AGGRO, m_creature); + + if (m_pInstance) + m_pInstance->SetData(TYPE_YMIRON, IN_PROGRESS); + } + + void KilledUnit(Unit* /*pVictim*/) override + { + switch (urand(0, 3)) + { + case 0: DoScriptText(SAY_SLAY_1, m_creature); break; + case 1: DoScriptText(SAY_SLAY_2, m_creature); break; + case 2: DoScriptText(SAY_SLAY_3, m_creature); break; + case 3: DoScriptText(SAY_SLAY_4, m_creature); break; + } + } + + void JustDied(Unit* /*pKiller*/) override + { + DoScriptText(SAY_DEATH, m_creature); + + // Burn the last spirit + if (Creature* pSpirit = m_creature->GetMap()->GetCreature(m_uiCurrentSpiritGuid)) + { + pSpirit->InterruptNonMeleeSpells(false); + pSpirit->CastSpell(pSpirit, SPELL_SPIRIT_DIES, false); + } + + if (m_pInstance) + m_pInstance->SetData(TYPE_YMIRON, DONE); + } + + void JustReachedHome() override + { + DoResetSpirits(); + + if (m_pInstance) + m_pInstance->SetData(TYPE_YMIRON, FAIL); + } + + // Wrapper which handles the spirits reset + void DoResetSpirits() + { + if (!m_pInstance) + return; + + for (uint8 i = 0; i < MAX_BOATS; ++i) + { + if (Creature* pSpirit = m_pInstance->GetSingleCreatureFromStorage(aYmironBoatsSpirits[i].uiSpiritTarget)) + pSpirit->AI()->EnterEvadeMode(); + } + } + + void DoChannelSpiritYmiron() + { + if (Creature* pSpirit = m_creature->GetMap()->GetCreature(m_uiCurrentSpiritGuid)) + pSpirit->CastSpell(m_creature, SPELL_CHANNEL_SPIRIT_YMIRON, false); + + // Channeling is finished - resume combat + if (m_creature->getVictim()) + { + m_creature->GetMotionMaster()->Clear(); + m_creature->GetMotionMaster()->MoveChase(m_creature->getVictim()); + } + + SetCombatMovement(true); + m_bIsChannelingSpirit = false; + + m_uiPhase = aYmironBoatsSpirits[m_vuiBoatPhases[m_uiBoats]].uiBoatPhase; + ++m_uiBoats; + } + + void JustSummoned(Creature* pSummoned) override + { + if (pSummoned->GetEntry() == NPC_SPIRIT_FOUNT) + DoCastSpellIfCan(pSummoned, SPELL_SPIRIT_FOUNT_BEAM, CAST_INTERRUPT_PREVIOUS); + } + + void MovementInform(uint32 uiMotionType, uint32 uiPointId) override + { + if (uiMotionType != POINT_MOTION_TYPE || !uiPointId) + return; + + if (Creature* pSpirit = m_creature->GetMap()->GetCreature(m_uiCurrentSpiritGuid)) + { + DoCastSpellIfCan(pSpirit, SPELL_CHANNEL_YMIRON_SPIRIT); + DoScriptText(aYmironBoatsSpirits[m_vuiBoatPhases[m_uiBoats]].iYellId, m_creature); + m_uiSpiritTransformTimer = 3000; + m_uiCombatResumeTimer = 6000; + } + } + + void UpdateAI(const uint32 uiDiff) override + { + if (!m_creature->SelectHostileTarget() || !m_creature->getVictim()) + return; + + if (m_uiSpiritTransformTimer) + { + if (m_uiSpiritTransformTimer <= uiDiff) + { + if (Creature* pSpirit = m_creature->GetMap()->GetCreature(m_uiCurrentSpiritGuid)) + { + pSpirit->CastSpell(pSpirit, aYmironBoatsSpirits[m_vuiBoatPhases[m_uiBoats]].uiSpiritSpell, true); + pSpirit->CastSpell(pSpirit, SPELL_EMERGE_STATE, true); + } + m_uiSpiritTransformTimer = 0; + } + else + m_uiSpiritTransformTimer -= uiDiff; + } + + if (m_uiCombatResumeTimer) + { + // This should be done on aura 48307 remove, but because of lack of core support, we'll handle it on normal timer + if (m_uiCombatResumeTimer <= uiDiff) + { + DoChannelSpiritYmiron(); + m_uiCombatResumeTimer = 0; + } + else + m_uiCombatResumeTimer -= uiDiff; + } + + // Don't attack while channeling on the boats + if (m_bIsChannelingSpirit) + return; + + if (m_uiBaneTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature, m_bIsRegularMode ? SPELL_BANE : SPELL_BANE_H) == CAST_OK) + m_uiBaneTimer = urand(20000, 25000); + } + else + m_uiBaneTimer -= uiDiff; + + if (m_uiFetidRotTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature->getVictim(), m_bIsRegularMode ? SPELL_FETID_ROT : SPELL_FETID_ROT_H) == CAST_OK) + m_uiFetidRotTimer = urand(10000, 15000); + } + else + m_uiFetidRotTimer -= uiDiff; + + if (m_uiDarkSlashTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature->getVictim(), SPELL_DARK_SLASH) == CAST_OK) + m_uiDarkSlashTimer = urand(30000, 35000); + } + else + m_uiDarkSlashTimer -= uiDiff; + + // Check the spirit phases (also don't allow him to change phase if below 1%) + if (m_creature->GetHealthPercent() < 100 - m_fHealthCheck && m_creature->GetHealthPercent() > 1.0f) + { + // change phase + DoCastSpellIfCan(m_creature, SPELL_SCREAMS_OF_THE_DEAD, CAST_INTERRUPT_PREVIOUS); + + // make the current spirit die (burn) + if (Creature* pSpirit = m_creature->GetMap()->GetCreature(m_uiCurrentSpiritGuid)) + { + pSpirit->InterruptNonMeleeSpells(false); + pSpirit->CastSpell(pSpirit, SPELL_SPIRIT_DIES, false); + } + + // Get a close point to the spirits and move near them + if (m_pInstance) + { + if (Creature* pSpirit = m_pInstance->GetSingleCreatureFromStorage(aYmironBoatsSpirits[m_vuiBoatPhases[m_uiBoats]].uiSpiritTarget)) + { + float fX, fY, fZ; + m_uiCurrentSpiritGuid = pSpirit->GetObjectGuid(); + pSpirit->GetContactPoint(m_creature, fX, fY, fZ, INTERACTION_DISTANCE); + m_creature->GetMotionMaster()->MovePoint(1, fX, fY, fZ); + } + } + + SetCombatMovement(false); + m_bIsChannelingSpirit = true; + m_fHealthCheck += m_bIsRegularMode ? 33.3f : 20.0f; + } + + switch (m_uiPhase) + { + case PHASE_BJORN: + + if (m_uiSpiritFountTimer) + { + if (m_uiSpiritFountTimer <= uiDiff) + { + if (DoCastSpellIfCan(m_creature, SPELL_SUMMON_SPIRIT_FOUNT) == CAST_OK) + m_uiSpiritFountTimer = 0; + } + else + m_uiSpiritFountTimer -= uiDiff; + } + + break; + case PHASE_HALDOR: + + if (m_uiSpiritStrikeTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature->getVictim(), m_bIsRegularMode ? SPELL_SPIRIT_STRIKE : SPELL_SPIRIT_STRIKE_H) == CAST_OK) + m_uiSpiritStrikeTimer = 5000; + } + else + m_uiSpiritStrikeTimer -= uiDiff; + + break; + case PHASE_RANULF: + + if (m_uiSpiritBurstTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature, m_bIsRegularMode ? SPELL_SPIRIT_BURST : SPELL_SPIRIT_BURST_H) == CAST_OK) + m_uiSpiritBurstTimer = 10000; + } + else + m_uiSpiritBurstTimer -= uiDiff; + + break; + case PHASE_TORGYN: + + if (m_uiAvengingSpiritsTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature, SPELL_AVENGING_SPIRITS) == CAST_OK) + m_uiAvengingSpiritsTimer = 15000; + } + else + m_uiAvengingSpiritsTimer -= uiDiff; + + break; + } + + DoMeleeAttackIfReady(); + } +}; + +CreatureAI* GetAI_boss_ymiron(Creature* pCreature) +{ + return new boss_ymironAI(pCreature); +} + +bool ProcessEventId_event_achiev_kings_bane(uint32 /*uiEventId*/, Object* pSource, Object* /*pTarget*/, bool /*bIsStart*/) +{ + if (instance_pinnacle* pInstance = (instance_pinnacle*)((Creature*)pSource)->GetInstanceData()) + { + if (pInstance->GetData(TYPE_YMIRON) != IN_PROGRESS) + return false; + + pInstance->SetData(TYPE_YMIRON, SPECIAL); + return true; + } + return false; +} + +void AddSC_boss_ymiron() +{ + Script* pNewScript; + + pNewScript = new Script; + pNewScript->Name = "boss_ymiron"; + pNewScript->GetAI = &GetAI_boss_ymiron; + pNewScript->RegisterSelf(); + + pNewScript = new Script; + pNewScript->Name = "event_achiev_kings_bane"; + pNewScript->pProcessEventId = &ProcessEventId_event_achiev_kings_bane; + pNewScript->RegisterSelf(); +} diff --git a/src/modules/SD2/scripts/northrend/utgarde_keep/utgarde_pinnacle/instance_utgarde_pinnacle.cpp b/src/modules/SD2/scripts/northrend/utgarde_keep/utgarde_pinnacle/instance_utgarde_pinnacle.cpp new file mode 100644 index 000000000..ec7d07d5d --- /dev/null +++ b/src/modules/SD2/scripts/northrend/utgarde_keep/utgarde_pinnacle/instance_utgarde_pinnacle.cpp @@ -0,0 +1,346 @@ +/* Copyright (C) 2006 - 2013 ScriptDev2 + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along 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_pinnacle +SD%Complete: 75% +SDComment: +SDCategory: Utgarde Pinnacle +EndScriptData */ + +#include "precompiled.h" +#include "utgarde_pinnacle.h" + +instance_pinnacle::instance_pinnacle(Map* pMap) : ScriptedInstance(pMap), + m_uiGortokOrbTimer(0), + m_uiGortokOrbPhase(0) +{ + Initialize(); +} + +void instance_pinnacle::Initialize() +{ + memset(&m_auiEncounter, 0, sizeof(m_auiEncounter)); + + for (uint8 i = 0; i < MAX_SPECIAL_ACHIEV_CRITS; ++i) + m_abAchievCriteria[i] = false; +} + +void instance_pinnacle::OnCreatureCreate(Creature* pCreature) +{ + switch (pCreature->GetEntry()) + { + case NPC_FURBOLG: + case NPC_WORGEN: + case NPC_JORMUNGAR: + case NPC_RHINO: + case NPC_BJORN: + case NPC_HALDOR: + case NPC_RANULF: + case NPC_TORGYN: + case NPC_SKADI: + case NPC_GRAUF: + m_mNpcEntryGuidStore[pCreature->GetEntry()] = pCreature->GetObjectGuid(); + break; + case NPC_WORLD_TRIGGER: + if (pCreature->GetPositionX() < 250.0f) + m_gortokEventTriggerGuid = pCreature->GetObjectGuid(); + else if (pCreature->GetPositionX() > 400.0f && pCreature->GetPositionX() < 500.0f) + m_skadiMobsTriggerGuid = pCreature->GetObjectGuid(); + break; + case NPC_YMIRJAR_HARPOONER: + case NPC_YMIRJAR_WARRIOR: + case NPC_YMIRJAR_WITCH_DOCTOR: + m_lskadiGauntletMobsList.push_back(pCreature->GetObjectGuid()); + break; + } +} + +void instance_pinnacle::OnObjectCreate(GameObject* pGo) +{ + switch (pGo->GetEntry()) + { + case GO_DOOR_SKADI: + if (m_auiEncounter[TYPE_SKADI] == DONE) + pGo->SetGoState(GO_STATE_ACTIVE); + break; + case GO_DOOR_YMIRON: + if (m_auiEncounter[TYPE_YMIRON] == DONE) + pGo->SetGoState(GO_STATE_ACTIVE); + break; + default: + return; + } + m_mGoEntryGuidStore[pGo->GetEntry()] = pGo->GetObjectGuid(); +} + +void instance_pinnacle::SetData(uint32 uiType, uint32 uiData) +{ + switch (uiType) + { + case TYPE_SVALA: + if (uiData == IN_PROGRESS || uiData == FAIL) + SetSpecialAchievementCriteria(TYPE_ACHIEV_INCREDIBLE_HULK, false); + m_auiEncounter[uiType] = uiData; + break; + case TYPE_GORTOK: + if (uiData == IN_PROGRESS) + { + if (Creature* pOrb = instance->GetCreature(m_gortokEventTriggerGuid)) + { + pOrb->SetLevitate(true); + pOrb->CastSpell(pOrb, SPELL_ORB_VISUAL, true); + pOrb->GetMotionMaster()->MovePoint(0, aOrbPositions[0][0], aOrbPositions[0][1], aOrbPositions[0][2]); + + m_uiGortokOrbTimer = 2000; + } + } + else if (uiData == FAIL) + { + if (Creature* pOrb = instance->GetCreature(m_gortokEventTriggerGuid)) + { + if (!pOrb->IsAlive()) + pOrb->Respawn(); + else + pOrb->RemoveAllAuras(); + + // For some reasone the Orb doesn't evade automatically + pOrb->GetMotionMaster()->MoveTargetedHome(); + } + + for (uint8 i = 0; i < MAX_ENCOUNTER; ++i) + { + // Reset each miniboss + if (Creature* pTemp = GetSingleCreatureFromStorage(aGortokMiniBosses[i])) + { + if (!pTemp->IsAlive()) + pTemp->Respawn(); + + pTemp->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE); + } + } + + m_uiGortokOrbPhase = 0; + } + m_auiEncounter[uiType] = uiData; + break; + case TYPE_SKADI: + // Don't process the event twice + if (m_auiEncounter[uiType] == uiData) + return; + switch (uiData) + { + case DONE: + DoUseDoorOrButton(GO_DOOR_SKADI); + break; + case SPECIAL: + // Prepare achievements + SetSpecialAchievementCriteria(TYPE_ACHIEV_LOVE_SKADI, true); + DoStartTimedAchievement(ACHIEVEMENT_CRITERIA_TYPE_KILL_CREATURE, ACHIEV_START_SKADI_ID); + + m_auiEncounter[uiType] = uiData; + return; + case FAIL: + // Handle Grauf evade - if event is in phase 1 + if (Creature* pGrauf = GetSingleCreatureFromStorage(NPC_GRAUF)) + pGrauf->AI()->EnterEvadeMode(); + + // no break; + case NOT_STARTED: + // Despawn all summons + for (GuidList::const_iterator itr = m_lskadiGauntletMobsList.begin(); itr != m_lskadiGauntletMobsList.end(); ++itr) + { + if (Creature* pYmirjar = instance->GetCreature(*itr)) + pYmirjar->ForcedDespawn(); + } + + // Reset position + if (Creature* pGrauf = GetSingleCreatureFromStorage(NPC_GRAUF)) + pGrauf->GetMotionMaster()->MoveTargetedHome(); + + // no break; + case IN_PROGRESS: + + // Remove the summon aura on phase 2 or fail + if (Creature* pTrigger = instance->GetCreature(m_skadiMobsTriggerGuid)) + pTrigger->RemoveAllAuras(); + break; + } + m_auiEncounter[uiType] = uiData; + break; + case TYPE_YMIRON: + if (uiData == DONE) + DoUseDoorOrButton(GO_DOOR_YMIRON); + else if (uiData == IN_PROGRESS) + SetSpecialAchievementCriteria(TYPE_ACHIEV_KINGS_BANE, true); + else if (uiData == SPECIAL) + SetSpecialAchievementCriteria(TYPE_ACHIEV_KINGS_BANE, false); + m_auiEncounter[uiType] = uiData; + break; + default: + script_error_log("Instance Pinnacle: SetData = %u for type %u does not exist/not implemented.", uiType, uiData); + return; + } + + // Saving also SPECIAL for this instance + if (uiData == DONE || uiData == SPECIAL) + { + OUT_SAVE_INST_DATA; + + std::ostringstream saveStream; + saveStream << m_auiEncounter[0] << " " << m_auiEncounter[1] << " " << m_auiEncounter[2] << " " << m_auiEncounter[3]; + + m_strInstData = saveStream.str(); + + SaveToDB(); + OUT_SAVE_INST_DATA_COMPLETE; + } +} + +uint32 instance_pinnacle::GetData(uint32 uiType) const +{ + if (uiType < MAX_ENCOUNTER) + return m_auiEncounter[uiType]; + + return 0; +} + +void instance_pinnacle::Load(const char* chrIn) +{ + if (!chrIn) + { + 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 < MAX_ENCOUNTER; ++i) + { + if (m_auiEncounter[i] == IN_PROGRESS) + m_auiEncounter[i] = NOT_STARTED; + } + + OUT_LOAD_INST_DATA_COMPLETE; +} + +void instance_pinnacle::SetSpecialAchievementCriteria(uint32 uiType, bool bIsMet) +{ + if (uiType < MAX_SPECIAL_ACHIEV_CRITS) + m_abAchievCriteria[uiType] = bIsMet; +} + +bool instance_pinnacle::CheckAchievementCriteriaMeet(uint32 uiCriteriaId, Player const* /*pSource*/, Unit const* /*pTarget*/, uint32 /*uiMiscValue1 = 0*/) const +{ + switch (uiCriteriaId) + { + case ACHIEV_CRIT_INCREDIBLE_HULK: + return m_abAchievCriteria[TYPE_ACHIEV_INCREDIBLE_HULK]; + case ACHIEV_CRIT_GIRL_LOVES_SKADI: + return m_abAchievCriteria[TYPE_ACHIEV_LOVE_SKADI]; + case ACHIEV_CRIT_KINGS_BANE: + return m_abAchievCriteria[TYPE_ACHIEV_KINGS_BANE]; + + default: + return false; + } +} + +void instance_pinnacle::OnCreatureEvade(Creature* pCreature) +{ + switch (pCreature->GetEntry()) + { + case NPC_FURBOLG: + case NPC_WORGEN: + case NPC_JORMUNGAR: + case NPC_RHINO: + SetData(TYPE_GORTOK, FAIL); + break; + case NPC_YMIRJAR_WARRIOR: + case NPC_YMIRJAR_WITCH_DOCTOR: + case NPC_YMIRJAR_HARPOONER: + // Handle Skadi gauntlet reset. Used instead of using spell 49308 + SetData(TYPE_SKADI, FAIL); + break; + } +} + +void instance_pinnacle::OnCreatureDeath(Creature* pCreature) +{ + switch (pCreature->GetEntry()) + { + case NPC_FURBOLG: + case NPC_WORGEN: + case NPC_JORMUNGAR: + case NPC_RHINO: + m_uiGortokOrbTimer = 3000; + break; + } +} + +void instance_pinnacle::Update(uint32 const uiDiff) +{ + if (m_uiGortokOrbTimer) + { + if (m_uiGortokOrbTimer <= uiDiff) + { + if (!m_uiGortokOrbPhase) + { + if (Creature* pOrb = instance->GetCreature(m_gortokEventTriggerGuid)) + pOrb->GetMotionMaster()->MovePoint(0, aOrbPositions[1][0], aOrbPositions[1][1], aOrbPositions[1][2]); + + m_uiGortokOrbTimer = 18000; + } + // Awaken Gortok if this is the last phase + else + { + uint8 uiMaxOrbPhase = instance->IsRegularDifficulty() ? 3 : 5; + uint32 uiSpellId = m_uiGortokOrbPhase == uiMaxOrbPhase ? SPELL_AWAKEN_GORTOK : SPELL_AWAKEN_SUBBOSS; + + if (Creature* pOrb = instance->GetCreature(m_gortokEventTriggerGuid)) + { + pOrb->CastSpell(pOrb, uiSpellId, false); + + if (m_uiGortokOrbPhase == uiMaxOrbPhase) + pOrb->ForcedDespawn(10000); + } + + m_uiGortokOrbTimer = 0; + } + ++m_uiGortokOrbPhase; + } + else + m_uiGortokOrbTimer -= uiDiff; + } +} + +InstanceData* GetInstanceData_instance_pinnacle(Map* pMap) +{ + return new instance_pinnacle(pMap); +} + +void AddSC_instance_pinnacle() +{ + Script* pNewScript; + + pNewScript = new Script; + pNewScript->Name = "instance_pinnacle"; + pNewScript->GetInstanceData = &GetInstanceData_instance_pinnacle; + pNewScript->RegisterSelf(); +} diff --git a/src/modules/SD2/scripts/northrend/utgarde_keep/utgarde_pinnacle/utgarde_pinnacle.h b/src/modules/SD2/scripts/northrend/utgarde_keep/utgarde_pinnacle/utgarde_pinnacle.h new file mode 100644 index 000000000..4f5681251 --- /dev/null +++ b/src/modules/SD2/scripts/northrend/utgarde_keep/utgarde_pinnacle/utgarde_pinnacle.h @@ -0,0 +1,115 @@ +/* Copyright (C) 2006 - 2013 ScriptDev2 + * This program is free software licensed under GPL version 2 + * Please see the included DOCS/LICENSE.TXT for more information */ + +#ifndef DEF_UTG_PINNACLE_H +#define DEF_UTG_PINNACLE_H + +enum +{ + MAX_ENCOUNTER = 4, + MAX_SPECIAL_ACHIEV_CRITS = 3, + + TYPE_SVALA = 0, + TYPE_GORTOK = 1, + TYPE_SKADI = 2, + TYPE_YMIRON = 3, + + TYPE_ACHIEV_INCREDIBLE_HULK = 0, + TYPE_ACHIEV_LOVE_SKADI = 1, + TYPE_ACHIEV_KINGS_BANE = 2, + + GO_STASIS_GENERATOR = 188593, + GO_DOOR_SKADI = 192173, + GO_DOOR_YMIRON = 192174, + + NPC_WORLD_TRIGGER = 22515, + + NPC_GRAUF = 26893, + NPC_SKADI = 26693, + NPC_YMIRJAR_WARRIOR = 26690, + NPC_YMIRJAR_WITCH_DOCTOR = 26691, + NPC_YMIRJAR_HARPOONER = 26692, + // NPC_FLAME_BREATH_TRIGGER = 28351, // triggers the freezing cloud spell in script + // NPC_WORLD_TRIGGER_LARGE = 23472, // only one spawn in this instance - casts 49308 during the gauntlet event + + NPC_FURBOLG = 26684, + NPC_WORGEN = 26683, + NPC_JORMUNGAR = 26685, + NPC_RHINO = 26686, + + // Ymiron spirits + NPC_BJORN = 27303, // front right + NPC_HALDOR = 27307, // front left + NPC_RANULF = 27308, // back left + NPC_TORGYN = 27309, // back right + + ACHIEV_CRIT_INCREDIBLE_HULK = 7322, // Svala, achiev - 2043 + ACHIEV_CRIT_GIRL_LOVES_SKADI = 7595, // Skadi, achiev - 2156 + ACHIEV_CRIT_KINGS_BANE = 7598, // Ymiron, achiev - 2157 + + ACHIEV_START_SKADI_ID = 17726, // Starts Skadi timed achiev - 1873 + + // Gortok event spells + SPELL_ORB_VISUAL = 48044, + SPELL_AWAKEN_SUBBOSS = 47669, + SPELL_AWAKEN_GORTOK = 47670, + + // Skadi event spells + // The reset check spell is cast by npc 23472 every 7 seconds during the event + // If the spell doesn't hit any player then the event resets + // SPELL_GAUNTLET_RESET_CHECK = 49308, // for the moment we don't use this because of the lack of core support +}; + +static const float aOrbPositions[2][3] = +{ + {238.6077f, -460.7103f, 112.5671f}, // Orb lift up + {279.26f, -452.1f, 110.0f}, // Orb center stop +}; + +static const uint32 aGortokMiniBosses[MAX_ENCOUNTER] = {NPC_WORGEN, NPC_FURBOLG, NPC_JORMUNGAR, NPC_RHINO}; + +class instance_pinnacle : public ScriptedInstance +{ + public: + instance_pinnacle(Map* pMap); + + void Initialize() override; + + void OnCreatureCreate(Creature* pCreature) override; + void OnObjectCreate(GameObject* pGo) override; + + void OnCreatureEvade(Creature* pCreature); + void OnCreatureDeath(Creature* pCreature) override; + + void SetData(uint32 uiType, uint32 uiData) override; + uint32 GetData(uint32 uiType) const override; + + void SetSpecialAchievementCriteria(uint32 uiType, bool bIsMet); + bool CheckAchievementCriteriaMeet(uint32 uiCriteriaId, Player const* pSource, Unit const* pTarget, uint32 uiMiscValue1 /* = 0*/) const override; + + void SetGortokEventStarter(ObjectGuid playerGuid) { m_gortokEventStarterGuid = playerGuid; } + ObjectGuid GetGortokEventStarter() { return m_gortokEventStarterGuid; } + ObjectGuid GetSkadiMobsTrigger() { return m_skadiMobsTriggerGuid; } + + const char* Save() const override { return m_strInstData.c_str(); } + void Load(const char* chrIn) override; + + void Update(uint32 uiDiff) override; + + private: + uint32 m_auiEncounter[MAX_ENCOUNTER]; + bool m_abAchievCriteria[MAX_SPECIAL_ACHIEV_CRITS]; + std::string m_strInstData; + + uint32 m_uiGortokOrbTimer; + uint8 m_uiGortokOrbPhase; + + ObjectGuid m_gortokEventTriggerGuid; + ObjectGuid m_gortokEventStarterGuid; + ObjectGuid m_skadiMobsTriggerGuid; + + GuidList m_lskadiGauntletMobsList; +}; + +#endif diff --git a/src/modules/SD2/scripts/northrend/vault_of_archavon/boss_archavon.cpp b/src/modules/SD2/scripts/northrend/vault_of_archavon/boss_archavon.cpp new file mode 100644 index 000000000..5a3b45660 --- /dev/null +++ b/src/modules/SD2/scripts/northrend/vault_of_archavon/boss_archavon.cpp @@ -0,0 +1,28 @@ +/* Copyright (C) 2006 - 2013 ScriptDev2 + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +/* ScriptData +SDName: boss_archavon +SD%Complete: 0 +SDComment: Placeholder +SDCategory: Vault of Archavon +EndScriptData */ + +#include "precompiled.h" + +void AddSC_boss_archavon() +{ +} diff --git a/src/modules/SD2/scripts/northrend/vault_of_archavon/boss_emalon.cpp b/src/modules/SD2/scripts/northrend/vault_of_archavon/boss_emalon.cpp new file mode 100644 index 000000000..e31ec112f --- /dev/null +++ b/src/modules/SD2/scripts/northrend/vault_of_archavon/boss_emalon.cpp @@ -0,0 +1,28 @@ +/* Copyright (C) 2006 - 2013 ScriptDev2 + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +/* ScriptData +SDName: boss_emalon +SD%Complete: 0 +SDComment: Placeholder +SDCategory: Vault of Archavon +EndScriptData */ + +#include "precompiled.h" + +void AddSC_boss_emalon() +{ +} diff --git a/src/modules/SD2/scripts/northrend/vault_of_archavon/boss_koralon.cpp b/src/modules/SD2/scripts/northrend/vault_of_archavon/boss_koralon.cpp new file mode 100644 index 000000000..addd69153 --- /dev/null +++ b/src/modules/SD2/scripts/northrend/vault_of_archavon/boss_koralon.cpp @@ -0,0 +1,28 @@ +/* Copyright (C) 2006 - 2013 ScriptDev2 + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +/* ScriptData +SDName: boss_koralon +SD%Complete: 0 +SDComment: Placeholder +SDCategory: Vault of Archavon +EndScriptData */ + +#include "precompiled.h" + +void AddSC_boss_koralon() +{ +} diff --git a/src/modules/SD2/scripts/northrend/vault_of_archavon/boss_toravon.cpp b/src/modules/SD2/scripts/northrend/vault_of_archavon/boss_toravon.cpp new file mode 100644 index 000000000..64afd567a --- /dev/null +++ b/src/modules/SD2/scripts/northrend/vault_of_archavon/boss_toravon.cpp @@ -0,0 +1,28 @@ +/* Copyright (C) 2006 - 2013 ScriptDev2 + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +/* ScriptData +SDName: boss_toravon +SD%Complete: 0 +SDComment: Placeholder +SDCategory: Vault of Archavon +EndScriptData */ + +#include "precompiled.h" + +void AddSC_boss_toravon() +{ +} diff --git a/src/modules/SD2/scripts/northrend/vault_of_archavon/instance_vault_of_archavon.cpp b/src/modules/SD2/scripts/northrend/vault_of_archavon/instance_vault_of_archavon.cpp new file mode 100644 index 000000000..54442c4fd --- /dev/null +++ b/src/modules/SD2/scripts/northrend/vault_of_archavon/instance_vault_of_archavon.cpp @@ -0,0 +1,28 @@ +/* Copyright (C) 2006 - 2013 ScriptDev2 + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along 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_vault_of_archavon +SD%Complete: 0 +SDComment: Placeholder +SDCategory: Vault of Archavon +EndScriptData */ + +#include "precompiled.h" + +void AddSC_instance_vault_of_archavon() +{ +} diff --git a/src/modules/SD2/scripts/northrend/vault_of_archavon/vault_of_archavon.h b/src/modules/SD2/scripts/northrend/vault_of_archavon/vault_of_archavon.h new file mode 100644 index 000000000..6b5f9ef47 --- /dev/null +++ b/src/modules/SD2/scripts/northrend/vault_of_archavon/vault_of_archavon.h @@ -0,0 +1,3 @@ +/* Copyright (C) 2006 - 2013 ScriptDev2 + * This program is free software licensed under GPL version 2 + * Please see the included DOCS/LICENSE.TXT for more information */ diff --git a/src/modules/SD2/scripts/northrend/violet_hold/boss_erekem.cpp b/src/modules/SD2/scripts/northrend/violet_hold/boss_erekem.cpp new file mode 100644 index 000000000..72cb383b4 --- /dev/null +++ b/src/modules/SD2/scripts/northrend/violet_hold/boss_erekem.cpp @@ -0,0 +1,262 @@ +/* Copyright (C) 2006 - 2013 ScriptDev2 + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +/* ScriptData +SDName: boss_erekem +SD%Complete: 90 +SDComment: Timers may need adjustments +SDCategory: Violet Hold +EndScriptData */ + +#include "precompiled.h" +#include "violet_hold.h" + +enum +{ + SAY_AGGRO = -1608012, + SAY_ADD_DIE_1 = -1608013, + SAY_ADD_DIE_2 = -1608014, + SAY_DEATH = -1608018, + // A few Sound IDs on SLAY, if there _is_ text related, fields -1608015 to -1608017 are free + SOUND_ID_SLAY_1 = 14222, + SOUND_ID_SLAY_2 = 14223, + SOUND_ID_SLAY_3 = 14224, + + SPELL_BLOODLUST = 54516, + SPELL_BREAK_BONDS_H = 59463, + SPELL_CHAIN_HEAL = 54481, + SPELL_CHAIN_HEAL_H = 59473, + SPELL_EARTH_SHIELD = 54479, + SPELL_EARTH_SHIELD_H = 59471, + SPELL_EARTH_SHOCK = 54511, + SPELL_LIGHTNING_BOLT = 53044, + SPELL_STORMSTRIKE = 51876, + + // Spells of adds + SPELL_GUSHING_WOUND = 39215, + SPELL_HOWLING_SCREECH = 54463, + SPELL_STRIKE = 14516 +}; + +struct boss_erekemAI : public ScriptedAI +{ + boss_erekemAI(Creature* pCreature) : ScriptedAI(pCreature) + { + m_pInstance = (instance_violet_hold*)pCreature->GetInstanceData(); + m_bIsRegularMode = pCreature->GetMap()->IsRegularDifficulty(); + + Reset(); + } + + instance_violet_hold* m_pInstance; + bool m_bIsRegularMode; + + uint32 m_uiBreakBondsTimer; + uint32 m_uiChainHealTimer; + uint32 m_uiEarthShieldTimer; + uint32 m_uiEarthShockTimer; + uint32 m_uiSpecialSpellTimer; + uint8 m_uiGuardiansDead; + + void Reset() override + { + m_uiSpecialSpellTimer = 0; + m_uiEarthShieldTimer = urand(2000, 3000); + m_uiEarthShockTimer = urand(4000, 9000); + m_uiChainHealTimer = urand(5000, 15000); + m_uiBreakBondsTimer = urand(25000, 30000); + m_uiGuardiansDead = 0; + } + + void Aggro(Unit* /*pWho*/) override + { + DoScriptText(SAY_AGGRO, m_creature); + } + + void JustDied(Unit* /*pKiller*/) override + { + DoScriptText(SAY_DEATH, m_creature); + } + + void KilledUnit(Unit* /*pVictim*/) override + { + switch (urand(0, 2)) + { + case 0: DoPlaySoundToSet(m_creature, SOUND_ID_SLAY_1); break; + case 1: DoPlaySoundToSet(m_creature, SOUND_ID_SLAY_2); break; + case 2: DoPlaySoundToSet(m_creature, SOUND_ID_SLAY_3); break; + } + } + + void GuardianJustDied() + { + DoScriptText(!m_uiGuardiansDead ? SAY_ADD_DIE_1 : SAY_ADD_DIE_2, m_creature); + ++m_uiGuardiansDead; + + // cast bloodlust if both guards are dead + if (m_uiGuardiansDead == 2) + DoCastSpellIfCan(m_creature, SPELL_BLOODLUST, CAST_INTERRUPT_PREVIOUS); + } + + void UpdateAI(const uint32 uiDiff) override + { + if (!m_creature->SelectHostileTarget() || !m_creature->getVictim()) + return; + + if (m_uiEarthShieldTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature, m_bIsRegularMode ? SPELL_EARTH_SHIELD : SPELL_EARTH_SHIELD_H, CAST_AURA_NOT_PRESENT) == CAST_OK) + m_uiEarthShieldTimer = urand(25000, 30000); + } + else + m_uiEarthShieldTimer -= uiDiff; + + if (m_uiEarthShockTimer < uiDiff) + { + if (Unit* pTarget = m_creature->SelectAttackingTarget(ATTACKING_TARGET_RANDOM, 0)) + { + if (DoCastSpellIfCan(pTarget, SPELL_EARTH_SHOCK) == CAST_OK) + m_uiEarthShockTimer = urand(8000, 13000); + } + } + else + m_uiEarthShockTimer -= uiDiff; + + if (m_uiChainHealTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature, m_bIsRegularMode ? SPELL_CHAIN_HEAL : SPELL_CHAIN_HEAL_H) == CAST_OK) + m_uiChainHealTimer = urand(15000, 25000); + } + else + m_uiChainHealTimer -= uiDiff; + + // Cast Stormstrike only if both guards are down + if (m_uiSpecialSpellTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature->getVictim(), m_uiGuardiansDead == 2 ? SPELL_STORMSTRIKE : SPELL_LIGHTNING_BOLT) == CAST_OK) + m_uiSpecialSpellTimer = urand(2000, 3000); + } + else + m_uiSpecialSpellTimer -= uiDiff; + + // Break bonds only on heroic + if (!m_bIsRegularMode) + { + if (m_uiBreakBondsTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature, SPELL_BREAK_BONDS_H) == CAST_OK) + m_uiBreakBondsTimer = urand(25000, 30000); + } + else + m_uiBreakBondsTimer -= uiDiff; + } + + DoMeleeAttackIfReady(); + } +}; + +CreatureAI* GetAI_boss_erekem(Creature* pCreature) +{ + return new boss_erekemAI(pCreature); +} + +struct npc_erekem_guardAI : public ScriptedAI +{ + npc_erekem_guardAI(Creature* pCreature) : ScriptedAI(pCreature) + { + m_pInstance = ((instance_violet_hold*)pCreature->GetInstanceData()); + Reset(); + } + + instance_violet_hold* m_pInstance; + + uint32 m_uiGushingWoundTimer; + uint32 m_uiHowlingScreechTimer; + uint32 m_uiStrikeTimer; + + void Reset() override + { + m_uiGushingWoundTimer = urand(9000, 14000); + m_uiHowlingScreechTimer = urand(8000, 12000); + m_uiStrikeTimer = urand(5000, 7000); + } + + void JustDied(Unit* /*pKiller*/) override + { + if (!m_pInstance) + return; + + if (Creature* pBoss = m_pInstance->GetSingleCreatureFromStorage(m_pInstance->GetData(TYPE_EREKEM) != DONE ? NPC_EREKEM : NPC_ARAKKOA)) + { + if (!pBoss->IsAlive()) + return; + + ((boss_erekemAI*)pBoss->AI())->GuardianJustDied(); + } + } + + void UpdateAI(const uint32 uiDiff) override + { + if (!m_creature->SelectHostileTarget() || !m_creature->getVictim()) + return; + + if (m_uiGushingWoundTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature->getVictim(), SPELL_GUSHING_WOUND) == CAST_OK) + m_uiGushingWoundTimer = urand(25000, 30000); + } + else + m_uiGushingWoundTimer -= uiDiff; + + if (m_uiHowlingScreechTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature, SPELL_HOWLING_SCREECH) == CAST_OK) + m_uiHowlingScreechTimer = urand(10000, 16000); + } + else + m_uiHowlingScreechTimer -= uiDiff; + + if (m_uiStrikeTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature->getVictim(), SPELL_STRIKE) == CAST_OK) + m_uiStrikeTimer = urand(5000, 7000); + } + else + m_uiStrikeTimer -= uiDiff; + + DoMeleeAttackIfReady(); + } +}; + +CreatureAI* GetAI_npc_erekem_guard(Creature* pCreature) +{ + return new npc_erekem_guardAI(pCreature); +} + +void AddSC_boss_erekem() +{ + Script* pNewScript; + + pNewScript = new Script; + pNewScript->Name = "boss_erekem"; + pNewScript->GetAI = &GetAI_boss_erekem; + pNewScript->RegisterSelf(); + + pNewScript = new Script; + pNewScript->Name = "npc_erekem_guard"; + pNewScript->GetAI = &GetAI_npc_erekem_guard; + pNewScript->RegisterSelf(); +} diff --git a/src/modules/SD2/scripts/northrend/violet_hold/boss_ichoron.cpp b/src/modules/SD2/scripts/northrend/violet_hold/boss_ichoron.cpp new file mode 100644 index 000000000..6794ab1fe --- /dev/null +++ b/src/modules/SD2/scripts/northrend/violet_hold/boss_ichoron.cpp @@ -0,0 +1,161 @@ +/* Copyright (C) 2006 - 2013 ScriptDev2 + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +/* ScriptData +SDName: boss_ichoron +SD%Complete: 50 +SDComment: Water Globule event NYI +SDCategory: Violet Hold +EndScriptData */ + +#include "precompiled.h" +#include "violet_hold.h" + +enum +{ + SAY_AGGRO = -1608019, + SAY_SHATTERING = -1608020, + SAY_SHIELD = -1608021, + SAY_SLAY_1 = -1608022, + SAY_SLAY_2 = -1608023, + SAY_SLAY_3 = -1608024, + SAY_ENRAGE = -1608025, + SAY_DEATH = -1608026, + EMOTE_BUBBLE = -1608028, + + SPELL_SPLASH = 59516, + SPELL_DRAINED = 59820, + SPELL_FRENZY = 54312, + SPELL_FRENZY_H = 59522, + SPELL_PROTECTIVE_BUBBLE = 54306, + SPELL_WATER_BLAST = 54237, + SPELL_WATER_BLAST_H = 59520, + SPELL_WATER_BOLT_VOLLEY = 54241, + SPELL_WATER_BOLT_VOLLEY_H = 59521, + SPELL_WATER_GLOBULE = 54260, + + SPELL_WATER_GLOBULE_SPAWN_1 = 54258, + SPELL_WATER_GLOBULE_SPAWN_2 = 54264, + SPELL_WATER_GLOBULE_SPAWN_3 = 54265, + SPELL_WATER_GLOBULE_SPAWN_4 = 54266, + SPELL_WATER_GLOBULE_SPAWN_5 = 54267, + + SPELL_MERGE = 54269, // used by globules + SPELL_WATER_GLOBULE_TRANS = 54268, +}; + +static const uint32 aWaterGlobuleSpells[5] = {SPELL_WATER_GLOBULE_SPAWN_1, SPELL_WATER_GLOBULE_SPAWN_2, SPELL_WATER_GLOBULE_SPAWN_3, SPELL_WATER_GLOBULE_SPAWN_4, SPELL_WATER_GLOBULE_SPAWN_5}; + +struct boss_ichoronAI : public ScriptedAI +{ + boss_ichoronAI(Creature* pCreature) : ScriptedAI(pCreature) + { + m_pInstance = (instance_violet_hold*)pCreature->GetInstanceData(); + m_bIsRegularMode = pCreature->GetMap()->IsRegularDifficulty(); + + Reset(); + } + + instance_violet_hold* m_pInstance; + bool m_bIsRegularMode; + + uint32 m_uiWaterBoltVolleyTimer; + uint32 m_uiWaterBlastTimer; + bool m_bIsFrenzy; + + void Reset() override + { + m_uiWaterBoltVolleyTimer = urand(10000, 12000); + m_uiWaterBlastTimer = 10000; + m_bIsFrenzy = false; + } + + void Aggro(Unit* /*pWho*/) override + { + DoScriptText(SAY_AGGRO, m_creature); + + DoCastSpellIfCan(m_creature, SPELL_PROTECTIVE_BUBBLE); + } + + void JustDied(Unit* /*pKiller*/) override + { + DoScriptText(SAY_DEATH, m_creature); + } + + void KilledUnit(Unit* pWho) override + { + if (pWho->GetTypeId() != TYPEID_PLAYER) + return; + + switch (urand(0, 2)) + { + case 0: DoScriptText(SAY_SLAY_1, m_creature); break; + case 1: DoScriptText(SAY_SLAY_2, m_creature); break; + case 2: DoScriptText(SAY_SLAY_3, m_creature); break; + } + } + + void UpdateAI(const uint32 uiDiff) override + { + if (!m_creature->SelectHostileTarget() || !m_creature->getVictim()) + return; + + if (m_uiWaterBlastTimer < uiDiff) + { + if (Unit* pTarget = m_creature->SelectAttackingTarget(ATTACKING_TARGET_RANDOM, 0)) + { + if (DoCastSpellIfCan(pTarget, m_bIsRegularMode ? SPELL_WATER_BLAST : SPELL_WATER_BLAST_H) == CAST_OK) + m_uiWaterBlastTimer = urand(8000, 14000); + } + } + else + m_uiWaterBlastTimer -= uiDiff; + + if (m_uiWaterBoltVolleyTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature, m_bIsRegularMode ? SPELL_WATER_BOLT_VOLLEY : SPELL_WATER_BOLT_VOLLEY_H) == CAST_OK) + m_uiWaterBoltVolleyTimer = urand(7000, 12000); + } + else + m_uiWaterBoltVolleyTimer -= uiDiff; + + if (!m_bIsFrenzy && m_creature->GetHealthPercent() < 25.0f) + { + if (DoCastSpellIfCan(m_creature, m_bIsRegularMode ? SPELL_FRENZY : SPELL_FRENZY_H) == CAST_OK) + { + DoScriptText(SAY_ENRAGE, m_creature); + m_bIsFrenzy = true; + } + } + + DoMeleeAttackIfReady(); + } +}; + +CreatureAI* GetAI_boss_ichoron(Creature* pCreature) +{ + return new boss_ichoronAI(pCreature); +} + +void AddSC_boss_ichoron() +{ + Script* pNewScript; + + pNewScript = new Script; + pNewScript->Name = "boss_ichoron"; + pNewScript->GetAI = &GetAI_boss_ichoron; + pNewScript->RegisterSelf(); +} diff --git a/src/modules/SD2/scripts/northrend/violet_hold/instance_violet_hold.cpp b/src/modules/SD2/scripts/northrend/violet_hold/instance_violet_hold.cpp new file mode 100644 index 000000000..35a1a06ee --- /dev/null +++ b/src/modules/SD2/scripts/northrend/violet_hold/instance_violet_hold.cpp @@ -0,0 +1,774 @@ +/* Copyright (C) 2006 - 2013 ScriptDev2 + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along 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_Violet_Hold +SD%Complete: 75 +SDComment: Prison defense system requires more research +SDCategory: Violet Hold +EndScriptData */ + +#include "precompiled.h" +#include "violet_hold.h" + +instance_violet_hold::instance_violet_hold(Map* pMap) : ScriptedInstance(pMap), + m_uiWorldState(0), + m_uiWorldStateSealCount(100), + m_uiWorldStatePortalCount(0), + + m_uiPortalId(0), + m_uiPortalTimer(0), + m_uiMaxCountPortalLoc(0), + + m_uiSealYellCount(0), + m_uiEventResetTimer(0), + + m_bIsVoidDance(false), + m_bIsDefenseless(false), + m_bIsDehydratation(false) +{ + Initialize(); +} + +void instance_violet_hold::Initialize() +{ + memset(&m_auiEncounter, 0, sizeof(m_auiEncounter)); + m_uiMaxCountPortalLoc = countof(afPortalLocation) - 1; +} + +void instance_violet_hold::ResetVariables() +{ + m_uiWorldStateSealCount = 100; + m_uiWorldStatePortalCount = 0; + m_uiSealYellCount = 0; +} + +void instance_violet_hold::ResetAll() +{ + ResetVariables(); + UpdateWorldState(false); + CallGuards(true); + SetIntroPortals(false); + // ToDo: reset the activation crystals when implemented + + for (std::vector::const_iterator itr = m_vRandomBosses.begin(); itr != m_vRandomBosses.end(); ++itr) + { + const BossInformation* pData = GetBossInformation((*itr)->uiEntry); + if (pData && m_auiEncounter[pData->uiType] == DONE) + { + // Despawn ghost boss + if (Creature* pGhostBoss = GetSingleCreatureFromStorage(pData->uiGhostEntry)) + pGhostBoss->ForcedDespawn(); + + // Spawn new boss replacement + if (Creature* pSummoner = GetSingleCreatureFromStorage(NPC_SINCLARI_ALT)) + pSummoner->SummonCreature(pData->uiGhostEntry, (*itr)->fX, (*itr)->fY, (*itr)->fZ, (*itr)->fO, TEMPSUMMON_DEAD_DESPAWN, 0); + + // Replace Erekem guards + if (pData->uiType == TYPE_EREKEM) + { + // Despawn ghost guards + for (GuidList::const_iterator itr = m_lArakkoaGuardList.begin(); itr != m_lArakkoaGuardList.end(); ++itr) + { + if (Creature* pGhostGuard = instance->GetCreature(*itr)) + pGhostGuard->ForcedDespawn(); + } + + m_lArakkoaGuardList.clear(); + + // Spawn new guards replacement + float fX, fY, fZ, fO; + for (GuidList::const_iterator itr = m_lErekemGuardList.begin(); itr != m_lErekemGuardList.end(); ++itr) + { + if (Creature* pGuard = instance->GetCreature(*itr)) + { + // Don't allow alive original guards while the boss is dead + if (!pGuard->IsDead()) + pGuard->ForcedDespawn(); + + // Spawn a ghost guard for each original guard + pGuard->GetRespawnCoord(fX, fY, fZ, &fO); + pGuard->SummonCreature(NPC_ARAKKOA_GUARD, fX, fY, fZ, fO, TEMPSUMMON_DEAD_DESPAWN, 0); + } + } + } + } + + // Close Door if still open + if (pData && (m_auiEncounter[pData->uiType] == DONE || m_auiEncounter[pData->uiType] == FAIL)) + UpdateCellForBoss(pData->uiEntry, true); + } +} + +void instance_violet_hold::OnCreatureCreate(Creature* pCreature) +{ + switch (pCreature->GetEntry()) + { + case NPC_SINCLARI: + case NPC_SINCLARI_ALT: + case NPC_DOOR_SEAL: + case NPC_EVENT_CONTROLLER: + break; + + case NPC_EREKEM: + case NPC_MORAGG: + case NPC_ICHORON: + case NPC_XEVOZZ: + case NPC_LAVANTHOR: + case NPC_ZURAMAT: + m_vRandomBossList.push_back(pCreature->GetEntry()); + break; + + case NPC_PORTAL_INTRO: + m_lIntroPortalList.push_back(pCreature->GetObjectGuid()); + return; + case NPC_HOLD_GUARD: + m_lGuardsList.push_back(pCreature->GetObjectGuid()); + return; + case NPC_EREKEM_GUARD: + m_lErekemGuardList.push_back(pCreature->GetObjectGuid()); + return; + case NPC_ARAKKOA_GUARD: + m_lArakkoaGuardList.push_back(pCreature->GetObjectGuid()); + return; + case NPC_ICHORON_SUMMON_TARGET: + m_lIchoronTargetsList.push_back(pCreature->GetObjectGuid()); + return; + + case NPC_ARAKKOA: + case NPC_VOID_LORD: + case NPC_ETHERAL: + case NPC_SWIRLING: + case NPC_WATCHER: + case NPC_LAVA_HOUND: + break; + + default: + return; + } + m_mNpcEntryGuidStore[pCreature->GetEntry()] = pCreature->GetObjectGuid(); +} + +void instance_violet_hold::OnObjectCreate(GameObject* pGo) +{ + switch (pGo->GetEntry()) + { + case GO_CELL_LAVANTHOR: + m_mBossToCellMap.insert(BossToCellMap::value_type(NPC_LAVANTHOR, pGo->GetObjectGuid())); + return; + case GO_CELL_MORAGG: + m_mBossToCellMap.insert(BossToCellMap::value_type(NPC_MORAGG, pGo->GetObjectGuid())); + return; + case GO_CELL_ZURAMAT: + m_mBossToCellMap.insert(BossToCellMap::value_type(NPC_ZURAMAT, pGo->GetObjectGuid())); + return; + case GO_CELL_XEVOZZ: + m_mBossToCellMap.insert(BossToCellMap::value_type(NPC_XEVOZZ, pGo->GetObjectGuid())); + return; + case GO_CELL_ICHORON: + m_mBossToCellMap.insert(BossToCellMap::value_type(NPC_ICHORON, pGo->GetObjectGuid())); + return; + case GO_CELL_EREKEM: + m_mBossToCellMap.insert(BossToCellMap::value_type(NPC_EREKEM, pGo->GetObjectGuid())); + return; + case GO_CELL_EREKEM_GUARD_L: + m_mBossToCellMap.insert(BossToCellMap::value_type(NPC_EREKEM, pGo->GetObjectGuid())); + return; + case GO_CELL_EREKEM_GUARD_R: + m_mBossToCellMap.insert(BossToCellMap::value_type(NPC_EREKEM, pGo->GetObjectGuid())); + return; + + case GO_INTRO_CRYSTAL: + case GO_PRISON_SEAL_DOOR: + break; + } + m_mGoEntryGuidStore[pGo->GetEntry()] = pGo->GetObjectGuid(); +} + +void instance_violet_hold::UpdateCellForBoss(uint32 uiBossEntry, bool bForceClosing /*= false*/) +{ + BossToCellMap::const_iterator itrCellLower = m_mBossToCellMap.lower_bound(uiBossEntry); + BossToCellMap::const_iterator itrCellUpper = m_mBossToCellMap.upper_bound(uiBossEntry); + + if (itrCellLower == itrCellUpper) + return; + + for (BossToCellMap::const_iterator itr = itrCellLower; itr != itrCellUpper; ++itr) + { + if (!bForceClosing) + DoUseDoorOrButton(itr->second); + else + { + GameObject* pGo = instance->GetGameObject(itr->second); + if (pGo && pGo->GetGoType() == GAMEOBJECT_TYPE_DOOR && pGo->GetGoState() == GO_STATE_ACTIVE) + pGo->ResetDoorOrButton(); + } + } +} + +void instance_violet_hold::UpdateWorldState(bool bEnable) +{ + m_uiWorldState = bEnable ? 1 : 0; + + DoUpdateWorldState(WORLD_STATE_ID, m_uiWorldState); + DoUpdateWorldState(WORLD_STATE_SEAL, m_uiWorldStateSealCount); + DoUpdateWorldState(WORLD_STATE_PORTALS, m_uiWorldStatePortalCount); +} + +void instance_violet_hold::OnPlayerEnter(Player* /*pPlayer*/) +{ + UpdateWorldState(m_auiEncounter[TYPE_MAIN] == IN_PROGRESS ? true : false); + + if (m_vRandomBosses.empty()) + { + SetRandomBosses(); + ResetAll(); + } +} + +void instance_violet_hold::SetData(uint32 uiType, uint32 uiData) +{ + debug_log("SD2: instance_violet_hold: SetData got type %u, data %u.", uiType, uiData); + + switch (uiType) + { + case TYPE_MAIN: + { + if (uiData == m_auiEncounter[uiType]) + return; + if (m_auiEncounter[uiType] == DONE) + return; + + switch (uiData) + { + case IN_PROGRESS: + // ToDo: enable the prison defense system when implemented + DoUseDoorOrButton(GO_PRISON_SEAL_DOOR); + UpdateWorldState(); + m_bIsDefenseless = true; + m_uiPortalId = urand(0, 2); + m_uiPortalTimer = 15000; + break; + case FAIL: + if (Creature* pSinclari = GetSingleCreatureFromStorage(NPC_SINCLARI)) + pSinclari->DealDamage(pSinclari, pSinclari->GetHealth(), NULL, DIRECT_DAMAGE, SPELL_SCHOOL_MASK_NORMAL, NULL, false); + if (Creature* pController = GetSingleCreatureFromStorage(NPC_EVENT_CONTROLLER)) + pController->AI()->EnterEvadeMode(); + // Reset the event (creature cleanup is handled in creature_linking) + DoUseDoorOrButton(GO_PRISON_SEAL_DOOR); // open instance door + ResetAll(); + m_uiEventResetTimer = 20000; // Timer may not be correct - 20 sec is default reset timer for blizz + break; + case DONE: + DoUseDoorOrButton(GO_PRISON_SEAL_DOOR); + UpdateWorldState(false); + break; + case SPECIAL: + break; + } + m_auiEncounter[uiType] = uiData; + break; + } + case TYPE_SEAL: + m_auiEncounter[uiType] = uiData; + if (uiData == SPECIAL) + { + --m_uiWorldStateSealCount; + DoUpdateWorldState(WORLD_STATE_SEAL, m_uiWorldStateSealCount); + + // Yell at 75%, 50% and 25% shield + if (m_uiWorldStateSealCount < 100 - 25 * m_uiSealYellCount) + { + if (Creature* pSinclari = GetSingleCreatureFromStorage(NPC_SINCLARI_ALT)) + { + // ToDo: I'm not sure if the last yell should be at 25% or at 5%. Needs research + ++m_uiSealYellCount; + DoScriptText(aSealWeakYell[m_uiSealYellCount - 1], pSinclari); + } + } + + // set achiev to failed + if (m_bIsDefenseless) + m_bIsDefenseless = false; + + if (!m_uiWorldStateSealCount) + { + SetData(TYPE_MAIN, FAIL); + SetData(TYPE_SEAL, NOT_STARTED); + } + } + break; + case TYPE_PORTAL: + { + switch (uiData) + { + case SPECIAL: // timer to next + m_uiPortalTimer = 90000; + break; + case DONE: // portal done, set timer to 5 secs + m_uiPortalTimer = 3000; + break; + } + m_auiEncounter[uiType] = uiData; + break; + } + case TYPE_LAVANTHOR: + case TYPE_MORAGG: + case TYPE_EREKEM: + case TYPE_ICHORON: + case TYPE_XEVOZZ: + case TYPE_ZURAMAT: + if (uiData == DONE) + m_uiPortalTimer = 35000; + if (m_auiEncounter[uiType] != DONE) // Keep the DONE-information stored + m_auiEncounter[uiType] = uiData; + // Handle achievements if necessary + if (uiData == IN_PROGRESS) + { + if (uiType == TYPE_ZURAMAT) + m_bIsVoidDance = true; + else if (uiType == TYPE_ICHORON) + m_bIsDehydratation = true; + } + if (uiData == SPECIAL && uiType == TYPE_ICHORON) + m_bIsDehydratation = false; + if (uiData == FAIL) + SetData(TYPE_MAIN, FAIL); + break; + case TYPE_CYANIGOSA: + if (uiData == DONE) + SetData(TYPE_MAIN, DONE); + if (uiData == FAIL) + SetData(TYPE_MAIN, FAIL); + m_auiEncounter[uiType] = uiData; + break; + default: + return; + } + + 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_strInstData = saveStream.str(); + + SaveToDB(); + OUT_SAVE_INST_DATA_COMPLETE; + } +} + +uint32 instance_violet_hold::GetData(uint32 uiType) const +{ + if (uiType < MAX_ENCOUNTER) + return m_auiEncounter[uiType]; + return 0; +} + +void instance_violet_hold::Load(const char* chrIn) +{ + if (!chrIn) + { + 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]; + + for (uint8 i = 0; i < MAX_ENCOUNTER; ++i) + { + if (m_auiEncounter[i] == IN_PROGRESS) + m_auiEncounter[i] = NOT_STARTED; + } + + OUT_LOAD_INST_DATA_COMPLETE; +} + +void instance_violet_hold::SetIntroPortals(bool bDeactivate) +{ + for (GuidList::const_iterator itr = m_lIntroPortalList.begin(); itr != m_lIntroPortalList.end(); ++itr) + { + if (Creature* pPortal = instance->GetCreature(*itr)) + { + if (bDeactivate) + pPortal->ForcedDespawn(); + else + pPortal->Respawn(); + } + } +} + +void instance_violet_hold::SpawnPortal() +{ + if (const PortalData* pData = GetPortalData()) + { + if (Creature* pController = GetSingleCreatureFromStorage(NPC_SINCLARI_ALT)) + { + uint32 uiPortalEntry = pData->pPortalType == PORTAL_TYPE_NORM ? NPC_PORTAL : NPC_PORTAL_ELITE; + + pController->SummonCreature(uiPortalEntry, pData->fX, pData->fY, pData->fZ, pData->fOrient, TEMPSUMMON_TIMED_OOC_OR_CORPSE_DESPAWN, 1800 * IN_MILLISECONDS); + } + } +} + +void instance_violet_hold::SetPortalId() +{ + if (IsCurrentPortalForTrash()) + { + // Find another Trash portal position + uint8 uiTemp = m_uiPortalId + urand(1, m_uiMaxCountPortalLoc - 1); + // Decrease m_uiMaxCountPortalLoc so that the center position is skipped + uiTemp %= m_uiMaxCountPortalLoc - 1; + + debug_log("SD2: instance_violet_hold: SetPortalId %u, old was id %u.", uiTemp, m_uiPortalId); + + m_uiPortalId = uiTemp; + } + else if (GetCurrentPortalNumber() == 18) + { + debug_log("SD2: instance_violet_hold: SetPortalId %u (Cyanigosa), old was id %u.", 0, m_uiPortalId); + m_uiPortalId = 0; + } + else + { + debug_log("SD2: instance_violet_hold: SetPortalId %u (is boss), old was id %u.", m_uiMaxCountPortalLoc, m_uiPortalId); + m_uiPortalId = m_uiMaxCountPortalLoc; + } +} + +BossSpawn* instance_violet_hold::CreateBossSpawnByEntry(uint32 uiEntry) +{ + BossSpawn* pBossSpawn = new BossSpawn; + pBossSpawn->uiEntry = uiEntry; + + if (Creature* pBoss = GetSingleCreatureFromStorage(uiEntry)) + pBoss->GetRespawnCoord(pBossSpawn->fX, pBossSpawn->fY, pBossSpawn->fZ, &(pBossSpawn->fO)); + + return pBossSpawn; +} + +void instance_violet_hold::SetRandomBosses() +{ + // Store bosses that are already done + for (uint8 i = 0; i < MAX_MINIBOSSES; ++i) + { + if (m_auiEncounter[aBossInformation[i].uiType] == DONE) + m_vRandomBosses.push_back(CreateBossSpawnByEntry(aBossInformation[i].uiEntry)); + } + + if (m_vRandomBosses.size() < 2) // Get some new random bosses + { + std::random_shuffle(m_vRandomBossList.begin(), m_vRandomBossList.end()); + // two required, in case the first is already pushed to m_vRandomBosses + if (m_vRandomBossList.size() < 2) + script_error_log("instance_violet_hold, Mini Bosses are not properly spawned"); + else + m_vRandomBossList.resize(2); + + // Fill up some random bosses + for (std::vector::const_iterator itr = m_vRandomBossList.begin(); itr != m_vRandomBossList.end(); ++itr) + { + if (m_vRandomBosses.empty() || m_vRandomBosses[0]->uiEntry != *itr) + m_vRandomBosses.push_back(CreateBossSpawnByEntry(*itr)); + } + } + + for (uint8 i = 0; i < m_vRandomBosses.size(); ++i) + debug_log("SD2: instance_violet_hold random boss %u is entry %u", i, m_vRandomBosses[i]->uiEntry); +} + +void instance_violet_hold::CallGuards(bool bRespawn) +{ + for (GuidList::const_iterator itr = m_lGuardsList.begin(); itr != m_lGuardsList.end(); ++itr) + { + if (Creature* pGuard = instance->GetCreature(*itr)) + { + if (bRespawn) + pGuard->Respawn(); + else if (pGuard->IsAlive()) + { + pGuard->SetWalk(false); + pGuard->GetMotionMaster()->MovePoint(0, fGuardExitLoc[0], fGuardExitLoc[1], fGuardExitLoc[2]); + pGuard->ForcedDespawn(6000); + } + } + } +} + +void instance_violet_hold::ProcessActivationCrystal(Unit* pUser, bool bIsIntro) +{ + if (Creature* pSummon = pUser->SummonCreature(NPC_DEFENSE_SYSTEM, fDefenseSystemLoc[0], fDefenseSystemLoc[1], fDefenseSystemLoc[2], fDefenseSystemLoc[3], TEMPSUMMON_TIMED_DESPAWN, 10000)) + { + pSummon->CastSpell(pSummon, SPELL_DEFENSE_SYSTEM_VISUAL, true); + + // TODO: figure out how the rest work + // NPC's NPC_DEFENSE_DUMMY_TARGET are probably channeling some spell to the defense system + } + + if (bIsIntro) + DoUseDoorOrButton(GO_INTRO_CRYSTAL); + + // else, kill (and despawn?) certain trash mobs. Also boss affected, but not killed. +} + +bool instance_violet_hold::CheckAchievementCriteriaMeet(uint32 uiCriteriaId, Player const* /*pSource*/, Unit const* /*pTarget*/, uint32 /*uiMiscValue1 = 0*/) const +{ + switch (uiCriteriaId) + { + // ToDo: uncomment these when they are implemented + // case ACHIEV_CRIT_DEFENSELES: + // return m_bIsDefenseless; + // case ACHIEV_CRIT_DEHYDRATATION: + // return m_bIsDehydratation; + case ACHIEV_CRIT_VOID_DANCE: + return m_bIsVoidDance; + + default: + return false; + } +} + +void instance_violet_hold::OnCreatureEnterCombat(Creature* pCreature) +{ + switch (pCreature->GetEntry()) + { + case NPC_ZURAMAT: + case NPC_VOID_LORD: + SetData(TYPE_ZURAMAT, IN_PROGRESS); + break; + case NPC_XEVOZZ: + case NPC_ETHERAL: + SetData(TYPE_XEVOZZ, IN_PROGRESS); + break; + case NPC_LAVANTHOR: + case NPC_LAVA_HOUND: + SetData(TYPE_LAVANTHOR, IN_PROGRESS); + break; + case NPC_MORAGG: + case NPC_WATCHER: + SetData(TYPE_MORAGG, IN_PROGRESS); + break; + case NPC_EREKEM: + case NPC_ARAKKOA: + SetData(TYPE_EREKEM, IN_PROGRESS); + break; + case NPC_ICHORON: + case NPC_SWIRLING: + SetData(TYPE_ICHORON, IN_PROGRESS); + break; + case NPC_CYANIGOSA: + SetData(TYPE_CYANIGOSA, IN_PROGRESS); + break; + case NPC_AZURE_CAPTAIN: + case NPC_AZURE_RAIDER: + case NPC_AZURE_SORCEROR: + case NPC_AZURE_STALKER: + case NPC_AZURE_INVADER: + case NPC_MAGE_HUNTER: + case NPC_AZURE_SPELLBREAKER: + case NPC_AZURE_BINDER: + case NPC_AZURE_MAGE_SLAYER: + // Interrupt door seal casting (if necessary) + pCreature->InterruptNonMeleeSpells(false); + break; + } +} + +void instance_violet_hold::OnCreatureEvade(Creature* pCreature) +{ + switch (pCreature->GetEntry()) + { + case NPC_ZURAMAT: + case NPC_VOID_LORD: + SetData(TYPE_ZURAMAT, FAIL); + pCreature->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_PASSIVE); + break; + case NPC_XEVOZZ: + case NPC_ETHERAL: + SetData(TYPE_XEVOZZ, FAIL); + pCreature->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_PASSIVE); + break; + case NPC_LAVANTHOR: + case NPC_LAVA_HOUND: + SetData(TYPE_LAVANTHOR, FAIL); + pCreature->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_PASSIVE); + break; + case NPC_MORAGG: + case NPC_WATCHER: + SetData(TYPE_MORAGG, FAIL); + pCreature->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_PASSIVE); + break; + case NPC_EREKEM: + case NPC_ARAKKOA: + SetData(TYPE_EREKEM, FAIL); + pCreature->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_PASSIVE); + break; + case NPC_EREKEM_GUARD: + pCreature->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_PASSIVE); + break; + case NPC_ICHORON: + case NPC_SWIRLING: + SetData(TYPE_ICHORON, FAIL); + pCreature->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_PASSIVE); + break; + case NPC_CYANIGOSA: + SetData(TYPE_CYANIGOSA, FAIL); + break; + case NPC_AZURE_CAPTAIN: + case NPC_AZURE_RAIDER: + case NPC_AZURE_SORCEROR: + case NPC_AZURE_STALKER: + case NPC_AZURE_INVADER: + case NPC_MAGE_HUNTER: + case NPC_AZURE_SPELLBREAKER: + case NPC_AZURE_BINDER: + case NPC_AZURE_MAGE_SLAYER: + // Allow them to finish off the door seal + pCreature->SetWalk(false); + pCreature->GetMotionMaster()->MovePoint(1, fSealAttackLoc[0], fSealAttackLoc[1], fSealAttackLoc[2]); + break; + } +} + +void instance_violet_hold::OnCreatureDeath(Creature* pCreature) +{ + switch (pCreature->GetEntry()) + { + case NPC_ZURAMAT: + case NPC_VOID_LORD: + SetData(TYPE_ZURAMAT, DONE); + break; + case NPC_XEVOZZ: + case NPC_ETHERAL: + SetData(TYPE_XEVOZZ, DONE); + break; + case NPC_LAVANTHOR: + case NPC_LAVA_HOUND: + SetData(TYPE_LAVANTHOR, DONE); + break; + case NPC_MORAGG: + case NPC_WATCHER: + SetData(TYPE_MORAGG, DONE); + break; + case NPC_EREKEM: + case NPC_ARAKKOA: + SetData(TYPE_EREKEM, DONE); + break; + case NPC_ICHORON: + case NPC_SWIRLING: + SetData(TYPE_ICHORON, DONE); + break; + case NPC_CYANIGOSA: + SetData(TYPE_CYANIGOSA, DONE); + break; + case NPC_VOID_SENTRY: + if (GetData(TYPE_ZURAMAT) == IN_PROGRESS) + m_bIsVoidDance = false; + break; + } +} + +void instance_violet_hold::Update(uint32 uiDiff) +{ + if (m_uiEventResetTimer) + { + if (m_uiEventResetTimer <= uiDiff) + { + if (Creature* pSinclari = GetSingleCreatureFromStorage(NPC_SINCLARI)) + pSinclari->Respawn(); + + m_uiEventResetTimer = 0; + } + else + m_uiEventResetTimer -= uiDiff; + } + + if (m_auiEncounter[TYPE_MAIN] != IN_PROGRESS) + return; + + if (m_uiPortalTimer) + { + if (m_uiPortalTimer <= uiDiff) + { + DoUpdateWorldState(WORLD_STATE_PORTALS, ++m_uiWorldStatePortalCount); + + SetPortalId(); + SpawnPortal(); + + m_uiPortalTimer = 0; + } + else + m_uiPortalTimer -= uiDiff; + } +} + +BossInformation const* instance_violet_hold::GetBossInformation(uint32 uiEntry/* = 0*/) +{ + uint32 mEntry = uiEntry; + if (!mEntry) + { + if (GetCurrentPortalNumber() == 6 && m_vRandomBosses.size() >= 1) + mEntry = m_vRandomBosses[0]->uiEntry; + else if (GetCurrentPortalNumber() == 12 && m_vRandomBosses.size() >= 2) + mEntry = m_vRandomBosses[1]->uiEntry; + } + + if (!mEntry) + return NULL; + + for (uint8 i = 0; i < MAX_MINIBOSSES; ++i) + { + if (aBossInformation[i].uiEntry == mEntry) + return &aBossInformation[i]; + } + + return NULL; +} + +instance_violet_hold::~instance_violet_hold() +{ + // Need to free std::vector m_vRandomBosses; + for (std::vector::const_iterator itr = m_vRandomBosses.begin(); itr != m_vRandomBosses.end(); ++itr) + { + if (*itr) + delete(*itr); + } +} + +InstanceData* GetInstanceData_instance_violet_hold(Map* pMap) +{ + return new instance_violet_hold(pMap); +} + +void AddSC_instance_violet_hold() +{ + Script* pNewScript; + + pNewScript = new Script; + pNewScript->Name = "instance_violet_hold"; + pNewScript->GetInstanceData = GetInstanceData_instance_violet_hold; + pNewScript->RegisterSelf(); +} diff --git a/src/modules/SD2/scripts/northrend/violet_hold/violet_hold.cpp b/src/modules/SD2/scripts/northrend/violet_hold/violet_hold.cpp new file mode 100644 index 000000000..786074388 --- /dev/null +++ b/src/modules/SD2/scripts/northrend/violet_hold/violet_hold.cpp @@ -0,0 +1,632 @@ +/* Copyright (C) 2006 - 2013 ScriptDev2 + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +/* ScriptData +SDName: Violet_Hold +SD%Complete: 80 +SDComment: Intro event required more research and core support. +SDCategory: Violet Hold +EndScriptData */ + +/* ContentData +go_activation_crystal +npc_door_seal +npc_sinclari +npc_prison_event_controller +npc_teleportation_portal +EndContentData */ + +#include "precompiled.h" +#include "violet_hold.h" +#include "escort_ai.h" + +/*###### +## go_activation_crystal +######*/ + +bool GOUse_go_activation_crystal(Player* pPlayer, GameObject* pGo) +{ + if (instance_violet_hold* pInstance = (instance_violet_hold*)pGo->GetInstanceData()) + pInstance->ProcessActivationCrystal(pPlayer); + + return false; +} + +/*###### +## npc_door_seal +######*/ + +bool EffectDummyCreature_npc_door_seal(Unit* /*pCaster*/, uint32 uiSpellId, SpellEffectIndex uiEffIndex, Creature* pCreatureTarget, ObjectGuid /*originalCasterGuid*/) +{ + // always check spellid and effectindex + if (uiSpellId == SPELL_DESTROY_DOOR_SEAL && uiEffIndex == EFFECT_INDEX_0) + { + if (instance_violet_hold* pInstance = (instance_violet_hold*)pCreatureTarget->GetInstanceData()) + pInstance->SetData(TYPE_SEAL, SPECIAL); + + // always return true when we are handling this spell and effect + return true; + } + + return false; +} + +/*###### +## npc_sinclari +######*/ + +enum +{ + SAY_BEGIN = -1608000, + SAY_LOCK_DOOR = -1608001, + SAY_VICTORY = -1608027, + + GOSSIP_ITEM_INTRO = -3608000, + GOSSIP_ITEM_START = -3608001, + GOSSIP_ITEM_TELEPORT = -3608002, + + GOSSIP_TEXT_ID_INTRO = 13853, + GOSSIP_TEXT_ID_START = 13854, + + SPELL_TELEPORT_INSIDE = 62138, // script effect - should trigger 62139 +}; + +struct npc_sinclariAI : public npc_escortAI +{ + npc_sinclariAI(Creature* pCreature) : npc_escortAI(pCreature) + { + m_pInstance = (instance_violet_hold*)pCreature->GetInstanceData(); + Reset(); + } + + instance_violet_hold* m_pInstance; + + bool m_bIsEpilogue; + + void Reset() override + { + m_bIsEpilogue = false; + } + + void WaypointReached(uint32 uiPointId) override + { + if (!m_pInstance) + return; + + switch (uiPointId) + { + case 0: + m_pInstance->ProcessActivationCrystal(m_creature, true); + break; + case 1: + DoScriptText(SAY_BEGIN, m_creature); + m_pInstance->SetIntroPortals(true); + m_pInstance->CallGuards(false); + break; + case 2: + DoScriptText(SAY_LOCK_DOOR, m_creature); + m_creature->SetFacingTo(0.05f); + break; + case 3: + m_pInstance->SetData(TYPE_MAIN, IN_PROGRESS); + break; + case 4: + m_creature->SetFlag(UNIT_NPC_FLAGS, UNIT_NPC_FLAG_GOSSIP); + SetEscortPaused(true); + break; + case 5: + m_creature->RemoveFlag(UNIT_NPC_FLAGS, UNIT_NPC_FLAG_GOSSIP); + DoScriptText(SAY_VICTORY, m_creature); + SetEscortPaused(true); + break; + } + } + + void JustRespawned() override + { + if (m_pInstance && m_pInstance->GetData(TYPE_MAIN) != DONE) + m_pInstance->SetData(TYPE_MAIN, NOT_STARTED); + + npc_escortAI::JustRespawned(); // Needed, to reset escort state, waypoints, etc + } + + void UpdateEscortAI(const uint32 /*uiDiff*/) override + { + // Say outro after event is finished + if (m_pInstance && m_pInstance->GetData(TYPE_MAIN) == DONE && !m_bIsEpilogue) + { + SetEscortPaused(false); + m_bIsEpilogue = true; + } + } +}; + +CreatureAI* GetAI_npc_sinclari(Creature* pCreature) +{ + return new npc_sinclariAI(pCreature); +} + +bool GossipHello_npc_sinclari(Player* pPlayer, Creature* pCreature) +{ + if (instance_violet_hold* pInstance = (instance_violet_hold*)pCreature->GetInstanceData()) + { + if (pInstance->GetData(TYPE_MAIN) != IN_PROGRESS) + pPlayer->ADD_GOSSIP_ITEM_ID(GOSSIP_ICON_CHAT, GOSSIP_ITEM_INTRO, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 1); + else + pPlayer->ADD_GOSSIP_ITEM_ID(GOSSIP_ICON_CHAT, GOSSIP_ITEM_TELEPORT, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 3); + } + + pPlayer->SEND_GOSSIP_MENU(GOSSIP_TEXT_ID_INTRO, pCreature->GetObjectGuid()); + return true; +} + +bool GossipSelect_npc_sinclari(Player* pPlayer, Creature* pCreature, uint32 /*uiSender*/, uint32 uiAction) +{ + if (uiAction == GOSSIP_ACTION_INFO_DEF + 1) + { + if (instance_violet_hold* pInstance = (instance_violet_hold*)pCreature->GetInstanceData()) + { + if (pInstance->GetData(TYPE_MAIN) == NOT_STARTED) + { + pPlayer->ADD_GOSSIP_ITEM_ID(GOSSIP_ICON_CHAT, GOSSIP_ITEM_START, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 2); + pPlayer->SEND_GOSSIP_MENU(GOSSIP_TEXT_ID_START, pCreature->GetObjectGuid()); + } + } + else + pPlayer->CLOSE_GOSSIP_MENU(); + } + + if (uiAction == GOSSIP_ACTION_INFO_DEF + 2) + { + if (instance_violet_hold* pInstance = (instance_violet_hold*)pCreature->GetInstanceData()) + { + pPlayer->CLOSE_GOSSIP_MENU(); + + if (pInstance->GetData(TYPE_MAIN) == NOT_STARTED) + { + pInstance->SetData(TYPE_MAIN, SPECIAL); + + if (npc_sinclariAI* pEscortAI = dynamic_cast(pCreature->AI())) + pEscortAI->Start(); + } + } + else + pPlayer->CLOSE_GOSSIP_MENU(); + } + + if (uiAction == GOSSIP_ACTION_INFO_DEF + 3) + { + pCreature->CastSpell(pPlayer, SPELL_TELEPORT_INSIDE, true); + pPlayer->CLOSE_GOSSIP_MENU(); + } + + return true; +} + +/*###### +## npc_prison_event_controller +######*/ + +struct npc_prison_event_controllerAI : public ScriptedAI +{ + npc_prison_event_controllerAI(Creature* pCreature) : ScriptedAI(pCreature) + { + m_pInstance = (instance_violet_hold*)pCreature->GetInstanceData(); + Reset(); + } + + instance_violet_hold* m_pInstance; + + GuidSet m_sTrashPackSet; + + uint32 m_uiSaboteurTimer; + uint8 m_uiSaboteurPhase; + uint8 m_uiCurrentTrashPortalId; + + ObjectGuid m_currentSaboteurGuid; + + void Reset() override + { + m_uiCurrentTrashPortalId = 0; + m_uiSaboteurPhase = 0; + m_uiSaboteurTimer = 0; + + m_currentSaboteurGuid.Clear(); + m_sTrashPackSet.clear(); + } + + void DoSetCurrentTrashPortal(uint8 uiPortalId) { m_uiCurrentTrashPortalId = uiPortalId; } + + void JustSummoned(Creature* pSummoned) override + { + switch (pSummoned->GetEntry()) + { + case NPC_AZURE_CAPTAIN: + DoScriptText(EMOTE_DRAGONFLIGHT_PORTAL, pSummoned); + // no break + case NPC_AZURE_RAIDER: + case NPC_AZURE_SORCEROR: + case NPC_AZURE_STALKER: + m_sTrashPackSet.insert(pSummoned->GetObjectGuid()); + // no break + case NPC_AZURE_INVADER: + case NPC_MAGE_HUNTER: + case NPC_AZURE_SPELLBREAKER: + case NPC_AZURE_BINDER: + case NPC_AZURE_MAGE_SLAYER: + pSummoned->SetWalk(false); + pSummoned->GetMotionMaster()->MovePoint(1, fSealAttackLoc[0], fSealAttackLoc[1], fSealAttackLoc[2]); + break; + case NPC_AZURE_SABOTEUR: + { + if (!m_pInstance) + return; + const BossInformation* pData = m_pInstance->GetBossInformation(); + if (pData) + { + pSummoned->SetWalk(false); + pSummoned->GetMotionMaster()->MovePoint(pData->uiWayPointId, pData->fX, pData->fY, pData->fZ); + } + m_currentSaboteurGuid = pSummoned->GetObjectGuid(); + break; + } + } + } + + void SummonedMovementInform(Creature* pSummoned, uint32 uiMotionType, uint32 uiPointId) override + { + if (uiMotionType != POINT_MOTION_TYPE && !uiPointId) + return; + + if (pSummoned->GetEntry() == NPC_AZURE_SABOTEUR) + { + // Prepare to release the boss + m_uiSaboteurPhase = 0; + m_uiSaboteurTimer = 1000; + pSummoned->CastSpell(pSummoned, SPELL_SHIELD_DISRUPTION, false); + } + // For other summons, cast destroy seal when they reach the door + else + pSummoned->CastSpell(pSummoned, SPELL_DESTROY_DOOR_SEAL, false); + } + + void SummonedCreatureJustDied(Creature* pSummoned) override + { + switch (pSummoned->GetEntry()) + { + case NPC_AZURE_CAPTAIN: + case NPC_AZURE_RAIDER: + case NPC_AZURE_SORCEROR: + case NPC_AZURE_STALKER: + { + if (m_sTrashPackSet.find(pSummoned->GetObjectGuid()) != m_sTrashPackSet.end()) + m_sTrashPackSet.erase(pSummoned->GetObjectGuid()); + + if (m_sTrashPackSet.empty()) + { + // no need if a new portal was made while this was in progress + if (m_uiCurrentTrashPortalId == m_pInstance->GetCurrentPortalNumber()) + m_pInstance->SetData(TYPE_PORTAL, DONE); + } + break; + } + } + } + + // Release a boss from a prison cell + void DoReleaseBoss() + { + if (!m_pInstance) + return; + + if (const BossInformation* pData = m_pInstance->GetBossInformation()) + { + if (Creature* pBoss = m_pInstance->GetSingleCreatureFromStorage(m_pInstance->GetData(pData->uiType) != DONE ? pData->uiEntry : pData->uiGhostEntry)) + { + m_pInstance->UpdateCellForBoss(pData->uiEntry); + if (pData->iSayEntry) + DoScriptText(pData->iSayEntry, pBoss); + + pBoss->GetMotionMaster()->MovePoint(1, pData->fX, pData->fY, pData->fZ); + pBoss->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_PASSIVE); + + // Handle Erekem guards + if (pData->uiType == TYPE_EREKEM) + { + GuidList lAddGuids; + if (m_pInstance) + m_pInstance->GetErekemGuardList(lAddGuids); + + float fMoveX; + for (GuidList::const_iterator itr = lAddGuids.begin(); itr != lAddGuids.end(); ++itr) + { + if (Creature* pAdd = m_pInstance->instance->GetCreature(*itr)) + { + fMoveX = (pData->fX - pAdd->GetPositionX()) * .25; + pAdd->GetMotionMaster()->MovePoint(0, pData->fX - fMoveX, pData->fY, pData->fZ); + pAdd->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_PASSIVE); + } + } + } + } + } + } + + void UpdateAI(const uint32 uiDiff) override + { + if (m_uiSaboteurTimer) + { + if (m_uiSaboteurTimer <= uiDiff) + { + Creature* pSaboteur = m_creature->GetMap()->GetCreature(m_currentSaboteurGuid); + if (!pSaboteur) + return; + + switch (m_uiSaboteurPhase) + { + case 0: + pSaboteur->CastSpell(pSaboteur, SPELL_SHIELD_DISRUPTION, false); + m_uiSaboteurTimer = 1000; + break; + case 1: + pSaboteur->CastSpell(pSaboteur, SPELL_SHIELD_DISRUPTION, false); + m_uiSaboteurTimer = 1000; + break; + case 2: + DoReleaseBoss(); + pSaboteur->CastSpell(pSaboteur, SPELL_SIMPLE_TELEPORT, false); + pSaboteur->ForcedDespawn(1000); + m_uiSaboteurTimer = 0; + break; + } + ++m_uiSaboteurPhase; + } + else + m_uiSaboteurTimer -= uiDiff; + } + } +}; + +CreatureAI* GetAI_npc_prison_event_controller(Creature* pCreature) +{ + return new npc_prison_event_controllerAI(pCreature); +} + +/*###### +## npc_teleportation_portal +######*/ + +static const uint32 aTrashPortalNpcs[4] = {NPC_AZURE_CAPTAIN, NPC_AZURE_RAIDER, NPC_AZURE_SORCEROR, NPC_AZURE_STALKER}; + +struct npc_teleportation_portalAI : public ScriptedAI +{ + npc_teleportation_portalAI(Creature* pCreature) : ScriptedAI(pCreature) + { + m_pInstance = (instance_violet_hold*)pCreature->GetInstanceData(); + m_uiMyPortalNumber = 0; + Reset(); + } + + instance_violet_hold* m_pInstance; + + bool m_bIntro; + uint32 m_uiMyPortalNumber; + uint32 m_uiCyanigosaMoveTimer; + + ObjectGuid m_cyanigosaGuid; + + void Reset() override + { + DoCastSpellIfCan(m_creature, SPELL_PORTAL_PERIODIC); + + m_bIntro = true; + m_uiCyanigosaMoveTimer = 0; + + if (m_pInstance) + m_uiMyPortalNumber = m_pInstance->GetCurrentPortalNumber(); + } + + void DoSummon() + { + if (!m_pInstance) + return; + + // Portal event used for intro + if (m_creature->GetEntry() == NPC_PORTAL_INTRO) + { + // ToDo: uncomment this when the information and DB data is confirmed. Right now the mobs may overrun the guards after a few min of fightning + // m_creature->SummonCreature(m_pInstance->GetRandomMobForIntroPortal(), 0, 0, 0, 0, TEMPSUMMON_DEAD_DESPAWN, 0); + return; + } + + // First summon tick + if (m_bIntro) + { + if (m_creature->GetEntry() == NPC_PORTAL) + { + // Summon a guardian keeper or Cyanigosa + if (m_uiMyPortalNumber == 18) + m_creature->SummonCreature(NPC_CYANIGOSA, 0.0f, 0.0f, 0.0f, 0.0f, TEMPSUMMON_TIMED_OOC_OR_DEAD_DESPAWN, 600 * IN_MILLISECONDS); + else + m_creature->SummonCreature(m_pInstance->GetRandomPortalEliteEntry(), 0.0f, 0.0f, 0.0f, 0.0f, TEMPSUMMON_TIMED_OOC_OR_DEAD_DESPAWN, 600 * IN_MILLISECONDS); + } + else if (m_creature->GetEntry() == NPC_PORTAL_ELITE) + { + // Allow the event controller to summon the mobs, for better movement handling + Creature* pController = m_pInstance->GetSingleCreatureFromStorage(NPC_EVENT_CONTROLLER); + if (!pController) + return; + + // Summon a squad or a saboteur + if (m_pInstance->IsCurrentPortalForTrash()) + { + float fX, fY, fZ; + for (uint8 i = 0; i < 4; ++i) + { + uint32 uiSummonId = aTrashPortalNpcs[i]; + + // Summon the trash pack around the portal + m_creature->GetNearPoint(m_creature, fX, fY, fZ, 0, 3.0f, M_PI_F / 2 * i); + pController->SummonCreature(uiSummonId, fX, fY, fZ, m_creature->GetOrientation(), TEMPSUMMON_TIMED_OOC_OR_DEAD_DESPAWN, 600 * IN_MILLISECONDS); + } + + // If this is a trash portal, set the current number in the + if (npc_prison_event_controllerAI* pControllerAI = dynamic_cast(pController->AI())) + pControllerAI->DoSetCurrentTrashPortal(m_uiMyPortalNumber); + } + else + pController->SummonCreature(NPC_AZURE_SABOTEUR, m_creature->GetPositionX(), m_creature->GetPositionY(), m_creature->GetPositionZ(), m_creature->GetOrientation(), TEMPSUMMON_TIMED_OOC_OR_DEAD_DESPAWN, 600 * IN_MILLISECONDS); + + m_creature->ForcedDespawn(5000); + } + + // Set special data for all the portals, except the last one + if (m_pInstance && m_uiMyPortalNumber != 18) + m_pInstance->SetData(TYPE_PORTAL, SPECIAL); + + m_bIntro = false; + } + else + { + // Allow the normal mobs to be summoned by the event controller + if (Creature* pController = m_pInstance->GetSingleCreatureFromStorage(NPC_EVENT_CONTROLLER)) + pController->SummonCreature(m_pInstance->GetRandomMobForNormalPortal(), m_creature->GetPositionX(), m_creature->GetPositionY(), m_creature->GetPositionZ(), m_creature->GetOrientation(), TEMPSUMMON_DEAD_DESPAWN, 0); + } + } + + void JustSummoned(Creature* pSummoned) override + { + switch (pSummoned->GetEntry()) + { + case NPC_CYANIGOSA: + m_cyanigosaGuid = pSummoned->GetObjectGuid(); + m_uiCyanigosaMoveTimer = 5000; + m_creature->ForcedDespawn(5000); + break; + case NPC_PORTAL_GUARDIAN: + DoScriptText(EMOTE_GUARDIAN_PORTAL, pSummoned); + DoCastSpellIfCan(pSummoned, SPELL_PORTAL_CHANNEL); + break; + case NPC_PORTAL_KEEPER: + DoScriptText(EMOTE_KEEPER_PORTAL, pSummoned); + DoCastSpellIfCan(pSummoned, SPELL_PORTAL_CHANNEL); + break; + case NPC_AZURE_BINDER_INTRO: + case NPC_AZURE_INVADER_INTRO: + case NPC_AZURE_SPELLBREAKER_INTRO: + case NPC_AZURE_MAGE_SLAYER_INTRO: + // Move them to the entrance. They will attack the guards automatically + pSummoned->SetWalk(false); + pSummoned->GetMotionMaster()->MovePoint(1, fSealAttackLoc[0], fSealAttackLoc[1], fSealAttackLoc[2]); + break; + } + } + + void SummonedCreatureJustDied(Creature* pSummoned) override + { + switch (pSummoned->GetEntry()) + { + case NPC_PORTAL_GUARDIAN: + case NPC_PORTAL_KEEPER: + m_creature->ForcedDespawn(3000); + // no need if a new portal was made while this was in progress + if (m_pInstance && m_uiMyPortalNumber == m_pInstance->GetCurrentPortalNumber()) + m_pInstance->SetData(TYPE_PORTAL, DONE); + break; + } + } + + void SummonedCreatureDespawn(Creature* pSummoned) override + { + switch (pSummoned->GetEntry()) + { + case NPC_PORTAL_GUARDIAN: + case NPC_PORTAL_KEEPER: + // Despawn in case of event reset + m_creature->ForcedDespawn(); + break; + } + } + + void UpdateAI(const uint32 uiDiff) override + { + if (m_uiCyanigosaMoveTimer) + { + if (m_uiCyanigosaMoveTimer <= uiDiff) + { + if (Creature* pCyanigosa = m_creature->GetMap()->GetCreature(m_cyanigosaGuid)) + pCyanigosa->GetMotionMaster()->MoveJump(afPortalLocation[8].fX, afPortalLocation[8].fY, afPortalLocation[8].fZ, pCyanigosa->GetSpeed(MOVE_RUN) * 2, 10.0f); + + m_uiCyanigosaMoveTimer = 0; + } + else + m_uiCyanigosaMoveTimer -= uiDiff; + } + } +}; + +CreatureAI* GetAI_npc_teleportation_portal(Creature* pCreature) +{ + return new npc_teleportation_portalAI(pCreature); +} + +bool EffectDummyCreature_npc_teleportation_portal(Unit* /*pCaster*/, uint32 uiSpellId, SpellEffectIndex uiEffIndex, Creature* pCreatureTarget, ObjectGuid /*originalCasterGuid*/) +{ + // always check spellid and effectindex + if (uiSpellId == SPELL_PORTAL_PERIODIC && uiEffIndex == EFFECT_INDEX_0) + { + if (npc_teleportation_portalAI* pPortalAI = dynamic_cast(pCreatureTarget->AI())) + pPortalAI->DoSummon(); + + // always return true when we are handling this spell and effect + return true; + } + + return false; +} + +void AddSC_violet_hold() +{ + Script* pNewScript; + + pNewScript = new Script; + pNewScript->Name = "go_activation_crystal"; + pNewScript->pGOUse = &GOUse_go_activation_crystal; + pNewScript->RegisterSelf(); + + pNewScript = new Script; + pNewScript->Name = "npc_door_seal"; + pNewScript->pEffectDummyNPC = &EffectDummyCreature_npc_door_seal; + pNewScript->RegisterSelf(); + + pNewScript = new Script; + pNewScript->Name = "npc_sinclari"; + pNewScript->GetAI = &GetAI_npc_sinclari; + pNewScript->pGossipHello = &GossipHello_npc_sinclari; + pNewScript->pGossipSelect = &GossipSelect_npc_sinclari; + pNewScript->RegisterSelf(); + + pNewScript = new Script; + pNewScript->Name = "npc_prison_event_controller"; + pNewScript->GetAI = &GetAI_npc_prison_event_controller; + pNewScript->RegisterSelf(); + + pNewScript = new Script; + pNewScript->Name = "npc_teleportation_portal"; + pNewScript->GetAI = &GetAI_npc_teleportation_portal; + pNewScript->pEffectDummyNPC = &EffectDummyCreature_npc_teleportation_portal; + pNewScript->RegisterSelf(); +} diff --git a/src/modules/SD2/scripts/northrend/violet_hold/violet_hold.h b/src/modules/SD2/scripts/northrend/violet_hold/violet_hold.h new file mode 100644 index 000000000..afeb057a1 --- /dev/null +++ b/src/modules/SD2/scripts/northrend/violet_hold/violet_hold.h @@ -0,0 +1,300 @@ +/* Copyright (C) 2006 - 2013 ScriptDev2 + * This program is free software licensed under GPL version 2 + * Please see the included DOCS/LICENSE.TXT for more information */ + +#ifndef DEF_VIOLET_H +#define DEF_VIOLET_H + +enum +{ + MAX_ENCOUNTER = 10, + MAX_MINIBOSSES = 6, + + TYPE_MAIN = 0, + TYPE_SEAL = 1, + TYPE_PORTAL = 2, + TYPE_LAVANTHOR = 3, + TYPE_MORAGG = 4, + TYPE_EREKEM = 5, + TYPE_ICHORON = 6, + TYPE_XEVOZZ = 7, + TYPE_ZURAMAT = 8, + TYPE_CYANIGOSA = 9, + + WORLD_STATE_ID = 3816, + WORLD_STATE_SEAL = 3815, + WORLD_STATE_PORTALS = 3810, + + GO_INTRO_CRYSTAL = 193615, + GO_PRISON_CRYSTAL = 193611, + GO_PRISON_SEAL_DOOR = 191723, + + GO_CELL_LAVANTHOR = 191566, + GO_CELL_MORAGG = 191606, + GO_CELL_ZURAMAT = 191565, + GO_CELL_EREKEM = 191564, + GO_CELL_EREKEM_GUARD_L = 191563, + GO_CELL_EREKEM_GUARD_R = 191562, + GO_CELL_XEVOZZ = 191556, + GO_CELL_ICHORON = 191722, + + NPC_EVENT_CONTROLLER = 30883, + NPC_PORTAL_INTRO = 31011, + NPC_PORTAL = 30679, + NPC_PORTAL_ELITE = 32174, + NPC_DOOR_SEAL = 30896, + + NPC_SINCLARI = 30658, + NPC_SINCLARI_ALT = 32204, // yeller for seal weakening and summoner for portals + NPC_HOLD_GUARD = 30659, + + NPC_EREKEM = 29315, + NPC_EREKEM_GUARD = 29395, + NPC_MORAGG = 29316, + NPC_ICHORON = 29313, + NPC_XEVOZZ = 29266, + NPC_LAVANTHOR = 29312, + NPC_ZURAMAT = 29314, + NPC_CYANIGOSA = 31134, + + NPC_PORTAL_GUARDIAN = 30660, + NPC_PORTAL_KEEPER = 30695, + + NPC_AZURE_INVADER = 30661, + NPC_AZURE_SPELLBREAKER = 30662, + NPC_AZURE_BINDER = 30663, + NPC_AZURE_MAGE_SLAYER = 30664, + NPC_MAGE_HUNTER = 30665, + NPC_AZURE_CAPTAIN = 30666, + NPC_AZURE_SORCEROR = 30667, + NPC_AZURE_RAIDER = 30668, + NPC_AZURE_STALKER = 32191, + + NPC_VOID_SENTRY = 29364, // Npc checked for Zuramat achiev + NPC_ICHORON_SUMMON_TARGET = 29326, // Npc which summons the Ichoron globules + + // used for intro + NPC_AZURE_BINDER_INTRO = 31007, + NPC_AZURE_INVADER_INTRO = 31008, + NPC_AZURE_SPELLBREAKER_INTRO = 31009, + NPC_AZURE_MAGE_SLAYER_INTRO = 31010, + + NPC_AZURE_SABOTEUR = 31079, + + NPC_DEFENSE_SYSTEM = 30837, + NPC_DEFENSE_DUMMY_TARGET = 30857, + + // 'Ghosts' for Killed mobs after Wipe + NPC_ARAKKOA = 32226, + NPC_ARAKKOA_GUARD = 32228, + NPC_VOID_LORD = 32230, + NPC_ETHERAL = 32231, + NPC_SWIRLING = 32234, + NPC_WATCHER = 32235, + NPC_LAVA_HOUND = 32237, + + SPELL_DEFENSE_SYSTEM_VISUAL = 57887, + SPELL_DEFENSE_SYSTEM_SPAWN = 57886, + SPELL_LIGHTNING_INTRO = 60038, // intro kill spells, also related to spell 58152 + SPELL_ARCANE_LIGHTNING = 57930, // damage spells, related to spell 57912 + + SPELL_DESTROY_DOOR_SEAL = 58040, // spell periodic cast by misc + SPELL_TELEPORTATION_PORTAL = 57687, // visual aura, but possibly not used? creature_template model for portals are same + + SPELL_SHIELD_DISRUPTION = 58291, // dummy when opening a cell + SPELL_SIMPLE_TELEPORT = 12980, // used after a cell has been opened - not sure if the id is correct + + SPELL_PORTAL_PERIODIC = 58008, // most likely the tick for each summon (tick each 15 seconds) + SPELL_PORTAL_CHANNEL = 58012, // the blue "stream" between portal and guardian/keeper + SPELL_PORTAL_BEAM = 56046, // large beam, unsure if really used here (or possible for something different) + + SPELL_PORTAL_VISUAL_1 = 57872, // no idea, but is possibly related based on it's visual appearence + SPELL_PORTAL_VISUAL_2 = 57630, + + SAY_SEAL_75 = -1608002, + SAY_SEAL_50 = -1608003, + SAY_SEAL_5 = -1608004, + + SAY_RELEASE_EREKEM = -1608008, + SAY_RELEASE_ICHORON = -1608009, + SAY_RELEASE_XEVOZZ = -1608010, + SAY_RELEASE_ZURAMAT = -1608011, + + EMOTE_GUARDIAN_PORTAL = -1608005, + EMOTE_DRAGONFLIGHT_PORTAL = -1608006, + EMOTE_KEEPER_PORTAL = -1608007, + + MAX_NORMAL_PORTAL = 8, + + ACHIEV_CRIT_DEFENSELES = 6803, // event achiev - 1816 + ACHIEV_CRIT_DEHYDRATATION = 7320, // Ichoron achiev - 2041 + ACHIEV_CRIT_VOID_DANCE = 7587, // Zuramat achiev - 2153 +}; + +static const float fDefenseSystemLoc[4] = {1888.146f, 803.382f, 58.604f, 3.072f}; +static const float fGuardExitLoc[3] = {1806.955f, 803.851f, 44.36f}; +static const float fSealAttackLoc[3] = {1858.027f, 804.11f, 44.008f}; + +static const uint32 aRandomPortalNpcs[5] = {NPC_AZURE_INVADER, NPC_MAGE_HUNTER, NPC_AZURE_SPELLBREAKER, NPC_AZURE_BINDER, NPC_AZURE_MAGE_SLAYER}; +static const uint32 aRandomIntroNpcs[4] = {NPC_AZURE_BINDER_INTRO, NPC_AZURE_INVADER_INTRO, NPC_AZURE_SPELLBREAKER_INTRO, NPC_AZURE_MAGE_SLAYER_INTRO}; + +static const int32 aSealWeakYell[3] = {SAY_SEAL_75, SAY_SEAL_50, SAY_SEAL_5}; + +enum ePortalType +{ + PORTAL_TYPE_NORM = 0, + PORTAL_TYPE_SQUAD, + PORTAL_TYPE_BOSS, +}; + +struct PortalData +{ + ePortalType pPortalType; + float fX, fY, fZ, fOrient; +}; + +static const PortalData afPortalLocation[] = +{ + {PORTAL_TYPE_NORM, 1936.07f, 803.198f, 53.3749f, 3.1241f}, // balcony + {PORTAL_TYPE_NORM, 1877.51f, 850.104f, 44.6599f, 4.7822f}, // erekem + {PORTAL_TYPE_NORM, 1890.64f, 753.471f, 48.7224f, 1.7104f}, // moragg + {PORTAL_TYPE_SQUAD, 1911.06f, 802.103f, 38.6465f, 2.8908f}, // below balcony + {PORTAL_TYPE_SQUAD, 1928.06f, 763.256f, 51.3167f, 2.3905f}, // bridge + {PORTAL_TYPE_SQUAD, 1924.26f, 847.661f, 47.1591f, 4.0202f}, // zuramat + {PORTAL_TYPE_NORM, 1914.16f, 832.527f, 38.6441f, 3.5160f}, // xevozz + {PORTAL_TYPE_NORM, 1857.30f, 764.145f, 38.6543f, 0.8339f}, // lavanthor + {PORTAL_TYPE_BOSS, 1890.73f, 803.309f, 38.4001f, 2.4139f}, // center +}; + +struct BossInformation +{ + uint32 uiType, uiEntry, uiGhostEntry, uiWayPointId; + float fX, fY, fZ; // Waypoint for Saboteur + int32 iSayEntry; +}; + +struct BossSpawn +{ + uint32 uiEntry; + float fX, fY, fZ, fO; +}; + +static const BossInformation aBossInformation[] = +{ + {TYPE_EREKEM, NPC_EREKEM, NPC_ARAKKOA, 1, 1877.03f, 853.84f, 43.33f, SAY_RELEASE_EREKEM}, + {TYPE_ZURAMAT, NPC_ZURAMAT, NPC_VOID_LORD, 1, 1922.41f, 847.95f, 47.15f, SAY_RELEASE_ZURAMAT}, + {TYPE_XEVOZZ, NPC_XEVOZZ, NPC_ETHERAL, 1, 1903.61f, 838.46f, 38.72f, SAY_RELEASE_XEVOZZ}, + {TYPE_ICHORON, NPC_ICHORON, NPC_SWIRLING, 1, 1915.52f, 779.13f, 35.94f, SAY_RELEASE_ICHORON}, + {TYPE_LAVANTHOR, NPC_LAVANTHOR, NPC_LAVA_HOUND, 1, 1855.28f, 760.85f, 38.65f, 0}, + {TYPE_MORAGG, NPC_MORAGG, NPC_WATCHER, 1, 1890.51f, 752.85f, 47.66f, 0} +}; + +class instance_violet_hold : public ScriptedInstance +{ + public: + instance_violet_hold(Map* pMap); + ~instance_violet_hold(); // Destructor used to free m_vRandomBosses + + void Initialize() override; + + void OnCreatureCreate(Creature* pCreature) override; + void OnObjectCreate(GameObject* pGo) override; + + void UpdateCellForBoss(uint32 uiBossEntry, bool bForceClosing = false); + + void SetIntroPortals(bool bDeactivate); + + void CallGuards(bool bRespawn); + + uint32 GetRandomPortalEliteEntry() { return (urand(0, 1) ? NPC_PORTAL_GUARDIAN : NPC_PORTAL_KEEPER); } + uint32 GetRandomMobForNormalPortal() { return aRandomPortalNpcs[urand(0, 4)]; } + uint32 GetRandomMobForIntroPortal() { return aRandomIntroNpcs[urand(0, 3)]; } + + uint32 GetCurrentPortalNumber() { return m_uiWorldStatePortalCount; } + + BossInformation const* GetBossInformation(uint32 uiEntry = 0); + + bool IsCurrentPortalForTrash() + { + if (m_uiWorldStatePortalCount % MAX_MINIBOSSES) + return true; + + return false; + } + + void ProcessActivationCrystal(Unit* pUser, bool bIsIntro = false); + + void GetErekemGuardList(GuidList& lGuardList) { lGuardList = GetData(TYPE_EREKEM) != DONE ? m_lErekemGuardList : m_lArakkoaGuardList; } + void GetIchoronTriggerList(GuidList& lList) { lList = m_lIchoronTargetsList; } + + void OnPlayerEnter(Player* pPlayer) override; + + void OnCreatureEnterCombat(Creature* pCreature) override; + void OnCreatureEvade(Creature* pCreature); + void OnCreatureDeath(Creature* pCreature) override; + + void SetData(uint32 uiType, uint32 uiData) override; + uint32 GetData(uint32 uiType) const override; + + bool CheckAchievementCriteriaMeet(uint32 uiCriteriaId, Player const* pSource, Unit const* pTarget, uint32 uiMiscValue1 /* = 0*/) const override; + + const char* Save() const override { return m_strInstData.c_str(); } + void Load(const char* chrIn) override; + + void Update(uint32 uiDiff) override; + + typedef std::multimap BossToCellMap; + + protected: + PortalData const* GetPortalData() { return &afPortalLocation[m_uiPortalId]; } + + void UpdateWorldState(bool bEnable = true); + + void SetRandomBosses(); + + void SpawnPortal(); + void SetPortalId(); + + void ResetAll(); + void ResetVariables(); + + bool IsNextPortalForTrash() + { + if ((m_uiWorldStatePortalCount + 1) % MAX_MINIBOSSES) + return true; + + return false; + } + + BossSpawn* CreateBossSpawnByEntry(uint32 uiEntry); + uint32 m_auiEncounter[MAX_ENCOUNTER]; + std::string m_strInstData; + + uint32 m_uiWorldState; + uint32 m_uiWorldStateSealCount; + uint32 m_uiWorldStatePortalCount; + + uint8 m_uiPortalId; + uint32 m_uiPortalTimer; + uint32 m_uiMaxCountPortalLoc; + + uint32 m_uiSealYellCount; + uint32 m_uiEventResetTimer; + + bool m_bIsVoidDance; + bool m_bIsDefenseless; + bool m_bIsDehydratation; + + BossToCellMap m_mBossToCellMap; + + GuidList m_lIntroPortalList; + GuidList m_lGuardsList; + GuidList m_lErekemGuardList; + GuidList m_lArakkoaGuardList; + GuidList m_lIchoronTargetsList; + std::vector m_vRandomBossList; + + std::vector m_vRandomBosses; +}; + +#endif diff --git a/src/modules/SD2/scripts/northrend/zuldrak.cpp b/src/modules/SD2/scripts/northrend/zuldrak.cpp new file mode 100644 index 000000000..088b039da --- /dev/null +++ b/src/modules/SD2/scripts/northrend/zuldrak.cpp @@ -0,0 +1,106 @@ +/* Copyright (C) 2006 - 2013 ScriptDev2 + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +/* ScriptData +SDName: Zuldrak +SD%Complete: 100 +SDComment: Quest support: 12934. +SDCategory: Zuldrak +EndScriptData */ + +/* ContentData +npc_gurgthock +EndContentData */ + +#include "precompiled.h" + +/*###### +## npc_gurgthock +######*/ + +enum +{ + QUEST_FROM_BEYOND = 12934, + + NPC_AZBARIN = 30026, + NPC_DUKE_SINGEN = 30019, + NPC_ERATHIUS = 30025, + NPC_GARGORAL = 30024 +}; + +static float m_afSpawnLocation[] = {5768.71f, -2969.29f, 273.816f}; +static uint32 m_auiBosses[] = {NPC_AZBARIN, NPC_DUKE_SINGEN, NPC_ERATHIUS, NPC_GARGORAL}; + +struct npc_gurgthockAI : public ScriptedAI +{ + npc_gurgthockAI(Creature* pCreature) : ScriptedAI(pCreature) { Reset(); } + + ObjectGuid m_playerGuid; + + void SetPlayer(Player* pPlayer) + { + m_playerGuid = pPlayer->GetObjectGuid(); + } + + void Reset() override + { + m_playerGuid.Clear(); + } + + void SummonedCreatureJustDied(Creature* pSummoned) override + { + uint32 uiEntry = pSummoned->GetEntry(); + for (uint8 i = 0; i < 4; ++i) + { + if (uiEntry == m_auiBosses[i]) + { + if (Player* pPlayer = m_creature->GetMap()->GetPlayer(m_playerGuid)) + pPlayer->GroupEventHappens(QUEST_FROM_BEYOND, m_creature); + + m_playerGuid.Clear(); + return; + } + } + } +}; + +bool QuestAccept_npc_gurgthock(Player* pPlayer, Creature* pCreature, const Quest* pQuest) +{ + if (pQuest->GetQuestId() == QUEST_FROM_BEYOND) + { + pCreature->SummonCreature(m_auiBosses[urand(0, 3)], m_afSpawnLocation[0], m_afSpawnLocation[1], m_afSpawnLocation[2], 0.0f, TEMPSUMMON_TIMED_OOC_OR_CORPSE_DESPAWN, 600000); + + if (npc_gurgthockAI* pGurthockAI = dynamic_cast(pCreature->AI())) + pGurthockAI->SetPlayer(pPlayer); + } + return true; +} + +CreatureAI* GetAI_npc_gurgthock(Creature* pCreature) +{ + return new npc_gurgthockAI(pCreature); +} + +void AddSC_zuldrak() +{ + Script* pNewScript; + + pNewScript = new Script; + pNewScript->Name = "npc_gurgthock"; + pNewScript->GetAI = &GetAI_npc_gurgthock; + pNewScript->pQuestAcceptNPC = &QuestAccept_npc_gurgthock; + pNewScript->RegisterSelf(); +} diff --git a/src/modules/SD2/scripts/outland/auchindoun/auchenai_crypts/boss_exarch_maladaar.cpp b/src/modules/SD2/scripts/outland/auchindoun/auchenai_crypts/boss_exarch_maladaar.cpp new file mode 100644 index 000000000..6f53aed4d --- /dev/null +++ b/src/modules/SD2/scripts/outland/auchindoun/auchenai_crypts/boss_exarch_maladaar.cpp @@ -0,0 +1,323 @@ +/* Copyright (C) 2006 - 2013 ScriptDev2 + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with 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, 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" + +enum +{ + SPELL_STOLEN_SOUL_DISPEL = 33326, + + SPELL_MOONFIRE = 37328, + SPELL_FIREBALL = 37329, + SPELL_MIND_FLAY = 37330, + SPELL_HEMORRHAGE = 37331, + SPELL_FROSTSHOCK = 37332, + SPELL_CURSE_OF_AGONY = 37334, + SPELL_MORTAL_STRIKE = 37335, + SPELL_FREEZING_TRAP = 37368, + SPELL_HAMMER_OF_JUSTICE = 37369, + SPELL_PLAGUE_STRIKE = 58339 +}; + +struct mob_stolen_soulAI : public ScriptedAI +{ + mob_stolen_soulAI(Creature* pCreature) : ScriptedAI(pCreature) {Reset();} + + uint8 m_uiStolenClass; + uint32 m_uiSpellTimer; + + ObjectGuid m_targetGuid; + + void Reset() override + { + m_uiSpellTimer = 1000; + } + + void SetSoulInfo(Unit* pTarget) + { + m_uiStolenClass = pTarget->getClass(); + m_targetGuid = pTarget->GetObjectGuid(); + m_creature->SetDisplayId(pTarget->GetDisplayId()); + } + + void JustDied(Unit* /*pKiller*/) override + { + if (Unit* pTarget = m_creature->GetMap()->GetUnit(m_targetGuid)) + DoCastSpellIfCan(pTarget, SPELL_STOLEN_SOUL_DISPEL, CAST_TRIGGERED); + } + + void UpdateAI(const uint32 uiDiff) override + { + if (!m_creature->SelectHostileTarget() || !m_creature->getVictim()) + return; + + if (m_uiSpellTimer < uiDiff) + { + switch (m_uiStolenClass) + { + case CLASS_WARRIOR: + DoCastSpellIfCan(m_creature->getVictim(), SPELL_MORTAL_STRIKE); + m_uiSpellTimer = 6000; + break; + case CLASS_PALADIN: + DoCastSpellIfCan(m_creature->getVictim(), SPELL_HAMMER_OF_JUSTICE); + m_uiSpellTimer = 6000; + break; + case CLASS_HUNTER: + DoCastSpellIfCan(m_creature->getVictim(), SPELL_FREEZING_TRAP); + m_uiSpellTimer = 20000; + break; + case CLASS_ROGUE: + DoCastSpellIfCan(m_creature->getVictim(), SPELL_HEMORRHAGE); + m_uiSpellTimer = 10000; + break; + case CLASS_PRIEST: + DoCastSpellIfCan(m_creature->getVictim(), SPELL_MIND_FLAY); + m_uiSpellTimer = 5000; + break; + case CLASS_SHAMAN: + DoCastSpellIfCan(m_creature->getVictim(), SPELL_FROSTSHOCK); + m_uiSpellTimer = 8000; + break; + case CLASS_MAGE: + DoCastSpellIfCan(m_creature->getVictim(), SPELL_FIREBALL); + m_uiSpellTimer = 5000; + break; + case CLASS_WARLOCK: + DoCastSpellIfCan(m_creature->getVictim(), SPELL_CURSE_OF_AGONY); + m_uiSpellTimer = 20000; + break; + case CLASS_DRUID: + DoCastSpellIfCan(m_creature->getVictim(), SPELL_MOONFIRE); + m_uiSpellTimer = 10000; + break; + case CLASS_DEATH_KNIGHT: + DoCastSpellIfCan(m_creature->getVictim(), SPELL_PLAGUE_STRIKE); + m_uiSpellTimer = 10000; + break; + } + } + else + m_uiSpellTimer -= uiDiff; + + DoMeleeAttackIfReady(); + } +}; + +CreatureAI* GetAI_mob_stolen_soul(Creature* pCreature) +{ + return new mob_stolen_soulAI(pCreature); +} + +enum +{ + SAY_INTRO = -1558000, + SAY_SUMMON = -1558001, + SAY_AGGRO_1 = -1558002, + SAY_AGGRO_2 = -1558003, + SAY_AGGRO_3 = -1558004, + SAY_ROAR = -1558005, + SAY_SOUL_CLEAVE = -1558006, + SAY_SLAY_1 = -1558007, + SAY_SLAY_2 = -1558008, + SAY_DEATH = -1558009, + + SPELL_RIBBON_OF_SOULS = 32422, + SPELL_SOUL_SCREAM = 32421, + SPELL_STOLEN_SOUL = 32346, + SPELL_STOLEN_SOUL_VISUAL = 32395, + SPELL_SUMMON_AVATAR = 32424, + + NPC_STOLEN_SOUL = 18441, + NPC_DORE = 19412 +}; + +struct boss_exarch_maladaarAI : public ScriptedAI +{ + boss_exarch_maladaarAI(Creature* pCreature) : ScriptedAI(pCreature) + { + m_bHasTaunted = false; + Reset(); + } + + ObjectGuid m_targetGuid; + + uint32 m_uiFearTimer; + uint32 m_uiRibbonOfSoulsTimer; + uint32 m_uiStolenSoulTimer; + + bool m_bHasTaunted; + bool m_bHasSummonedAvatar; + + void Reset() override + { + m_targetGuid.Clear(); + + m_uiFearTimer = urand(11000, 29000); + m_uiRibbonOfSoulsTimer = urand(4000, 8000); + m_uiStolenSoulTimer = urand(19000, 31000); + + m_bHasSummonedAvatar = false; + } + + void MoveInLineOfSight(Unit* pWho) override + { + if (!m_bHasTaunted && pWho->GetTypeId() == TYPEID_PLAYER && m_creature->IsWithinDistInMap(pWho, 150.0f) && m_creature->IsWithinLOSInMap(pWho)) + { + DoScriptText(SAY_INTRO, m_creature); + m_bHasTaunted = true; + } + + ScriptedAI::MoveInLineOfSight(pWho); + } + + void Aggro(Unit* /*pWho*/) override + { + switch (urand(0, 2)) + { + case 0: DoScriptText(SAY_AGGRO_1, m_creature); break; + case 1: DoScriptText(SAY_AGGRO_2, m_creature); break; + case 2: DoScriptText(SAY_AGGRO_3, m_creature); break; + } + } + + void JustSummoned(Creature* pSummoned) override + { + if (pSummoned->GetEntry() == NPC_STOLEN_SOUL) + { + // SPELL_STOLEN_SOUL_VISUAL has shapeshift effect, but not implemented feature in mangos for this spell. + pSummoned->CastSpell(pSummoned, SPELL_STOLEN_SOUL_VISUAL, false); + + if (Player* pTarget = m_creature->GetMap()->GetPlayer(m_targetGuid)) + { + if (mob_stolen_soulAI* pSoulAI = dynamic_cast(pSummoned->AI())) + pSoulAI->SetSoulInfo(pTarget); + + pSummoned->AI()->AttackStart(pTarget); + } + } + } + + void KilledUnit(Unit* /*pVictim*/) override + { + if (urand(0, 1)) + return; + + DoScriptText(urand(0, 1) ? SAY_SLAY_1 : SAY_SLAY_2, m_creature); + } + + void JustDied(Unit* /*pKiller*/) override + { + DoScriptText(SAY_DEATH, m_creature); + + // When Exarch Maladaar is defeated D'ore appear. + DoSpawnCreature(NPC_DORE, 0.0f, 0.0f, 0.0f, 0.0f, TEMPSUMMON_TIMED_DESPAWN, 600000); + } + + void SpellHitTarget(Unit* pTarget, const SpellEntry* pSpell) override + { + if (pSpell->Id == SPELL_STOLEN_SOUL && pTarget->GetTypeId() == TYPEID_PLAYER) + DoSpawnCreature(NPC_STOLEN_SOUL, 0.0f, 0.0f, 0.0f, 0.0f, TEMPSUMMON_TIMED_OOC_DESPAWN, 10000); + } + + void UpdateAI(const uint32 uiDiff) override + { + if (!m_creature->SelectHostileTarget() || !m_creature->getVictim()) + return; + + if (!m_bHasSummonedAvatar && m_creature->GetHealthPercent() < 25.0f) + { + if (DoCastSpellIfCan(m_creature, SPELL_SUMMON_AVATAR) == CAST_OK) + { + DoScriptText(SAY_SUMMON, m_creature); + m_bHasSummonedAvatar = true; + m_uiStolenSoulTimer = urand(15000, 30000); + } + } + + if (m_uiStolenSoulTimer < uiDiff) + { + if (Unit* pTarget = m_creature->SelectAttackingTarget(ATTACKING_TARGET_RANDOM, 0, SPELL_STOLEN_SOUL, SELECT_FLAG_PLAYER)) + { + if (DoCastSpellIfCan(pTarget, SPELL_STOLEN_SOUL) == CAST_OK) + { + if (urand(0, 1)) + DoScriptText(urand(0, 1) ? SAY_ROAR : SAY_SOUL_CLEAVE, m_creature); + + m_targetGuid = pTarget->GetObjectGuid(); + + m_uiStolenSoulTimer = urand(35000, 67000); + } + } + } + else + m_uiStolenSoulTimer -= uiDiff; + + if (m_uiRibbonOfSoulsTimer < uiDiff) + { + if (Unit* pTarget = m_creature->SelectAttackingTarget(ATTACKING_TARGET_RANDOM, 0)) + { + if (DoCastSpellIfCan(pTarget, SPELL_RIBBON_OF_SOULS) == CAST_OK) + m_uiRibbonOfSoulsTimer = urand(4000, 18000); + } + } + else + m_uiRibbonOfSoulsTimer -= uiDiff; + + if (m_uiFearTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature, SPELL_SOUL_SCREAM) == CAST_OK) + m_uiFearTimer = urand(13000, 30000); + } + else + m_uiFearTimer -= uiDiff; + + DoMeleeAttackIfReady(); + } +}; + +CreatureAI* GetAI_boss_exarch_maladaar(Creature* pCreature) +{ + return new boss_exarch_maladaarAI(pCreature); +} + +void AddSC_boss_exarch_maladaar() +{ + Script* pNewScript; + + pNewScript = new Script; + pNewScript->Name = "boss_exarch_maladaar"; + pNewScript->GetAI = &GetAI_boss_exarch_maladaar; + pNewScript->RegisterSelf(); + + pNewScript = new Script; + pNewScript->Name = "mob_stolen_soul"; + pNewScript->GetAI = &GetAI_mob_stolen_soul; + pNewScript->RegisterSelf(); +} diff --git a/src/modules/SD2/scripts/outland/auchindoun/auchenai_crypts/boss_shirrak.cpp b/src/modules/SD2/scripts/outland/auchindoun/auchenai_crypts/boss_shirrak.cpp new file mode 100644 index 000000000..da8649392 --- /dev/null +++ b/src/modules/SD2/scripts/outland/auchindoun/auchenai_crypts/boss_shirrak.cpp @@ -0,0 +1,152 @@ +/* Copyright (C) 2006 - 2013 ScriptDev2 + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +/* ScriptData +SDName: boss_shirrak +SD%Complete: 100 +SDComment: +SDCategory: Auchindoun, Auchenai Crypts +EndScriptData */ + +#include "precompiled.h" + +enum +{ + EMOTE_FOCUS = -1558010, + + SPELL_CARNIVOROUS_BITE = 36383, + SPELL_CARNIVOROUS_BITE_H = 39382, + SPELL_INHIBIT_MAGIC = 32264, + SPELL_ATTRACT_MAGIC = 32265, + + SPELL_FOCUS_TARGET_VISUAL = 32286, + NPC_FOCUS_FIRE = 18374 +}; + +struct boss_shirrakAI : public ScriptedAI +{ + boss_shirrakAI(Creature* pCreature) : ScriptedAI(pCreature) + { + m_bIsRegularMode = pCreature->GetMap()->IsRegularDifficulty(); + Reset(); + } + + bool m_bIsRegularMode; + + uint32 m_uiCarnivorousBiteTimer; + uint32 m_uiFocusFireTimer; + uint32 m_uiAttractMagicTimer; + + uint8 m_uiFocusFireCount; + + ObjectGuid m_focusTargetGuid; + + void Reset() override + { + m_uiCarnivorousBiteTimer = urand(4000, 7000); + m_uiFocusFireTimer = 15000; + m_uiAttractMagicTimer = urand(20000, 24000); + m_uiFocusFireCount = 0; + + DoCastSpellIfCan(m_creature, SPELL_INHIBIT_MAGIC); + } + + void JustSummoned(Creature* pSummoned) override + { + // The focus fire creature casts the focus fire visual + if (pSummoned->GetEntry() == NPC_FOCUS_FIRE) + pSummoned->CastSpell(pSummoned, SPELL_FOCUS_TARGET_VISUAL, true); + } + + void UpdateAI(const uint32 uiDiff) override + { + if (!m_creature->SelectHostileTarget() || !m_creature->getVictim()) + return; + + if (m_uiCarnivorousBiteTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature, m_bIsRegularMode ? SPELL_CARNIVOROUS_BITE : SPELL_CARNIVOROUS_BITE_H) == CAST_OK) + m_uiCarnivorousBiteTimer = urand(4000, 10000); + } + else + m_uiCarnivorousBiteTimer -= uiDiff; + + if (m_uiAttractMagicTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature, SPELL_ATTRACT_MAGIC) == CAST_OK) + m_uiAttractMagicTimer = urand(25000, 38000); + } + else + m_uiAttractMagicTimer -= uiDiff; + + if (m_uiFocusFireTimer < uiDiff) + { + ++m_uiFocusFireCount; + Unit* pTarget = NULL; + + switch (m_uiFocusFireCount) + { + case 1: + { + // engage the target + pTarget = m_creature->SelectAttackingTarget(ATTACKING_TARGET_RANDOM, 1, uint32(0), SELECT_FLAG_PLAYER); + + if (!pTarget) + pTarget = m_creature->getVictim(); + + DoScriptText(EMOTE_FOCUS, m_creature, pTarget); + m_focusTargetGuid = pTarget->GetObjectGuid(); + // no break; + } + case 2: + // we have a delay of 1 sec between the summons + m_uiFocusFireTimer = 1000; + break; + case 3: + // reset the timers and the summon count + m_uiFocusFireCount = 0; + m_uiFocusFireTimer = 15000; + break; + } + + if (!pTarget) + pTarget = m_creature->GetMap()->GetUnit(m_focusTargetGuid); + + // Summon focus fire at target location + if (pTarget) + m_creature->SummonCreature(NPC_FOCUS_FIRE, pTarget->GetPositionX(), pTarget->GetPositionY(), pTarget->GetPositionZ(), 0, TEMPSUMMON_TIMED_DESPAWN, 10000); + } + else + m_uiFocusFireTimer -= uiDiff; + + DoMeleeAttackIfReady(); + } +}; + +CreatureAI* GetAI_boss_shirrak(Creature* pCreature) +{ + return new boss_shirrakAI(pCreature); +} + +void AddSC_boss_shirrak() +{ + Script* pNewScript; + + pNewScript = new Script; + pNewScript->Name = "boss_shirrak"; + pNewScript->GetAI = &GetAI_boss_shirrak; + pNewScript->RegisterSelf(); +} diff --git a/src/modules/SD2/scripts/outland/auchindoun/mana_tombs/boss_nexusprince_shaffar.cpp b/src/modules/SD2/scripts/outland/auchindoun/mana_tombs/boss_nexusprince_shaffar.cpp new file mode 100644 index 000000000..f1a75cf08 --- /dev/null +++ b/src/modules/SD2/scripts/outland/auchindoun/mana_tombs/boss_nexusprince_shaffar.cpp @@ -0,0 +1,187 @@ +/* Copyright (C) 2006 - 2013 ScriptDev2 + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with 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: 100 +SDComment: ToDo: move the Ethereal Beacon script to eventAI +SDCategory: Auchindoun, Mana Tombs +EndScriptData */ + +/* ContentData +boss_nexusprince_shaffar +mob_ethereal_beacon +EndContentData */ + +#include "precompiled.h" + +enum +{ + SAY_INTRO = -1557000, + SAY_AGGRO_1 = -1557001, + SAY_AGGRO_2 = -1557002, + SAY_AGGRO_3 = -1557003, + SAY_SLAY_1 = -1557004, + 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 18431 + // SPELL_ETHEREAL_BEACON_VISUAL = 32368, // included in creature_template_addon +}; + +struct boss_nexusprince_shaffarAI : public ScriptedAI +{ + boss_nexusprince_shaffarAI(Creature* pCreature) : ScriptedAI(pCreature) + { + m_bHasTaunted = false; + Reset(); + } + + uint32 m_uiBlinkTimer; + uint32 m_uiBeaconTimer; + uint32 m_uiFireBallTimer; + uint32 m_uiFrostboltTimer; + uint32 m_uiFrostNovaTimer; + + bool m_bHasTaunted; + + void Reset() override + { + m_uiBlinkTimer = 30000; + m_uiBeaconTimer = urand(12000, 15000); + m_uiFireBallTimer = urand(2000, 12000); + m_uiFrostboltTimer = urand(1000, 14000); + m_uiFrostNovaTimer = urand(18000, 25000); + } + + void MoveInLineOfSight(Unit* pWho) override + { + if (!m_bHasTaunted && pWho->GetTypeId() == TYPEID_PLAYER && m_creature->IsWithinDistInMap(pWho, 100.0f) && m_creature->IsWithinLOSInMap(pWho)) + { + DoScriptText(SAY_INTRO, m_creature); + m_bHasTaunted = true; + } + + ScriptedAI::MoveInLineOfSight(pWho); + } + + void Aggro(Unit* /*pWho*/) override + { + switch (urand(0, 2)) + { + case 0: DoScriptText(SAY_AGGRO_1, m_creature); break; + case 1: DoScriptText(SAY_AGGRO_2, m_creature); break; + case 2: DoScriptText(SAY_AGGRO_3, m_creature); break; + } + } + + void JustSummoned(Creature* pSummoned) override + { + if (Unit* pTarget = m_creature->SelectAttackingTarget(ATTACKING_TARGET_RANDOM, 0)) + pSummoned->AI()->AttackStart(pTarget); + } + + void KilledUnit(Unit* /*pVictim*/) override + { + DoScriptText(urand(0, 1) ? SAY_SLAY_1 : SAY_SLAY_2, m_creature); + } + + void JustDied(Unit* /*pKiller*/) override + { + DoScriptText(SAY_DEAD, m_creature); + } + + void UpdateAI(const uint32 uiDiff) override + { + if (!m_creature->SelectHostileTarget() || !m_creature->getVictim()) + return; + + if (m_uiFrostNovaTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature, SPELL_FROSTNOVA) == CAST_OK) + m_uiFrostNovaTimer = urand(10000, 20000); + } + else + m_uiFrostNovaTimer -= uiDiff; + + if (m_uiFrostboltTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature->getVictim(), SPELL_FROSTBOLT) == CAST_OK) + m_uiFrostboltTimer = urand(3000, 8000); + } + else + m_uiFrostboltTimer -= uiDiff; + + if (m_uiFireBallTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature->getVictim(), SPELL_FIREBALL) == CAST_OK) + m_uiFireBallTimer = urand(3000, 8000); + } + else + m_uiFireBallTimer -= uiDiff; + + if (m_uiBlinkTimer <= uiDiff) + { + if (DoCastSpellIfCan(m_creature, SPELL_BLINK) == CAST_OK) + { + // 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() == CHASE_MOTION_TYPE) + m_creature->GetMotionMaster()->MovementExpired(); + + m_uiBlinkTimer = urand(25000, 30000); + } + } + else + m_uiBlinkTimer -= uiDiff; + + if (m_uiBeaconTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature, SPELL_ETHEREAL_BEACON) == CAST_OK) + { + if (!urand(0, 3)) + DoScriptText(SAY_SUMMON, m_creature); + + m_uiBeaconTimer = urand(45000, 75000); + } + } + else + m_uiBeaconTimer -= uiDiff; + + DoMeleeAttackIfReady(); + } +}; + +CreatureAI* GetAI_boss_nexusprince_shaffar(Creature* pCreature) +{ + return new boss_nexusprince_shaffarAI(pCreature); +} + +void AddSC_boss_nexusprince_shaffar() +{ + Script* pNewScript; + + pNewScript = new Script; + pNewScript->Name = "boss_nexusprince_shaffar"; + pNewScript->GetAI = &GetAI_boss_nexusprince_shaffar; + pNewScript->RegisterSelf(); +} diff --git a/src/modules/SD2/scripts/outland/auchindoun/mana_tombs/boss_pandemonius.cpp b/src/modules/SD2/scripts/outland/auchindoun/mana_tombs/boss_pandemonius.cpp new file mode 100644 index 000000000..e1c3f6cb4 --- /dev/null +++ b/src/modules/SD2/scripts/outland/auchindoun/mana_tombs/boss_pandemonius.cpp @@ -0,0 +1,143 @@ +/* Copyright (C) 2006 - 2013 ScriptDev2 + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with 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: 80 +SDComment: Not known how void blast is done (amount of rapid cast seems to be related to players in party). +SDCategory: Auchindoun, Mana Tombs +EndScriptData */ + +#include "precompiled.h" + +enum +{ + SAY_AGGRO_1 = -1557008, + SAY_AGGRO_2 = -1557009, + SAY_AGGRO_3 = -1557010, + SAY_KILL_1 = -1557011, + SAY_KILL_2 = -1557012, + SAY_DEATH = -1557013, + EMOTE_DARK_SHELL = -1557014, + + SPELL_VOID_BLAST = 32325, + SPELL_VOID_BLAST_H = 38760, + SPELL_DARK_SHELL = 32358, + SPELL_DARK_SHELL_H = 38759, + + MAX_VOID_BLASTS = 5, +}; + +struct boss_pandemoniusAI : public ScriptedAI +{ + boss_pandemoniusAI(Creature* pCreature) : ScriptedAI(pCreature) + { + m_bIsRegularMode = pCreature->GetMap()->IsRegularDifficulty(); + Reset(); + } + + bool m_bIsRegularMode; + + uint32 m_uiVoidBlastTimer; + uint32 m_uiDarkShellTimer; + uint8 m_uiVoidBlastCounter; + + void Reset() override + { + m_uiVoidBlastTimer = urand(15000, 20000); + m_uiDarkShellTimer = 15000; + m_uiVoidBlastCounter = 0; + } + + void JustDied(Unit* /*pKiller*/) override + { + DoScriptText(SAY_DEATH, m_creature); + } + + void KilledUnit(Unit* /*pVictim*/) override + { + DoScriptText(urand(0, 1) ? SAY_KILL_1 : SAY_KILL_2, m_creature); + } + + void Aggro(Unit* /*pWho*/) override + { + switch (urand(0, 2)) + { + case 0: DoScriptText(SAY_AGGRO_1, m_creature); break; + case 1: DoScriptText(SAY_AGGRO_2, m_creature); break; + case 2: DoScriptText(SAY_AGGRO_3, m_creature); break; + } + } + + void UpdateAI(const uint32 uiDiff) override + { + if (!m_creature->SelectHostileTarget() || !m_creature->getVictim()) + return; + + if (m_uiVoidBlastTimer < uiDiff) + { + if (Unit* pTarget = m_creature->SelectAttackingTarget(ATTACKING_TARGET_RANDOM, 0)) + DoCastSpellIfCan(pTarget, m_bIsRegularMode ? SPELL_VOID_BLAST : SPELL_VOID_BLAST_H); + + // reset timer and counter when counter has reached the max limit + if (m_uiVoidBlastCounter == MAX_VOID_BLASTS) + { + m_uiVoidBlastTimer = urand(25000, 30000); + m_uiVoidBlastCounter = 0; + } + // cast the void blasts in a row until we reach the max limit + else + { + m_uiVoidBlastTimer = 500; + ++m_uiVoidBlastCounter; + } + } + else + m_uiVoidBlastTimer -= uiDiff; + + // use the darkshell only when the boss isn't casting the void blasts + if (!m_uiVoidBlastCounter) + { + if (m_uiDarkShellTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature, m_bIsRegularMode ? SPELL_DARK_SHELL : SPELL_DARK_SHELL_H) == CAST_OK) + { + DoScriptText(EMOTE_DARK_SHELL, m_creature); + m_uiDarkShellTimer = urand(25000, 30000); + } + } + else + m_uiDarkShellTimer -= uiDiff; + } + + DoMeleeAttackIfReady(); + } +}; + +CreatureAI* GetAI_boss_pandemonius(Creature* pCreature) +{ + return new boss_pandemoniusAI(pCreature); +} + +void AddSC_boss_pandemonius() +{ + Script* pNewScript; + + pNewScript = new Script; + pNewScript->Name = "boss_pandemonius"; + pNewScript->GetAI = &GetAI_boss_pandemonius; + pNewScript->RegisterSelf(); +} diff --git a/src/modules/SD2/scripts/outland/auchindoun/sethekk_halls/boss_anzu.cpp b/src/modules/SD2/scripts/outland/auchindoun/sethekk_halls/boss_anzu.cpp new file mode 100644 index 000000000..e49bc58ff --- /dev/null +++ b/src/modules/SD2/scripts/outland/auchindoun/sethekk_halls/boss_anzu.cpp @@ -0,0 +1,236 @@ +/* Copyright (C) 2006 - 2013 ScriptDev2 + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +/* ScriptData +SDName: boss_anzu +SD%Complete: 70 +SDComment: Intro event NYI. +SDCategory: Auchindoun, Sethekk Halls +EndScriptData */ + +#include "precompiled.h" +#include "sethekk_halls.h" + +enum +{ + SAY_BANISH = -1556018, + SAY_WHISPER_MAGIC_1 = -1556019, + SAY_WHISPER_MAGIC_2 = -1556021, + SAY_WHISPER_MAGIC_3 = -1556022, + EMOTE_BIRD_STONE = -1556020, + + SPELL_FLESH_RIP = 40199, + SPELL_SCREECH = 40184, + SPELL_SPELL_BOMB = 40303, + SPELL_CYCLONE = 40321, + SPELL_BANISH_SELF = 42354, + + NPC_BROOD_OF_ANZU = 23132, + + // Helper birds + NPC_HAWK_SPIRIT = 23134, // casts 40237 + NPC_FALCON_SPIRIT = 23135, // casts 40241 + NPC_EAGLE_SPIRIT = 23136, // casts 40240 + + MAX_BROODS = 5, +}; + +static const uint32 aSpiritsEntries[3] = {NPC_FALCON_SPIRIT, NPC_HAWK_SPIRIT, NPC_EAGLE_SPIRIT}; + +struct boss_anzuAI : public ScriptedAI +{ + boss_anzuAI(Creature* pCreature) : ScriptedAI(pCreature) + { + m_pInstance = (instance_sethekk_halls*)pCreature->GetInstanceData(); + Reset(); + } + + instance_sethekk_halls* m_pInstance; + + uint32 m_uiFleshRipTimer; + uint32 m_uiScreechTimer; + uint32 m_uiSpellBombTimer; + uint32 m_uiCycloneTimer; + float m_fHealthCheck; + + GuidList m_lBirdsGuidList; + + void Reset() override + { + m_uiFleshRipTimer = urand(9000, 10000); + m_uiScreechTimer = 23000; + m_uiSpellBombTimer = 17000; + m_uiCycloneTimer = 5000; + m_fHealthCheck = 75.0f; + } + + void Aggro(Unit* /*pWho*/) override + { + // Note: this should be moved to the intro event when implemented! + DoSummonBirdHelpers(); + + if (m_pInstance) + m_pInstance->SetData(TYPE_ANZU, IN_PROGRESS); + } + + void JustDied(Unit* /*pKiller*/) override + { + DespawnBirdHelpers(); + + if (m_pInstance) + m_pInstance->SetData(TYPE_ANZU, DONE); + } + + void JustReachedHome() override + { + DespawnBirdHelpers(); + m_creature->ForcedDespawn(); + + if (m_pInstance) + m_pInstance->SetData(TYPE_ANZU, FAIL); + } + + void JustSummoned(Creature* pSummoned) override + { + if (pSummoned->GetEntry() == NPC_BROOD_OF_ANZU) + { + if (Unit* pTarget = m_creature->SelectAttackingTarget(ATTACKING_TARGET_RANDOM, 0)) + pSummoned->AI()->AttackStart(pTarget); + } + else + { + DoScriptText(EMOTE_BIRD_STONE, pSummoned); + m_lBirdsGuidList.push_back(pSummoned->GetObjectGuid()); + } + } + + void DoSummonBroodsOfAnzu() + { + if (!m_pInstance) + return; + + // Note: the birds should fly around the room for about 10 seconds before starting to attack the players + if (GameObject* pClaw = m_pInstance->GetSingleGameObjectFromStorage(GO_RAVENS_CLAW)) + { + float fX, fY, fZ; + for (uint8 i = 0; i < MAX_BROODS; ++i) + { + m_creature->GetRandomPoint(pClaw->GetPositionX(), pClaw->GetPositionY(), pClaw->GetPositionZ(), 7.0f, fX, fY, fZ); + m_creature->SummonCreature(NPC_BROOD_OF_ANZU, fX, fY, fZ, 0, TEMPSUMMON_TIMED_OOC_OR_DEAD_DESPAWN, 30000); + } + } + } + + void DoSummonBirdHelpers() + { + float fX, fY, fZ, fAng; + for (uint8 i = 0; i < 3; ++i) + { + fAng = 2 * M_PI_F / 3 * i; + m_creature->GetNearPoint(m_creature, fX, fY, fZ, 0, 15.0f, fAng); + m_creature->SummonCreature(aSpiritsEntries[i], fX, fY, fZ, fAng + M_PI_F, TEMPSUMMON_CORPSE_DESPAWN, 0); + } + } + + void DespawnBirdHelpers() + { + for (GuidList::const_iterator itr = m_lBirdsGuidList.begin(); itr != m_lBirdsGuidList.end(); ++itr) + { + if (Creature* pTemp = m_creature->GetMap()->GetCreature(*itr)) + pTemp->ForcedDespawn(); + } + } + + void UpdateAI(const uint32 uiDiff) override + { + if (!m_creature->SelectHostileTarget() || !m_creature->getVictim()) + return; + + // Banish at 66% and 33%; Boss can still use spells while banished + if (m_creature->GetHealthPercent() < m_fHealthCheck) + { + if (DoCastSpellIfCan(m_creature, SPELL_BANISH_SELF) == CAST_OK) + { + DoScriptText(SAY_BANISH, m_creature); + DoSummonBroodsOfAnzu(); + m_fHealthCheck -= 40.0f; + } + } + + if (m_uiFleshRipTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature->getVictim(), SPELL_FLESH_RIP) == CAST_OK) + m_uiFleshRipTimer = urand(10000, 20000); + } + else + m_uiFleshRipTimer -= uiDiff; + + if (m_uiScreechTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature, SPELL_SCREECH) == CAST_OK) + m_uiScreechTimer = urand(31000, 35000); + } + else + m_uiScreechTimer -= uiDiff; + + if (m_uiSpellBombTimer < uiDiff) + { + if (Unit* pTarget = m_creature->SelectAttackingTarget(ATTACKING_TARGET_RANDOM, 0)) + { + if (DoCastSpellIfCan(pTarget, SPELL_SPELL_BOMB) == CAST_OK) + { + switch (urand(0, 2)) + { + case 0: DoScriptText(SAY_WHISPER_MAGIC_1, m_creature, pTarget); break; + case 1: DoScriptText(SAY_WHISPER_MAGIC_2, m_creature, pTarget); break; + case 2: DoScriptText(SAY_WHISPER_MAGIC_3, m_creature, pTarget); break; + } + m_uiSpellBombTimer = urand(24000, 40000); + } + } + } + else + m_uiSpellBombTimer -= uiDiff; + + if (m_uiCycloneTimer < uiDiff) + { + if (Unit* pTarget = m_creature->SelectAttackingTarget(ATTACKING_TARGET_RANDOM, 1)) + { + if (DoCastSpellIfCan(pTarget, SPELL_CYCLONE) == CAST_OK) + m_uiCycloneTimer = 21000; + } + } + else + m_uiCycloneTimer -= uiDiff; + + DoMeleeAttackIfReady(); + } +}; + +CreatureAI* GetAI_boss_anzu(Creature* pCreature) +{ + return new boss_anzuAI(pCreature); +} + +void AddSC_boss_anzu() +{ + Script* pNewScript; + + pNewScript = new Script; + pNewScript->Name = "boss_anzu"; + pNewScript->GetAI = &GetAI_boss_anzu; + pNewScript->RegisterSelf(); +} diff --git a/src/modules/SD2/scripts/outland/auchindoun/sethekk_halls/boss_darkweaver_syth.cpp b/src/modules/SD2/scripts/outland/auchindoun/sethekk_halls/boss_darkweaver_syth.cpp new file mode 100644 index 000000000..16aa47eab --- /dev/null +++ b/src/modules/SD2/scripts/outland/auchindoun/sethekk_halls/boss_darkweaver_syth.cpp @@ -0,0 +1,231 @@ +/* Copyright (C) 2006 - 2013 ScriptDev2 + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with 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: 100 +SDComment: +SDCategory: Auchindoun, Sethekk Halls +EndScriptData */ + +#include "precompiled.h" + +enum +{ + SAY_SUMMON = -1556000, + SAY_AGGRO_1 = -1556001, + SAY_AGGRO_2 = -1556002, + SAY_AGGRO_3 = -1556003, + SAY_SLAY_1 = -1556004, + SAY_SLAY_2 = -1556005, + SAY_DEATH = -1556006, + + SPELL_FROST_SHOCK = 12548, + SPELL_FROST_SHOCK_H = 21401, + SPELL_FLAME_SHOCK = 15039, + SPELL_FLAME_SHOCK_H = 15616, + SPELL_SHADOW_SHOCK = 33620, + SPELL_SHADOW_SHOCK_H = 38136, + SPELL_ARCANE_SHOCK = 33534, + SPELL_ARCANE_SHOCK_H = 38135, + + SPELL_CHAIN_LIGHTNING = 15659, + SPELL_CHAIN_LIGHTNING_H = 15305, + + SPELL_SUMMON_SYTH_FIRE = 33537, // Spawns 19203 + SPELL_SUMMON_SYTH_ARCANE = 33538, // Spawns 19205 + SPELL_SUMMON_SYTH_FROST = 33539, // Spawns 19204 + SPELL_SUMMON_SYTH_SHADOW = 33540, // Spawns 19206 + + // Npc entries + NPC_FIRE_ELEMENTAL = 19203, + NPC_FROST_ELEMENTAL = 19204, + NPC_ARCANE_ELEMENTAL = 19205, + NPC_SHADOW_ELEMENTAL = 19206, +}; + +struct boss_darkweaver_sythAI : public ScriptedAI +{ + boss_darkweaver_sythAI(Creature* pCreature) : ScriptedAI(pCreature) + { + m_bIsRegularMode = pCreature->GetMap()->IsRegularDifficulty(); + Reset(); + } + + bool m_bIsRegularMode; + uint32 m_uiFlameshockTimer; + uint32 m_uiArcaneshockTimer; + uint32 m_uiFrostshockTimer; + uint32 m_uiShadowshockTimer; + uint32 m_uiChainlightningTimer; + + float m_fHpCheck; + + void Reset() override + { + m_uiFlameshockTimer = 18000; + m_uiArcaneshockTimer = 19000; + m_uiFrostshockTimer = 18000; + m_uiShadowshockTimer = 17000; + m_uiChainlightningTimer = urand(6000, 9000); + + m_fHpCheck = 90.0f; + } + + void Aggro(Unit* /*pWho*/) override + { + switch (urand(0, 2)) + { + case 0: DoScriptText(SAY_AGGRO_1, m_creature); break; + case 1: DoScriptText(SAY_AGGRO_2, m_creature); break; + case 2: DoScriptText(SAY_AGGRO_3, m_creature); break; + } + } + + void JustDied(Unit* /*pKiller*/) override + { + DoScriptText(SAY_DEATH, m_creature); + } + + void KilledUnit(Unit* /*pVictim*/) override + { + if (urand(0, 1)) + return; + + DoScriptText(urand(0, 1) ? SAY_SLAY_1 : SAY_SLAY_2, m_creature); + } + + void JustSummoned(Creature* pSummoned) override + { + switch (pSummoned->GetEntry()) + { + case NPC_FIRE_ELEMENTAL: + pSummoned->ApplySpellImmune(0, IMMUNITY_SCHOOL, SPELL_SCHOOL_MASK_FIRE, true); + break; + case NPC_FROST_ELEMENTAL: + pSummoned->ApplySpellImmune(0, IMMUNITY_SCHOOL, SPELL_SCHOOL_MASK_FROST, true); + break; + case NPC_ARCANE_ELEMENTAL: + pSummoned->ApplySpellImmune(0, IMMUNITY_SCHOOL, SPELL_SCHOOL_MASK_ARCANE, true); + break; + case NPC_SHADOW_ELEMENTAL: + pSummoned->ApplySpellImmune(0, IMMUNITY_SCHOOL, SPELL_SCHOOL_MASK_SHADOW, true); + break; + } + + if (Unit* pTarget = m_creature->SelectAttackingTarget(ATTACKING_TARGET_RANDOM, 0)) + pSummoned->AI()->AttackStart(pTarget); + } + + // Wrapper to handle the elementals summon + void SythSummoning() + { + DoScriptText(SAY_SUMMON, m_creature); + + if (m_creature->IsNonMeleeSpellCasted(false)) + m_creature->InterruptNonMeleeSpells(false); + + DoCastSpellIfCan(m_creature, SPELL_SUMMON_SYTH_ARCANE, CAST_TRIGGERED); // front + DoCastSpellIfCan(m_creature, SPELL_SUMMON_SYTH_FIRE, CAST_TRIGGERED); // back + DoCastSpellIfCan(m_creature, SPELL_SUMMON_SYTH_FROST, CAST_TRIGGERED); // left + DoCastSpellIfCan(m_creature, SPELL_SUMMON_SYTH_SHADOW, CAST_TRIGGERED); // right + } + + void UpdateAI(const uint32 uiDiff) override + { + if (!m_creature->SelectHostileTarget() || !m_creature->getVictim()) + return; + + // Summon elementals at 90%, 50% and 10% health + if (m_creature->GetHealthPercent() < m_fHpCheck) + { + SythSummoning(); + m_fHpCheck -= 40.0f; + } + + if (m_uiFlameshockTimer < uiDiff) + { + if (Unit* pTarget = m_creature->SelectAttackingTarget(ATTACKING_TARGET_RANDOM, 0)) + { + if (DoCastSpellIfCan(pTarget, m_bIsRegularMode ? SPELL_FLAME_SHOCK : SPELL_FLAME_SHOCK_H) == CAST_OK) + m_uiFlameshockTimer = m_bIsRegularMode ? urand(13000, 28000) : urand(11000, 20000); + } + } + else + m_uiFlameshockTimer -= uiDiff; + + if (m_uiArcaneshockTimer < uiDiff) + { + if (Unit* pTarget = m_creature->SelectAttackingTarget(ATTACKING_TARGET_RANDOM, 0)) + { + if (DoCastSpellIfCan(pTarget, m_bIsRegularMode ? SPELL_ARCANE_SHOCK : SPELL_ARCANE_SHOCK_H) == CAST_OK) + m_uiArcaneshockTimer = m_bIsRegularMode ? urand(13000, 28000) : urand(11000, 20000); + } + } + else + m_uiArcaneshockTimer -= uiDiff; + + if (m_uiFrostshockTimer < uiDiff) + { + if (Unit* pTarget = m_creature->SelectAttackingTarget(ATTACKING_TARGET_RANDOM, 0)) + { + if (DoCastSpellIfCan(pTarget, m_bIsRegularMode ? SPELL_FROST_SHOCK : SPELL_FROST_SHOCK_H) == CAST_OK) + m_uiFrostshockTimer = m_bIsRegularMode ? urand(13000, 28000) : urand(11000, 20000); + } + } + else + m_uiFrostshockTimer -= uiDiff; + + if (m_uiShadowshockTimer < uiDiff) + { + if (Unit* pTarget = m_creature->SelectAttackingTarget(ATTACKING_TARGET_RANDOM, 0)) + { + if (DoCastSpellIfCan(pTarget, m_bIsRegularMode ? SPELL_SHADOW_SHOCK : SPELL_SHADOW_SHOCK_H) == CAST_OK) + m_uiShadowshockTimer = m_bIsRegularMode ? urand(13000, 28000) : urand(11000, 20000); + } + } + else + m_uiShadowshockTimer -= uiDiff; + + if (m_uiChainlightningTimer < uiDiff) + { + if (Unit* pTarget = m_creature->SelectAttackingTarget(ATTACKING_TARGET_RANDOM, 0)) + { + if (DoCastSpellIfCan(pTarget, m_bIsRegularMode ? SPELL_CHAIN_LIGHTNING : SPELL_CHAIN_LIGHTNING_H) == CAST_OK) + m_uiChainlightningTimer = m_bIsRegularMode ? urand(14000, 26000) : urand(13000, 19000); + } + } + else + m_uiChainlightningTimer -= uiDiff; + + DoMeleeAttackIfReady(); + } +}; + +CreatureAI* GetAI_boss_darkweaver_syth(Creature* pCreature) +{ + return new boss_darkweaver_sythAI(pCreature); +} + +void AddSC_boss_darkweaver_syth() +{ + Script* pNewScript; + + pNewScript = new Script; + pNewScript->Name = "boss_darkweaver_syth"; + pNewScript->GetAI = &GetAI_boss_darkweaver_syth; + pNewScript->RegisterSelf(); +} diff --git a/src/modules/SD2/scripts/outland/auchindoun/sethekk_halls/boss_talon_king_ikiss.cpp b/src/modules/SD2/scripts/outland/auchindoun/sethekk_halls/boss_talon_king_ikiss.cpp new file mode 100644 index 000000000..4ab1161ec --- /dev/null +++ b/src/modules/SD2/scripts/outland/auchindoun/sethekk_halls/boss_talon_king_ikiss.cpp @@ -0,0 +1,222 @@ +/* Copyright (C) 2006 - 2013 ScriptDev2 + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with 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: 100 +SDComment: +SDCategory: Auchindoun, Sethekk Halls +EndScriptData */ + +#include "precompiled.h" +#include "sethekk_halls.h" + +enum +{ + SAY_INTRO = -1556007, + SAY_AGGRO_1 = -1556008, + SAY_AGGRO_2 = -1556009, + SAY_AGGRO_3 = -1556010, + SAY_SLAY_1 = -1556011, + SAY_SLAY_2 = -1556012, + SAY_DEATH = -1556013, + EMOTE_ARCANE_EXP = -1556015, + + SPELL_BLINK = 38194, + SPELL_MANA_SHIELD = 38151, + SPELL_ARCANE_BUBBLE = 9438, + SPELL_SLOW_H = 35032, + + SPELL_POLYMORPH = 38245, + SPELL_POLYMORPH_H = 43309, + + SPELL_ARCANE_VOLLEY = 35059, + SPELL_ARCANE_VOLLEY_H = 40424, + + SPELL_ARCANE_EXPLOSION = 38197, + SPELL_ARCANE_EXPLOSION_H = 40425, +}; + +struct boss_talon_king_ikissAI : public ScriptedAI +{ + boss_talon_king_ikissAI(Creature* pCreature) : ScriptedAI(pCreature) + { + m_pInstance = (ScriptedInstance*)pCreature->GetInstanceData(); + m_bIsRegularMode = pCreature->GetMap()->IsRegularDifficulty(); + m_bIntro = false; + Reset(); + } + + ScriptedInstance* m_pInstance; + bool m_bIsRegularMode; + + uint32 m_uiArcaneVolleyTimer; + uint32 m_uiSheepTimer; + uint32 m_uiSlowTimer; + uint8 m_uiBlinkPhase; + float m_fHealthCheck; + + bool m_bManaShield; + bool m_bBlink; + bool m_bIntro; + + void Reset() override + { + m_uiArcaneVolleyTimer = urand(5000, 12000); + m_uiSheepTimer = 8000; + m_uiSlowTimer = urand(9000, 13000); + m_uiBlinkPhase = 0; + m_fHealthCheck = 80.0f; + + m_bBlink = false; + m_bManaShield = false; + } + + void MoveInLineOfSight(Unit* pWho) override + { + if (!m_creature->getVictim() && pWho->IsTargetableForAttack() && (m_creature->IsHostileTo(pWho)) && pWho->isInAccessablePlaceFor(m_creature)) + { + if (!m_bIntro && m_creature->IsWithinDistInMap(pWho, 100.0f)) + { + m_bIntro = true; + DoScriptText(SAY_INTRO, m_creature); + } + } + + ScriptedAI::MoveInLineOfSight(pWho); + } + + void Aggro(Unit* /*pWho*/) override + { + switch (urand(0, 2)) + { + case 0: DoScriptText(SAY_AGGRO_1, m_creature); break; + case 1: DoScriptText(SAY_AGGRO_2, m_creature); break; + case 2: DoScriptText(SAY_AGGRO_3, m_creature); break; + } + + if (m_pInstance) + m_pInstance->SetData(TYPE_IKISS, IN_PROGRESS); + } + + void JustDied(Unit* /*pKiller*/) override + { + DoScriptText(SAY_DEATH, m_creature); + + if (m_pInstance) + m_pInstance->SetData(TYPE_IKISS, DONE); + } + + void JustReachedHome() override + { + if (m_pInstance) + m_pInstance->SetData(TYPE_IKISS, FAIL); + } + + void KilledUnit(Unit* /*pVctim*/) override + { + DoScriptText(urand(0, 1) ? SAY_SLAY_1 : SAY_SLAY_2, m_creature); + } + + void UpdateAI(const uint32 uiDiff) override + { + if (!m_creature->SelectHostileTarget() || !m_creature->getVictim()) + return; + + if (EnterEvadeIfOutOfCombatArea(uiDiff)) + return; + + if (m_bBlink) + { + DoCastSpellIfCan(m_creature, m_bIsRegularMode ? SPELL_ARCANE_EXPLOSION : SPELL_ARCANE_EXPLOSION_H); + DoCastSpellIfCan(m_creature, SPELL_ARCANE_BUBBLE, CAST_TRIGGERED); + DoResetThreat(); + m_bBlink = false; + } + + if (m_uiArcaneVolleyTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature, m_bIsRegularMode ? SPELL_ARCANE_VOLLEY : SPELL_ARCANE_VOLLEY_H) == CAST_OK) + m_uiArcaneVolleyTimer = urand(8000, 12000); + } + else + m_uiArcaneVolleyTimer -= uiDiff; + + if (m_uiSheepTimer < uiDiff) + { + // second top aggro target in normal, random target in heroic + if (Unit* pTarget = m_bIsRegularMode ? m_creature->SelectAttackingTarget(ATTACKING_TARGET_TOPAGGRO, 1) : m_creature->SelectAttackingTarget(ATTACKING_TARGET_RANDOM, 0)) + DoCastSpellIfCan(pTarget, m_bIsRegularMode ? SPELL_POLYMORPH : SPELL_POLYMORPH_H); + m_uiSheepTimer = urand(15000, 17500); + } + else + m_uiSheepTimer -= uiDiff; + + if (!m_bManaShield && m_creature->GetHealthPercent() < 15.0f) + { + if (DoCastSpellIfCan(m_creature, SPELL_MANA_SHIELD) == CAST_OK) + m_bManaShield = true; + } + + if (!m_bIsRegularMode) + { + if (m_uiSlowTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature, SPELL_SLOW_H) == CAST_OK) + m_uiSlowTimer = urand(15000, 24000); + } + else + m_uiSlowTimer -= uiDiff; + } + + if (m_creature->GetHealthPercent() < m_fHealthCheck) + { + if (DoCastSpellIfCan(m_creature, SPELL_BLINK, CAST_INTERRUPT_PREVIOUS) == CAST_OK) + { + m_bBlink = true; + DoScriptText(EMOTE_ARCANE_EXP, m_creature); + + // There is no relationship between the health percentages + switch (m_uiBlinkPhase) + { + case 0: m_fHealthCheck = 50.0f; break; + case 1: m_fHealthCheck = 25.0f; break; + case 2: m_fHealthCheck = 0.0f; break; + } + + ++m_uiBlinkPhase; + } + } + + if (!m_bBlink) + DoMeleeAttackIfReady(); + } +}; + +CreatureAI* GetAI_boss_talon_king_ikiss(Creature* pCreature) +{ + return new boss_talon_king_ikissAI(pCreature); +} + +void AddSC_boss_talon_king_ikiss() +{ + Script* pNewScript; + + pNewScript = new Script; + pNewScript->Name = "boss_talon_king_ikiss"; + pNewScript->GetAI = &GetAI_boss_talon_king_ikiss; + pNewScript->RegisterSelf(); +} diff --git a/src/modules/SD2/scripts/outland/auchindoun/sethekk_halls/instance_sethekk_halls.cpp b/src/modules/SD2/scripts/outland/auchindoun/sethekk_halls/instance_sethekk_halls.cpp new file mode 100644 index 000000000..0eba1aba4 --- /dev/null +++ b/src/modules/SD2/scripts/outland/auchindoun/sethekk_halls/instance_sethekk_halls.cpp @@ -0,0 +1,186 @@ +/* Copyright (C) 2006 - 2013 ScriptDev2 + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along 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: 60 +SDComment: Summoning event for Anzu NYI +SDCategory: Auchindoun, Sethekk Halls +EndScriptData */ + +#include "precompiled.h" +#include "sethekk_halls.h" + +instance_sethekk_halls::instance_sethekk_halls(Map* pMap) : ScriptedInstance(pMap) +{ + Initialize(); +} +void instance_sethekk_halls::Initialize() +{ + memset(&m_auiEncounter, 0, sizeof(m_auiEncounter)); +} + +void instance_sethekk_halls::OnCreatureCreate(Creature* pCreature) +{ + if (pCreature->GetEntry() == NPC_ANZU) + m_mNpcEntryGuidStore[NPC_ANZU] = pCreature->GetObjectGuid(); +} + +void instance_sethekk_halls::OnObjectCreate(GameObject* pGo) +{ + switch (pGo->GetEntry()) + { + case GO_IKISS_DOOR: + if (m_auiEncounter[TYPE_IKISS] == DONE) + pGo->SetGoState(GO_STATE_ACTIVE); + break; + case GO_IKISS_CHEST: + if (m_auiEncounter[TYPE_IKISS] == DONE) + pGo->RemoveFlag(GAMEOBJECT_FLAGS, GO_FLAG_NO_INTERACT | GO_FLAG_INTERACT_COND); + break; + case GO_RAVENS_CLAW: + break; + + default: + return; + } + + m_mGoEntryGuidStore[pGo->GetEntry()] = pGo->GetObjectGuid(); +} + +void instance_sethekk_halls::SetData(uint32 uiType, uint32 uiData) +{ + switch (uiType) + { + case TYPE_SYTH: + m_auiEncounter[uiType] = uiData; + break; + case TYPE_ANZU: + m_auiEncounter[uiType] = uiData; + // Respawn the Raven's Claw if event fails + if (uiData == FAIL) + { + if (GameObject* pClaw = GetSingleGameObjectFromStorage(GO_RAVENS_CLAW)) + pClaw->Respawn(); + } + break; + case TYPE_IKISS: + if (uiData == DONE) + { + DoUseDoorOrButton(GO_IKISS_DOOR, DAY); + DoToggleGameObjectFlags(GO_IKISS_CHEST, GO_FLAG_NO_INTERACT | GO_FLAG_INTERACT_COND, false); + } + m_auiEncounter[uiType] = uiData; + break; + default: + return; + } + + if (uiData == DONE) + { + OUT_SAVE_INST_DATA; + + std::ostringstream saveStream; + saveStream << m_auiEncounter[0] << " " << m_auiEncounter[1] << " " << m_auiEncounter[2]; + + m_strInstData = saveStream.str(); + + SaveToDB(); + OUT_SAVE_INST_DATA_COMPLETE; + } +} + +uint32 instance_sethekk_halls::GetData(uint32 uiType) const +{ + if (uiType < MAX_ENCOUNTER) + return m_auiEncounter[uiType]; + + return 0; +} + +void instance_sethekk_halls::Load(const char* chrIn) +{ + if (!chrIn) + { + 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]; + + for (uint8 i = 0; i < MAX_ENCOUNTER; ++i) + { + if (m_auiEncounter[i] == IN_PROGRESS) + m_auiEncounter[i] = NOT_STARTED; + } + + OUT_LOAD_INST_DATA_COMPLETE; +} + +bool instance_sethekk_halls::CheckAchievementCriteriaMeet(uint32 uiCriteriaId, Player const* pSource, Unit const* /*pTarget*/, uint32 /*uiMiscValue1 = 0*/) const +{ + if (uiCriteriaId != ACHIEV_CRITA_TURKEY_TIME) + return false; + + if (!pSource) + return false; + + return pSource->HasItemOrGemWithIdEquipped(ITEM_PILGRIMS_HAT, 1) && (pSource->HasItemOrGemWithIdEquipped(ITEM_PILGRIMS_DRESS, 1) + || pSource->HasItemOrGemWithIdEquipped(ITEM_PILGRIMS_ROBE, 1) || pSource->HasItemOrGemWithIdEquipped(ITEM_PILGRIMS_ATTIRE, 1)); +} + +InstanceData* GetInstanceData_instance_sethekk_halls(Map* pMap) +{ + return new instance_sethekk_halls(pMap); +} + +bool ProcessEventId_event_spell_summon_raven_god(uint32 /*uiEventId*/, Object* pSource, Object* /*pTarget*/, bool bIsStart) +{ + if (bIsStart && pSource->GetTypeId() == TYPEID_PLAYER) + { + if (instance_sethekk_halls* pInstance = (instance_sethekk_halls*)((Player*)pSource)->GetInstanceData()) + { + // This should be checked by despawning the Raven Claw Go; However it's better to double check the condition + if (pInstance->GetData(TYPE_ANZU) == DONE || pInstance->GetData(TYPE_ANZU) == IN_PROGRESS) + return true; + + // Don't summon him twice + if (pInstance->GetSingleCreatureFromStorage(NPC_ANZU, true)) + return true; + + // ToDo: add more code here to handle the summoning event. For the moment it's handled in DB because of the missing info + } + } + return false; +} + +void AddSC_instance_sethekk_halls() +{ + Script* pNewScript; + + pNewScript = new Script; + pNewScript->Name = "instance_sethekk_halls"; + pNewScript->GetInstanceData = &GetInstanceData_instance_sethekk_halls; + pNewScript->RegisterSelf(); + + pNewScript = new Script; + pNewScript->Name = "event_spell_summon_raven_god"; + pNewScript->pProcessEventId = &ProcessEventId_event_spell_summon_raven_god; + pNewScript->RegisterSelf(); +} diff --git a/src/modules/SD2/scripts/outland/auchindoun/sethekk_halls/sethekk_halls.h b/src/modules/SD2/scripts/outland/auchindoun/sethekk_halls/sethekk_halls.h new file mode 100644 index 000000000..9f52e8bb9 --- /dev/null +++ b/src/modules/SD2/scripts/outland/auchindoun/sethekk_halls/sethekk_halls.h @@ -0,0 +1,62 @@ +/* Copyright (C) 2006 - 2013 ScriptDev2 + * This program is free software licensed under GPL version 2 + * Please see the included DOCS/LICENSE.TXT for more information */ + +#ifndef DEF_SETHEKK_HALLS_H +#define DEF_SETHEKK_HALLS_H + +enum +{ + MAX_ENCOUNTER = 3, + + TYPE_SYTH = 0, + TYPE_ANZU = 1, + TYPE_IKISS = 2, + + NPC_ANZU = 23035, + NPC_RAVEN_GOD_TARGET = 23057, + + GO_IKISS_DOOR = 177203, + GO_IKISS_CHEST = 187372, + GO_RAVENS_CLAW = 185554, + + SAY_ANZU_INTRO_1 = -1556016, + SAY_ANZU_INTRO_2 = -1556017, + + // possible spells used for Anzu summoning event + SPELL_PORTAL = 39952, + SPELL_SUMMONING_BEAMS = 39978, + SPELL_RED_LIGHTNING = 39990, + + ACHIEV_CRITA_TURKEY_TIME = 11142, + ITEM_PILGRIMS_HAT = 46723, + ITEM_PILGRIMS_DRESS = 44785, + ITEM_PILGRIMS_ROBE = 46824, + ITEM_PILGRIMS_ATTIRE = 46800, +}; + +class instance_sethekk_halls : public ScriptedInstance +{ + public: + instance_sethekk_halls(Map* pMap); + ~instance_sethekk_halls() {} + + void Initialize() override; + + void OnCreatureCreate(Creature* pCreature) override; + void OnObjectCreate(GameObject* pGo) override; + + void SetData(uint32 uiType, uint32 uiData) override; + uint32 GetData(uint32 uiType) const override; + + bool CheckAchievementCriteriaMeet(uint32 uiCriteriaId, Player const* pSource, Unit const* pTarget, uint32 uiMiscValue1 /* = 0*/) const override; + + const char* Save() const override { return m_strInstData.c_str(); } + void Load(const char* chrIn) override; + + private: + uint32 m_auiEncounter[MAX_ENCOUNTER]; + std::string m_strInstData; +}; + +#endif diff --git a/src/modules/SD2/scripts/outland/auchindoun/shadow_labyrinth/boss_ambassador_hellmaw.cpp b/src/modules/SD2/scripts/outland/auchindoun/shadow_labyrinth/boss_ambassador_hellmaw.cpp new file mode 100644 index 000000000..60dcd69f2 --- /dev/null +++ b/src/modules/SD2/scripts/outland/auchindoun/shadow_labyrinth/boss_ambassador_hellmaw.cpp @@ -0,0 +1,169 @@ +/* Copyright (C) 2006 - 2013 ScriptDev2 + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with 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 "shadow_labyrinth.h" + +enum +{ + SAY_AGGRO_1 = -1555001, + SAY_AGGRO_2 = -1555002, + SAY_AGGRO_3 = -1555003, + SAY_HELP = -1555004, + SAY_SLAY_1 = -1555005, + SAY_SLAY_2 = -1555006, + SAY_DEATH = -1555007, + + SPELL_CORROSIVE_ACID = 33551, + SPELL_FEAR = 33547, + SPELL_ENRAGE = 34970 +}; + +struct boss_ambassador_hellmawAI : public ScriptedAI +{ + boss_ambassador_hellmawAI(Creature* pCreature) : ScriptedAI(pCreature) + { + m_pInstance = (instance_shadow_labyrinth*)pCreature->GetInstanceData(); + m_bIsRegularMode = pCreature->GetMap()->IsRegularDifficulty(); + Reset(); + } + + instance_shadow_labyrinth* m_pInstance; + bool m_bIsRegularMode; + + uint32 m_uiBanishTimer; + uint32 m_uiCorrosiveAcidTimer; + uint32 m_uiFearTimer; + uint32 m_uiEnrageTimer; + bool m_bIsEnraged; + + void Reset() override + { + m_uiBanishTimer = 2000; + m_uiCorrosiveAcidTimer = urand(20000, 23000); + m_uiFearTimer = urand(20000, 26000); + m_uiEnrageTimer = 3 * MINUTE * IN_MILLISECONDS; + m_bIsEnraged = false; + } + + void JustReachedHome() override + { + if (m_pInstance) + m_pInstance->SetData(TYPE_HELLMAW, FAIL); + } + + void Aggro(Unit* /*pWho*/) override + { + switch (urand(0, 2)) + { + case 0: DoScriptText(SAY_AGGRO_1, m_creature); break; + case 1: DoScriptText(SAY_AGGRO_2, m_creature); break; + case 2: DoScriptText(SAY_AGGRO_3, m_creature); break; + } + + if (m_pInstance) + m_pInstance->SetData(TYPE_HELLMAW, IN_PROGRESS); + } + + void KilledUnit(Unit* /*pVictim*/) override + { + DoScriptText(urand(0, 1) ? SAY_SLAY_1 : SAY_SLAY_2, m_creature); + } + + void JustDied(Unit* /*pKiller*/) override + { + DoScriptText(SAY_DEATH, m_creature); + + if (m_pInstance) + m_pInstance->SetData(TYPE_HELLMAW, DONE); + } + + void UpdateAI(const uint32 uiDiff) override + { + if (m_uiBanishTimer) + { + if (m_uiBanishTimer <= uiDiff) + { + if (!m_pInstance) + return; + + // Check for banish + if (m_pInstance->IsHellmawUnbanished()) + { + m_creature->RemoveAurasDueToSpell(SPELL_BANISH); + m_creature->GetMotionMaster()->MoveWaypoint(); + m_uiBanishTimer = 0; + } + } + else + m_uiBanishTimer -= uiDiff; + } + + if (!m_creature->SelectHostileTarget() || !m_creature->getVictim()) + return; + + if (m_uiCorrosiveAcidTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature, SPELL_CORROSIVE_ACID) == CAST_OK) + m_uiCorrosiveAcidTimer = urand(23000, 35000); + } + else + m_uiCorrosiveAcidTimer -= uiDiff; + + if (m_uiFearTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature, SPELL_FEAR) == CAST_OK) + m_uiFearTimer = urand(20000, 38000); + } + else + m_uiFearTimer -= uiDiff; + + if (!m_bIsRegularMode && !m_bIsEnraged) + { + if (m_uiEnrageTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature, SPELL_ENRAGE) == CAST_OK) + m_bIsEnraged = true; + } + else + m_uiEnrageTimer -= uiDiff; + } + + DoMeleeAttackIfReady(); + } +}; + +CreatureAI* GetAI_boss_ambassador_hellmaw(Creature* pCreature) +{ + return new boss_ambassador_hellmawAI(pCreature); +} + +void AddSC_boss_ambassador_hellmaw() +{ + Script* pNewScript; + + pNewScript = new Script; + pNewScript->Name = "boss_ambassador_hellmaw"; + pNewScript->GetAI = &GetAI_boss_ambassador_hellmaw; + pNewScript->RegisterSelf(); +} diff --git a/src/modules/SD2/scripts/outland/auchindoun/shadow_labyrinth/boss_blackheart_the_inciter.cpp b/src/modules/SD2/scripts/outland/auchindoun/shadow_labyrinth/boss_blackheart_the_inciter.cpp new file mode 100644 index 000000000..14a1b2bcb --- /dev/null +++ b/src/modules/SD2/scripts/outland/auchindoun/shadow_labyrinth/boss_blackheart_the_inciter.cpp @@ -0,0 +1,201 @@ +/* Copyright (C) 2006 - 2013 ScriptDev2 + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with 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: 90 +SDComment: Not all yells are implemented. +SDCategory: Auchindoun, Shadow Labyrinth +EndScriptData */ + +#include "precompiled.h" +#include "shadow_labyrinth.h" + +enum +{ + SPELL_INCITE_CHAOS = 33676, // triggers 33684 on party members + SPELL_CHARGE = 33709, + SPELL_WAR_STOMP = 33707, + + SAY_INTRO1 = -1555008, + SAY_INTRO2 = -1555009, + SAY_INTRO3 = -1555010, + SAY_AGGRO1 = -1555011, + SAY_AGGRO2 = -1555012, + SAY_AGGRO3 = -1555013, + SAY_SLAY1 = -1555014, + SAY_SLAY2 = -1555015, + SAY_HELP = -1555016, + SAY_DEATH = -1555017, + + SAY2_INTRO1 = -1555018, + SAY2_INTRO2 = -1555019, + SAY2_INTRO3 = -1555020, + SAY2_AGGRO1 = -1555021, + SAY2_AGGRO2 = -1555022, + SAY2_AGGRO3 = -1555023, + SAY2_SLAY1 = -1555024, + SAY2_SLAY2 = -1555025, + SAY2_HELP = -1555026, + SAY2_DEATH = -1555027, +}; + +struct boss_blackheart_the_inciterAI : public ScriptedAI +{ + boss_blackheart_the_inciterAI(Creature* pCreature) : ScriptedAI(pCreature) + { + m_pInstance = (ScriptedInstance*)pCreature->GetInstanceData(); + Reset(); + } + + ScriptedInstance* m_pInstance; + + uint32 m_uiInciteChaosTimer; + uint32 m_uiInciteChaosWaitTimer; + uint32 m_uiChargeTimer; + uint32 m_uiKnockbackTimer; + + GuidVector m_vTargetsGuids; + + void Reset() override + { + m_uiInciteChaosWaitTimer = 0; + m_uiInciteChaosTimer = 15000; + m_uiChargeTimer = urand(30000, 37000); + m_uiKnockbackTimer = urand(10000, 14000); + } + + void KilledUnit(Unit* /*pVictim*/) override + { + DoScriptText(urand(0, 1) ? SAY_SLAY1 : SAY_SLAY2, m_creature); + } + + void JustDied(Unit* /*pKiller*/) override + { + DoScriptText(SAY_DEATH, m_creature); + + if (m_pInstance) + m_pInstance->SetData(TYPE_INCITER, DONE); + } + + void Aggro(Unit* /*pWho*/) override + { + switch (urand(0, 2)) + { + case 0: DoScriptText(SAY_AGGRO1, m_creature); break; + case 1: DoScriptText(SAY_AGGRO2, m_creature); break; + case 2: DoScriptText(SAY_AGGRO3, m_creature); break; + } + + if (m_pInstance) + m_pInstance->SetData(TYPE_INCITER, IN_PROGRESS); + } + + void JustReachedHome() override + { + if (m_pInstance) + m_pInstance->SetData(TYPE_INCITER, FAIL); + } + + void EnterEvadeMode() + { + // if we are waiting for Incite chaos to expire don't evade + if (m_uiInciteChaosWaitTimer) + return; + + ScriptedAI::EnterEvadeMode(); + } + + void UpdateAI(const uint32 uiDiff) override + { + if (m_uiInciteChaosWaitTimer) + { + if (m_uiInciteChaosWaitTimer <= uiDiff) + { + // Restart attack on all targets + for (GuidVector::const_iterator itr = m_vTargetsGuids.begin(); itr != m_vTargetsGuids.end(); ++itr) + { + if (Unit* pTarget = m_creature->GetMap()->GetUnit(*itr)) + AttackStart(pTarget); + } + + m_creature->HandleEmote(EMOTE_STATE_NONE); + m_uiInciteChaosWaitTimer = 0; + } + else + m_uiInciteChaosWaitTimer -= uiDiff; + } + + // Return since we have no pTarget + if (!m_creature->SelectHostileTarget() || !m_creature->getVictim()) + return; + + if (m_uiInciteChaosTimer < uiDiff) + { + // Store the threat list + m_vTargetsGuids.clear(); + m_creature->FillGuidsListFromThreatList(m_vTargetsGuids); + + if (DoCastSpellIfCan(m_creature, SPELL_INCITE_CHAOS) == CAST_OK) + { + m_creature->HandleEmote(EMOTE_STATE_LAUGH); + m_uiInciteChaosTimer = 55000; + m_uiInciteChaosWaitTimer = 16000; + return; + } + } + else + m_uiInciteChaosTimer -= uiDiff; + + // Charge Timer + if (m_uiChargeTimer < uiDiff) + { + if (Unit* pTarget = m_creature->SelectAttackingTarget(ATTACKING_TARGET_RANDOM, 1, SPELL_CHARGE, SELECT_FLAG_NOT_IN_MELEE_RANGE)) + { + if (DoCastSpellIfCan(pTarget, SPELL_CHARGE) == CAST_OK) + m_uiChargeTimer = urand(30000, 43000); + } + } + else + m_uiChargeTimer -= uiDiff; + + // Knockback Timer + if (m_uiKnockbackTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature, SPELL_WAR_STOMP) == CAST_OK) + m_uiKnockbackTimer = urand(15000, 30000); + } + else + m_uiKnockbackTimer -= uiDiff; + + DoMeleeAttackIfReady(); + } +}; + +CreatureAI* GetAI_boss_blackheart_the_inciter(Creature* pCreature) +{ + return new boss_blackheart_the_inciterAI(pCreature); +} + +void AddSC_boss_blackheart_the_inciter() +{ + Script* pNewScript; + + pNewScript = new Script; + pNewScript->Name = "boss_blackheart_the_inciter"; + pNewScript->GetAI = &GetAI_boss_blackheart_the_inciter; + pNewScript->RegisterSelf(); +} diff --git a/src/modules/SD2/scripts/outland/auchindoun/shadow_labyrinth/boss_grandmaster_vorpil.cpp b/src/modules/SD2/scripts/outland/auchindoun/shadow_labyrinth/boss_grandmaster_vorpil.cpp new file mode 100644 index 000000000..161389a7a --- /dev/null +++ b/src/modules/SD2/scripts/outland/auchindoun/shadow_labyrinth/boss_grandmaster_vorpil.cpp @@ -0,0 +1,335 @@ +/* Copyright (C) 2006 - 2013 ScriptDev2 + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with 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: 90 +SDComment: Timers may need adjustments +SDCategory: Auchindoun, Shadow Labyrinth +EndScriptData */ + +#include "precompiled.h" +#include "shadow_labyrinth.h" + +enum +{ + SAY_INTRO = -1555028, + SAY_AGGRO_1 = -1555029, + SAY_AGGRO_2 = -1555030, + SAY_AGGRO_3 = -1555031, + SAY_HELP = -1555032, + SAY_SLAY_1 = -1555033, + SAY_SLAY_2 = -1555034, + SAY_DEATH = -1555035, + + SPELL_DRAW_SHADOWS = 33563, // should trigger spell 33558 which is missing; so we need to hack the teleport + SPELL_VOID_PORTAL_A = 33566, // spell only summon one unit, but we use it for the visual effect and summon the 4 other portals manual way(only one spell exist) + SPELL_SHADOW_BOLT_VOLLEY = 32963, + SPELL_RAIN_OF_FIRE = 33617, + SPELL_RAIN_OF_FIRE_H = 39363, + SPELL_BANISH_H = 38791, + SPELL_SUMMON_VOIDWALKER_A = 33582, // the void travelers are summond at portal locations according to DB coords + SPELL_SUMMON_VOIDWALKER_B = 33583, + SPELL_SUMMON_VOIDWALKER_C = 33584, + SPELL_SUMMON_VOIDWALKER_D = 33585, + SPELL_SUMMON_VOIDWALKER_E = 33586, + + SPELL_VOID_PORTAL_VISUAL = 33569, + + SPELL_EMPOWERING_SHADOWS = 33783, + SPELL_EMPOWERING_SHADOWS_H = 39364, + SPELL_SHADOW_NOVA = 33846, + + NPC_VOID_PORTAL = 19224, + NPC_VOID_TRAVELER = 19226, + + MAX_PORTALS = 4 +}; + +struct SummonLocations +{ + float m_fX, m_fY, m_fZ; +}; + +// Summon locations for the void portals +static const SummonLocations aVorpilLocation[MAX_PORTALS] = +{ + { -262.40f, -229.57f, 17.08f}, + { -260.35f, -297.56f, 17.08f}, + { -292.05f, -270.37f, 12.68f}, + { -301.64f, -255.97f, 12.68f} +}; + +static const float aVorpilTeleportLoc[3] = { -253.06f, -264.02f, 17.08f}; + +static const uint32 aTravelerSummonSpells[5] = {SPELL_SUMMON_VOIDWALKER_A, SPELL_SUMMON_VOIDWALKER_B, SPELL_SUMMON_VOIDWALKER_C, SPELL_SUMMON_VOIDWALKER_D, SPELL_SUMMON_VOIDWALKER_E}; + +struct boss_grandmaster_vorpilAI : public ScriptedAI +{ + boss_grandmaster_vorpilAI(Creature* pCreature) : ScriptedAI(pCreature) + { + m_pInstance = (ScriptedInstance*)pCreature->GetInstanceData(); + m_bIsRegularMode = pCreature->GetMap()->IsRegularDifficulty(); + m_bHasDoneIntro = false; + Reset(); + } + + ScriptedInstance* m_pInstance; + bool m_bIsRegularMode; + + uint32 m_uiShadowBoltVolleyTimer; + uint32 m_uiDrawShadowsTimer; + uint32 m_uiRainOfFireTimer; + uint32 m_uiVoidTravelerTimer; + uint32 m_uiBanishTimer; + bool m_bHasDoneIntro; + + void Reset() override + { + m_uiShadowBoltVolleyTimer = urand(13000, 19000); + m_uiDrawShadowsTimer = urand(38000, 44000); + m_uiRainOfFireTimer = 0; + m_uiVoidTravelerTimer = 5000; + m_uiBanishTimer = urand(12000, 16000); + } + + void MoveInLineOfSight(Unit* pWho) override + { + // not sure about right radius + if (!m_bHasDoneIntro && pWho->GetTypeId() == TYPEID_PLAYER && pWho->IsWithinDistInMap(m_creature, 50.0f) && pWho->IsWithinLOSInMap(m_creature)) + { + DoScriptText(SAY_INTRO, m_creature); + m_bHasDoneIntro = true; + } + + ScriptedAI::MoveInLineOfSight(pWho); + } + + void Aggro(Unit* /*pWho*/) override + { + switch (urand(0, 2)) + { + case 0: DoScriptText(SAY_AGGRO_1, m_creature); break; + case 1: DoScriptText(SAY_AGGRO_2, m_creature); break; + case 2: DoScriptText(SAY_AGGRO_3, m_creature); break; + } + + DoCastSpellIfCan(m_creature, SPELL_VOID_PORTAL_A); + + // summon the other 4 portals + for (uint8 i = 0; i < MAX_PORTALS; ++i) + m_creature->SummonCreature(NPC_VOID_PORTAL, aVorpilLocation[i].m_fX, aVorpilLocation[i].m_fY, aVorpilLocation[i].m_fZ, 0.0f, TEMPSUMMON_CORPSE_DESPAWN, 0); + + if (m_pInstance) + m_pInstance->SetData(TYPE_VORPIL, IN_PROGRESS); + } + + void JustReachedHome() override + { + if (m_pInstance) + m_pInstance->SetData(TYPE_VORPIL, FAIL); + } + + void KilledUnit(Unit* /*pVictim*/) override + { + DoScriptText(urand(0, 1) ? SAY_SLAY_1 : SAY_SLAY_2, m_creature); + } + + void JustSummoned(Creature* pSummoned) override + { + if (pSummoned->GetEntry() == NPC_VOID_TRAVELER) + pSummoned->GetMotionMaster()->MoveFollow(m_creature, 0.0f, 0.0f); + + if (pSummoned->GetEntry() == NPC_VOID_PORTAL) + pSummoned->CastSpell(pSummoned, SPELL_VOID_PORTAL_VISUAL, true); + } + + void JustDied(Unit* /*pKiller*/) override + { + DoScriptText(SAY_DEATH, m_creature); + + if (m_pInstance) + m_pInstance->SetData(TYPE_VORPIL, DONE); + } + + // Wrapper to teleport all players to the platform - Workaround for missing spell + void DoTeleportToPlatform() + { + m_creature->NearTeleportTo(aVorpilTeleportLoc[0], aVorpilTeleportLoc[1], aVorpilTeleportLoc[2], 0.0f); + + float fX, fY, fZ; + + GuidVector vGuids; + m_creature->FillGuidsListFromThreatList(vGuids); + for (GuidVector::const_iterator itr = vGuids.begin(); itr != vGuids.end(); ++itr) + { + Unit* pTarget = m_creature->GetMap()->GetUnit(*itr); + + if (pTarget && pTarget->GetTypeId() == TYPEID_PLAYER) + { + pTarget->GetRandomPoint(aVorpilTeleportLoc[0], aVorpilTeleportLoc[1], aVorpilTeleportLoc[2], 4.0f, fX, fY, fZ); + DoTeleportPlayer(pTarget, fX, fY, fZ, m_creature->GetAngle(fX, fY)); + } + } + } + + void UpdateAI(const uint32 uiDiff) override + { + // Return since we have no target + if (!m_creature->SelectHostileTarget() || !m_creature->getVictim()) + return; + + if (m_uiRainOfFireTimer) + { + if (m_uiRainOfFireTimer <= uiDiff) + { + SetCombatMovement(false, true); + DoTeleportToPlatform(); + + if (DoCastSpellIfCan(m_creature, m_bIsRegularMode ? SPELL_RAIN_OF_FIRE : SPELL_RAIN_OF_FIRE_H, CAST_INTERRUPT_PREVIOUS) == CAST_OK) + m_uiRainOfFireTimer = 0; + + SetCombatMovement(true); + + return; // Nothing more todo after the players had been teleported + } + else + m_uiRainOfFireTimer -= uiDiff; + } + + if (m_uiShadowBoltVolleyTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature, SPELL_SHADOW_BOLT_VOLLEY) == CAST_OK) + m_uiShadowBoltVolleyTimer = urand(10000, 26000); + } + else + m_uiShadowBoltVolleyTimer -= uiDiff; + + if (m_uiDrawShadowsTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature, SPELL_DRAW_SHADOWS) == CAST_OK) + { + m_uiDrawShadowsTimer = urand(36000, 44000); + m_uiRainOfFireTimer = 1000; + } + } + else + m_uiDrawShadowsTimer -= uiDiff; + + if (m_uiVoidTravelerTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature, aTravelerSummonSpells[urand(0, 4)]) == CAST_OK) + { + DoScriptText(SAY_HELP, m_creature); + m_uiVoidTravelerTimer = urand(10000, 15000); + } + } + else + m_uiVoidTravelerTimer -= uiDiff; + + if (!m_bIsRegularMode) + { + if (m_uiBanishTimer < uiDiff) + { + if (Unit* pTarget = m_creature->SelectAttackingTarget(ATTACKING_TARGET_RANDOM, 1)) + { + if (DoCastSpellIfCan(pTarget, SPELL_BANISH_H) == CAST_OK) + m_uiBanishTimer = urand(17000, 23000); + } + } + else + m_uiBanishTimer -= uiDiff; + } + + DoMeleeAttackIfReady(); + } +}; + +CreatureAI* GetAI_boss_grandmaster_vorpil(Creature* pCreature) +{ + return new boss_grandmaster_vorpilAI(pCreature); +} + +struct npc_void_travelerAI : public ScriptedAI +{ + npc_void_travelerAI(Creature* pCreature) : ScriptedAI(pCreature) + { + m_bIsRegularMode = pCreature->GetMap()->IsRegularDifficulty(); + Reset(); + } + + bool m_bIsRegularMode; + bool m_bHasExploded; + + uint32 m_uiDeathTimer; + + void Reset() override + { + m_uiDeathTimer = 0; + m_bHasExploded = false; + } + + void MoveInLineOfSight(Unit* pWho) override + { + if (!m_bHasExploded && pWho->GetEntry() == NPC_VORPIL && pWho->IsWithinDistInMap(m_creature, 3.0f)) + { + if (DoCastSpellIfCan(m_creature, SPELL_SHADOW_NOVA) == CAST_OK) + { + m_bHasExploded = true; + m_uiDeathTimer = 1000; + } + } + } + + void AttackStart(Unit* /*pWho*/) override { } + + void UpdateAI(const uint32 uiDiff) override + { + if (m_uiDeathTimer) + { + if (m_uiDeathTimer <= uiDiff) + { + if (DoCastSpellIfCan(m_creature, m_bIsRegularMode ? SPELL_EMPOWERING_SHADOWS : SPELL_EMPOWERING_SHADOWS_H, CAST_TRIGGERED) == CAST_OK) + { + m_creature->DealDamage(m_creature, m_creature->GetHealth(), NULL, DIRECT_DAMAGE, SPELL_SCHOOL_MASK_NORMAL, NULL, false); + m_uiDeathTimer = 0; + } + } + else + m_uiDeathTimer -= uiDiff; + } + } +}; + +CreatureAI* GetAI_npc_void_traveler(Creature* pCreature) +{ + return new npc_void_travelerAI(pCreature); +} + +void AddSC_boss_grandmaster_vorpil() +{ + Script* pNewScript; + + pNewScript = new Script; + pNewScript->Name = "boss_grandmaster_vorpil"; + pNewScript->GetAI = &GetAI_boss_grandmaster_vorpil; + pNewScript->RegisterSelf(); + + pNewScript = new Script; + pNewScript->Name = "npc_void_traveler"; + pNewScript->GetAI = &GetAI_npc_void_traveler; + pNewScript->RegisterSelf(); +} diff --git a/src/modules/SD2/scripts/outland/auchindoun/shadow_labyrinth/boss_murmur.cpp b/src/modules/SD2/scripts/outland/auchindoun/shadow_labyrinth/boss_murmur.cpp new file mode 100644 index 000000000..a5b62f8fc --- /dev/null +++ b/src/modules/SD2/scripts/outland/auchindoun/shadow_labyrinth/boss_murmur.cpp @@ -0,0 +1,169 @@ +/* Copyright (C) 2006 - 2013 ScriptDev2 + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +/* ScriptData +SDName: Boss_Murmur +SD%Complete: 75 +SDComment: Sonic Boom and Murmur's Touch require additional research and core support +SDCategory: Auchindoun, Shadow Labyrinth +EndScriptData */ + +#include "precompiled.h" +#include "shadow_labyrinth.h" + +enum +{ + EMOTE_SONIC_BOOM = -1555036, + + // Intro spells - used on npcs + SPELL_SUPPRESSION_BLAST = 33332, + SPELL_MURMURS_WRATH = 33331, + SPELL_MURMURS_WRATH_2 = 33329, + + SPELL_MAGNETIC_PULL = 33689, + SPELL_SONIC_BOOM = 33923, // dummy spell - triggers 33666 + SPELL_SONIC_BOOM_H = 38796, // dummy spell - triggers 38795 + SPELL_MURMURS_TOUCH = 33711, // on expire silences the party members using shockwave - 33686 - also related to spell 33760 + SPELL_MURMURS_TOUCH_H = 38794, + SPELL_RESONANCE = 33657, + + SPELL_SONIC_SHOCK = 38797, // Heroic Spell + SPELL_THUNDERING_STORM = 39365, // Heroic Spell +}; + +struct boss_murmurAI : public Scripted_NoMovementAI +{ + boss_murmurAI(Creature* pCreature) : Scripted_NoMovementAI(pCreature) + { + m_pInstance = (ScriptedInstance*)pCreature->GetInstanceData(); + m_bIsRegularMode = pCreature->GetMap()->IsRegularDifficulty(); + Reset(); + } + + ScriptedInstance* m_pInstance; + bool m_bIsRegularMode; + + uint32 m_uiSonicBoomTimer; + uint32 m_uiMurmursTouchTimer; + uint32 m_uiResonanceTimer; + uint32 m_uiMagneticPullTimer; + uint32 m_uiSonicShockTimer; + uint32 m_uiThunderingStormTimer; + + void Reset() override + { + m_uiSonicBoomTimer = urand(21000, 35000); + m_uiMurmursTouchTimer = urand(9000, 18000); + m_uiResonanceTimer = urand(1000, 7000); + m_uiMagneticPullTimer = urand(15000, 25000); + m_uiSonicShockTimer = urand(5000, 15000); + m_uiThunderingStormTimer = urand(10000, 50000); + + // Boss has only 0.4 of max health + m_creature->SetHealth(uint32(m_creature->GetMaxHealth()*.4)); + } + + void UpdateAI(const uint32 uiDiff) override + { + // Return since we have no target + if (!m_creature->SelectHostileTarget() || !m_creature->getVictim()) + return; + + // SonicBoom_Timer + if (m_uiSonicBoomTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature, m_bIsRegularMode ? SPELL_SONIC_BOOM : SPELL_SONIC_BOOM_H) == CAST_OK) + { + DoScriptText(EMOTE_SONIC_BOOM, m_creature); + m_uiSonicBoomTimer = urand(31000, 38000); + } + } + else + m_uiSonicBoomTimer -= uiDiff; + + // MurmursTouch_Timer + if (m_uiMurmursTouchTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature, m_bIsRegularMode ? SPELL_MURMURS_TOUCH : SPELL_MURMURS_TOUCH_H) == CAST_OK) + m_uiMurmursTouchTimer = m_bIsRegularMode ? urand(21000, 21000) : urand(29000, 40000); + } + else + m_uiMurmursTouchTimer -= uiDiff; + + // Resonance_Timer - cast if no target is in range + if (!m_creature->CanReachWithMeleeAttack(m_creature->getVictim())) + { + if (m_uiResonanceTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature, SPELL_RESONANCE) == CAST_OK) + m_uiResonanceTimer = urand(5000, 12000); + } + else + m_uiResonanceTimer -= uiDiff; + } + + // MagneticPull_Timer + if (m_uiMagneticPullTimer < uiDiff) + { + if (Unit* pTarget = m_creature->SelectAttackingTarget(ATTACKING_TARGET_RANDOM, 0, SPELL_MAGNETIC_PULL, SELECT_FLAG_PLAYER | SELECT_FLAG_NOT_IN_MELEE_RANGE)) + { + if (DoCastSpellIfCan(pTarget, SPELL_MAGNETIC_PULL) == CAST_OK) + m_uiMagneticPullTimer = urand(21000, 30000); + } + } + else + m_uiMagneticPullTimer -= uiDiff; + + if (!m_bIsRegularMode) + { + if (m_uiSonicShockTimer < uiDiff) + { + if (Unit* pTarget = m_creature->SelectAttackingTarget(ATTACKING_TARGET_RANDOM, 0, SPELL_SONIC_SHOCK, SELECT_FLAG_IN_MELEE_RANGE)) + { + if (DoCastSpellIfCan(pTarget, SPELL_SONIC_SHOCK) == CAST_OK) + m_uiSonicShockTimer = urand(3000, 10000); + } + } + else + m_uiSonicShockTimer -= uiDiff; + + if (m_uiThunderingStormTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature, SPELL_THUNDERING_STORM) == CAST_OK) + m_uiThunderingStormTimer = urand(5000, 6000); + } + else + m_uiThunderingStormTimer -= uiDiff; + } + + DoMeleeAttackIfReady(); + } +}; + +CreatureAI* GetAI_boss_murmur(Creature* pCreature) +{ + return new boss_murmurAI(pCreature); +} + +void AddSC_boss_murmur() +{ + Script* pNewScript; + + pNewScript = new Script; + pNewScript->Name = "boss_murmur"; + pNewScript->GetAI = &GetAI_boss_murmur; + pNewScript->RegisterSelf(); +} diff --git a/src/modules/SD2/scripts/outland/auchindoun/shadow_labyrinth/instance_shadow_labyrinth.cpp b/src/modules/SD2/scripts/outland/auchindoun/shadow_labyrinth/instance_shadow_labyrinth.cpp new file mode 100644 index 000000000..f7bf5bbbf --- /dev/null +++ b/src/modules/SD2/scripts/outland/auchindoun/shadow_labyrinth/instance_shadow_labyrinth.cpp @@ -0,0 +1,196 @@ +/* Copyright (C) 2006 - 2013 ScriptDev2 + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along 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 "shadow_labyrinth.h" + +/* Shadow Labyrinth encounters: +1 - Ambassador Hellmaw event +2 - Blackheart the Inciter event +3 - Grandmaster Vorpil event +4 - Murmur event +*/ + +instance_shadow_labyrinth::instance_shadow_labyrinth(Map* pMap) : ScriptedInstance(pMap) +{ + Initialize(); +} + +void instance_shadow_labyrinth::Initialize() +{ + memset(&m_auiEncounter, 0, sizeof(m_auiEncounter)); +} + +void instance_shadow_labyrinth::OnObjectCreate(GameObject* pGo) +{ + switch (pGo->GetEntry()) + { + case GO_REFECTORY_DOOR: + if (m_auiEncounter[2] == DONE) + pGo->SetGoState(GO_STATE_ACTIVE); + break; + case GO_SCREAMING_HALL_DOOR: + if (m_auiEncounter[3] == DONE) + pGo->SetGoState(GO_STATE_ACTIVE); + break; + + default: + return; + } + + m_mGoEntryGuidStore[pGo->GetEntry()] = pGo->GetObjectGuid(); +} + +void instance_shadow_labyrinth::OnCreatureCreate(Creature* pCreature) +{ + switch (pCreature->GetEntry()) + { + case NPC_VORPIL: + case NPC_HELLMAW: + m_mNpcEntryGuidStore[pCreature->GetEntry()] = pCreature->GetObjectGuid(); + break; + } +} + +void instance_shadow_labyrinth::SetData(uint32 uiType, uint32 uiData) +{ + switch (uiType) + { + case TYPE_HELLMAW: + m_auiEncounter[0] = uiData; + break; + + case TYPE_INCITER: + if (uiData == DONE) + DoUseDoorOrButton(GO_REFECTORY_DOOR); + m_auiEncounter[1] = uiData; + break; + + case TYPE_VORPIL: + if (uiData == DONE) + DoUseDoorOrButton(GO_SCREAMING_HALL_DOOR); + m_auiEncounter[2] = uiData; + break; + + case TYPE_MURMUR: + m_auiEncounter[3] = uiData; + 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_strInstData = saveStream.str(); + + SaveToDB(); + OUT_SAVE_INST_DATA_COMPLETE; + } +} + +uint32 instance_shadow_labyrinth::GetData(uint32 uiType) const +{ + switch (uiType) + { + case TYPE_HELLMAW: return m_auiEncounter[0]; + case TYPE_INCITER: return m_auiEncounter[1]; + case TYPE_VORPIL: return m_auiEncounter[2]; + case TYPE_MURMUR: return m_auiEncounter[3]; + + default: + return 0; + } +} + +void instance_shadow_labyrinth::SetData64(uint32 uiData, uint64 uiGuid) +{ + // If Hellmaw already completed, just ignore + if (GetData(TYPE_HELLMAW) == DONE) + return; + + // Note: this is handled in Acid. The purpose is check which Cabal Ritualists is alive, in case of server reset + // The function is triggered by eventAI on generic timer + if (uiData == DATA_CABAL_RITUALIST) + m_sRitualistsAliveGUIDSet.insert(ObjectGuid(uiGuid)); +} + +void instance_shadow_labyrinth::OnCreatureDeath(Creature* pCreature) +{ + // unbanish Hellmaw when all Cabal Ritualists are dead + if (pCreature->GetEntry() == NPC_CABAL_RITUALIST) + { + m_sRitualistsAliveGUIDSet.erase(pCreature->GetObjectGuid()); + + if (m_sRitualistsAliveGUIDSet.empty()) + { + if (Creature* pHellmaw = GetSingleCreatureFromStorage(NPC_HELLMAW)) + { + // yell intro and remove banish aura + DoScriptText(SAY_HELLMAW_INTRO, pHellmaw); + pHellmaw->GetMotionMaster()->MoveWaypoint(); + pHellmaw->RemoveAurasDueToSpell(SPELL_BANISH); + } + } + } +} + +void instance_shadow_labyrinth::Load(const char* chrIn) +{ + if (!chrIn) + { + 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 < 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* pNewScript; + + pNewScript = new Script; + pNewScript->Name = "instance_shadow_labyrinth"; + pNewScript->GetInstanceData = &GetInstanceData_instance_shadow_labyrinth; + pNewScript->RegisterSelf(); +} diff --git a/src/modules/SD2/scripts/outland/auchindoun/shadow_labyrinth/shadow_labyrinth.h b/src/modules/SD2/scripts/outland/auchindoun/shadow_labyrinth/shadow_labyrinth.h new file mode 100644 index 000000000..fb2dc14d0 --- /dev/null +++ b/src/modules/SD2/scripts/outland/auchindoun/shadow_labyrinth/shadow_labyrinth.h @@ -0,0 +1,61 @@ +/* Copyright (C) 2006 - 2013 ScriptDev2 + * This program is free software licensed under GPL version 2 + * Please see the included DOCS/LICENSE.TXT for more information */ + +#ifndef DEF_SHADOW_LABYRINTH_H +#define DEF_SHADOW_LABYRINTH_H + +enum +{ + MAX_ENCOUNTER = 4, + + TYPE_HELLMAW = 1, + // TYPE_OVERSEER = 2, // obsolete id used by acid + TYPE_INCITER = 3, + TYPE_VORPIL = 4, + TYPE_MURMUR = 5, + + DATA_CABAL_RITUALIST = 1, // DO NOT CHANGE! Used by Acid. - used to check the Cabal Ritualists alive + + NPC_HELLMAW = 18731, + NPC_VORPIL = 18732, + NPC_CABAL_RITUALIST = 18794, + + GO_REFECTORY_DOOR = 183296, // door opened when blackheart the inciter dies + GO_SCREAMING_HALL_DOOR = 183295, // door opened when grandmaster vorpil dies + + SAY_HELLMAW_INTRO = -1555000, + + SPELL_BANISH = 30231, // spell is handled in creature_template_addon; +}; + +class instance_shadow_labyrinth : public ScriptedInstance +{ + public: + instance_shadow_labyrinth(Map* pMap); + + void Initialize() override; + + void OnObjectCreate(GameObject* pGo) override; + void OnCreatureCreate(Creature* pCreature) override; + + void OnCreatureDeath(Creature* pCreature) override; + + void SetData(uint32 uiType, uint32 uiData) override; + uint32 GetData(uint32 uiType) const override; + + void SetData64(uint32 uiType, uint64 uiGuid) override; + + const char* Save() const override { return m_strInstData.c_str(); } + void Load(const char* chrIn) override; + + bool IsHellmawUnbanished() { return m_sRitualistsAliveGUIDSet.empty(); } + + private: + uint32 m_auiEncounter[MAX_ENCOUNTER]; + std::string m_strInstData; + + GuidSet m_sRitualistsAliveGUIDSet; +}; + +#endif diff --git a/src/modules/SD2/scripts/outland/black_temple/black_temple.cpp b/src/modules/SD2/scripts/outland/black_temple/black_temple.cpp new file mode 100644 index 000000000..3e61b0820 --- /dev/null +++ b/src/modules/SD2/scripts/outland/black_temple/black_temple.cpp @@ -0,0 +1,68 @@ +/* Copyright (C) 2006 - 2013 ScriptDev2 + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along 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 "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 = (ScriptedInstance*)pCreature->GetInstanceData(); + + if (pInstance && (pInstance->GetData(TYPE_SUPREMUS) >= DONE) && (pInstance->GetData(TYPE_NAJENTUS) >= DONE)) + pPlayer->ADD_GOSSIP_ITEM(GOSSIP_ICON_CHAT, GOSSIP_OLUM1, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 1); + + pPlayer->SEND_GOSSIP_MENU(pPlayer->GetGossipTextId(pCreature), pCreature->GetObjectGuid()); + 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* pNewScript; + + pNewScript = new Script; + pNewScript->Name = "npc_spirit_of_olum"; + pNewScript->pGossipHello = &GossipHello_npc_spirit_of_olum; + pNewScript->pGossipSelect = &GossipSelect_npc_spirit_of_olum; + pNewScript->RegisterSelf(); +} diff --git a/src/modules/SD2/scripts/outland/black_temple/black_temple.h b/src/modules/SD2/scripts/outland/black_temple/black_temple.h new file mode 100644 index 000000000..d0d401703 --- /dev/null +++ b/src/modules/SD2/scripts/outland/black_temple/black_temple.h @@ -0,0 +1,93 @@ +/* Copyright (C) 2006 - 2013 ScriptDev2 + * This program is free software licensed under GPL version 2 + * Please see the included DOCS/LICENSE.TXT for more information */ + +#ifndef DEF_BLACK_TEMPLE_H +#define DEF_BLACK_TEMPLE_H + +enum +{ + MAX_ENCOUNTER = 9, + + TYPE_NAJENTUS = 0, + TYPE_SUPREMUS = 1, + TYPE_SHADE = 2, + TYPE_GOREFIEND = 3, + TYPE_BLOODBOIL = 4, + TYPE_RELIQUIARY = 5, + TYPE_SHAHRAZ = 6, + TYPE_COUNCIL = 7, + TYPE_ILLIDAN = 8, + + // NPC_WARLORD_NAJENTUS = 22887, + // NPC_SUPREMUS = 22898, + NPC_SHADE_OF_AKAMA = 22841, + NPC_AKAMA_SHADE = 22990, + NPC_RELIQUARY_OF_SOULS = 22856, + NPC_ILLIDARI_COUNCIL = 23426, + NPC_COUNCIL_VOICE = 23499, + NPC_LADY_MALANDE = 22951, + NPC_ZEREVOR = 22950, + NPC_GATHIOS = 22949, + NPC_VERAS = 22952, + NPC_AKAMA = 23089, + NPC_MAIEV_SHADOWSONG = 23197, + NPC_ILLIDAN_STORMRAGE = 22917, + + NPC_ASH_CHANNELER = 23421, + NPC_CREATURE_GENERATOR = 23210, + NPC_ILLIDAN_DOOR_TRIGGER = 23412, + NPC_GLAIVE_TARGET = 23448, + NPC_SPIRIT_OF_OLUM = 23411, + NPC_SPIRIT_OF_UDALO = 23410, + + GO_NAJENTUS_GATE = 185483, + GO_SUPREMUS_DOORS = 185882, + GO_SHADE_OF_AKAMA = 185478, + GO_GOREFIEND_DOOR = 186153, + GO_GURTOGG_DOOR = 185892, + GO_PRE_SHAHRAZ_DOOR = 185479, + GO_POST_SHAHRAZ_DOOR = 185482, + GO_PRE_COUNCIL_DOOR = 185481, + GO_COUNCIL_DOOR = 186152, + GO_ILLIDAN_GATE = 185905, + GO_ILLIDAN_DOOR_R = 186261, + GO_ILLIDAN_DOOR_L = 186262, +}; + +class instance_black_temple : public ScriptedInstance +{ + public: + instance_black_temple(Map* pMap); + + void Initialize() override; + + bool IsEncounterInProgress() const override; + + void OnPlayerEnter(Player* pPlayer) override; + void OnCreatureCreate(Creature* pCreature) override; + void OnObjectCreate(GameObject* pGo) override; + + void SetData(uint32 uiType, uint32 uiData) override; + uint32 GetData(uint32 uiType) const override; + + void GetChannelersGuidList(GuidList& lList) { lList = m_lChannelersGuidList; } + void GetGeneratorGuidVector(GuidVector& vVector) { vVector = m_vCreatureGeneratorGuidVector; } + void GetGlaiveTargetGuidVector(GuidVector& vVector) { vVector = m_vGlaiveTargetGuidVector; } + + const char* Save() const override { return m_strInstData.c_str(); } + void Load(const char* chrIn) override; + + private: + void DoOpenPreMotherDoor(); + void DoSpawnAkamaIfCan(); + + uint32 m_auiEncounter[MAX_ENCOUNTER]; + std::string m_strInstData; + + GuidList m_lChannelersGuidList; + GuidVector m_vCreatureGeneratorGuidVector; + GuidVector m_vGlaiveTargetGuidVector; +}; + +#endif diff --git a/src/modules/SD2/scripts/outland/black_temple/boss_bloodboil.cpp b/src/modules/SD2/scripts/outland/black_temple/boss_bloodboil.cpp new file mode 100644 index 000000000..aa8337a31 --- /dev/null +++ b/src/modules/SD2/scripts/outland/black_temple/boss_bloodboil.cpp @@ -0,0 +1,304 @@ +/* Copyright (C) 2006 - 2013 ScriptDev2 + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with 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: 90 +SDComment: Timers may need adjustments. +SDCategory: Black Temple +EndScriptData */ + +#include "precompiled.h" +#include "black_temple.h" + +enum +{ + // Speech'n'Sound + SAY_AGGRO = -1564029, + SAY_SLAY1 = -1564030, + SAY_SLAY2 = -1564031, + SAY_SPECIAL1 = -1564032, + SAY_SPECIAL2 = -1564033, + SAY_ENRAGE1 = -1564034, + SAY_ENRAGE2 = -1564035, + SAY_DEATH = -1564036, + + // Spells + // Phase 1 + SPELL_FEL_ACID_1 = 40508, + SPELL_ARCING_SMASH_1 = 40457, + SPELL_EJECT_1 = 40486, + SPELL_ACIDIC_WOUND = 40481, + SPELL_BLOODBOIL = 42005, + SPELL_BEWILDERING_STRIKE = 40491, + + // Phase 2 + SPELL_ACID_GEYSER = 40630, + SPELL_FEL_ACID_2 = 40595, + SPELL_ARCING_SMASH_2 = 40599, + SPELL_EJECT_2 = 40597, + SPELL_INSIGNIFIGANCE = 40618, + SPELL_FEL_RAGE = 40594, + SPELL_FEL_RAGE_PLAYER_1 = 40604, + SPELL_FEL_RAGE_PLAYER_2 = 40616, + SPELL_FEL_RAGE_PLAYER_3 = 41625, + SPELL_FEL_RAGE_4 = 40617, // spell not confirmed + SPELL_FEL_RAGE_5 = 46787, // spell not confirmed + SPELL_TAUNT_GURTOGG = 40603, + + // Other spells + SPELL_CHARGE = 40602, // spell not confirmed + SPELL_BERSERK = 27680, + + MAX_BLOODBOILS = 5, +}; + +struct boss_gurtogg_bloodboilAI : public ScriptedAI +{ + boss_gurtogg_bloodboilAI(Creature* pCreature) : ScriptedAI(pCreature) + { + m_pInstance = (ScriptedInstance*)pCreature->GetInstanceData(); + Reset(); + } + + ScriptedInstance* m_pInstance; + + uint32 m_uiBloodboilTimer; + uint32 m_uiAcidGeyserTimer; + uint32 m_uiAcidicWoundTimer; + uint32 m_uiArcingSmashTimer; + uint32 m_uiFelAcidTimer; + uint32 m_uiEjectTimer; + uint32 m_uiStrikeTimer; + uint32 m_uiPhaseChangeTimer; + uint32 m_uiBerserkTimer; + uint8 m_uiBloodboilCount; + + bool m_bIsPhase1; + + void Reset() override + { + m_uiBloodboilTimer = 10000; + m_uiBloodboilCount = 0; + m_uiAcidGeyserTimer = 1000; + m_uiAcidicWoundTimer = 6000; + m_uiArcingSmashTimer = 19000; + m_uiFelAcidTimer = 25000; + m_uiEjectTimer = 10000; + m_uiStrikeTimer = 15000; + m_uiPhaseChangeTimer = MINUTE * IN_MILLISECONDS; + m_uiBerserkTimer = 10 * MINUTE * IN_MILLISECONDS; + + m_bIsPhase1 = true; + } + + void JustReachedHome() override + { + if (m_pInstance) + m_pInstance->SetData(TYPE_BLOODBOIL, FAIL); + } + + void Aggro(Unit* /*pWho*/) override + { + DoScriptText(SAY_AGGRO, m_creature); + + if (m_pInstance) + m_pInstance->SetData(TYPE_BLOODBOIL, IN_PROGRESS); + } + + void KilledUnit(Unit* /*pVictim*/) override + { + DoScriptText(urand(0, 1) ? SAY_SLAY1 : SAY_SLAY2, m_creature); + } + + void JustDied(Unit* /*pKiller*/) override + { + if (m_pInstance) + m_pInstance->SetData(TYPE_BLOODBOIL, DONE); + + DoScriptText(SAY_DEATH, m_creature); + } + + void UpdateAI(const uint32 uiDiff) override + { + if (!m_creature->SelectHostileTarget() || !m_creature->getVictim()) + return; + + if (m_uiArcingSmashTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature, m_bIsPhase1 ? SPELL_ARCING_SMASH_1 : SPELL_ARCING_SMASH_2) == CAST_OK) + m_uiArcingSmashTimer = 10000; + } + else + m_uiArcingSmashTimer -= uiDiff; + + if (m_uiFelAcidTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature->getVictim(), m_bIsPhase1 ? SPELL_FEL_ACID_1 : SPELL_FEL_ACID_2) == CAST_OK) + m_uiFelAcidTimer = 25000; + } + else + m_uiFelAcidTimer -= uiDiff; + + // Phase 1 spells + if (m_bIsPhase1) + { + if (m_uiStrikeTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature->getVictim(), SPELL_BEWILDERING_STRIKE) == CAST_OK) + m_uiStrikeTimer = 20000; + } + else + m_uiStrikeTimer -= uiDiff; + + if (m_uiEjectTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature->getVictim(), SPELL_EJECT_1) == CAST_OK) + { + // Script effect: reduce threat on main target + m_creature->GetThreatManager().modifyThreatPercent(m_creature->getVictim(), -40); + m_uiEjectTimer = 15000; + } + } + else + m_uiEjectTimer -= uiDiff; + + if (m_uiAcidicWoundTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature->getVictim(), SPELL_ACIDIC_WOUND) == CAST_OK) + m_uiAcidicWoundTimer = 10000; + } + else + m_uiAcidicWoundTimer -= uiDiff; + + if (m_uiBloodboilTimer) + { + if (m_uiBloodboilTimer <= uiDiff) + { + if (DoCastSpellIfCan(m_creature, SPELL_BLOODBOIL) == CAST_OK) + { + ++m_uiBloodboilCount; + + // Allow only 5 Bloodboils per phase. + if (m_uiBloodboilCount == MAX_BLOODBOILS) + m_uiBloodboilTimer = 0; + else + m_uiBloodboilTimer = 10000; + } + } + else + m_uiBloodboilTimer -= uiDiff; + } + } + // Phase 2 spells + else + { + if (m_uiAcidGeyserTimer) + { + if (m_uiAcidGeyserTimer <= uiDiff) + { + if (DoCastSpellIfCan(m_creature, SPELL_ACID_GEYSER) == CAST_OK) + m_uiAcidGeyserTimer = 0; + } + else + m_uiAcidGeyserTimer -= uiDiff; + } + + if (m_uiEjectTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature->getVictim(), SPELL_EJECT_2) == CAST_OK) + m_uiEjectTimer = 15000; + } + else + m_uiEjectTimer -= uiDiff; + } + + if (m_uiPhaseChangeTimer < uiDiff) + { + if (m_bIsPhase1) + { + if (Unit* pTarget = m_creature->SelectAttackingTarget(ATTACKING_TARGET_RANDOM, 0)) + { + // Buff self + if (DoCastSpellIfCan(m_creature, SPELL_FEL_RAGE) == CAST_OK) + { + DoScriptText(urand(0, 1) ? SAY_SPECIAL1 : SAY_SPECIAL2, m_creature); + + // Debuff player + DoCastSpellIfCan(pTarget, SPELL_FEL_RAGE_PLAYER_1, CAST_TRIGGERED); + DoCastSpellIfCan(pTarget, SPELL_FEL_RAGE_PLAYER_2, CAST_TRIGGERED); + DoCastSpellIfCan(pTarget, SPELL_FEL_RAGE_PLAYER_3, CAST_TRIGGERED); + // Allow player to taunt Gurtogg + pTarget->CastSpell(m_creature, SPELL_TAUNT_GURTOGG, true); + + // Don't allow others to generate threat + DoCastSpellIfCan(m_creature, SPELL_INSIGNIFIGANCE, CAST_TRIGGERED); + + // Reset timers + m_bIsPhase1 = false; + m_uiAcidGeyserTimer = 1000; + m_uiPhaseChangeTimer = 30000; + } + } + } + else + { + // Reset timers + m_bIsPhase1 = true; + m_uiBloodboilTimer = 10000; + m_uiBloodboilCount = 0; + m_uiAcidicWoundTimer += 2000; + m_uiArcingSmashTimer += 2000; + m_uiFelAcidTimer += 2000; + m_uiEjectTimer += 2000; + m_uiPhaseChangeTimer = 60000; + } + } + else + m_uiPhaseChangeTimer -= uiDiff; + + if (m_uiBerserkTimer) + { + if (m_uiBerserkTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature, SPELL_BERSERK) == CAST_OK) + { + DoScriptText(urand(0, 1) ? SAY_ENRAGE1 : SAY_ENRAGE2, m_creature); + m_uiBerserkTimer = 0; + } + } + else + m_uiBerserkTimer -= uiDiff; + } + + DoMeleeAttackIfReady(); + } +}; + +CreatureAI* GetAI_boss_gurtogg_bloodboil(Creature* pCreature) +{ + return new boss_gurtogg_bloodboilAI(pCreature); +} + +void AddSC_boss_gurtogg_bloodboil() +{ + Script* pNewScript; + + pNewScript = new Script; + pNewScript->Name = "boss_gurtogg_bloodboil"; + pNewScript->GetAI = &GetAI_boss_gurtogg_bloodboil; + pNewScript->RegisterSelf(); +} diff --git a/src/modules/SD2/scripts/outland/black_temple/boss_illidan.cpp b/src/modules/SD2/scripts/outland/black_temple/boss_illidan.cpp new file mode 100644 index 000000000..6b1311d28 --- /dev/null +++ b/src/modules/SD2/scripts/outland/black_temple/boss_illidan.cpp @@ -0,0 +1,1726 @@ +/* Copyright (C) 2006 - 2013 ScriptDev2 + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with 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: Movement during flight phase NYI. Some other fine details may need adjustments. +SDCategory: Black Temple +EndScriptData */ + +#include "precompiled.h" +#include "black_temple.h" +#include "escort_ai.h" + +enum +{ + /************* Quotes and Sounds ***********************/ + // Intro yells and gossip + SAY_AKAMA_BEWARE = -1564120, + SAY_AKAMA_OPEN_DOOR_1 = -1564131, + SAY_AKAMA_OPEN_DOOR_2 = -1564132, + SAY_UDALO_OPEN_DOOR_3 = -1564133, + SAY_OLUM_OPEN_DOOR_4 = -1564134, + + // Gossip for when a player clicks Akama + GOSSIP_ITEM_PREPARE = -3564001, + GOSSIP_ITEM_START_EVENT = -3564002, + TEXT_ID_AKAMA_ILLIDAN_PREPARE = 10465, // ToDo: fix text id - this entry is wrong -> "The time has come to face Illidan, $N. Are you ready?" + TEXT_ID_AKAMA_ILLIDAN_START = 10835, + + // Event speech + SAY_ILLIDAN_SPEECH_1 = -1564097, + SAY_AKAMA_SPEECH_2 = -1564098, + SAY_ILLIDAN_SPEECH_3 = -1564099, + SAY_AKAMA_SPEECH_4 = -1564100, + SAY_ILLIDAN_SPEECH_5 = -1564101, // aggro + SAY_ILLIDAN_MINION = -1564121, + SAY_AKAMA_LEAVE = -1564122, + SAY_ILLIDAN_SPEECH_6 = -1564102, + SAY_MAIEV_SPEECH_7 = -1564103, + SAY_ILLIDAN_SPEECH_8 = -1564104, + SAY_MAIEV_SPEECH_9 = -1564105, + SAY_MAIEV_TRAP = -1564118, + + // Epilogue speech + SAY_MAIEV_EPILOGUE_1 = -1564107, + SAY_ILLIDAN_EPILOGUE_2 = -1564108, + SAY_MAIEV_EPILOGUE_3 = -1564109, + SAY_MAIEV_EPILOGUE_4 = -1564110, + SAY_AKAMA_EPILOGUE_5 = -1564111, + + // Combat yells + SAY_KILL1 = -1564123, + SAY_KILL2 = -1564124, + SAY_TAKEOFF = -1564125, + SAY_SUMMONFLAMES = -1564126, + SAY_EYE_BLAST = -1564127, + SAY_MORPH = -1564128, + SAY_FRENZY = -1564106, + SAY_BERSERK = -1564129, + + // Note: this yells may not be used. Need additional research + SAY_TAUNT_1 = -1564112, + SAY_TAUNT_2 = -1564113, + SAY_TAUNT_3 = -1564114, + SAY_TAUNT_4 = -1564115, + + SAY_MAIEV_TAUNT_1 = -1564116, + SAY_MAIEV_TAUNT_2 = -1564117, + SAY_MAIEV_TAUNT_3 = -1564119, + + + /************** Spells *************/ + // Normal Form + SPELL_SHEAR = 41032, // Reduces Max. Health by 60% for 7 seconds. Can stack 19 times. 1.5 second cast + SPELL_FLAME_CRASH = 40832, // Summons an invis/unselect passive mob that has an uiAura of flame in a circle around him. + SPELL_DRAW_SOUL = 40904, // 5k Shadow Damage in front of him. Heals Illidan for 100k health (script effect) + SPELL_PARASITIC_SHADOWFIEND = 41917, // DoT of 3k Shadow every 2 seconds. Lasts 10 seconds. (Script effect: Summon 2 parasites once the debuff has ticked off) + // SPELL_SUMMON_PARASITICS = 41915, // Summons 2 Parasitic Shadowfiends on the target. Handled in core. + SPELL_AGONIZING_FLAMES = 40834, // triggers 40932 + SPELL_FRENZY = 40683, // Increases damage by 50% and attack speed by 30%. 20 seconds, PHASE 5 ONLY + + // Flying (Phase 2) + SPELL_THROW_GLAIVE = 39635, // triggers 41466 - Throws the first glaive on the ground + SPELL_THROW_GLAIVE_VISUAL = 39849, // triggers 41466 - Throws the second glaive on the ground + SPELL_GLAIVE_RETURNS = 39873, // Glaive flies back to Illidan + SPELL_FIREBALL = 40598, // 2.5k-3.5k damage in 10 yard radius. 2 second cast time. + SPELL_DARK_BARRAGE = 40585, // 10 second channeled spell, 3k shadow damage per second. + SPELL_EYE_BLAST_DUMMY = 39908, // This does the blue beam channel - targets 23070 + + // Demon Form + SPELL_DEMON_TRANSFORM_1 = 40511, // start transform animation - spell sequence: 40398, 40506, 40510 - handled in core + SPELL_DEMON_TRANSFORM_2 = 40398, // Second uiPhase of animations (kneel) + SPELL_DEMON_TRANSFORM_3 = 40510, // Final uiPhase of animations (stand up and roar) + SPELL_DEMON_FORM = 40506, // Transforms into Demon Illidan. Has an Aura of Dread on him. + SPELL_SHADOW_BLAST = 41078, // 8k - 11k Shadow Damage. Targets highest threat. Has a splash effect, damaging anyone in 20 yards of the target. + SPELL_FLAME_BURST = 41126, // triggers 41131 + SPELL_SUMMON_SHADOW_DEMONS = 41117, // summons 23375 + + // Other Illidan spells + SPELL_KNEEL_INTRO = 39656, // Before beginning encounter, this is how he appears (talking to Wilson). + SPELL_SUMMMON_MAIEV = 40403, // summons 23197 + SPELL_TELEPORT_MAIEV = 41221, + SPELL_SHADOW_PRISON = 40647, // Illidan casts this spell to immobilize entire raid when he summons Maiev. + SPELL_CAGE_TRAP = 40693, // Cast by Illidan on Maiev - teleports Maiev for the trap + SPELL_DEATH = 41220, // This spell doesn't do anything except stun Illidan and set him on his knees. + SPELL_BERSERK = 45078, // Damage increased by 500%, attack speed by 150% + + + /************** Non-Illidan Spells *************/ + // Akama + SPELL_AKAMA_DOOR_FAIL = 41271, // Akama's first door attempt + SPELL_AKAMA_DOOR_CHANNEL = 41268, // Akama's channel spell on the door before the Temple Summit + SPELL_DEATHSWORN_DOOR_CHANNEL = 41269, // Olum and Udalo's channel spell on the door before the Temple Summit + SPELL_HEALING_POTION = 40535, // Akama uses this to heal himself to full. + SPELL_CHAIN_LIGHTNING = 40536, + + // Maiev + SPELL_SHADOW_STRIKE = 40685, + SPELL_THROW_DAGGER = 41152, + SPELL_CAGE_TRAP_SUMMON = 40694, // summons npc 23304 and go 185916 + SPELL_TELEPORT_VISUAL = 41236, + + // Misc Summoned + SPELL_FLAME_CRASH_EFFECT = 40836, // Firey blue ring of circle that the other flame crash summons + SPELL_EYE_BLAST_TRIGGER = 40017, // This summons Demon Form every few seconds and deals ~20k damage in its radius + // SPELL_DEMON_FIRE = 40029, // Blue fire trail left by Eye Blast. Deals 2k per second if players stand on it. + SPELL_BLAZE_EFFECT = 40610, // Green flame on the ground, triggers damage (5k) every few seconds + + // Blade of Azzinoth + SPELL_RANGE_MARKER = 41997, // Dummy effect used by the Blade of Azzinoth to check the range of the Azzinoth flame - needs core support + SPELL_SUMMON_TEAR_AZZINOTH = 39855, // Summons 22997 + SPELL_AZZINOTH_CHANNEL = 39857, // Glaives cast it on Flames + + // Flame of Azzinoth + SPELL_FLAME_BLAST = 40631, // Flames of Azzinoth use this. Frontal cone AoE 7k-9k damage. + SPELL_CHARGE = 42003, // Flames of Azzinoth charges whoever is too far from them. They enrage after this + SPELL_UNCAGED_WRATH = 39869, + SPELL_BLAZE = 40637, // summons 23259 + + // Shadow Demon + SPELL_SHADOW_DEMON_PASSIVE = 41079, // Adds the "shadowform" uiAura to Shadow Demons. + SPELL_CONSUME_SOUL = 41080, // Once the Shadow Demons reach their target, they use this to kill them + SPELL_PARALYZE = 41083, // Shadow Demons cast this on their target + + // Cage spells + SPELL_CAGE_TRAP_PERIODIC = 40760, // purpose unk + SPELL_CAGE_TRAP_DUMMY = 40761, // purpose unk + SPELL_CAGED = 40695, // Caged Trap triggers will cast this on Illidan if he is within 3 yards + + + /************** Creature Summons **************/ + NPC_ILLIDARI_ELITE = 23226, // attacks Akama on the stairs + NPC_FLAME_CRASH = 23336, // has aura 40836 + // NPC_PARASITIC_SHADOWFIEND = 23498, // has aura 41913 (in c_t_a) + NPC_BLADE_OF_AZZINOTH = 22996, // has aura 41997 and summons 22997 on spawn + NPC_FLAME_OF_AZZINOTH = 22997, + NPC_ILLIDAN_TARGET = 23070, // the eye blast target - has aura 40017 + // NPC_DEMON_FIRE = 23069, // has aura 40029 (in EventAI) + NPC_BLAZE = 23259, // has aura 40610 + NPC_SHADOW_DEMON = 23375, + // NPC_CAGE_TRAP_DISTURB_TRIGGER = 23304, + + GO_CAGE_TRAP = 185916, + + /************** Others **************/ + EQUIP_ID_MAIN_HAND = 32837, + EQUIP_ID_OFF_HAND = 32838, + + MAX_ILLIDARI_ELITES = 10, + MAX_CAGE_SPELLS = 8, + MAX_FLAME_AZZINOTH = 2, + + DUMMY_EMOTE_ID_1 = 1, + DUMMY_EMOTE_ID_2 = 2, + DUMMY_EMOTE_ID_3 = 3, +}; + +static const uint32 aCagedSummonSpells[MAX_CAGE_SPELLS] = { 40696, 40697, 40698, 40699, 40700, 40701, 40702, 40703 }; +static const uint32 aCagedVisualSpells[MAX_CAGE_SPELLS] = { 40704, 40707, 40708, 40709, 40710, 40711, 40712, 40713 }; + +static const DialogueEntry aIntroDialogue[] = +{ + {SAY_AKAMA_OPEN_DOOR_1, NPC_AKAMA, 4000}, + {SPELL_AKAMA_DOOR_FAIL, 0, 9000}, + {SAY_AKAMA_OPEN_DOOR_2, NPC_AKAMA, 6000}, + {NPC_SPIRIT_OF_OLUM, 0, 2000}, + {SAY_UDALO_OPEN_DOOR_3, NPC_SPIRIT_OF_UDALO, 2000}, + {SAY_OLUM_OPEN_DOOR_4, NPC_SPIRIT_OF_OLUM, 4000}, + {SPELL_AKAMA_DOOR_CHANNEL, 0, 11000}, + {GO_ILLIDAN_GATE, 0, 4000}, + {NPC_SPIRIT_OF_UDALO, 0, 0}, + {0, 0, 0}, +}; + +static const DialogueEntry aEventDialogue[] = +{ + // Akama intro + {NPC_AKAMA, 0, 1000}, + {SAY_ILLIDAN_SPEECH_1, NPC_ILLIDAN_STORMRAGE, 3000}, + {EMOTE_ONESHOT_QUESTION, 0, 3000}, + {DUMMY_EMOTE_ID_1, 0, 3000}, + {DUMMY_EMOTE_ID_2, 0, 3000}, + {SAY_AKAMA_SPEECH_2, NPC_AKAMA, 10000}, + {SAY_ILLIDAN_SPEECH_3, NPC_ILLIDAN_STORMRAGE, 3000}, + {DUMMY_EMOTE_ID_3, 0, 4000}, + {SAY_AKAMA_SPEECH_4, NPC_AKAMA, 4000}, + {EQUIP_ID_MAIN_HAND, 0, 1000}, + {SAY_ILLIDAN_SPEECH_5, NPC_ILLIDAN_STORMRAGE, 4000}, + {NPC_ILLIDAN_STORMRAGE, 0, 0}, + // Akama leaves fight + {SAY_ILLIDAN_MINION, NPC_ILLIDAN_STORMRAGE, 8000}, + {SAY_AKAMA_LEAVE, NPC_AKAMA, 0}, + // Maiev cutscene + {SAY_ILLIDAN_SPEECH_6, NPC_ILLIDAN_STORMRAGE, 7000}, + {SPELL_SUMMMON_MAIEV, 0, 1000}, + {SAY_MAIEV_SPEECH_7, NPC_MAIEV_SHADOWSONG, 2000}, + {EMOTE_ONESHOT_EXCLAMATION, 0, 6000}, + {SAY_ILLIDAN_SPEECH_8, NPC_ILLIDAN_STORMRAGE, 7000}, + {SAY_MAIEV_SPEECH_9, NPC_MAIEV_SHADOWSONG, 2000}, + {EMOTE_ONESHOT_YES, 0, 5000}, + {NPC_MAIEV_SHADOWSONG, 0, 0}, + {0, 0, 0}, +}; + +static const DialogueEntry aEpilogueDialogue[] = +{ + {SAY_MAIEV_EPILOGUE_1, NPC_MAIEV_SHADOWSONG, 6000}, + {SAY_ILLIDAN_EPILOGUE_2, NPC_ILLIDAN_STORMRAGE, 18000}, + {NPC_ILLIDAN_STORMRAGE, 0, 2000}, + {SAY_MAIEV_EPILOGUE_3, NPC_MAIEV_SHADOWSONG, 13000}, + {SAY_MAIEV_EPILOGUE_4, NPC_MAIEV_SHADOWSONG, 2000}, + {SPELL_TELEPORT_VISUAL, 0, 0}, + {0, 0, 0}, +}; + +/*** Phase Names ***/ +enum Phase +{ + PHASE_AKAMA = 1, + PHASE_BLADES = 2, + PHASE_DUAL_NORMAL = 3, + PHASE_DUAL_DEMON = 4, + PHASE_MAIEV = 5, + PHASE_TRANSITION = 6, +}; + +struct Locations +{ + float fX, fY, fZ; +}; + +static const Locations aCenterLoc[] = +{ + {705.012f, 305.721f, 354.723f}, // front location + {676.740f, 305.297f, 353.192f}, // center location +}; + +static const Locations aIllidariElitesPos[MAX_ILLIDARI_ELITES] = +{ + {743.9686f, 289.6447f, 311.1807f}, + {753.8425f, 286.562f, 310.9353f}, + {745.2552f, 322.1574f, 310.4596f}, + {745.3237f, 283.986f, 309.2765f}, + {750.0472f, 282.3274f, 309.4353f}, + {747.0576f, 326.4268f, 309.0688f}, + {751.0878f, 327.6505f, 309.4576f}, + {748.8422f, 288.062f, 310.9782f}, + {750.0322f, 323.6064f, 310.2757f}, + {754.0332f, 325.8136f, 310.3195f}, +}; + +static const Locations aEyeBlastPos[] = +{ + // spawn + {650.600f, 258.124f, 352.996f}, // back left + {651.867f, 353.212f, 352.996f}, // back right + {710.010f, 266.950f, 352.996f}, // front left + {711.003f, 343.562f, 352.996f}, // front right + // target - left + {742.212f, 338.333f, 352.996f}, // front right + {674.559f, 375.761f, 352.996f}, // back right + // target - right + {741.545f, 270.640f, 352.996f}, // front left + {671.943f, 235.718f, 352.996f}, // back left + // center back + {639.511f, 305.852f, 353.264f} +}; + +/*###### +## boss_illidan_stormrage +######*/ + +struct boss_illidan_stormrageAI : public ScriptedAI, private DialogueHelper +{ + boss_illidan_stormrageAI(Creature* pCreature) : ScriptedAI(pCreature), + DialogueHelper(aEventDialogue) + { + m_pInstance = (instance_black_temple*)pCreature->GetInstanceData(); + InitializeDialogueHelper(m_pInstance); + Reset(); + } + + instance_black_temple* m_pInstance; + + Phase m_uiPhase; + uint32 m_uiBerserkTimer; + + bool m_bHasSummonedElites; + float m_fTargetMoveX, m_fTargetMoveY, m_fTargetMoveZ; + + uint32 m_uiShearTimer; + uint32 m_uiDrawSoulTimer; + uint32 m_uiFlameCrashTimer; + uint32 m_uiShadowFiendTimer; + + uint32 m_uiFireballTimer; + uint32 m_uiEyeBlastTimer; + uint32 m_uiDarkBarrageTimer; + uint32 m_uiSummonBladesTimer; // Animate summoning the Blades of Azzinoth in Phase 2 + uint32 m_uiCenterMoveTimer; + uint32 m_uiLandTimer; // This is used at the end of uiPhase 2 to signal Illidan landing after Flames are dead + uint8 m_uiLandStage; + uint8 m_uiFlameAzzinothKilled; + + uint32 m_uiAgonizingFlamesTimer; + uint32 m_uiTransformTimer; + + uint32 m_uiShadowBlastTimer; + uint32 m_uiFlameBurstTimer; + uint32 m_uiShadowDemonTimer; + Phase m_uiPrevPhase; // store the previous phase in transition + + uint32 m_uiEnrageTimer; + uint32 m_uiTrapTimer; + + GuidList m_lBladesGuidList; + + void Reset() override + { + m_uiPhase = PHASE_AKAMA; + m_uiBerserkTimer = 25 * MINUTE * IN_MILLISECONDS; + + m_bHasSummonedElites = false; + + m_uiShearTimer = urand(10000, 15000); + m_uiFlameCrashTimer = 30000; + m_uiShadowFiendTimer = 25000; + m_uiDrawSoulTimer = 35000; + + m_uiFlameAzzinothKilled = 0; + m_uiSummonBladesTimer = 0; + m_uiCenterMoveTimer = 0; + m_uiFireballTimer = 5000; + m_uiDarkBarrageTimer = 45000; + m_uiEyeBlastTimer = 15000; + m_uiLandTimer = 0; + m_uiLandStage = 0; + + m_uiAgonizingFlamesTimer = 35000; + m_uiTransformTimer = 0; + + m_uiShadowBlastTimer = urand(1000, 2000); + m_uiFlameBurstTimer = 10000; + m_uiShadowDemonTimer = 30000; + + m_uiEnrageTimer = 40000; + m_uiTrapTimer = urand(30000, 40000); + + m_lBladesGuidList.clear(); + + // Reset boss + m_creature->SetLevitate(false); + SetCombatMovement(true); + + m_creature->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_PASSIVE); + m_creature->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE); + SetEquipmentSlots(false, EQUIP_UNEQUIP, EQUIP_UNEQUIP, EQUIP_NO_CHANGE); + } + + void GetAIInformation(ChatHandler& reader) override + { + reader.PSendSysMessage("Boss Illidan, current uiPhase = %u", m_uiPhase); + } + + void Aggro(Unit* /*pWho*/) override + { + if (m_pInstance) + m_pInstance->SetData(TYPE_ILLIDAN, IN_PROGRESS); + } + + // Do not attack using LoS function. The attack is triggered in script + void MoveInLineOfSight(Unit* /*pWho*/) override { } + + void JustReachedHome() override + { + if (m_pInstance) + m_pInstance->SetData(TYPE_ILLIDAN, FAIL); + } + + void JustDied(Unit* /*pKiller*/) override + { + m_creature->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE); + + if (m_pInstance) + m_pInstance->SetData(TYPE_ILLIDAN, DONE); + } + + void KilledUnit(Unit* pVictim) override + { + if (pVictim->GetTypeId() != TYPEID_PLAYER) + return; + + DoScriptText(urand(0, 1) ? SAY_KILL1 : SAY_KILL2, m_creature); + } + + void DamageTaken(Unit* /*pDealer*/, uint32& uiDamage) override + { + if (uiDamage < m_creature->GetHealth()) + return; + + // Make sure it won't die by accident + if (m_creature->HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE)) + { + uiDamage = 0; + return; + }; + + uiDamage = 0; + m_creature->InterruptNonMeleeSpells(true); + m_creature->SetHealth(1); + m_creature->StopMoving(); + m_creature->RemoveAllAurasOnDeath(); + m_creature->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE); + m_creature->ClearAllReactives(); + + DoCastSpellIfCan(m_creature, SPELL_DEATH, CAST_TRIGGERED); + DoCastSpellIfCan(m_creature, SPELL_TELEPORT_MAIEV, CAST_TRIGGERED); + m_creature->GetMotionMaster()->Clear(); + m_creature->GetMotionMaster()->MoveIdle(); + + // Signal Maiev to start the outro dialogue + if (m_pInstance) + { + if (Creature* pMaiev = m_pInstance->GetSingleCreatureFromStorage(NPC_MAIEV_SHADOWSONG)) + pMaiev->AI()->KilledUnit(m_creature); + } + } + + void JustDidDialogueStep(int32 iEntry) override + { + switch (iEntry) + { + case NPC_AKAMA: + if (m_pInstance) + { + if (Creature* pAkama = m_pInstance->GetSingleCreatureFromStorage(NPC_AKAMA)) + m_creature->SetFacingToObject(pAkama); + } + m_creature->RemoveAurasDueToSpell(SPELL_KNEEL_INTRO); + break; + case EMOTE_ONESHOT_QUESTION: + case DUMMY_EMOTE_ID_1: + case DUMMY_EMOTE_ID_2: + case DUMMY_EMOTE_ID_3: + m_creature->HandleEmote(EMOTE_ONESHOT_QUESTION); + break; + case EQUIP_ID_MAIN_HAND: + SetEquipmentSlots(false, EQUIP_ID_MAIN_HAND, EQUIP_ID_OFF_HAND, EQUIP_NO_CHANGE); + break; + case NPC_ILLIDAN_STORMRAGE: + m_creature->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_PASSIVE); + m_creature->SetInCombatWithZone(); + if (m_pInstance) + { + if (Creature* pAkama = m_pInstance->GetSingleCreatureFromStorage(NPC_AKAMA)) + { + pAkama->AI()->AttackStart(m_creature); + AttackStart(pAkama); + } + } + break; + case SAY_AKAMA_LEAVE: + DoResetThreat(); + if (m_pInstance) + { + // Remove Akama from threat list and allow him to fight the Illidari elites + if (Creature* pAkama = m_pInstance->GetSingleCreatureFromStorage(NPC_AKAMA)) + { + pAkama->AI()->EnterEvadeMode(); + m_creature->GetThreatManager().modifyThreatPercent(pAkama, -101); + } + } + break; + case SPELL_SUMMMON_MAIEV: + DoCastSpellIfCan(m_creature, SPELL_SUMMMON_MAIEV); + break; + case EMOTE_ONESHOT_EXCLAMATION: + if (m_pInstance) + { + if (Creature* pMaiev = m_pInstance->GetSingleCreatureFromStorage(NPC_MAIEV_SHADOWSONG)) + pMaiev->HandleEmote(EMOTE_ONESHOT_EXCLAMATION); + } + break; + case EMOTE_ONESHOT_YES: + if (m_pInstance) + { + if (Creature* pMaiev = m_pInstance->GetSingleCreatureFromStorage(NPC_MAIEV_SHADOWSONG)) + pMaiev->HandleEmote(EMOTE_ONESHOT_YES); + } + break; + case NPC_MAIEV_SHADOWSONG: + // Resume combat and attack Maiev + m_creature->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE); + m_creature->SetTargetGuid(m_creature->getVictim()->GetObjectGuid()); + SetCombatMovement(false); + m_creature->GetMotionMaster()->Clear(); + m_creature->GetMotionMaster()->MoveChase(m_creature->getVictim()); + if (m_pInstance) + { + if (Creature* pMaiev = m_pInstance->GetSingleCreatureFromStorage(NPC_MAIEV_SHADOWSONG)) + pMaiev->AI()->AttackStart(m_creature); + } + m_uiPhase = PHASE_MAIEV; + m_uiTransformTimer = 60000; + break; + } + } + + void JustSummoned(Creature* pSummoned) override + { + switch (pSummoned->GetEntry()) + { + case NPC_FLAME_CRASH: + pSummoned->CastSpell(pSummoned, SPELL_FLAME_CRASH_EFFECT, false); + break; + case NPC_BLADE_OF_AZZINOTH: + pSummoned->CastSpell(pSummoned, SPELL_RANGE_MARKER, true); + pSummoned->CastSpell(pSummoned, SPELL_SUMMON_TEAR_AZZINOTH, true); + m_lBladesGuidList.push_back(pSummoned->GetObjectGuid()); + break; + case NPC_ILLIDAN_TARGET: + pSummoned->SetWalk(false); + pSummoned->CastSpell(pSummoned, SPELL_EYE_BLAST_TRIGGER, true); + pSummoned->GetMotionMaster()->MovePoint(0, m_fTargetMoveX, m_fTargetMoveY, m_fTargetMoveZ); + DoCastSpellIfCan(pSummoned, SPELL_EYE_BLAST_DUMMY, CAST_TRIGGERED); + break; + case NPC_SHADOW_DEMON: + pSummoned->CastSpell(pSummoned, SPELL_SHADOW_DEMON_PASSIVE, true); + if (Unit* pTarget = m_creature->SelectAttackingTarget(ATTACKING_TARGET_RANDOM, 0, SPELL_PARALYZE, SELECT_FLAG_PLAYER)) + { + // Dummy attack function - used only to set the target + pSummoned->AI()->AttackStart(pTarget); + pSummoned->CastSpell(pTarget, SPELL_PARALYZE, true); + + // Move towards target (which is stunned) + float fX, fY, fZ; + pTarget->GetContactPoint(pSummoned, fX, fY, fZ); + pSummoned->GetMotionMaster()->MovePoint(1, fX, fY, fZ); + } + break; + case NPC_MAIEV_SHADOWSONG: + pSummoned->SetFacingToObject(m_creature); + m_creature->SetTargetGuid(pSummoned->GetObjectGuid()); + break; + } + } + + // Wrapper to start the combat dialogue + void DoStartCombatEvent() { StartNextDialogueText(NPC_AKAMA); } + + // Wrapper to land Illidan when both flames are killed + void DoInformFlameKilled() + { + // Land Illidan if both Flames are killed + ++m_uiFlameAzzinothKilled; + + if (m_uiFlameAzzinothKilled == MAX_FLAME_AZZINOTH) + { + m_uiLandTimer = 5000; + m_uiPhase = PHASE_TRANSITION; + m_creature->InterruptNonMeleeSpells(false); + } + } + + // Wrapper to handle the Eye Blast cast + bool DoCastEyeBlastIfCan() + { + if (m_creature->IsNonMeleeSpellCasted(false)) + return false; + + DoScriptText(SAY_EYE_BLAST, m_creature); + + // Set spawn and target loc + uint8 uiSpawnLoc = urand(0, 3); + uint8 uiTargetLoc = 0; + switch (uiSpawnLoc) + { + case 0: uiTargetLoc = urand(4, 5); break; + case 1: uiTargetLoc = urand(6, 7); break; + case 2: uiTargetLoc = urand(0, 1) ? 5 : 8; break; + case 3: uiTargetLoc = urand(7, 8); break; + } + + m_fTargetMoveX = aEyeBlastPos[uiTargetLoc].fX; + m_fTargetMoveY = aEyeBlastPos[uiTargetLoc].fY; + m_fTargetMoveZ = aEyeBlastPos[uiTargetLoc].fZ; + m_creature->SummonCreature(NPC_ILLIDAN_TARGET, aEyeBlastPos[uiSpawnLoc].fX, aEyeBlastPos[uiSpawnLoc].fY, aEyeBlastPos[uiSpawnLoc].fZ, 0, TEMPSUMMON_TIMED_DESPAWN, 15000); + + return true; + } + + void UpdateAI(const uint32 uiDiff) override + { + DialogueUpdate(uiDiff); + + if (!m_creature->SelectHostileTarget() || !m_creature->getVictim()) + return; + + // Make Akama evade combat at 85% + if (!m_bHasSummonedElites && m_creature->GetHealthPercent() < 85.0f) + { + StartNextDialogueText(SAY_ILLIDAN_MINION); + m_bHasSummonedElites = true; + } + + // Phase 1 to 2 transition + if (m_uiPhase == PHASE_AKAMA && m_creature->GetHealthPercent() < 65.0f) + { + DoScriptText(SAY_TAKEOFF, m_creature); + m_uiSummonBladesTimer = 10000; + m_uiCenterMoveTimer = 2000; + m_uiPhase = PHASE_BLADES; + + m_creature->RemoveAllAuras(); + m_creature->SetLevitate(true); + SetCombatMovement(false); + m_creature->GetMotionMaster()->Clear(); + m_creature->HandleEmote(EMOTE_ONESHOT_LIFTOFF); + m_creature->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE); + return; + } + + // Summon Maiev at 30% hp + if (m_uiPhase == PHASE_DUAL_NORMAL && m_creature->GetHealthPercent() <= 30.0f) + { + if (DoCastSpellIfCan(m_creature, SPELL_SHADOW_PRISON, CAST_INTERRUPT_PREVIOUS) == CAST_OK) + { + StartNextDialogueText(SAY_ILLIDAN_SPEECH_6); + m_creature->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE); + + SetCombatMovement(false); + m_creature->GetMotionMaster()->Clear(); + m_creature->GetMotionMaster()->MoveIdle(); + + m_uiPhase = PHASE_TRANSITION; + m_uiTransformTimer = 0; + } + return; + } + + if (m_uiBerserkTimer) + { + if (m_uiBerserkTimer <= uiDiff) + { + if (DoCastSpellIfCan(m_creature, SPELL_BERSERK) == CAST_OK) + { + DoScriptText(SAY_BERSERK, m_creature); + m_uiBerserkTimer = 0; + } + } + else + m_uiBerserkTimer -= uiDiff; + } + + switch (m_uiPhase) + { + case PHASE_MAIEV: + + // Phase 5 spell only + if (m_uiEnrageTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature, SPELL_FRENZY) == CAST_OK) + { + DoScriptText(SAY_FRENZY, m_creature); + m_uiEnrageTimer = 40000; + } + } + else + m_uiEnrageTimer -= uiDiff; + + if (m_uiTrapTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature, SPELL_CAGE_TRAP) == CAST_OK) + m_uiTrapTimer = urand(40000, 50000); + } + else + m_uiTrapTimer -= uiDiff; + + // no break; + case PHASE_DUAL_NORMAL: + + // Phase 3 and 5 spells + if (m_uiAgonizingFlamesTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature, SPELL_AGONIZING_FLAMES) == CAST_OK) + m_uiAgonizingFlamesTimer = 60000; + } + else + m_uiAgonizingFlamesTimer -= uiDiff; + + if (m_uiTransformTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature, SPELL_DEMON_TRANSFORM_1) == CAST_OK) + { + DoScriptText(SAY_MORPH, m_creature); + + m_uiPrevPhase = m_uiPhase; + m_uiPhase = PHASE_TRANSITION; + m_uiTransformTimer = 12500; + m_uiFlameBurstTimer = 10000; + m_uiShadowDemonTimer = 30000; + + SetCombatMovement(false); + m_creature->GetMotionMaster()->Clear(); + m_creature->GetMotionMaster()->MoveIdle(); + } + } + else + m_uiTransformTimer -= uiDiff; + + // no break; + case PHASE_AKAMA: + + if (m_uiShearTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature->getVictim(), SPELL_SHEAR) == CAST_OK) + m_uiShearTimer = urand(10000, 15000); + } + else + m_uiShearTimer -= uiDiff; + + if (m_uiFlameCrashTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature, SPELL_FLAME_CRASH) == CAST_OK) + m_uiFlameCrashTimer = urand(25000, 35000); + } + else + m_uiFlameCrashTimer -= uiDiff; + + if (m_uiShadowFiendTimer < uiDiff) + { + if (Unit* pTarget = m_creature->SelectAttackingTarget(ATTACKING_TARGET_RANDOM, 1, SPELL_PARASITIC_SHADOWFIEND, SELECT_FLAG_PLAYER)) + { + if (DoCastSpellIfCan(pTarget, SPELL_PARASITIC_SHADOWFIEND) == CAST_OK) + m_uiShadowFiendTimer = 40000; + } + } + else + m_uiShadowFiendTimer -= uiDiff; + + if (m_uiDrawSoulTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature, SPELL_DRAW_SOUL) == CAST_OK) + m_uiDrawSoulTimer = 35000; + } + else + m_uiDrawSoulTimer -= uiDiff; + + DoMeleeAttackIfReady(); + + break; + case PHASE_BLADES: + + if (m_uiCenterMoveTimer) + { + if (m_uiCenterMoveTimer <= uiDiff) + { + // The movement is not very clear - it may be possible that he is moving around the center during this phase + // ToDo: this requires additional resarch. For now bring him near home position + m_creature->GetMotionMaster()->MovePoint(0, aCenterLoc[0].fX, aCenterLoc[0].fY, aCenterLoc[0].fZ); + m_uiCenterMoveTimer = 0; + } + else + m_uiCenterMoveTimer -= uiDiff; + } + + if (m_uiSummonBladesTimer) + { + if (m_uiSummonBladesTimer <= uiDiff) + { + if (m_pInstance) + { + // Need to provide explicit glaive targets + GuidVector vTargetsVect; + m_pInstance->GetGlaiveTargetGuidVector(vTargetsVect); + + Creature* pGlaive1 = m_creature->GetMap()->GetCreature(vTargetsVect[0]); + Creature* pGlaive2 = m_creature->GetMap()->GetCreature(vTargetsVect[1]); + if (!pGlaive1 || !pGlaive2) + return; + + // Summon both blades and remove them from equipment + if (DoCastSpellIfCan(pGlaive1, SPELL_THROW_GLAIVE_VISUAL) == CAST_OK) + { + DoCastSpellIfCan(pGlaive2, SPELL_THROW_GLAIVE, CAST_TRIGGERED); + SetEquipmentSlots(false, EQUIP_UNEQUIP, EQUIP_UNEQUIP, EQUIP_NO_CHANGE); + m_uiSummonBladesTimer = 0; + } + } + } + else + m_uiSummonBladesTimer -= uiDiff; + + // no other spells during takeoff + return; + } + + if (m_uiFireballTimer < uiDiff) + { + if (Unit* pTarget = m_creature->SelectAttackingTarget(ATTACKING_TARGET_RANDOM, 0)) + { + if (DoCastSpellIfCan(pTarget, SPELL_FIREBALL) == CAST_OK) + m_uiFireballTimer = urand(2000, 3000); + } + } + else + m_uiFireballTimer -= uiDiff; + + if (m_uiDarkBarrageTimer < uiDiff) + { + if (Unit* pTarget = m_creature->SelectAttackingTarget(ATTACKING_TARGET_RANDOM, 0)) + { + if (DoCastSpellIfCan(pTarget, SPELL_DARK_BARRAGE) == CAST_OK) + m_uiDarkBarrageTimer = 45000; + } + } + else + m_uiDarkBarrageTimer -= uiDiff; + + if (m_uiEyeBlastTimer < uiDiff) + { + if (DoCastEyeBlastIfCan()) + { + m_uiEyeBlastTimer = urand(35000, 45000); + m_uiFireballTimer = 15000; + } + } + else + m_uiEyeBlastTimer -= uiDiff; + + break; + case PHASE_DUAL_DEMON: + + // Handle phase transition at 30% + if (m_uiPrevPhase == PHASE_DUAL_NORMAL && m_creature->GetHealthPercent() <= 30.0f) + { + if (DoCastSpellIfCan(m_creature, SPELL_DEMON_TRANSFORM_1) == CAST_OK) + { + m_uiTransformTimer = 12500; + m_uiPhase = PHASE_TRANSITION; + + SetCombatMovement(true); + m_creature->GetMotionMaster()->Clear(); + m_creature->GetMotionMaster()->MoveChase(m_creature->getVictim()); + } + } + + if (m_uiTransformTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature, SPELL_DEMON_TRANSFORM_1) == CAST_OK) + { + m_uiTransformTimer = 12500; + m_uiPhase = PHASE_TRANSITION; + + SetCombatMovement(true); + m_creature->GetMotionMaster()->Clear(); + m_creature->GetMotionMaster()->MoveChase(m_creature->getVictim()); + } + } + else + m_uiTransformTimer -= uiDiff; + + if (m_uiShadowDemonTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature, SPELL_SUMMON_SHADOW_DEMONS) == CAST_OK) + m_uiShadowDemonTimer = 60000; + } + else + m_uiShadowDemonTimer -= uiDiff; + + if (m_uiShadowBlastTimer < uiDiff) + { + if (Unit* pTarget = m_creature->SelectAttackingTarget(ATTACKING_TARGET_TOPAGGRO, 0)) + { + if (DoCastSpellIfCan(pTarget, SPELL_SHADOW_BLAST) == CAST_OK) + m_uiShadowBlastTimer = urand(2000, 3000); + } + } + else + m_uiShadowBlastTimer -= uiDiff; + + if (m_uiFlameBurstTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature, SPELL_FLAME_BURST) == CAST_OK) + m_uiFlameBurstTimer = 20000; + } + else + m_uiFlameBurstTimer -= uiDiff; + + break; + case PHASE_TRANSITION: + + if (m_uiLandTimer) + { + if (m_uiLandTimer <= uiDiff) + { + switch (m_uiLandStage) + { + case 0: + // Despawn the blades + for (GuidList::const_iterator itr = m_lBladesGuidList.begin(); itr != m_lBladesGuidList.end(); ++itr) + { + if (Creature* pBlade = m_creature->GetMap()->GetCreature(*itr)) + { + pBlade->CastSpell(m_creature, SPELL_GLAIVE_RETURNS, true); + pBlade->ForcedDespawn(500); + } + } + m_uiLandTimer = 5000; + break; + case 1: + // Set the equipment and land + SetEquipmentSlots(false, EQUIP_ID_MAIN_HAND, EQUIP_ID_OFF_HAND, EQUIP_NO_CHANGE); + + m_creature->SetLevitate(false); + m_creature->HandleEmote(EMOTE_ONESHOT_LAND); + m_uiLandTimer = 2000; + break; + case 2: + // Start phase 3 + DoResetThreat(); + m_uiPhase = PHASE_DUAL_NORMAL; + + SetCombatMovement(true); + m_creature->GetMotionMaster()->MoveChase(m_creature->getVictim()); + m_creature->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE); + m_uiTransformTimer = 64000; + m_uiLandTimer = 0; + break; + } + ++m_uiLandStage; + } + else + m_uiLandTimer -= uiDiff; + } + + if (m_uiTransformTimer) + { + if (m_uiTransformTimer <= uiDiff) + { + // Drop the transform time from the spell timers + if (m_creature->HasAura(SPELL_DEMON_FORM)) + { + DoResetThreat(); + m_uiPhase = PHASE_DUAL_DEMON; + m_uiShadowDemonTimer = 17000; + m_uiFlameBurstTimer = 7000; + m_uiTransformTimer = 47000; + } + else + { + m_uiPhase = m_uiPrevPhase; + m_uiEnrageTimer = 40000; + m_uiTransformTimer = 60000; + m_uiTrapTimer = urand(30000, 40000); + } + } + else + m_uiTransformTimer -= uiDiff; + } + + break; + } + } +}; + +/*###### +## npc_akama_illidan +######*/ + +struct npc_akama_illidanAI : public npc_escortAI, private DialogueHelper +{ + npc_akama_illidanAI(Creature* pCreature) : npc_escortAI(pCreature), + DialogueHelper(aIntroDialogue) + { + m_pInstance = (ScriptedInstance*)pCreature->GetInstanceData(); + InitializeDialogueHelper(m_pInstance); + Reset(); + } + + ScriptedInstance* m_pInstance; + + uint32 m_uiSummonMinionTimer; + uint32 m_uiHealDelayTimer; + uint32 m_uiChainLightningTimer; + + bool m_bFightMinions; + bool m_bIsIntroFinished; + + void Reset() override + { + m_uiSummonMinionTimer = 2000; + m_uiHealDelayTimer = 0; + m_uiChainLightningTimer = urand(5000, 10000); + + if (!HasEscortState(STATE_ESCORT_ESCORTING)) + { + m_bFightMinions = false; + m_bIsIntroFinished = false; + } + } + + void MoveInLineOfSight(Unit* pWho) override + { + if (HasEscortState(STATE_ESCORT_ESCORTING)) + return; + + // Star the event + if (pWho->GetTypeId() == TYPEID_PLAYER && pWho->IsWithinDistInMap(m_creature, 70.0f) && pWho->IsWithinLOSInMap(m_creature)) + Start(true); + } + + void AttackStart(Unit* pWho) override + { + // Don't attack Illidan again + if (m_bIsIntroFinished && pWho->GetEntry() == NPC_ILLIDAN_STORMRAGE) + return; + + npc_escortAI::AttackStart(pWho); + } + + void EnterEvadeMode() override + { + // Called first when evading from Illidan + if (!m_bIsIntroFinished) + { + SetEscortPaused(false); + m_bIsIntroFinished = true; + } + + // Go back to epilogue position + if (m_pInstance && m_pInstance->GetData(TYPE_ILLIDAN) == DONE) + { + SetEscortPaused(false); + m_bFightMinions = false; + } + + npc_escortAI::EnterEvadeMode(); + } + + void WaypointReached(uint32 uiPointId) override + { + switch (uiPointId) + { + case 0: + SetEscortPaused(true); + m_creature->SetFlag(UNIT_NPC_FLAGS, UNIT_NPC_FLAG_GOSSIP); + break; + case 9: + SetEscortPaused(true); + if (m_pInstance) + { + if (Creature* pTrigger = m_pInstance->GetSingleCreatureFromStorage(NPC_ILLIDAN_DOOR_TRIGGER)) + m_creature->SetFacingToObject(pTrigger); + } + StartNextDialogueText(SAY_AKAMA_OPEN_DOOR_1); + break; + case 16: + SetEscortPaused(true); + m_creature->SetFlag(UNIT_NPC_FLAGS, UNIT_NPC_FLAG_GOSSIP); + if (m_pInstance) + { + if (Creature* pIllidan = m_pInstance->GetSingleCreatureFromStorage(NPC_ILLIDAN_STORMRAGE)) + m_creature->SetFacingToObject(pIllidan); + } + break; + case 17: + SetEscortPaused(true); + if (m_pInstance) + { + if (Creature* pIllidan = m_pInstance->GetSingleCreatureFromStorage(NPC_ILLIDAN_STORMRAGE)) + { + if (boss_illidan_stormrageAI* pIllidanAI = dynamic_cast(pIllidan->AI())) + pIllidanAI->DoStartCombatEvent(); + + m_creature->SetFacingToObject(pIllidan); + } + } + break; + case 24: + SetEscortPaused(true); + m_bFightMinions = true; + break; + case 30: + SetEscortPaused(true); + if (m_pInstance) + { + // Move to a close point to Illidan + if (Creature* pIllidan = m_pInstance->GetSingleCreatureFromStorage(NPC_ILLIDAN_STORMRAGE)) + { + float fX, fY, fZ; + pIllidan->GetContactPoint(m_creature, fX, fY, fZ); + m_creature->GetMotionMaster()->MovePoint(100, fX, fY, fZ); + } + } + break; + } + } + + void MovementInform(uint32 uiMoveType, uint32 uiPointId) override + { + npc_escortAI::MovementInform(uiMoveType, uiPointId); + + if (uiMoveType != POINT_MOTION_TYPE || uiPointId != 100) + return; + + // Do outro + if (m_pInstance) + { + if (Creature* pIllidan = m_pInstance->GetSingleCreatureFromStorage(NPC_ILLIDAN_STORMRAGE)) + m_creature->SetFacingToObject(pIllidan); + } + + DoScriptText(SAY_AKAMA_EPILOGUE_5, m_creature); + m_creature->ForcedDespawn(10000); + } + + void JustDidDialogueStep(int32 iEntry) override + { + switch (iEntry) + { + case SPELL_AKAMA_DOOR_FAIL: + DoCastSpellIfCan(m_creature, SPELL_AKAMA_DOOR_FAIL); + break; + case NPC_SPIRIT_OF_OLUM: + m_creature->SummonCreature(NPC_SPIRIT_OF_OLUM, 751.64f, 297.22f, 312.21f, 6.03f, TEMPSUMMON_TIMED_DESPAWN, 25000); + m_creature->SummonCreature(NPC_SPIRIT_OF_UDALO, 751.47f, 311.01f, 312.19f, 0.0f, TEMPSUMMON_TIMED_DESPAWN, 25000); + break; + case SPELL_AKAMA_DOOR_CHANNEL: + DoCastSpellIfCan(m_creature, SPELL_AKAMA_DOOR_CHANNEL); + if (m_pInstance) + { + if (Creature* pOlum = m_pInstance->GetSingleCreatureFromStorage(NPC_SPIRIT_OF_OLUM)) + pOlum->CastSpell(pOlum, SPELL_DEATHSWORN_DOOR_CHANNEL, true); + if (Creature* pUdalo = m_pInstance->GetSingleCreatureFromStorage(NPC_SPIRIT_OF_UDALO)) + pUdalo->CastSpell(pUdalo, SPELL_DEATHSWORN_DOOR_CHANNEL, true); + } + break; + case GO_ILLIDAN_GATE: + if (m_pInstance) + m_pInstance->DoUseDoorOrButton(GO_ILLIDAN_GATE); + break; + case NPC_SPIRIT_OF_UDALO: + SetEscortPaused(false); + break; + } + } + + void JustSummoned(Creature* pSummoned) override + { + switch (pSummoned->GetEntry()) + { + case NPC_ILLIDARI_ELITE: + pSummoned->AI()->AttackStart(m_creature); + break; + case NPC_SPIRIT_OF_OLUM: + case NPC_SPIRIT_OF_UDALO: + pSummoned->RemoveFlag(UNIT_NPC_FLAGS, UNIT_NPC_FLAG_GOSSIP); + break; + } + } + + // Wrapper to handle event resume + void DoResumeEvent() + { + SetEscortPaused(false); + m_creature->RemoveFlag(UNIT_NPC_FLAGS, UNIT_NPC_FLAG_GOSSIP); + } + + void UpdateEscortAI(const uint32 uiDiff) override + { + DialogueUpdate(uiDiff); + + if (m_bFightMinions) + { + if (m_uiSummonMinionTimer < uiDiff) + { + for (uint8 i = 0; i < MAX_ILLIDARI_ELITES; ++i) + m_creature->SummonCreature(NPC_ILLIDARI_ELITE, aIllidariElitesPos[i].fX, aIllidariElitesPos[i].fY, aIllidariElitesPos[i].fZ, 0, TEMPSUMMON_DEAD_DESPAWN, 0); + + m_uiSummonMinionTimer = urand(35000, 50000); + } + else + m_uiSummonMinionTimer -= uiDiff; + } + + if (!m_creature->SelectHostileTarget() || !m_creature->getVictim()) + return; + + if (m_uiHealDelayTimer) + { + if (m_uiHealDelayTimer <= uiDiff) + m_uiHealDelayTimer = 0; + else + m_uiHealDelayTimer -= uiDiff; + } + + if (m_bFightMinions) + { + if (m_uiChainLightningTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature->getVictim(), SPELL_CHAIN_LIGHTNING) == CAST_OK) + m_uiChainLightningTimer = urand(4000, 8000); + } + else + m_uiChainLightningTimer -= uiDiff; + } + + if (m_creature->GetHealthPercent() < 10.0f && !m_uiHealDelayTimer) + { + if (DoCastSpellIfCan(m_creature, SPELL_HEALING_POTION) == CAST_OK) + m_uiHealDelayTimer = 30000; + } + + DoMeleeAttackIfReady(); + } +}; + +bool GossipHello_npc_akama_illidan(Player* pPlayer, Creature* pCreature) +{ + // Before climbing the stairs + if (pCreature->GetPositionZ() < 300.0f) + { + pPlayer->ADD_GOSSIP_ITEM_ID(GOSSIP_ICON_CHAT, GOSSIP_ITEM_PREPARE, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 2); + pPlayer->SEND_GOSSIP_MENU(TEXT_ID_AKAMA_ILLIDAN_PREPARE, pCreature->GetObjectGuid()); + } + // Before starting combat + else + { + pPlayer->ADD_GOSSIP_ITEM_ID(GOSSIP_ICON_CHAT, GOSSIP_ITEM_START_EVENT, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 2); + pPlayer->SEND_GOSSIP_MENU(TEXT_ID_AKAMA_ILLIDAN_START, pCreature->GetObjectGuid()); + } + + return true; +} + +bool GossipSelect_npc_akama_illidan(Player* pPlayer, Creature* pCreature, uint32 /*uiSender*/, uint32 uiAction) +{ + if (uiAction == GOSSIP_ACTION_INFO_DEF + 1 || GOSSIP_ACTION_INFO_DEF + 2) + { + pPlayer->CLOSE_GOSSIP_MENU(); + + if (npc_akama_illidanAI* pAkamaAI = dynamic_cast(pCreature->AI())) + pAkamaAI->DoResumeEvent(); + } + + return true; +} + +/*###### +## boss_maiev +######*/ + +struct boss_maievAI : public ScriptedAI, private DialogueHelper +{ + boss_maievAI(Creature* pCreature) : ScriptedAI(pCreature), + DialogueHelper(aEpilogueDialogue) + { + m_pInstance = (ScriptedInstance*)pCreature->GetInstanceData(); + InitializeDialogueHelper(m_pInstance); + Reset(); + }; + + ScriptedInstance* m_pInstance; + + uint32 m_uiTauntTimer; + uint32 m_uiShadowStriketimer; + uint32 m_uiThrowDaggerTimer; + + bool m_bHasYelledTrap; + + void Reset() override + { + m_uiTauntTimer = urand(40000, 60000); + m_uiShadowStriketimer = urand(4000, 8000); + m_uiThrowDaggerTimer = urand(6000, 10000); + m_bHasYelledTrap = false; + + // Not sure if this is correct, but she seems to ignore all the shadow damage inflicted + m_creature->ApplySpellImmune(0, IMMUNITY_SCHOOL, SPELL_SCHOOL_MASK_SHADOW, true); + } + + void KilledUnit(Unit* pVictim) override + { + // Dummy function - used to start the epilogue + if (pVictim->GetEntry() == NPC_ILLIDAN_STORMRAGE) + StartNextDialogueText(SAY_MAIEV_EPILOGUE_1); + } + + // Custom evade - don't allow her to return to home position + void EnterEvadeMode() override + { + m_creature->RemoveAllAurasOnEvade(); + m_creature->DeleteThreatList(); + m_creature->CombatStop(true); + m_creature->LoadCreatureAddon(true); + + m_creature->SetLootRecipient(NULL); + + Reset(); + } + + // Attack only by script + void MoveInLineOfSight(Unit* /*pWho*/) override { } + + void JustDidDialogueStep(int32 iEntry) override + { + switch (iEntry) + { + case NPC_ILLIDAN_STORMRAGE: + if (m_pInstance) + { + if (Creature* pIllidan = m_pInstance->GetSingleCreatureFromStorage(NPC_ILLIDAN_STORMRAGE)) + pIllidan->DealDamage(pIllidan, pIllidan->GetHealth(), NULL, DIRECT_DAMAGE, SPELL_SCHOOL_MASK_NONE, NULL, false); + } + break; + case SPELL_TELEPORT_VISUAL: + if (DoCastSpellIfCan(m_creature, SPELL_TELEPORT_VISUAL) == CAST_OK) + m_creature->ForcedDespawn(1000); + break; + } + } + + void SpellHit(Unit* /*pCaster*/, const SpellEntry* pSpell) override + { + if (pSpell->Id == SPELL_CAGE_TRAP) + { + // Yell only the first time + if (!m_bHasYelledTrap) + { + DoScriptText(SAY_MAIEV_TRAP, m_creature); + m_bHasYelledTrap = true; + } + DoCastSpellIfCan(m_creature, SPELL_CAGE_TRAP_SUMMON, CAST_TRIGGERED); + } + } + + void UpdateAI(const uint32 uiDiff) override + { + DialogueUpdate(uiDiff); + + if (!m_creature->SelectHostileTarget() || !m_creature->getVictim()) + return; + + if (m_uiTauntTimer < uiDiff) + { + switch (urand(0, 2)) + { + case 0: DoScriptText(SAY_MAIEV_TAUNT_1, m_creature); break; + case 1: DoScriptText(SAY_MAIEV_TAUNT_2, m_creature); break; + case 2: DoScriptText(SAY_MAIEV_TAUNT_3, m_creature); break; + } + m_uiTauntTimer = urand(40000, 60000); + } + else + m_uiTauntTimer -= uiDiff; + + if (m_uiShadowStriketimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature->getVictim(), SPELL_SHADOW_STRIKE) == CAST_OK) + m_uiShadowStriketimer = urand(12000, 16000); + } + else + m_uiShadowStriketimer -= uiDiff; + + if (m_uiThrowDaggerTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature->getVictim(), SPELL_THROW_DAGGER) == CAST_OK) + m_uiThrowDaggerTimer = urand(6000, 10000); + } + else + m_uiThrowDaggerTimer -= uiDiff; + + DoMeleeAttackIfReady(); + } +}; + +/*###### +## npc_cage_trap_trigger +######*/ + +struct npc_cage_trap_triggerAI : public ScriptedAI +{ + npc_cage_trap_triggerAI(Creature* pCreature) : ScriptedAI(pCreature) { Reset(); } + + bool m_bActive; + + void Reset() override + { + m_bActive = false; + } + + void AttackStart(Unit* /*pWho*/) override { } + + void MoveInLineOfSight(Unit* pWho) override + { + if (!m_bActive && pWho->GetEntry() == NPC_ILLIDAN_STORMRAGE && m_creature->IsWithinDistInMap(pWho, 3.0f)) + { + pWho->CastSpell(pWho, SPELL_CAGED, true); + + // Cast the visual effects + for (uint8 i = 0; i < MAX_CAGE_SPELLS; ++i) + DoCastSpellIfCan(m_creature, aCagedSummonSpells[i], CAST_TRIGGERED); + + for (uint8 i = 0; i < MAX_CAGE_SPELLS; ++i) + DoCastSpellIfCan(m_creature, aCagedVisualSpells[i], CAST_TRIGGERED); + + if (GameObject* pCageTrap = GetClosestGameObjectWithEntry(m_creature, GO_CAGE_TRAP, 5.0f)) + pCageTrap->Use(m_creature); + + m_bActive = true; + m_creature->ForcedDespawn(15000); + } + } + + void UpdateAI(const uint32 /*uiDiff*/) override { } +}; + +/*###### +## npc_flame_of_azzinoth +######*/ + +struct npc_flame_of_azzinothAI : public ScriptedAI +{ + npc_flame_of_azzinothAI(Creature* pCreature) : ScriptedAI(pCreature) {Reset();} + + uint32 m_uiFlameBlastTimer; + uint32 m_uiSummonBlazeTimer; + uint32 m_uiChargeTimer; + uint32 m_uiWrathCheckTimer; + + void Reset() override + { + m_uiFlameBlastTimer = 10000; + m_uiSummonBlazeTimer = 0; + m_uiChargeTimer = 5000; + m_uiWrathCheckTimer = 1000; + } + + void JustSummoned(Creature* pSummoned) override + { + if (pSummoned->GetEntry() == NPC_BLAZE) + pSummoned->CastSpell(pSummoned, SPELL_BLAZE_EFFECT, false); + } + + void UpdateAI(const uint32 uiDiff) override + { + if (!m_creature->SelectHostileTarget() || !m_creature->getVictim()) + return; + + if (m_uiFlameBlastTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature->getVictim(), SPELL_FLAME_BLAST) == CAST_OK) + { + m_uiFlameBlastTimer = 10000; + m_uiSummonBlazeTimer = 3000; + } + } + else + m_uiFlameBlastTimer -= uiDiff; + + if (m_uiSummonBlazeTimer) + { + if (m_uiSummonBlazeTimer <= uiDiff) + { + if (DoCastSpellIfCan(m_creature, SPELL_BLAZE) == CAST_OK) + m_uiSummonBlazeTimer = 0; + } + else + m_uiSummonBlazeTimer -= uiDiff; + } + + // Workaround for broken aura 41997; the creature should enrage if not within dist of 30 from summoner + // This should be done by checking if aura 41997 is removed from self, when getting out of range + if (m_uiWrathCheckTimer) + { + if (m_uiWrathCheckTimer <= uiDiff) + { + if (GetClosestCreatureWithEntry(m_creature, NPC_BLADE_OF_AZZINOTH, 30.0f)) + m_uiWrathCheckTimer = 1000; + else + { + if (DoCastSpellIfCan(m_creature, SPELL_UNCAGED_WRATH, CAST_TRIGGERED) == CAST_OK) + m_uiWrathCheckTimer = 0; + } + } + else + m_uiWrathCheckTimer -= uiDiff; + } + + // Try to find a suitable target to charge + if (m_uiChargeTimer < uiDiff) + { + std::vector suitableTargets; + ThreatList const& threatList = m_creature->GetThreatManager().getThreatList(); + + for (ThreatList::const_iterator itr = threatList.begin(); itr != threatList.end(); ++itr) + { + if (Unit* pTarget = m_creature->GetMap()->GetUnit((*itr)->getUnitGuid())) + { + if (pTarget->GetTypeId() == TYPEID_PLAYER && !pTarget->IsWithinDist(m_creature, 30.0f)) + suitableTargets.push_back(pTarget); + } + } + + if (suitableTargets.empty()) + m_uiChargeTimer = 3000; + else + { + Unit* pTarget = suitableTargets[urand(0, suitableTargets.size() - 1)]; + + if (pTarget) + { + if (DoCastSpellIfCan(pTarget, SPELL_CHARGE) == CAST_OK) + m_uiChargeTimer = urand(5000, 10000); + } + } + } + else + m_uiChargeTimer -= uiDiff; + + DoMeleeAttackIfReady(); + } +}; + +/*###### +## npc_shadow_demon +######*/ + +struct npc_shadow_demonAI : public ScriptedAI +{ + npc_shadow_demonAI(Creature* pCreature) : ScriptedAI(pCreature) { Reset(); } + + ObjectGuid m_targetGuid; + + void Reset() override {} + + void AttackStart(Unit* pWho) override + { + // Function used to set target only - the npc doesn't really attack + m_targetGuid = pWho->GetObjectGuid(); + } + + void MoveInLineOfSight(Unit* /*pWho*/) override { } + + void JustDied(Unit* /*pKiller*/) override + { + if (Player* pPlayer = m_creature->GetMap()->GetPlayer(m_targetGuid)) + pPlayer->RemoveAurasDueToSpell(SPELL_PARALYZE); + } + + void MovementInform(uint32 uiMovementType, uint32 uiPointId) override + { + if (uiMovementType != POINT_MOTION_TYPE || !uiPointId) + return; + + if (Player* pPlayer = m_creature->GetMap()->GetPlayer(m_targetGuid)) + { + if (DoCastSpellIfCan(pPlayer, SPELL_CONSUME_SOUL) == CAST_OK) + m_creature->ForcedDespawn(1000); + } + } + + void UpdateAI(const uint32 /*uiDiff*/) override { } +}; + +/*###### +## npc_blade_of_azzinoth +######*/ + +struct npc_blade_of_azzinothAI : public ScriptedAI +{ + npc_blade_of_azzinothAI(Creature* pCreature) : ScriptedAI(pCreature) + { + m_pInstance = (ScriptedInstance*)pCreature->GetInstanceData(); + Reset(); + } + + ScriptedInstance* m_pInstance; + + void Reset() override {} + + // Do-Nothing-But-Stand-There + void AttackStart(Unit* /*pWho*/) override { } + void MoveInLineOfSight(Unit* /*pWho*/) override { } + + void JustSummoned(Creature* pSummoned) override + { + // Summon a Flame pear each blade + if (pSummoned->GetEntry() == NPC_FLAME_OF_AZZINOTH) + { + DoCastSpellIfCan(pSummoned, SPELL_AZZINOTH_CHANNEL, CAST_TRIGGERED); + pSummoned->SetInCombatWithZone(); + } + } + + void SummonedCreatureJustDied(Creature* pSummoned) override + { + // Inform Illidan when a flame is killed + if (pSummoned->GetEntry() == NPC_FLAME_OF_AZZINOTH) + { + if (!m_pInstance) + return; + + // For some reason it doesn't work with Spell Hit for SPELL_GLAIVE_RETURNS script effect, so we need to inform him manually + if (Creature* pIllidan = m_pInstance->GetSingleCreatureFromStorage(NPC_ILLIDAN_STORMRAGE)) + { + if (boss_illidan_stormrageAI* pIllidanAI = dynamic_cast(pIllidan->AI())) + pIllidanAI->DoInformFlameKilled(); + } + } + } + + void UpdateAI(const uint32 /*uiDiff*/) override { } +}; + +CreatureAI* GetAI_boss_illidan_stormrage(Creature* pCreature) +{ + return new boss_illidan_stormrageAI(pCreature); +} + +CreatureAI* GetAI_npc_akama_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 npc_flame_of_azzinothAI(pCreature); +} + +CreatureAI* GetAI_npc_cage_trap_trigger(Creature* pCreature) +{ + return new npc_cage_trap_triggerAI(pCreature); +} + +CreatureAI* GetAI_npc_shadow_demon(Creature* pCreature) +{ + return new npc_shadow_demonAI(pCreature); +} + +CreatureAI* GetAI_npc_blade_of_azzinoth(Creature* pCreature) +{ + return new npc_blade_of_azzinothAI(pCreature); +} + +void AddSC_boss_illidan() +{ + Script* pNewScript; + + pNewScript = new Script; + pNewScript->Name = "boss_illidan_stormrage"; + pNewScript->GetAI = &GetAI_boss_illidan_stormrage; + pNewScript->RegisterSelf(); + + pNewScript = new Script; + pNewScript->Name = "npc_akama_illidan"; + pNewScript->GetAI = &GetAI_npc_akama_illidan; + pNewScript->pGossipHello = &GossipHello_npc_akama_illidan; + pNewScript->pGossipSelect = &GossipSelect_npc_akama_illidan; + pNewScript->RegisterSelf(); + + pNewScript = new Script; + pNewScript->Name = "boss_maiev_shadowsong"; + pNewScript->GetAI = &GetAI_boss_maiev; + pNewScript->RegisterSelf(); + + pNewScript = new Script; + pNewScript->Name = "mob_flame_of_azzinoth"; + pNewScript->GetAI = &GetAI_mob_flame_of_azzinoth; + pNewScript->RegisterSelf(); + + pNewScript = new Script; + pNewScript->Name = "mob_blade_of_azzinoth"; + pNewScript->GetAI = &GetAI_npc_blade_of_azzinoth; + pNewScript->RegisterSelf(); + + pNewScript = new Script; + pNewScript->Name = "mob_cage_trap_trigger"; + pNewScript->GetAI = &GetAI_npc_cage_trap_trigger; + pNewScript->RegisterSelf(); + + pNewScript = new Script; + pNewScript->Name = "mob_shadow_demon"; + pNewScript->GetAI = &GetAI_npc_shadow_demon; + pNewScript->RegisterSelf(); +} diff --git a/src/modules/SD2/scripts/outland/black_temple/boss_mother_shahraz.cpp b/src/modules/SD2/scripts/outland/black_temple/boss_mother_shahraz.cpp new file mode 100644 index 000000000..2bf955024 --- /dev/null +++ b/src/modules/SD2/scripts/outland/black_temple/boss_mother_shahraz.cpp @@ -0,0 +1,234 @@ +/* Copyright (C) 2006 - 2013 ScriptDev2 + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with 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 and Fatal Attraction need core support. Timers may need some tunning. +SDCategory: Black Temple +EndScriptData */ + +#include "precompiled.h" +#include "black_temple.h" + +enum +{ + // Speech'n'Sounds + SAY_TAUNT_1 = -1564018, + SAY_TAUNT_2 = -1564019, + SAY_TAUNT_3 = -1564020, + SAY_AGGRO = -1564021, + SAY_SPELL_1 = -1564022, + SAY_SPELL_2 = -1564023, + SAY_SPELL_3 = -1564024, + SAY_SLAY_1 = -1564025, + SAY_SLAY_2 = -1564026, + SAY_ENRAGE = -1564027, + SAY_DEATH = -1564028, + + // Spells + SPELL_SINFUL_PERIODIC = 40862, // periodic triggers 40827 + SPELL_SINISTER_PERIODIC = 40863, // periodic triggers 40859 + SPELL_VILE_PERIODIC = 40865, // periodic triggers 40860 + SPELL_WICKED_PERIODIC = 40866, // periodic triggers 40861 + SPELL_FATAL_ATTRACTION = 40869, // dummy, triggers 41001 + SPELL_SILENCING_SHRIEK = 40823, + SPELL_SABER_LASH_PROC = 40816, // procs 40810 and 43690 on melee damage + SPELL_FRENZY = 23537, + SPELL_BERSERK = 45078, +}; + +static const uint32 aPrismaticAuras[] = +{ + 40880, // Shadow + 40882, // Fire + 40883, // Nature + 40891, // Arcane + 40896, // Frost + 40897, // Holy +}; + +static const uint32 aPeriodicBeams[] = {SPELL_SINFUL_PERIODIC, SPELL_SINISTER_PERIODIC, SPELL_VILE_PERIODIC, SPELL_WICKED_PERIODIC}; + +struct boss_shahrazAI : public ScriptedAI +{ + boss_shahrazAI(Creature* pCreature) : ScriptedAI(pCreature) + { + m_pInstance = (instance_black_temple*)pCreature->GetInstanceData(); + Reset(); + } + + instance_black_temple* m_pInstance; + + uint32 m_uiBeamTimer; + uint32 m_uiPrismaticShieldTimer; + uint32 m_uiFatalAttractionTimer; + uint32 m_uiShriekTimer; + uint32 m_uiRandomYellTimer; + uint32 m_uiBerserkTimer; + uint8 m_uiCurrentBeam; + + bool m_bIsEnraged; + + void Reset() override + { + m_uiBeamTimer = urand(5000, 10000); + m_uiCurrentBeam = urand(0, 3); + m_uiPrismaticShieldTimer = 0; + m_uiFatalAttractionTimer = 25000; + m_uiShriekTimer = 30000; + m_uiRandomYellTimer = urand(70000, 110000); + m_uiBerserkTimer = 10 * MINUTE * IN_MILLISECONDS; + + m_bIsEnraged = false; + + DoCastSpellIfCan(m_creature, SPELL_SABER_LASH_PROC); + } + + void Aggro(Unit* /*pWho*/) override + { + if (m_pInstance) + m_pInstance->SetData(TYPE_SHAHRAZ, IN_PROGRESS); + + DoScriptText(SAY_AGGRO, m_creature); + } + + void JustReachedHome() override + { + if (m_pInstance) + m_pInstance->SetData(TYPE_SHAHRAZ, FAIL); + } + + void KilledUnit(Unit* /*pVictim*/) override + { + DoScriptText(urand(0, 1) ? SAY_SLAY_1 : SAY_SLAY_2, m_creature); + } + + void JustDied(Unit* /*pKiller*/) override + { + if (m_pInstance) + m_pInstance->SetData(TYPE_SHAHRAZ, DONE); + + DoScriptText(SAY_DEATH, m_creature); + } + + void UpdateAI(const uint32 uiDiff) override + { + if (!m_creature->SelectHostileTarget() || !m_creature->getVictim()) + return; + + if (m_creature->GetHealthPercent() < 10.0f && !m_bIsEnraged) + { + if (DoCastSpellIfCan(m_creature, SPELL_FRENZY) == CAST_OK) + { + DoScriptText(SAY_ENRAGE, m_creature); + m_bIsEnraged = true; + } + } + + // Randomly cast one beam. + if (m_uiBeamTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature, aPeriodicBeams[m_uiCurrentBeam]) == CAST_OK) + { + uint8 uiNextBeam = (m_uiCurrentBeam + urand(1, 3)) % 4; + m_uiCurrentBeam = uiNextBeam; + m_uiBeamTimer = urand(10000, 13000); + } + } + else + m_uiBeamTimer -= uiDiff; + + // Random Prismatic Shield every 15 seconds. + if (m_uiPrismaticShieldTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature, aPrismaticAuras[urand(0, 5)]) == CAST_OK) + m_uiPrismaticShieldTimer = 15000; + } + else + m_uiPrismaticShieldTimer -= uiDiff; + + if (m_uiFatalAttractionTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature, SPELL_FATAL_ATTRACTION) == CAST_OK) + { + switch (urand(0, 2)) + { + case 0: DoScriptText(SAY_SPELL_1, m_creature); break; + case 1: DoScriptText(SAY_SPELL_2, m_creature); break; + case 2: DoScriptText(SAY_SPELL_3, m_creature); break; + } + m_uiFatalAttractionTimer = urand(30000, 40000); + } + } + else + m_uiFatalAttractionTimer -= uiDiff; + + if (m_uiShriekTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature, SPELL_SILENCING_SHRIEK) == CAST_OK) + m_uiShriekTimer = 30000; + } + else + m_uiShriekTimer -= uiDiff; + + if (m_uiBerserkTimer) + { + if (m_uiBerserkTimer <= uiDiff) + { + if (DoCastSpellIfCan(m_creature, SPELL_BERSERK) == CAST_OK) + { + DoScriptText(SAY_ENRAGE, m_creature); + m_uiBerserkTimer = 0; + } + } + else + m_uiBerserkTimer -= uiDiff; + } + + // Random taunts + if (m_uiRandomYellTimer < uiDiff) + { + switch (urand(0, 2)) + { + case 0: DoScriptText(SAY_TAUNT_1, m_creature); break; + case 1: DoScriptText(SAY_TAUNT_2, m_creature); break; + case 2: DoScriptText(SAY_TAUNT_3, m_creature); break; + } + + m_uiRandomYellTimer = urand(60000, 150000); + } + else + m_uiRandomYellTimer -= uiDiff; + + DoMeleeAttackIfReady(); + } +}; + +CreatureAI* GetAI_boss_shahraz(Creature* pCreature) +{ + return new boss_shahrazAI(pCreature); +} + +void AddSC_boss_mother_shahraz() +{ + Script* pNewScript; + + pNewScript = new Script; + pNewScript->Name = "boss_mother_shahraz"; + pNewScript->GetAI = &GetAI_boss_shahraz; + pNewScript->RegisterSelf(); +} diff --git a/src/modules/SD2/scripts/outland/black_temple/boss_reliquary_of_souls.cpp b/src/modules/SD2/scripts/outland/black_temple/boss_reliquary_of_souls.cpp new file mode 100644 index 000000000..213b64737 --- /dev/null +++ b/src/modules/SD2/scripts/outland/black_temple/boss_reliquary_of_souls.cpp @@ -0,0 +1,712 @@ +/* Copyright (C) 2006 - 2013 ScriptDev2 + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +/* ScriptData +SDName: Boss_Reliquary_of_Souls +SD%Complete: 90 +SDComment: Persistent Area Auras for each Essence (Aura of Suffering, Aura of Desire, Aura of Anger) requires core support. +SDCategory: Black Temple +EndScriptData */ + +#include "precompiled.h" +#include "black_temple.h" + +enum +{ + // Sound'n'speech + // Suffering + SUFF_SAY_FREED = -1564047, + SUFF_SAY_AGGRO = -1564048, + SUFF_SAY_SLAY1 = -1564049, + SUFF_SAY_SLAY2 = -1564050, + SUFF_SAY_FRENZY = -1564051, + SUFF_SAY_RECAP = -1564052, + SUFF_SAY_AFTER = -1564053, + EMOTE_BOSS_GENERIC_ENRAGED = -1000006, + + // Desire + DESI_SAY_FREED = -1564055, + DESI_SAY_SLAY1 = -1564056, + DESI_SAY_SLAY2 = -1564057, + DESI_SAY_SLAY3 = -1564058, + DESI_SAY_SPEC = -1564059, + DESI_SAY_RECAP = -1564060, + DESI_SAY_AFTER = -1564061, + + // Anger + ANGER_SAY_FREED = -1564062, + ANGER_SAY_FREED2 = -1564063, + ANGER_SAY_SLAY1 = -1564064, + ANGER_SAY_SLAY2 = -1564065, + ANGER_SAY_SPEC = -1564066, + ANGER_SAY_BEFORE = -1564067, + ANGER_SAY_DEATH = -1564068, + + // Spells + // Suffering + SPELL_AURA_OF_SUFFERING = 41292, + SPELL_AURA_OF_SUFFERING_ARMOR = 42017, + SPELL_SUFFERING_PASSIVE = 41296, + SPELL_SUFFERING_PASSIVE_2 = 41623, + SPELL_FRENZY = 41305, + SPELL_SOUL_DRAIN = 41303, + + // Desire + SPELL_AURA_OF_DESIRE = 41350, + SPELL_RUNE_SHIELD = 41431, + SPELL_DEADEN = 41410, + SPELL_SPIRIT_SHOCK = 41426, + + // Anger + SPELL_AURA_OF_ANGER = 41337, + SPELL_SEETHE = 41364, + SPELL_SOUL_SCREAM = 41545, + SPELL_SPITE = 41376, // triggers 41377 after 2 seconds + + // Generic + SPELL_SUMMON_ESSENCE_SUFFERING = 41488, + SPELL_SUMMON_ESSENCE_DESIRE = 41493, + SPELL_SUMMON_ESSENCE_ANGER = 41496, + SPELL_SUMMON_ENSLAVED_SOUL = 41537, + + // Soul spells + SPELL_ENSLAVED_SOUL_PASSIVE = 41535, + SPELL_SOUL_RELEASE = 41542, + + // Summons + NPC_ESSENCE_SUFFERING = 23418, + NPC_ESSENCE_DESIRE = 23419, + NPC_ESSENCE_ANGER = 23420, + NPC_ENSLAVED_SOUL = 23469, + + // Phases + PHASE_0_NOT_BEGUN = 0, + PHASE_1_SUFFERING = 1, + PHASE_2_DESIRE = 2, + PHASE_3_ANGER = 3, + + MAX_ENSLAVED_SOULS = 36, +}; + +/*###### +## boss_reliquary_of_souls +######*/ + +struct boss_reliquary_of_soulsAI : public Scripted_NoMovementAI +{ + boss_reliquary_of_soulsAI(Creature* pCreature) : Scripted_NoMovementAI(pCreature) + { + m_pInstance = (ScriptedInstance*)pCreature->GetInstanceData(); + Reset(); + } + + ScriptedInstance* m_pInstance; + + uint8 m_uiPhase; + uint8 m_uiSoulSummonedCount; + uint8 m_uiSoulDeathCount; + + uint32 m_uiSummonEssenceTimer; + uint32 m_uiSummonSoulTimer; + uint32 m_uiAnimationTimer; + uint32 m_uiAnimResetTimer; + + void Reset() override + { + m_uiPhase = PHASE_0_NOT_BEGUN; + m_uiSoulDeathCount = 0; + m_uiSoulSummonedCount = 0; + + m_uiSummonSoulTimer = 1000; + m_uiAnimationTimer = 0; + m_uiAnimResetTimer = 0; + m_uiSummonEssenceTimer = 0; + + // Reset animation + m_creature->HandleEmote(EMOTE_STATE_NONE); + } + + void JustDied(Unit* /*pKiller*/) override + { + if (m_pInstance) + m_pInstance->SetData(TYPE_RELIQUIARY, DONE); + } + + void JustReachedHome() override + { + if (m_pInstance) + m_pInstance->SetData(TYPE_RELIQUIARY, FAIL); + } + + void AttackStart(Unit* /*pWho*/) override { } + + void MoveInLineOfSight(Unit* pWho) override + { + if (m_uiPhase == PHASE_0_NOT_BEGUN && pWho->GetTypeId() == TYPEID_PLAYER && !((Player*)pWho)->isGameMaster() && + m_creature->IsWithinDistInMap(pWho, m_creature->GetAttackDistance(pWho)) && m_creature->IsWithinLOSInMap(pWho)) + { + // Start phase 1 + m_uiPhase = PHASE_1_SUFFERING; + m_uiSummonEssenceTimer = 7000; + m_uiAnimationTimer = 4000; + + // Set the player in combat with the boss + pWho->SetInCombatWith(m_creature); + m_creature->AddThreat(pWho); + + // Start animation + m_creature->SetStandState(UNIT_STAND_STATE_STAND); + + if (m_pInstance) + m_pInstance->SetData(TYPE_RELIQUIARY, IN_PROGRESS); + } + } + + void JustSummoned(Creature* pSummoned) override + { + switch (pSummoned->GetEntry()) + { + case NPC_ESSENCE_SUFFERING: + DoScriptText(SUFF_SAY_FREED, pSummoned); + break; + case NPC_ESSENCE_DESIRE: + DoScriptText(DESI_SAY_FREED, pSummoned); + break; + case NPC_ESSENCE_ANGER: + DoScriptText(ANGER_SAY_FREED, pSummoned); + break; + } + + // All summons are set in combat + pSummoned->SetInCombatWithZone(); + } + + void SummonedCreatureJustDied(Creature* pSummoned) override + { + // Self kill when the Essence of Anger is killed + if (pSummoned->GetEntry() == NPC_ESSENCE_ANGER) + m_creature->DealDamage(m_creature, m_creature->GetHealth(), NULL, DIRECT_DAMAGE, SPELL_SCHOOL_MASK_NORMAL, NULL, false); + } + + void SummonedMovementInform(Creature* pSummoned, uint32 uiMoveType, uint32 uiPointId) override + { + if (uiMoveType != POINT_MOTION_TYPE || !uiPointId) + return; + + // Switch to next phase when the essence gets back + switch (pSummoned->GetEntry()) + { + case NPC_ESSENCE_SUFFERING: + DoScriptText(SUFF_SAY_AFTER, pSummoned); + m_uiPhase = PHASE_2_DESIRE;; + break; + case NPC_ESSENCE_DESIRE: + DoScriptText(DESI_SAY_AFTER, pSummoned); + m_uiPhase = PHASE_3_ANGER; + break; + } + + // Despawn and set animation + pSummoned->ForcedDespawn(); + + m_uiSoulDeathCount = 0; + m_uiSoulSummonedCount = 0; + m_uiAnimResetTimer = 2000; + // Reset animation + m_creature->HandleEmote(EMOTE_ONESHOT_EMERGE); + } + + // Wrapper to count the dead spirits + void DoNotifySouldDead() + { + ++m_uiSoulDeathCount; + + // Prepare to summon the essence + if (m_uiSoulDeathCount == MAX_ENSLAVED_SOULS) + { + m_uiSummonEssenceTimer = 7000; + m_uiAnimationTimer = 4000; + } + } + + void UpdateAI(const uint32 uiDiff) override + { + // Animation for opening the Reliquary + if (m_uiAnimationTimer) + { + if (m_uiAnimationTimer <= uiDiff) + { + m_creature->HandleEmote(EMOTE_ONESHOT_SUBMERGE); + m_uiAnimationTimer = 0; + } + else + m_uiAnimationTimer -= uiDiff; + } + + // Animation for reset Reliquary + if (m_uiAnimResetTimer) + { + if (m_uiAnimResetTimer <= uiDiff) + { + // Reset animation + m_creature->HandleEmote(EMOTE_STATE_NONE); + m_uiAnimResetTimer = 0; + } + else + m_uiAnimResetTimer -= uiDiff; + } + + // Summon the Essence on timer + if (m_uiSummonEssenceTimer) + { + if (m_uiSummonEssenceTimer <= uiDiff) + { + uint32 uiSpellId = 0; + switch (m_uiPhase) + { + case PHASE_1_SUFFERING: uiSpellId = SPELL_SUMMON_ESSENCE_SUFFERING; break; + case PHASE_2_DESIRE: uiSpellId = SPELL_SUMMON_ESSENCE_DESIRE; break; + case PHASE_3_ANGER: uiSpellId = SPELL_SUMMON_ESSENCE_ANGER; break; + } + + if (DoCastSpellIfCan(m_creature, uiSpellId) == CAST_OK) + { + m_creature->HandleEmote(EMOTE_STATE_SUBMERGED); + m_uiSummonEssenceTimer = 0; + } + } + else + m_uiSummonEssenceTimer -= uiDiff; + } + + // Summon Enslaved souls between the essence + switch (m_uiPhase) + { + case PHASE_2_DESIRE: + case PHASE_3_ANGER: + + if (m_uiSoulSummonedCount < MAX_ENSLAVED_SOULS) + { + if (m_uiSummonSoulTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature, SPELL_SUMMON_ENSLAVED_SOUL) == CAST_OK) + { + ++m_uiSoulSummonedCount; + m_uiSummonSoulTimer = 500; + } + } + else + m_uiSummonSoulTimer -= uiDiff; + } + + break; + } + } +}; + +/*###### +## essence_base_AI +######*/ + +struct essence_base_AI : public ScriptedAI +{ + essence_base_AI(Creature* pCreature) : ScriptedAI(pCreature) + { + m_pInstance = (ScriptedInstance*)pCreature->GetInstanceData(); + m_bIsPhaseFinished = false; + } + + ScriptedInstance* m_pInstance; + + bool m_bIsPhaseFinished; + + virtual void OnPhaseFinished() {} + + void JustReachedHome() override + { + // Reset encounter and despawn Essence + if (m_pInstance) + { + if (Creature* pReliquary = m_pInstance->GetSingleCreatureFromStorage(NPC_RELIQUARY_OF_SOULS)) + pReliquary->AI()->EnterEvadeMode(); + } + + m_creature->ForcedDespawn(); + } + + void DamageTaken(Unit* /*pKiller*/, uint32& uiDamage) override + { + if (uiDamage < m_creature->GetHealth()) + return; + + // Prevent glitch if in fake death + if (m_bIsPhaseFinished) + { + uiDamage = 0; + return; + } + + uiDamage = 0; + + m_creature->InterruptNonMeleeSpells(true); + m_creature->SetHealth(0); + m_creature->StopMoving(); + m_creature->ClearComboPointHolders(); + m_creature->RemoveAllAurasOnDeath(); + m_creature->ModifyAuraState(AURA_STATE_HEALTHLESS_20_PERCENT, false); + m_creature->ModifyAuraState(AURA_STATE_HEALTHLESS_35_PERCENT, false); + m_creature->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE); + m_creature->ClearAllReactives(); + m_creature->GetMotionMaster()->Clear(); + + if (!m_pInstance) + return; + + // Move to home position + if (Creature* pReliquary = m_pInstance->GetSingleCreatureFromStorage(NPC_RELIQUARY_OF_SOULS)) + m_creature->GetMotionMaster()->MovePoint(1, pReliquary->GetPositionX(), pReliquary->GetPositionY(), pReliquary->GetPositionZ()); + + m_bIsPhaseFinished = true; + + OnPhaseFinished(); + } +}; + +/*###### +## boss_essence_of_suffering +######*/ + +struct boss_essence_of_sufferingAI : public essence_base_AI +{ + boss_essence_of_sufferingAI(Creature* pCreature) : essence_base_AI(pCreature) { Reset(); } + + uint32 m_uiEnrageTimer; + uint32 m_uiSoulDrainTimer; + + void Reset() override + { + DoCastSpellIfCan(m_creature, SPELL_AURA_OF_SUFFERING, CAST_TRIGGERED); + DoCastSpellIfCan(m_creature, SPELL_SUFFERING_PASSIVE, CAST_TRIGGERED); + + m_uiEnrageTimer = 45000; + m_uiSoulDrainTimer = 20000; + } + + void KilledUnit(Unit* /*pVictim*/) override + { + DoScriptText(urand(0, 1) ? SUFF_SAY_SLAY1 : SUFF_SAY_SLAY2, m_creature); + } + + void OnPhaseFinished() + { + DoScriptText(SUFF_SAY_RECAP, m_creature); + } + + void UpdateAI(const uint32 uiDiff) override + { + if (!m_creature->SelectHostileTarget() || !m_creature->getVictim()) + return; + + if (m_uiEnrageTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature, SPELL_FRENZY) == CAST_OK) + { + DoScriptText(EMOTE_BOSS_GENERIC_ENRAGED, m_creature); + DoScriptText(SUFF_SAY_FRENZY, m_creature); + m_uiEnrageTimer = 45000; + } + } + else + m_uiEnrageTimer -= uiDiff; + + if (m_uiSoulDrainTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature, SPELL_SOUL_DRAIN) == CAST_OK) + m_uiSoulDrainTimer = urand(45000, 60000); + } + else + m_uiSoulDrainTimer -= uiDiff; + + DoMeleeAttackIfReady(); + } +}; + +/*###### +## boss_essence_of_desire +######*/ + +struct boss_essence_of_desireAI : public essence_base_AI +{ + boss_essence_of_desireAI(Creature* pCreature) : essence_base_AI(pCreature) { Reset(); } + + uint32 m_uiRuneShieldTimer; + uint32 m_uiDeadenTimer; + uint32 m_uiSoulShockTimer; + + void Reset() override + { + m_uiRuneShieldTimer = urand(10000, 15000); + m_uiDeadenTimer = 15000; + m_uiSoulShockTimer = urand(5000, 10000); + + DoCastSpellIfCan(m_creature, SPELL_AURA_OF_DESIRE); + } + + void KilledUnit(Unit* /*pVictim*/) override + { + switch (urand(0, 2)) + { + case 0: DoScriptText(DESI_SAY_SLAY1, m_creature); break; + case 1: DoScriptText(DESI_SAY_SLAY2, m_creature); break; + case 2: DoScriptText(DESI_SAY_SLAY3, m_creature); break; + } + } + + void OnPhaseFinished() + { + DoScriptText(DESI_SAY_RECAP, m_creature); + } + + void UpdateAI(const uint32 uiDiff) override + { + if (!m_creature->SelectHostileTarget() || !m_creature->getVictim()) + return; + + if (m_uiRuneShieldTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature, SPELL_RUNE_SHIELD) == CAST_OK) + m_uiRuneShieldTimer = 15000; + } + else + m_uiRuneShieldTimer -= uiDiff; + + if (m_uiDeadenTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature->getVictim(), SPELL_DEADEN) == CAST_OK) + { + DoScriptText(DESI_SAY_SPEC, m_creature); + m_uiDeadenTimer = 30000; + } + } + else + m_uiDeadenTimer -= uiDiff; + + if (m_uiSoulShockTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature->getVictim(), SPELL_SPIRIT_SHOCK) == CAST_OK) + m_uiSoulShockTimer = urand(5000, 10000); + } + else + m_uiSoulShockTimer -= uiDiff; + + DoMeleeAttackIfReady(); + } +}; + +/*###### +## boss_essence_of_anger +######*/ + +struct boss_essence_of_angerAI : public ScriptedAI +{ + boss_essence_of_angerAI(Creature* pCreature) : ScriptedAI(pCreature) + { + m_pInstance = (ScriptedInstance*)pCreature->GetInstanceData(); + Reset(); + } + + ScriptedInstance* m_pInstance; + + uint32 m_uiSeetheTimer; + uint32 m_uiSoulScreamTimer; + uint32 m_uiSpiteTimer; + + void Reset() override + { + m_uiSeetheTimer = 5000; + m_uiSoulScreamTimer = 10000; + m_uiSpiteTimer = 20000; + + DoCastSpellIfCan(m_creature, SPELL_AURA_OF_ANGER); + } + + void KilledUnit(Unit* /*pVictim*/) override + { + DoScriptText(urand(0, 1) ? ANGER_SAY_SLAY1 : ANGER_SAY_SLAY2, m_creature); + } + + void JustDied(Unit* /*pKiller*/) override + { + DoScriptText(ANGER_SAY_DEATH, m_creature); + } + + void JustReachedHome() override + { + // Reset encounter and despawn Essence + if (m_pInstance) + { + if (Creature* pReliquary = m_pInstance->GetSingleCreatureFromStorage(NPC_RELIQUARY_OF_SOULS)) + pReliquary->AI()->EnterEvadeMode(); + } + + m_creature->ForcedDespawn(); + } + + void UpdateAI(const uint32 uiDiff) override + { + if (!m_creature->SelectHostileTarget() || !m_creature->getVictim()) + return; + + if (m_uiSeetheTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature, SPELL_SEETHE) == CAST_OK) + m_uiSeetheTimer = urand(20000, 30000); + } + else + m_uiSeetheTimer -= uiDiff; + + if (m_uiSoulScreamTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature, SPELL_SOUL_SCREAM) == CAST_OK) + m_uiSoulScreamTimer = 10000; + } + else + m_uiSoulScreamTimer -= uiDiff; + + if (m_uiSpiteTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature, SPELL_SPITE) == CAST_OK) + { + DoScriptText(ANGER_SAY_BEFORE, m_creature); + m_uiSpiteTimer = 20000; + } + } + else + m_uiSpiteTimer -= uiDiff; + + DoMeleeAttackIfReady(); + } +}; + +/*###### +## npc_enslaved_soul +######*/ + +struct npc_enslaved_soulAI : public ScriptedAI +{ + npc_enslaved_soulAI(Creature* pCreature) : ScriptedAI(pCreature) + { + m_pInstance = (ScriptedInstance*)pCreature->GetInstanceData(); + Reset(); + } + + ScriptedInstance* m_pInstance; + + void Reset() override + { + DoCastSpellIfCan(m_creature, SPELL_ENSLAVED_SOUL_PASSIVE); + } + + void JustDied(Unit* /*pKiller*/) override + { + DoCastSpellIfCan(m_creature, SPELL_SOUL_RELEASE, CAST_TRIGGERED); + + // Notify the main boss about the spirit death. Needs to be done here, because the spirit is summoned with triggered spell + if (m_pInstance) + { + if (Creature* pReliquary = m_pInstance->GetSingleCreatureFromStorage(NPC_RELIQUARY_OF_SOULS)) + { + if (boss_reliquary_of_soulsAI* pBossAI = dynamic_cast(pReliquary->AI())) + pBossAI->DoNotifySouldDead(); + } + } + } + + void JustReachedHome() override + { + // Reset encounter and despawn the spirit + if (m_pInstance) + { + if (Creature* pReliquary = m_pInstance->GetSingleCreatureFromStorage(NPC_RELIQUARY_OF_SOULS)) + pReliquary->AI()->EnterEvadeMode(); + } + + m_creature->ForcedDespawn(); + } + + void UpdateAI(const uint32 /*uiDiff*/) override + { + if (!m_creature->SelectHostileTarget() || !m_creature->getVictim()) + return; + + DoMeleeAttackIfReady(); + } +}; + +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* pNewScript; + + pNewScript = new Script; + pNewScript->Name = "boss_reliquary_of_souls"; + pNewScript->GetAI = &GetAI_boss_reliquary_of_souls; + pNewScript->RegisterSelf(); + + pNewScript = new Script; + pNewScript->Name = "boss_essence_of_suffering"; + pNewScript->GetAI = &GetAI_boss_essence_of_suffering; + pNewScript->RegisterSelf(); + + pNewScript = new Script; + pNewScript->Name = "boss_essence_of_desire"; + pNewScript->GetAI = &GetAI_boss_essence_of_desire; + pNewScript->RegisterSelf(); + + pNewScript = new Script; + pNewScript->Name = "boss_essence_of_anger"; + pNewScript->GetAI = &GetAI_boss_essence_of_anger; + pNewScript->RegisterSelf(); + + pNewScript = new Script; + pNewScript->Name = "npc_enslaved_soul"; + pNewScript->GetAI = &GetAI_npc_enslaved_soul; + pNewScript->RegisterSelf(); +} diff --git a/src/modules/SD2/scripts/outland/black_temple/boss_shade_of_akama.cpp b/src/modules/SD2/scripts/outland/black_temple/boss_shade_of_akama.cpp new file mode 100644 index 000000000..c20086fab --- /dev/null +++ b/src/modules/SD2/scripts/outland/black_temple/boss_shade_of_akama.cpp @@ -0,0 +1,729 @@ +/* Copyright (C) 2006 - 2013 ScriptDev2 + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with 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: Some adjustments may be required once the Shade Soul Channel stacking is fixed in core. Epilogue positions need more work. +SDCategory: Black Temple +EndScriptData */ + +#include "precompiled.h" +#include "black_temple.h" + +enum +{ + // yells + SAY_DEATH = -1564013, + SAY_LOW_HEALTH = -1564014, + // Ending cinematic text + SAY_FREE_1 = -1564130, + SAY_FREE_2 = -1564015, + SAY_BROKEN_FREE_01 = -1564016, + SAY_BROKEN_FREE_02 = -1564017, + + // gossip + GOSSIP_ITEM_START_ENCOUNTER = -3564000, + TEXT_ID_AKAMA = 10866, + + // Akama spells + SPELL_STEALTH = 34189, + SPELL_DESTRUCTIVE_POISON = 40874, + SPELL_CHAIN_LIGHTNING = 39945, // old spell was 42024 -> probably wrong + SPELL_AKAMA_SOUL_CHANNEL = 40447, // channeled during the event + SPELL_AKAMA_SOUL_RETRIEVE = 40902, // used for the epilogue + + // Other spells + SPELL_SUMMON_DEFENDER = 40474, + SPELL_SUMMON_SORCERER = 40476, + // SPELL_VERTEX_SHADE_BLACK = 39833, // used by the shade - in c_t_a + SPELL_SHADE_SOUL_CHANNEL = 40401, // channel spell, used to banish the shade + SPELL_SUMMON_SHADE_TRIGGER = 40955, + + // npcs + NPC_ASH_SORCERER = 23215, + NPC_ASH_DEFENDER = 23216, + NPC_ASH_ELEMENTAL = 23523, + NPC_ASH_ROGUE = 23318, + NPC_ASH_SPIRITBIND = 23524, + NPC_ASH_BROKEN = 23319, + + // akama's phases + PHASE_CHANNEL = 1, + PHASE_COMBAT = 2, + PHASE_EPILOGUE = 3, + + MAX_CHANNELERS = 6, +}; + +static const uint32 auiRandSpawnEntry[] = +{ + NPC_ASH_ELEMENTAL, + NPC_ASH_ROGUE, + NPC_ASH_SPIRITBIND +}; + +static const DialogueEntry aOutroDialogue[] = +{ + {SPELL_AKAMA_SOUL_RETRIEVE, 0, 18000}, + {EMOTE_ONESHOT_ROAR, 0, 2000}, + {SAY_FREE_1, NPC_AKAMA_SHADE, 5000}, + {SAY_FREE_2, NPC_AKAMA_SHADE, 20000}, + {SAY_BROKEN_FREE_01, 0, 2000}, + {EMOTE_STATE_KNEEL, 0, 5000}, + {SAY_BROKEN_FREE_02, 0, 0}, + {0, 0, 0}, +}; + +struct Location +{ + float m_fX, m_fY, m_fZ; +}; + +static const Location afAkamaWP[] = +{ + {516.885193f, 400.836060f, 112.784f}, + {469.597443f, 402.264404f, 118.537f} +}; + +static const Location afBrokenSpawnLoc[] = +{ + {541.375916f, 401.439575f, 112.784f}, // The place where Akama channels + {534.130005f, 352.394531f, 112.784f}, // Behind a 'pillar' which is behind the east alcove +}; + +/*###### +## npc_akama +######*/ + +struct npc_akamaAI : public ScriptedAI, private DialogueHelper +{ + npc_akamaAI(Creature* pCreature) : ScriptedAI(pCreature), + DialogueHelper(aOutroDialogue) + { + m_pInstance = (instance_black_temple*)pCreature->GetInstanceData(); + InitializeDialogueHelper(m_pInstance); + Reset(); + } + + instance_black_temple* m_pInstance; + + uint8 m_uiPhase; + + uint32 m_uiDestructivePoisonTimer; + uint32 m_uiLightningBoltTimer; + + uint32 m_uiSummonPackTimer; + uint32 m_uiSummonDefenderTimer; + uint32 m_uiSummonSorcererTimer; + + uint8 m_uiChannelersDead; + + GuidList m_lBrokenGUIDList; + GuidList m_lSorcerersGUIDList; + + bool m_bHasYelledOnce; + + void Reset() override + { + SetCombatMovement(false); + + m_uiPhase = 0; + + m_uiDestructivePoisonTimer = 15000; + m_uiLightningBoltTimer = 10000; + + m_uiSummonPackTimer = 5000; + m_uiSummonDefenderTimer = 10000; + m_uiSummonSorcererTimer = 10000; + + m_uiChannelersDead = 0; + + m_bHasYelledOnce = false; + + m_lBrokenGUIDList.clear(); + m_lSorcerersGUIDList.clear(); + + DoCastSpellIfCan(m_creature, SPELL_STEALTH); + m_creature->SetFlag(UNIT_NPC_FLAGS, UNIT_NPC_FLAG_GOSSIP); + } + + void AttackedBy(Unit* pAttacker) override + { + // When the Shade starts to attack Akama, switch to melee phase + if (m_uiPhase == PHASE_CHANNEL && pAttacker->GetEntry() == NPC_SHADE_OF_AKAMA) + { + m_creature->InterruptNonMeleeSpells(false); + AttackStart(pAttacker); + m_uiPhase = PHASE_COMBAT; + + // despawn all sorcerers at this point + for (GuidList::const_iterator itr = m_lSorcerersGUIDList.begin(); itr != m_lSorcerersGUIDList.end(); ++itr) + { + if (Creature* pSorcerer = m_creature->GetMap()->GetCreature(*itr)) + pSorcerer->ForcedDespawn(); + } + } + } + + void KilledUnit(Unit* pVictim) override + { + // Note: this is called from the Shade, Channeler and Sorcerer script + // If the function is changed in the future, please review this. + switch (pVictim->GetEntry()) + { + case NPC_SHADE_OF_AKAMA: + m_uiPhase = PHASE_EPILOGUE; + + m_creature->GetMotionMaster()->MovePoint(PHASE_EPILOGUE, afAkamaWP[1].m_fX, afAkamaWP[1].m_fY, afAkamaWP[1].m_fZ); + break; + case NPC_ASH_SORCERER: + // Decrease the sorcerer counter + m_lSorcerersGUIDList.remove(pVictim->GetObjectGuid()); + break; + case NPC_ASH_CHANNELER: + + ++m_uiChannelersDead; + + // Move the shade to Akama when all channelers are dead + // Note: the boss should be already slowly moving, but this isn't possible because of the missing stack for the speed debuff + if (m_uiChannelersDead == MAX_CHANNELERS) + { + if (m_pInstance) + { + if (Creature* pShade = m_pInstance->GetSingleCreatureFromStorage(NPC_SHADE_OF_AKAMA)) + { + float fX, fY, fZ; + m_creature->GetContactPoint(pShade, fX, fY, fZ); + pShade->GetMotionMaster()->MovePoint(1, fX, fY, fZ); + } + } + } + break; + } + } + + void JustDied(Unit* /*pKiller*/) override + { + DoScriptText(SAY_DEATH, m_creature); + m_creature->SetCorpseDelay(30); + + if (m_pInstance) + { + // Reset the shade + if (Creature* pShade = m_pInstance->GetSingleCreatureFromStorage(NPC_SHADE_OF_AKAMA)) + pShade->AI()->EnterEvadeMode(); + } + } + + void CorpseRemoved(uint32& uiRespawnDelay) override + { + // Resapwn after 5 min + uiRespawnDelay = 5 * MINUTE; + } + + void JustSummoned(Creature* pSummoned) override + { + switch (pSummoned->GetEntry()) + { + case NPC_ASH_SORCERER: + { + pSummoned->SetWalk(false); + m_lSorcerersGUIDList.push_back(pSummoned->GetObjectGuid()); + + float fX, fY, fZ; + if (m_pInstance) + { + if (Creature* pShade = m_pInstance->GetSingleCreatureFromStorage(NPC_SHADE_OF_AKAMA)) + { + pShade->GetNearPoint(pShade, fX, fY, fZ, 0, 20.0f, pShade->GetAngle(pSummoned)); + pSummoned->GetMotionMaster()->MovePoint(1, fX, fY, fZ); + } + } + break; + } + case NPC_ASH_BROKEN: + { + float fX, fY, fZ; + m_lBrokenGUIDList.push_back(pSummoned->GetObjectGuid()); + + m_creature->GetNearPoint(m_creature, fX, fY, fZ, 0, 30.0f, m_creature->GetAngle(pSummoned)); + pSummoned->GetMotionMaster()->MovePoint(0, fX, fY, fZ); + break; + } + case NPC_ASH_DEFENDER: + pSummoned->AI()->AttackStart(m_creature); + break; + default: + pSummoned->SetInCombatWithZone(); + break; + } + } + + void MovementInform(uint32 uiMoveType, uint32 uiPointId) override + { + if (uiMoveType != POINT_MOTION_TYPE || !m_pInstance) + return; + + switch (uiPointId) + { + case PHASE_CHANNEL: + if (DoCastSpellIfCan(m_creature, SPELL_AKAMA_SOUL_CHANNEL) == CAST_OK) + { + m_uiPhase = PHASE_CHANNEL; + + GuidList m_lChannelersList; + m_pInstance->GetChannelersGuidList(m_lChannelersList); + + for (GuidList::const_iterator itr = m_lChannelersList.begin(); itr != m_lChannelersList.end(); ++itr) + { + if (Creature* pChanneler = m_creature->GetMap()->GetCreature(*itr)) + pChanneler->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE); + } + } + break; + case PHASE_EPILOGUE: + // Start epilogue here + if (Creature* pShade = m_pInstance->GetSingleCreatureFromStorage(NPC_SHADE_OF_AKAMA)) + m_creature->SetFacingToObject(pShade); + + StartNextDialogueText(SPELL_AKAMA_SOUL_RETRIEVE); + break; + } + } + + void JustDidDialogueStep(int32 iEntry) override + { + switch (iEntry) + { + case SPELL_AKAMA_SOUL_RETRIEVE: + DoCastSpellIfCan(m_creature, SPELL_AKAMA_SOUL_RETRIEVE); + break; + case EMOTE_ONESHOT_ROAR: + m_creature->HandleEmote(EMOTE_ONESHOT_ROAR); + break; + case SAY_FREE_1: + DoSummonBrokenAshtongue(); + break; + case SAY_BROKEN_FREE_01: + if (Creature* pBroken = GetClosestCreatureWithEntry(m_creature, NPC_ASH_BROKEN, 35.0f)) + DoScriptText(SAY_BROKEN_FREE_01, pBroken); + break; + case EMOTE_STATE_KNEEL: + for (GuidList::const_iterator itr = m_lBrokenGUIDList.begin(); itr != m_lBrokenGUIDList.end(); ++itr) + { + if (Creature* pBroken = m_creature->GetMap()->GetCreature(*itr)) + pBroken->HandleEmote(EMOTE_STATE_KNEEL); + } + break; + case SAY_BROKEN_FREE_02: + for (GuidList::const_iterator itr = m_lBrokenGUIDList.begin(); itr != m_lBrokenGUIDList.end(); ++itr) + { + if (Creature* pBroken = m_creature->GetMap()->GetCreature(*itr)) + DoScriptText(SAY_BROKEN_FREE_02, pBroken); + } + break; + } + } + + // Wrapper to start the Akama event + void DoStartEvent() + { + if (m_pInstance) + m_pInstance->SetData(TYPE_SHADE, IN_PROGRESS); + + m_creature->RemoveAurasDueToSpell(SPELL_STEALTH); + m_creature->RemoveFlag(UNIT_NPC_FLAGS, UNIT_NPC_FLAG_GOSSIP); + m_creature->GetMotionMaster()->MovePoint(PHASE_CHANNEL, afAkamaWP[0].m_fX, afAkamaWP[0].m_fY, afAkamaWP[0].m_fZ); + } + + // Wrapper to summon ashtongue mobs + void DoSummonAshtongue(uint32 uiSpellId = 0) + { + if (!m_pInstance) + return; + + GuidVector vGeneratorsVect; + m_pInstance->GetGeneratorGuidVector(vGeneratorsVect); + Creature* pGenerator = m_creature->GetMap()->GetCreature(vGeneratorsVect[urand(0, 1)]); + if (!pGenerator) + return; + + // Summon mobs by spell + if (uiSpellId) + pGenerator->CastSpell(pGenerator, uiSpellId, true, NULL, NULL, m_creature->GetObjectGuid()); + // Summon ashtongue pack + else + { + float fX, fY, fZ; + for (uint8 i = 0; i < countof(auiRandSpawnEntry); ++i) + { + pGenerator->GetRandomPoint(pGenerator->GetPositionX(), pGenerator->GetPositionY(), pGenerator->GetPositionZ(), 5.0f, fX, fY, fZ); + m_creature->SummonCreature(auiRandSpawnEntry[i], fX, fY, fZ, 0, TEMPSUMMON_DEAD_DESPAWN, 0); + } + } + } + + // Wrapper to summon the npcs for the epilogue + void DoSummonBrokenAshtongue() + { + if (!m_pInstance) + return; + + float fX, fY, fZ; + + // Spawn 4 Broken in the center and behind the column + for (uint8 i = 0; i < countof(afBrokenSpawnLoc); ++i) + { + for (uint8 j = 0; j < 4; ++j) + { + fX = afBrokenSpawnLoc[i].m_fX; + fY = afBrokenSpawnLoc[i].m_fY + (j * 7); + fZ = afBrokenSpawnLoc[i].m_fZ; + + m_creature->SummonCreature(NPC_ASH_BROKEN, fX, fY, fZ, 0, TEMPSUMMON_TIMED_DESPAWN, 10 * MINUTE * IN_MILLISECONDS); + } + } + + GuidVector vGeneratorsVect; + m_pInstance->GetGeneratorGuidVector(vGeneratorsVect); + + // Spawn 4 Broken at each generator + for (uint8 i = 0; i < vGeneratorsVect.size(); ++i) + { + if (Creature* pGenerator = m_creature->GetMap()->GetCreature(vGeneratorsVect[i])) + { + for (uint8 j = 0; j < 4; ++j) + { + pGenerator->GetRandomPoint(pGenerator->GetPositionX(), pGenerator->GetPositionY(), pGenerator->GetPositionZ(), 10.0f, fX, fY, fZ); + m_creature->SummonCreature(NPC_ASH_BROKEN, fX, fY, fZ, 0, TEMPSUMMON_TIMED_DESPAWN, 10 * MINUTE * IN_MILLISECONDS); + } + } + } + } + + void UpdateAI(const uint32 uiDiff) override + { + switch (m_uiPhase) + { + case PHASE_CHANNEL: + + if (m_uiSummonDefenderTimer < uiDiff) + { + DoSummonAshtongue(SPELL_SUMMON_DEFENDER); + m_uiSummonDefenderTimer = 15000; + } + else + m_uiSummonDefenderTimer -= uiDiff; + + if (m_lSorcerersGUIDList.size() <= m_uiChannelersDead) + { + if (m_uiSummonSorcererTimer < uiDiff) + { + DoSummonAshtongue(SPELL_SUMMON_SORCERER); + m_uiSummonSorcererTimer = urand(20000, 30000); + } + else + m_uiSummonSorcererTimer -= uiDiff; + } + + if (m_uiSummonPackTimer < uiDiff) + { + DoSummonAshtongue(); + m_uiSummonPackTimer = 35000; + } + else + m_uiSummonPackTimer -= uiDiff; + + break; + case PHASE_COMBAT: + + if (!m_creature->SelectHostileTarget() || !m_creature->getVictim()) + return; + + if (!m_bHasYelledOnce && m_creature->GetHealthPercent() < 15.0f) + { + DoScriptText(SAY_LOW_HEALTH, m_creature); + m_bHasYelledOnce = true; + } + + if (m_uiDestructivePoisonTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature->getVictim(), SPELL_DESTRUCTIVE_POISON) == CAST_OK) + m_uiDestructivePoisonTimer = 15000; + } + else + m_uiDestructivePoisonTimer -= uiDiff; + + if (m_uiLightningBoltTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature->getVictim(), SPELL_CHAIN_LIGHTNING) == CAST_OK) + m_uiLightningBoltTimer = 10000; + } + else + m_uiLightningBoltTimer -= uiDiff; + + DoMeleeAttackIfReady(); + + break; + case PHASE_EPILOGUE: + DialogueUpdate(uiDiff); + break; + } + } +}; + +bool GossipHello_npc_akama(Player* pPlayer, Creature* pCreature) +{ + if (ScriptedInstance* pInstance = (ScriptedInstance*)pCreature->GetInstanceData()) + { + if (pInstance->GetData(TYPE_SHADE) != DONE) + pPlayer->ADD_GOSSIP_ITEM_ID(GOSSIP_ICON_CHAT, GOSSIP_ITEM_START_ENCOUNTER, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 1); + } + + pPlayer->SEND_GOSSIP_MENU(TEXT_ID_AKAMA, pCreature->GetObjectGuid()); + return true; +} + +bool GossipSelect_npc_akama(Player* pPlayer, Creature* pCreature, uint32 /*uiSender*/, uint32 uiAction) +{ + if (uiAction == GOSSIP_ACTION_INFO_DEF + 1) // Fight time + { + pPlayer->CLOSE_GOSSIP_MENU(); + + if (npc_akamaAI* pAkamaAI = dynamic_cast(pCreature->AI())) + pAkamaAI->DoStartEvent(); + } + + return true; +} + +/*###### +## boss_shade_of_akama +######*/ + +struct boss_shade_of_akamaAI : public ScriptedAI +{ + boss_shade_of_akamaAI(Creature* pCreature) : ScriptedAI(pCreature) + { + m_pInstance = (ScriptedInstance*)pCreature->GetInstanceData(); + Reset(); + } + + ScriptedInstance* m_pInstance; + + void Reset() override + { + SetCombatMovement(false); + m_creature->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE); + } + + void JustReachedHome() override + { + if (m_pInstance) + m_pInstance->SetData(TYPE_SHADE, FAIL); + } + + void KilledUnit(Unit* pVictim) override + { + if (pVictim->GetEntry() == NPC_AKAMA) + EnterEvadeMode(); + } + + void JustDied(Unit* /*pKiller*/) override + { + DoCastSpellIfCan(m_creature, SPELL_SUMMON_SHADE_TRIGGER, CAST_TRIGGERED); + + if (m_pInstance) + { + m_pInstance->SetData(TYPE_SHADE, DONE); + + // Inform Akama that the Shade is dead + if (Creature* pAkama = m_pInstance->GetSingleCreatureFromStorage(NPC_AKAMA_SHADE)) + pAkama->AI()->KilledUnit(m_creature); + } + } + + void MovementInform(uint32 uiMoveType, uint32 uiPointId) override + { + if (uiMoveType != POINT_MOTION_TYPE || !uiPointId || !m_pInstance) + return; + + // Set in combat with Akama + if (Creature* pAkama = m_pInstance->GetSingleCreatureFromStorage(NPC_AKAMA_SHADE)) + { + m_creature->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE); + + // Shade should move to Akama, not the other way around + AttackStart(pAkama); + + // Crazy amount of threat + m_creature->AddThreat(pAkama, 10000000.0f); + pAkama->AddThreat(m_creature, 10000000.0f); + } + } + + void UpdateAI(const uint32 /*uiDiff*/) override + { + if (!m_creature->SelectHostileTarget() || !m_creature->getVictim()) + return; + + DoMeleeAttackIfReady(); + } +}; + +/*###### +## mob_ashtongue_channeler +######*/ + +struct mob_ashtongue_channelerAI : public ScriptedAI +{ + mob_ashtongue_channelerAI(Creature* pCreature) : ScriptedAI(pCreature) + { + m_pInstance = (ScriptedInstance*)pCreature->GetInstanceData(); + Reset(); + } + + ScriptedInstance* m_pInstance; + + uint32 m_uiBanishTimer; + + void Reset() override + { + m_uiBanishTimer = 5000; + + m_creature->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE); + } + + void JustDied(Unit* /*pKiller*/) override + { + if (!m_pInstance) + return; + + // Inform Akama that one channeler is dead + if (Creature* pAkama = m_pInstance->GetSingleCreatureFromStorage(NPC_AKAMA_SHADE)) + pAkama->AI()->KilledUnit(m_creature); + } + + void AttackStart(Unit* /*pWho*/) override {} + void MoveInLineOfSight(Unit* /*pWho*/) override {} + + void UpdateAI(const uint32 uiDiff) override + { + if (m_uiBanishTimer) + { + if (m_uiBanishTimer <= uiDiff) + { + if (DoCastSpellIfCan(m_creature, SPELL_SHADE_SOUL_CHANNEL)) + m_uiBanishTimer = 0; + } + else + m_uiBanishTimer -= uiDiff; + } + } +}; + +/*###### +## mob_ashtongue_sorcerer +######*/ + +struct mob_ashtongue_sorcererAI : public ScriptedAI +{ + mob_ashtongue_sorcererAI(Creature* pCreature) : ScriptedAI(pCreature) + { + m_pInstance = (ScriptedInstance*)pCreature->GetInstanceData(); + Reset(); + } + + ScriptedInstance* m_pInstance; + + void Reset() override {} + + void AttackStart(Unit* /*pWho*/) override {} + void MoveInLineOfSight(Unit* /*pWho*/) override {} + + void JustDied(Unit* /*pKiller*/) override + { + if (!m_pInstance) + return; + + // Inform Akama that one sorcerer is dead + if (Creature* pAkama = m_pInstance->GetSingleCreatureFromStorage(NPC_AKAMA_SHADE)) + pAkama->AI()->KilledUnit(m_creature); + } + + void MovementInform(uint32 uiMoveType, uint32 uiPointId) override + { + if (uiMoveType != POINT_MOTION_TYPE || !uiPointId) + return; + + // Channel on the Shade when reached the calculated point + if (DoCastSpellIfCan(m_creature, SPELL_SHADE_SOUL_CHANNEL) == CAST_OK) + { + m_creature->GetMotionMaster()->Clear(); + m_creature->GetMotionMaster()->MoveIdle(); + } + } + + void UpdateAI(const uint32 /*uiDiff*/) override {} +}; + +CreatureAI* GetAI_npc_akama_shade(Creature* pCreature) +{ + return new npc_akamaAI(pCreature); +} + +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); +} + +void AddSC_boss_shade_of_akama() +{ + Script* pNewScript; + + pNewScript = new Script; + pNewScript->Name = "npc_akama_shade"; + pNewScript->GetAI = &GetAI_npc_akama_shade; + pNewScript->pGossipHello = &GossipHello_npc_akama; + pNewScript->pGossipSelect = &GossipSelect_npc_akama; + pNewScript->RegisterSelf(); + + pNewScript = new Script; + pNewScript->Name = "boss_shade_of_akama"; + pNewScript->GetAI = &GetAI_boss_shade_of_akama; + pNewScript->RegisterSelf(); + + pNewScript = new Script; + pNewScript->Name = "mob_ashtongue_channeler"; + pNewScript->GetAI = &GetAI_mob_ashtongue_channeler; + pNewScript->RegisterSelf(); + + pNewScript = new Script; + pNewScript->Name = "mob_ashtongue_sorcerer"; + pNewScript->GetAI = &GetAI_mob_ashtongue_sorcerer; + pNewScript->RegisterSelf(); +} diff --git a/src/modules/SD2/scripts/outland/black_temple/boss_supremus.cpp b/src/modules/SD2/scripts/outland/black_temple/boss_supremus.cpp new file mode 100644 index 000000000..7ae36ef27 --- /dev/null +++ b/src/modules/SD2/scripts/outland/black_temple/boss_supremus.cpp @@ -0,0 +1,335 @@ +/* Copyright (C) 2006 - 2013 ScriptDev2 + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with 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: 90 +SDComment: Unknown if other speed-changes happen, remove AI for trigger mobs in next step +SDCategory: Black Temple +EndScriptData */ + +#include "precompiled.h" +#include "black_temple.h" + +enum +{ + EMOTE_NEW_TARGET = -1564010, + EMOTE_PUNCH_GROUND = -1564011, + EMOTE_GROUND_CRACK = -1564012, + + // Spells + SPELL_HATEFUL_STRIKE = 41926, + SPELL_CHARGE = 41581, + SPELL_MOLTEN_FLAME = 40980, + SPELL_VOLCANIC_ERUPTION_BOSS = 40276, + SPELL_VOLCANIC_ERUPTION_VOLCANO = 40117, + SPELL_MOLTEN_PUNCH = 40126, + SPELL_BERSERK = 45078, + SPELL_SLOW_SELF = 41922, + + NPC_VOLCANO = 23085, + NPC_STALKER = 23095, +}; + +/* Non existed spells that were used in 3.2 + * Stalker: 40257 41930 + * Supremus: 33420 41582 41925 41951 + */ + +const float RANGE_MOLTEN_PUNCH = 40.0; + +/* These floats are related to the speed-hack near end of script; + * Statet at wowwiki: "If the gaze target is further away than 40 yards, he dashes at about five times the normal run speed until the range is about 20 yards." + * TODO But this is currently not confirmed otherwise to be actually happening + * const float RANGE_MIN_DASHING = 20.0; + * const float SPEED_DASHING = 5.0; + * const float SPEED_CHASE = 0.9f; + */ + +// TODO Remove this 'script' when combat movement can be proper prevented from core-side +struct molten_flameAI : public Scripted_NoMovementAI +{ + molten_flameAI(Creature* pCreature) : Scripted_NoMovementAI(pCreature) { Reset(); } + + void Reset() override {} + void AttackStart(Unit* /*pWho*/) override {} + void MoveInLineOfSight(Unit* /*pWho*/) override {} + void UpdateAI(const uint32 /*uiDiff*/) override {} +}; + +// TODO Remove this 'script' when combat movement can be proper prevented from core-side +struct npc_volcanoAI : public Scripted_NoMovementAI +{ + npc_volcanoAI(Creature* pCreature) : Scripted_NoMovementAI(pCreature) { Reset(); } + + void Reset() override {} + void AttackStart(Unit* /*pWho*/) override {} + void MoveInLineOfSight(Unit* /*pWho*/) override {} + void UpdateAI(const uint32 /*uiDiff*/) override {} +}; + +struct boss_supremusAI : public ScriptedAI +{ + boss_supremusAI(Creature* pCreature) : ScriptedAI(pCreature) + { + m_pInstance = (ScriptedInstance*)pCreature->GetInstanceData(); + Reset(); + } + + ScriptedInstance* m_pInstance; + + uint32 m_uiSummonFlameTimer; + uint32 m_uiSwitchTargetTimer; + uint32 m_uiPhaseSwitchTimer; + uint32 m_uiSummonVolcanoTimer; + uint32 m_uiHatefulStrikeTimer; + uint32 m_uiBerserkTimer; + uint32 m_uiMoltenPunchTimer; + + bool m_bTankPhase; + + GuidList m_lSummonedGUIDs; + + void Reset() override + { + m_uiHatefulStrikeTimer = 5000; + m_uiSummonFlameTimer = 20000; + m_uiPhaseSwitchTimer = 60000; + m_uiMoltenPunchTimer = 8000; + m_uiBerserkTimer = 15 * MINUTE * IN_MILLISECONDS; + + m_bTankPhase = true; + } + + void JustReachedHome() override + { + if (m_pInstance) + m_pInstance->SetData(TYPE_SUPREMUS, NOT_STARTED); + } + + void Aggro(Unit* /*pWho*/) override + { + if (m_pInstance) + m_pInstance->SetData(TYPE_SUPREMUS, IN_PROGRESS); + } + + void JustDied(Unit* /*pKiller*/) override + { + if (m_pInstance) + m_pInstance->SetData(TYPE_SUPREMUS, DONE); + + for (GuidList::const_iterator itr = m_lSummonedGUIDs.begin(); itr != m_lSummonedGUIDs.end(); ++itr) + { + if (Creature* pSummoned = m_creature->GetMap()->GetCreature(*itr)) + pSummoned->ForcedDespawn(); + } + } + + void JustSummoned(Creature* pSummoned) override + { + if (pSummoned->GetEntry() == NPC_STALKER) + { + Unit* pTarget = m_creature->SelectAttackingTarget(ATTACKING_TARGET_RANDOM, 1); + if (!pTarget) + pTarget = m_creature->getVictim(); + + if (pTarget) + { + pSummoned->GetMotionMaster()->Clear(); + pSummoned->GetMotionMaster()->MoveFollow(pTarget, 0.0f, 0.0f); + pSummoned->CastSpell(pSummoned, SPELL_MOLTEN_FLAME, false, NULL, NULL, m_creature->GetObjectGuid()); + } + } + + else if (pSummoned->GetEntry() == NPC_VOLCANO) + pSummoned->CastSpell(pSummoned, SPELL_VOLCANIC_ERUPTION_VOLCANO, false, NULL, NULL, m_creature->GetObjectGuid()); + } + + Unit* GetHatefulStrikeTarget() + { + uint32 uiHealth = 0; + Unit* pTarget = NULL; + + ThreatList const& tList = m_creature->GetThreatManager().getThreatList(); + for (ThreatList::const_iterator iter = tList.begin(); iter != tList.end(); ++iter) + { + Unit* pUnit = m_creature->GetMap()->GetUnit((*iter)->getUnitGuid()); + + if (pUnit && m_creature->CanReachWithMeleeAttack(pUnit)) + { + if (pUnit->GetHealth() > uiHealth) + { + uiHealth = pUnit->GetHealth(); + pTarget = pUnit; + } + } + } + + return pTarget; + } + + void KilledUnit(Unit* pKilled) override + { + // The current target is the fixated target - repick a new one + if (!m_bTankPhase && pKilled == m_creature->getVictim()) + m_uiSwitchTargetTimer = 0; + } + + void UpdateAI(const uint32 uiDiff) override + { + if (!m_creature->SelectHostileTarget() || !m_creature->getVictim()) + return; + + if (m_uiBerserkTimer) + { + if (m_uiBerserkTimer <= uiDiff) + { + if (DoCastSpellIfCan(m_creature, SPELL_BERSERK) == CAST_OK) + m_uiBerserkTimer = 0; + } + else + m_uiBerserkTimer -= uiDiff; + } + + if (m_uiSummonFlameTimer < uiDiff) + { + // This currently is entirely screwed, because the npc is summoned somewhere far away as of big bounding box of supremus + if (DoCastSpellIfCan(m_creature, SPELL_MOLTEN_PUNCH) == CAST_OK) + m_uiSummonFlameTimer = 20000; + } + else + m_uiSummonFlameTimer -= uiDiff; + + if (m_uiPhaseSwitchTimer < uiDiff) + { + if (!m_bTankPhase) + { + m_bTankPhase = true; + m_creature->RemoveAurasDueToSpell(SPELL_SLOW_SELF); + m_creature->FixateTarget(NULL); + } + else + { + m_bTankPhase = false; + m_uiSwitchTargetTimer = 0; + m_uiSummonVolcanoTimer = 2000; + + DoCastSpellIfCan(m_creature, SPELL_SLOW_SELF, CAST_INTERRUPT_PREVIOUS); + } + + m_uiPhaseSwitchTimer = MINUTE * IN_MILLISECONDS; + } + else + m_uiPhaseSwitchTimer -= uiDiff; + + if (m_bTankPhase) + { + if (m_uiHatefulStrikeTimer < uiDiff) + { + if (Unit* pTarget = GetHatefulStrikeTarget()) + { + if (DoCastSpellIfCan(pTarget, SPELL_HATEFUL_STRIKE) == CAST_OK) + m_uiHatefulStrikeTimer = 5000; + } + } + else + m_uiHatefulStrikeTimer -= uiDiff; + } + else // !m_bTankPhase + { + if (m_uiSwitchTargetTimer < uiDiff) + { + if (Unit* pTarget = m_creature->SelectAttackingTarget(ATTACKING_TARGET_RANDOM, 0)) + { + m_creature->FixateTarget(pTarget); + DoScriptText(EMOTE_NEW_TARGET, m_creature); + m_uiSwitchTargetTimer = 10000; + } + } + else + m_uiSwitchTargetTimer -= uiDiff; + + if (m_uiSummonVolcanoTimer < uiDiff) + { + Unit* pTarget = m_creature->SelectAttackingTarget(ATTACKING_TARGET_RANDOM, 1); + + if (DoCastSpellIfCan(pTarget ? pTarget : m_creature->getVictim(), SPELL_VOLCANIC_ERUPTION_BOSS) == CAST_OK) + { + DoScriptText(EMOTE_GROUND_CRACK, m_creature); + m_uiSummonVolcanoTimer = 10000; + } + } + else + m_uiSummonVolcanoTimer -= uiDiff; + + if (m_uiMoltenPunchTimer < uiDiff) + { + if (m_creature->GetCombatDistance(m_creature->getVictim(), false) < RANGE_MOLTEN_PUNCH) + { + DoCastSpellIfCan(m_creature->getVictim(), SPELL_CHARGE); + DoScriptText(EMOTE_PUNCH_GROUND, m_creature); + } + m_uiMoltenPunchTimer = 8000; // might be better with small timer and some sort of cast-chance + } + else + m_uiMoltenPunchTimer -= uiDiff; + + /* Not understood how this really must work + * if (m_creature->GetSpeedRate(MOVE_RUN) > SPEED_CHASE && m_creature->GetCombatDistance(m_creature->getVictim()) < RANGE_MIN_DASHING) + * m_creature->SetSpeedRate(MOVE_RUN, SPEED_CHASE); + * else if (m_creature->GetCombatDistance(m_creature->getVictim()) > RANGE_MOLTEN_PUNCH) + * m_creature->SetSpeedRate(MOVE_RUN, SPEED_DASHING); + */ + } + + DoMeleeAttackIfReady(); + } +}; + +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* pNewScript; + + pNewScript = new Script; + pNewScript->Name = "boss_supremus"; + pNewScript->GetAI = &GetAI_boss_supremus; + pNewScript->RegisterSelf(); + + pNewScript = new Script; + pNewScript->Name = "molten_flame"; + pNewScript->GetAI = &GetAI_molten_flame; + pNewScript->RegisterSelf(); + + pNewScript = new Script; + pNewScript->Name = "npc_volcano"; + pNewScript->GetAI = &GetAI_npc_volcano; + pNewScript->RegisterSelf(); +} diff --git a/src/modules/SD2/scripts/outland/black_temple/boss_teron_gorefiend.cpp b/src/modules/SD2/scripts/outland/black_temple/boss_teron_gorefiend.cpp new file mode 100644 index 000000000..a765cb198 --- /dev/null +++ b/src/modules/SD2/scripts/outland/black_temple/boss_teron_gorefiend.cpp @@ -0,0 +1,227 @@ +/* Copyright (C) 2006 - 2013 ScriptDev2 + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with 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 "black_temple.h" + +enum +{ + // Speech'n'sound + SAY_INTRO = -1564037, + SAY_AGGRO = -1564038, + SAY_SLAY1 = -1564039, + SAY_SLAY2 = -1564040, + SAY_SPELL1 = -1564041, + SAY_SPELL2 = -1564042, + SAY_SPECIAL1 = -1564043, + SAY_SPECIAL2 = -1564044, + SAY_ENRAGE = -1564045, + SAY_DEATH = -1564046, + + // Spells - boss spells + SPELL_INCINERATE = 40239, + SPELL_CRUSHING_SHADOWS = 40243, + SPELL_SHADOW_OF_DEATH = 40251, + SPELL_BERSERK = 45078, + SPELL_SUMMON_DOOM_BLOSSOM = 40188, + SPELL_SUMMON_SKELETON_1 = 40270, + SPELL_SUMMON_SKELETON_2 = 41948, + SPELL_SUMMON_SKELETON_3 = 41949, + SPELL_SUMMON_SKELETON_4 = 41950, + SPELL_SUMMON_SPIRIT = 40266, + SPELL_DESTROY_SPIRIT = 41626, // purpose unk + SPELL_DESTROY_ALL_SPIRITS = 44659, // purpose unk + + // Spells - other + // SPELL_ATROPHY = 40327, // Shadowy Constructs use this when they get within melee range of a player + SPELL_SHADOWY_CONSTRUCT = 40326, + + // NPC_DOOM_BLOSSOM = 23123, // scripted in eventAI + NPC_SHADOWY_CONSTRUCT = 23111, // scripted in eventAI + // NPC_VENGEFUL_SPIRIT = 23109, // npc controlled by the dead player +}; + +struct boss_teron_gorefiendAI : public ScriptedAI +{ + boss_teron_gorefiendAI(Creature* pCreature) : ScriptedAI(pCreature) + { + m_pInstance = (ScriptedInstance*)pCreature->GetInstanceData(); + m_bIntroDone = false; + Reset(); + } + + ScriptedInstance* m_pInstance; + + uint32 m_uiIncinerateTimer; + uint32 m_uiSummonDoomBlossomTimer; + uint32 m_uiBerserkTimer; + uint32 m_uiCrushingShadowsTimer; + uint32 m_uiShadowOfDeathTimer; + + bool m_bIntroDone; + + void Reset() override + { + m_uiIncinerateTimer = urand(20000, 30000); + m_uiSummonDoomBlossomTimer = urand(5000, 10000); + m_uiShadowOfDeathTimer = 10000; + m_uiCrushingShadowsTimer = 22000; + m_uiBerserkTimer = 10 * MINUTE * IN_MILLISECONDS; + } + + void JustReachedHome() override + { + if (m_pInstance) + m_pInstance->SetData(TYPE_GOREFIEND, FAIL); + } + + void Aggro(Unit* /*pWho*/) override + { + DoScriptText(SAY_AGGRO, m_creature); + + if (m_pInstance) + m_pInstance->SetData(TYPE_GOREFIEND, IN_PROGRESS); + } + + void MoveInLineOfSight(Unit* pWho) override + { + if (!m_bIntroDone && pWho->GetTypeId() == TYPEID_PLAYER && m_creature->IsWithinDistInMap(pWho, 60.0f)) + { + DoScriptText(SAY_INTRO, m_creature); + m_bIntroDone = true; + } + + ScriptedAI::MoveInLineOfSight(pWho); + } + + void KilledUnit(Unit* pVictim) override + { + if (pVictim->GetTypeId() != TYPEID_PLAYER) + return; + + DoScriptText(urand(0, 1) ? SAY_SLAY1 : SAY_SLAY2, m_creature); + } + + void JustDied(Unit* /*pKiller*/) override + { + if (m_pInstance) + m_pInstance->SetData(TYPE_GOREFIEND, DONE); + + DoScriptText(SAY_DEATH, m_creature); + } + + void JustSummoned(Creature* pSummoned) override + { + if (pSummoned->GetEntry() == NPC_SHADOWY_CONSTRUCT) + pSummoned->CastSpell(pSummoned, SPELL_SHADOWY_CONSTRUCT, true); + + pSummoned->SetInCombatWithZone(); + } + + void UpdateAI(const uint32 uiDiff) override + { + if (!m_creature->SelectHostileTarget() || !m_creature->getVictim()) + return; + + if (m_uiSummonDoomBlossomTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature, SPELL_SUMMON_DOOM_BLOSSOM) == CAST_OK) + { + if (urand(0, 1)) + DoScriptText(urand(0, 1) ? SAY_SPELL1 : SAY_SPELL2, m_creature); + + m_uiSummonDoomBlossomTimer = 35000; + } + } + else + m_uiSummonDoomBlossomTimer -= uiDiff; + + if (m_uiIncinerateTimer < uiDiff) + { + Unit* pTarget = m_creature->SelectAttackingTarget(ATTACKING_TARGET_RANDOM, 1); + + if (DoCastSpellIfCan(pTarget ? pTarget : m_creature->getVictim(), SPELL_INCINERATE) == CAST_OK) + m_uiIncinerateTimer = urand(20000, 50000); + } + else + m_uiIncinerateTimer -= uiDiff; + + if (m_uiCrushingShadowsTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature, SPELL_CRUSHING_SHADOWS) == CAST_OK) + { + if (urand(0, 1)) + DoScriptText(urand(0, 1) ? SAY_SPECIAL1 : SAY_SPECIAL2, m_creature); + + m_uiCrushingShadowsTimer = urand(10000, 26000); + } + } + else + m_uiCrushingShadowsTimer -= uiDiff; + + if (m_uiShadowOfDeathTimer < uiDiff) + { + if (Unit* pTarget = m_creature->SelectAttackingTarget(ATTACKING_TARGET_RANDOM, 1, SPELL_SHADOW_OF_DEATH, SELECT_FLAG_PLAYER)) + { + if (DoCastSpellIfCan(pTarget, SPELL_SHADOW_OF_DEATH) == CAST_OK) + { + DoScriptText(urand(0, 1) ? SAY_SPECIAL1 : SAY_SPECIAL2, m_creature); + m_uiShadowOfDeathTimer = 30000; + } + } + } + else + m_uiShadowOfDeathTimer -= uiDiff; + + if (m_uiBerserkTimer) + { + if (m_uiBerserkTimer <= uiDiff) + { + if (DoCastSpellIfCan(m_creature, SPELL_BERSERK) == CAST_OK) + { + DoScriptText(SAY_ENRAGE, m_creature); + m_uiBerserkTimer = 0; + } + } + else + m_uiBerserkTimer -= uiDiff; + } + + DoMeleeAttackIfReady(); + } +}; + +CreatureAI* GetAI_boss_teron_gorefiend(Creature* pCreature) +{ + return new boss_teron_gorefiendAI(pCreature); +} + +void AddSC_boss_teron_gorefiend() +{ + Script* pNewScript; + + pNewScript = new Script; + pNewScript->Name = "boss_teron_gorefiend"; + pNewScript->GetAI = &GetAI_boss_teron_gorefiend; + pNewScript->RegisterSelf(); +} diff --git a/src/modules/SD2/scripts/outland/black_temple/boss_warlord_najentus.cpp b/src/modules/SD2/scripts/outland/black_temple/boss_warlord_najentus.cpp new file mode 100644 index 000000000..7a51ad8d4 --- /dev/null +++ b/src/modules/SD2/scripts/outland/black_temple/boss_warlord_najentus.cpp @@ -0,0 +1,218 @@ +/* Copyright (C) 2006 - 2013 ScriptDev2 + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +/* ScriptData +SDName: Boss_Warlord_Najentus +SD%Complete: 90 +SDComment: Core spell support for Needle Spine (spells 39992, 39835) missing, no change from SD2 needed +SDCategory: Black Temple +EndScriptData */ + +#include "precompiled.h" +#include "black_temple.h" + +enum +{ + SAY_AGGRO = -1564000, + SAY_NEEDLE1 = -1564001, + SAY_NEEDLE2 = -1564002, + SAY_SLAY1 = -1564003, + SAY_SLAY2 = -1564004, + SAY_SPECIAL1 = -1564005, + SAY_SPECIAL2 = -1564006, + SAY_ENRAGE1 = -1564007, // is this text actually in use? + SAY_ENRAGE2 = -1564008, + SAY_DEATH = -1564009, + + SPELL_CRASHINGWAVE = 40100, + SPELL_NEEDLE_SPINE = 39992, + SPELL_TIDAL_BURST = 39878, + SPELL_TIDAL_SHIELD = 39872, + SPELL_IMPALING_SPINE = 39837, + SPELL_CREATE_NAJENTUS_SPINE = 39956, + SPELL_HURL_SPINE = 39948, + SPELL_BERSERK = 26662 +}; + +struct boss_najentusAI : public ScriptedAI +{ + boss_najentusAI(Creature* pCreature) : ScriptedAI(pCreature) + { + m_pInstance = (ScriptedInstance*)pCreature->GetInstanceData(); + Reset(); + } + + ScriptedInstance* m_pInstance; + + uint32 m_uiNeedleSpineTimer; + uint32 m_uiEnrageTimer; + uint32 m_uiSpecialYellTimer; + uint32 m_uiTidalShieldTimer; + uint32 m_uiImpalingSpineTimer; + + bool m_bIsShielded; + + void Reset() override + { + m_bIsShielded = false; + + m_uiNeedleSpineTimer = 10000; + m_uiEnrageTimer = MINUTE * 8 * IN_MILLISECONDS; + m_uiSpecialYellTimer = urand(45000, 120000); + m_uiTidalShieldTimer = 60000; + m_uiImpalingSpineTimer = 20000; + + SetCombatMovement(true); + } + + void JustReachedHome() override + { + if (m_pInstance) + m_pInstance->SetData(TYPE_NAJENTUS, NOT_STARTED); + } + + void KilledUnit(Unit* /*pVictim*/) override + { + DoScriptText(urand(0, 1) ? SAY_SLAY1 : SAY_SLAY2, m_creature); + } + + void JustDied(Unit* /*pKiller*/) override + { + if (m_pInstance) + m_pInstance->SetData(TYPE_NAJENTUS, DONE); + + DoScriptText(SAY_DEATH, m_creature); + } + + void SpellHit(Unit* /*pCaster*/, const SpellEntry* pSpell) override + { + if (m_bIsShielded && pSpell->Id == SPELL_HURL_SPINE) + { + if (m_creature->HasAura(SPELL_TIDAL_SHIELD)) + m_creature->RemoveAurasDueToSpell(SPELL_TIDAL_SHIELD); + + DoCastSpellIfCan(m_creature, SPELL_TIDAL_BURST); + m_bIsShielded = false; + + SetCombatMovement(true); + if (m_creature->getVictim()) + m_creature->GetMotionMaster()->MoveChase(m_creature->getVictim()); + } + } + + void Aggro(Unit* /*pWho*/) override + { + if (m_pInstance) + m_pInstance->SetData(TYPE_NAJENTUS, IN_PROGRESS); + + DoScriptText(SAY_AGGRO, m_creature); + } + + void UpdateAI(const uint32 uiDiff) override + { + if (!m_creature->SelectHostileTarget() || !m_creature->getVictim()) + return; + + // If shield expired after 45s, attack again + if (m_bIsShielded && m_uiTidalShieldTimer < 16000 && !m_creature->HasAura(SPELL_TIDAL_SHIELD)) + { + m_bIsShielded = false; + + SetCombatMovement(true); + m_creature->GetMotionMaster()->MoveChase(m_creature->getVictim()); + } + + if (m_uiEnrageTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature, SPELL_BERSERK, CAST_INTERRUPT_PREVIOUS) == CAST_OK) + { + m_uiEnrageTimer = MINUTE * 8 * IN_MILLISECONDS; + DoScriptText(SAY_ENRAGE2, m_creature); + } + } + else + m_uiEnrageTimer -= uiDiff; + + if (m_uiSpecialYellTimer < uiDiff) + { + DoScriptText(urand(0, 1) ? SAY_SPECIAL1 : SAY_SPECIAL2, m_creature); + m_uiSpecialYellTimer = urand(25000, 100000); + } + else + m_uiSpecialYellTimer -= uiDiff; + + if (m_uiImpalingSpineTimer < uiDiff) + { + Unit* pTarget = m_creature->SelectAttackingTarget(ATTACKING_TARGET_RANDOM, 1, SPELL_IMPALING_SPINE, SELECT_FLAG_PLAYER); + + if (!pTarget) + pTarget = m_creature->getVictim(); + + if (pTarget && pTarget->GetTypeId() == TYPEID_PLAYER) + { + DoCastSpellIfCan(pTarget, SPELL_IMPALING_SPINE); + m_uiImpalingSpineTimer = 20000; + + DoScriptText(urand(0, 1) ? SAY_NEEDLE1 : SAY_NEEDLE2, m_creature); + } + } + else + m_uiImpalingSpineTimer -= uiDiff; + + if (m_uiTidalShieldTimer < uiDiff) + { + DoCastSpellIfCan(m_creature, SPELL_TIDAL_SHIELD, CAST_INTERRUPT_PREVIOUS | CAST_TRIGGERED); + + m_creature->GetMotionMaster()->Clear(false); + m_creature->GetMotionMaster()->MoveIdle(); + SetCombatMovement(false); + + m_bIsShielded = true; + m_uiTidalShieldTimer = 60000; + + // Skip needle splines for 10s + m_uiNeedleSpineTimer += 10000; + } + else + m_uiTidalShieldTimer -= uiDiff; + + // Needle + if (m_uiNeedleSpineTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature, SPELL_NEEDLE_SPINE) == CAST_OK) + m_uiNeedleSpineTimer = 3000; + } + else + m_uiNeedleSpineTimer -= uiDiff; + + DoMeleeAttackIfReady(); + } +}; + +CreatureAI* GetAI_boss_najentus(Creature* pCreature) +{ + return new boss_najentusAI(pCreature); +} + +void AddSC_boss_najentus() +{ + Script* pNewScript; + + pNewScript = new Script; + pNewScript->Name = "boss_najentus"; + pNewScript->GetAI = &GetAI_boss_najentus; + pNewScript->RegisterSelf(); +} diff --git a/src/modules/SD2/scripts/outland/black_temple/illidari_council.cpp b/src/modules/SD2/scripts/outland/black_temple/illidari_council.cpp new file mode 100644 index 000000000..8ca8a6345 --- /dev/null +++ b/src/modules/SD2/scripts/outland/black_temple/illidari_council.cpp @@ -0,0 +1,811 @@ +/* Copyright (C) 2006 - 2013 ScriptDev2 + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along 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: 90 +SDComment: The shared health is done by workaround - proper spells are NYI. +SDCategory: Black Temple +EndScriptData */ + +#include "precompiled.h" +#include "black_temple.h" + +enum +{ + // Speech'n'Sounds + SAY_GATH_AGGRO = -1564069, + SAY_GATH_SLAY = -1564085, + SAY_GATH_SLAY_COMNT = -1564089, + SAY_GATH_DEATH = -1564093, + SAY_GATH_SPECIAL1 = -1564077, + SAY_GATH_SPECIAL2 = -1564081, + SAY_GATH_BERSERK = -1564073, + + SAY_VERA_AGGRO = -1564070, + SAY_VERA_SLAY = -1564086, + SAY_VERA_COMNT = -1564089, + SAY_VERA_DEATH = -1564094, + SAY_VERA_SPECIAL1 = -1564078, + SAY_VERA_SPECIAL2 = -1564082, + SAY_VERA_BERSERK = -1564074, + + SAY_MALA_AGGRO = -1564071, + SAY_MALA_SLAY = -1564087, + SAY_MALA_COMNT = -1564090, + SAY_MALA_DEATH = -1564095, + SAY_MALA_SPECIAL1 = -1564079, + SAY_MALA_SPECIAL2 = -1564083, + SAY_MALA_BERSERK = -1564075, + + SAY_ZERE_AGGRO = -1564072, + SAY_ZERE_SLAY = -1564088, + SAY_ZERE_COMNT = -1564091, + SAY_ZERE_DEATH = -1564096, + SAY_ZERE_SPECIAL1 = -1564080, + SAY_ZERE_SPECIAL2 = -1564084, + SAY_ZERE_BERSERK = -1564076, + + // High Nethermancer Zerevor's spells + SPELL_FLAMESTRIKE = 41481, + SPELL_BLIZZARD = 41482, + SPELL_ARCANE_BOLT = 41483, + SPELL_ARCANE_EXPLOSION = 41524, + SPELL_DAMPEN_MAGIC = 41478, + + // Lady Malande's spells + SPELL_EMPOWERED_SMITE = 41471, + SPELL_CIRCLE_OF_HEALING = 41455, + SPELL_REFLECTIVE_SHIELD = 41475, + SPELL_DIVINE_WRATH = 41472, + + // Gathios the Shatterer's spells + SPELL_BLESS_PROTECTION = 41450, + SPELL_BLESS_SPELLWARD = 41451, + SPELL_CONSECRATION = 41541, + SPELL_HAMMER_OF_JUSTICE = 41468, + SPELL_SEAL_OF_COMMAND = 41469, + SPELL_SEAL_OF_BLOOD = 41459, + SPELL_CHROMATIC_AURA = 41453, + SPELL_DEVOTION_AURA = 41452, + SPELL_JUDGMENT = 41467, // triggers 41473 (41470 or 41461) + + // Veras Darkshadow's spells + SPELL_DEADLY_POISON = 41485, + SPELL_ENVENOM = 41487, + SPELL_VANISH_TELEPORT = 41479, + SPELL_VANISH = 41476, + + SPELL_BERSERK = 45078, + // SPELL_BALANCE_OF_POWER = 41341, // somehow related to 41344 + SPELL_SHARED_RULE_DAM = 41342, + SPELL_SHARED_RULE_HEAL = 41343, + SPELL_EMPYREAL_EQUIVALENCY = 41333, + SPELL_EMPYREAL_BALANCE = 41499, +}; + +static const DialogueEntry aCouncilDialogue[] = +{ + {SAY_GATH_AGGRO, NPC_GATHIOS, 5000}, + {SAY_VERA_AGGRO, NPC_VERAS, 5500}, + {SAY_MALA_AGGRO, NPC_LADY_MALANDE, 5000}, + {SAY_ZERE_AGGRO, NPC_ZEREVOR, 0}, + {SAY_GATH_BERSERK, NPC_GATHIOS, 2000}, + {SAY_VERA_BERSERK, NPC_VERAS, 6000}, + {SAY_MALA_BERSERK, NPC_LADY_MALANDE, 5000}, + {SAY_ZERE_BERSERK, NPC_ZEREVOR, 0}, + {0, 0, 0}, +}; + +static const uint32 aCouncilMember[] = {NPC_GATHIOS, NPC_VERAS, NPC_LADY_MALANDE, NPC_ZEREVOR}; + +/*###### +## mob_blood_elf_council_voice_trigger +######*/ + +struct mob_blood_elf_council_voice_triggerAI : public ScriptedAI +{ + mob_blood_elf_council_voice_triggerAI(Creature* pCreature) : ScriptedAI(pCreature), + m_councilDialogue(aCouncilDialogue) + { + m_pInstance = (ScriptedInstance*)(m_creature->GetInstanceData()); + m_councilDialogue.InitializeDialogueHelper(m_pInstance); + Reset(); + } + + ScriptedInstance* m_pInstance; + DialogueHelper m_councilDialogue; + + uint32 m_uiEnrageTimer; + uint32 m_uiAggroYellTimer; + + void Reset() override + { + m_uiEnrageTimer = 0; + m_uiAggroYellTimer = 0; + } + + void StartVoiceEvent() + { + m_uiAggroYellTimer = 500; + m_uiEnrageTimer = 15 * MINUTE * IN_MILLISECONDS; + } + + void UpdateAI(const uint32 uiDiff) override + { + m_councilDialogue.DialogueUpdate(uiDiff); + + if (m_uiAggroYellTimer) + { + if (m_uiAggroYellTimer <= uiDiff) + { + // Start yells + m_councilDialogue.StartNextDialogueText(SAY_GATH_AGGRO); + m_uiAggroYellTimer = 0; + } + else + m_uiAggroYellTimer -= uiDiff; + } + + if (m_uiEnrageTimer) + { + if (m_uiEnrageTimer <= uiDiff) + { + // Cast berserk on all members + for (uint8 i = 0; i < 4; ++i) + { + if (Creature* pMember = m_pInstance->GetSingleCreatureFromStorage(aCouncilMember[i])) + pMember->CastSpell(pMember, SPELL_BERSERK, true); + } + // Start yells + m_councilDialogue.StartNextDialogueText(SAY_GATH_BERSERK); + m_uiEnrageTimer = 0; + } + else + m_uiEnrageTimer -= uiDiff; + } + } +}; + +/*###### +## mob_illidari_council +######*/ + +struct mob_illidari_councilAI : public ScriptedAI +{ + mob_illidari_councilAI(Creature* pCreature) : ScriptedAI(pCreature) + { + m_pInstance = (ScriptedInstance*)pCreature->GetInstanceData(); + Reset(); + } + + ScriptedInstance* m_pInstance; + + uint32 m_uiEquivalencyTimer; + + bool m_bEventBegun; + bool m_bEventEnd; + + void Reset() override + { + m_bEventBegun = false; + m_bEventEnd = false; + + m_uiEquivalencyTimer = urand(2000, 3000); + } + + void AttackStart(Unit* /*pWho*/) override { } + void MoveInLineOfSight(Unit* /*pWho*/) override { } + + void JustDied(Unit* /*pKiller*/) override + { + DoEndEvent(); + + if (m_pInstance) + m_pInstance->SetData(TYPE_COUNCIL, DONE); + } + + void DoStartEvent() + { + if (!m_pInstance || m_bEventBegun) + return; + + // Prevent further handling for next council uiMember aggroing + m_bEventBegun = true; + + // Start the event for the Voice Trigger + if (Creature* pVoiceTrigger = m_pInstance->GetSingleCreatureFromStorage(NPC_COUNCIL_VOICE)) + { + if (mob_blood_elf_council_voice_triggerAI* pVoiceAI = dynamic_cast(pVoiceTrigger->AI())) + pVoiceAI->StartVoiceEvent(); + } + + DoCastSpellIfCan(m_creature, SPELL_EMPYREAL_BALANCE); + } + + void DoEndEvent() + { + if (!m_pInstance || m_bEventEnd) + return; + + // Prevent further handling for next council uiMember death + m_bEventEnd = true; + + // Kill all the other council members + for (uint8 i = 0; i < 4; ++i) + { + Creature* pMember = m_pInstance->GetSingleCreatureFromStorage(aCouncilMember[i]); + if (pMember && pMember->IsAlive()) + pMember->DealDamage(pMember, pMember->GetHealth(), NULL, DIRECT_DAMAGE, SPELL_SCHOOL_MASK_NORMAL, NULL, false); + } + + // Self kill the voice trigger and the controller + if (Creature* pVoiceTrigger = m_pInstance->GetSingleCreatureFromStorage(NPC_COUNCIL_VOICE)) + pVoiceTrigger->DealDamage(pVoiceTrigger, pVoiceTrigger->GetHealth(), NULL, DIRECT_DAMAGE, SPELL_SCHOOL_MASK_NORMAL, NULL, false); + + m_creature->DealDamage(m_creature, m_creature->GetHealth(), NULL, DIRECT_DAMAGE, SPELL_SCHOOL_MASK_NORMAL, NULL, false); + } + + void UpdateAI(const uint32 uiDiff) override + { + // Make the council members health equal every 2-3 secs + if (m_bEventBegun && !m_bEventEnd) + { + if (m_uiEquivalencyTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature, SPELL_EMPYREAL_EQUIVALENCY) == CAST_OK) + m_uiEquivalencyTimer = urand(2000, 3000); + } + else + m_uiEquivalencyTimer -= uiDiff; + } + } +}; + +/*###### +## boss_illidari_council +######*/ + +struct boss_illidari_councilAI : public ScriptedAI +{ + boss_illidari_councilAI(Creature* pCreature) : ScriptedAI(pCreature) + { + m_pInstance = (ScriptedInstance*)pCreature->GetInstanceData(); + } + + ScriptedInstance* m_pInstance; + + void Aggro(Unit* /*pWho*/) override + { + if (m_pInstance) + { + // Note: council aggro handled by creature linking + + if (Creature* pController = m_pInstance->GetSingleCreatureFromStorage(NPC_ILLIDARI_COUNCIL)) + { + if (mob_illidari_councilAI* pControlAI = dynamic_cast(pController->AI())) + pControlAI->DoStartEvent(); + } + + m_pInstance->SetData(TYPE_COUNCIL, IN_PROGRESS); + } + } + + void JustDied(Unit* /*pKiller*/) override + { + if (m_pInstance) + { + if (Creature* pController = m_pInstance->GetSingleCreatureFromStorage(NPC_ILLIDARI_COUNCIL)) + { + if (mob_illidari_councilAI* pControlAI = dynamic_cast(pController->AI())) + pControlAI->DoEndEvent(); + } + + m_pInstance->SetData(TYPE_COUNCIL, DONE); + } + } + + void JustReachedHome() override + { + if (m_pInstance) + { + // Note: council respawn handled by creature linking + + if (Creature* pVoiceTrigger = m_pInstance->GetSingleCreatureFromStorage(NPC_COUNCIL_VOICE)) + pVoiceTrigger->AI()->EnterEvadeMode(); + + if (Creature* pController = m_pInstance->GetSingleCreatureFromStorage(NPC_ILLIDARI_COUNCIL)) + pController->AI()->EnterEvadeMode(); + + m_pInstance->SetData(TYPE_COUNCIL, FAIL); + } + } + + void DamageTaken(Unit* pDoneBy, uint32& uiDamage) override + { + int32 uiDamageTaken = (int32)uiDamage; + m_creature->CastCustomSpell(m_creature, SPELL_SHARED_RULE_DAM, &uiDamageTaken, NULL, NULL, true); + } + + void HealedBy(Unit* pHealer, uint32& uiHealedAmount) override + { + int32 uHealTaken = (int32)uiHealedAmount; + m_creature->CastCustomSpell(m_creature, SPELL_SHARED_RULE_HEAL, &uHealTaken, NULL, NULL, true); + } +}; + +/*###### +## boss_gathios_the_shatterer +######*/ + +struct boss_gathios_the_shattererAI : public boss_illidari_councilAI +{ + boss_gathios_the_shattererAI(Creature* pCreature) : boss_illidari_councilAI(pCreature) { Reset(); } + + uint32 m_uiConsecrationTimer; + uint32 m_uiHammerOfJusticeTimer; + uint32 m_uiSealTimer; + uint32 m_uiAuraTimer; + uint32 m_uiBlessingTimer; + uint32 m_uiJudgmentTimer; + + void Reset() override + { + m_uiConsecrationTimer = 40000; + m_uiHammerOfJusticeTimer = 10000; + m_uiSealTimer = 40000; + m_uiAuraTimer = 90000; + m_uiBlessingTimer = 60000; + m_uiJudgmentTimer = 0; + } + + void KilledUnit(Unit* /*pVictim*/) override + { + DoScriptText(SAY_GATH_SLAY, m_creature); + } + + void JustDied(Unit* pKiller) override + { + DoScriptText(SAY_GATH_DEATH, m_creature); + + boss_illidari_councilAI::JustDied(pKiller); + } + + void UpdateAI(const uint32 uiDiff) override + { + if (!m_creature->SelectHostileTarget() || !m_creature->getVictim()) + return; + + if (m_uiBlessingTimer < uiDiff) + { + if (Unit* pTarget = DoSelectLowestHpFriendly(80.0f)) + { + if (DoCastSpellIfCan(pTarget, urand(0, 1) ? SPELL_BLESS_SPELLWARD : SPELL_BLESS_PROTECTION) == CAST_OK) + m_uiBlessingTimer = 60000; + } + } + else + m_uiBlessingTimer -= uiDiff; + + if (m_uiConsecrationTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature, SPELL_CONSECRATION) == CAST_OK) + m_uiConsecrationTimer = urand(10000, 15000); + } + else + m_uiConsecrationTimer -= uiDiff; + + if (m_uiHammerOfJusticeTimer < uiDiff) + { + if (Unit* pTarget = m_creature->SelectAttackingTarget(ATTACKING_TARGET_RANDOM, 0, SPELL_HAMMER_OF_JUSTICE, SELECT_FLAG_PLAYER | SELECT_FLAG_NOT_IN_MELEE_RANGE)) + { + if (DoCastSpellIfCan(pTarget, SPELL_HAMMER_OF_JUSTICE) == CAST_OK) + m_uiHammerOfJusticeTimer = 20000; + } + } + else + m_uiHammerOfJusticeTimer -= uiDiff; + + if (m_uiSealTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature, urand(0, 1) ? SPELL_SEAL_OF_COMMAND : SPELL_SEAL_OF_BLOOD) == CAST_OK) + { + m_uiSealTimer = 40000; + + if (urand(0, 1)) + m_uiJudgmentTimer = urand(4000, 7000); + } + } + else + m_uiSealTimer -= uiDiff; + + if (m_uiAuraTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature, urand(0, 1) ? SPELL_DEVOTION_AURA : SPELL_CHROMATIC_AURA) == CAST_OK) + m_uiAuraTimer = 90000; + } + else + m_uiAuraTimer -= uiDiff; + + if (m_uiJudgmentTimer) + { + if (m_uiJudgmentTimer <= uiDiff) + { + if (DoCastSpellIfCan(m_creature->getVictim(), SPELL_JUDGMENT) == CAST_OK) + m_uiJudgmentTimer = 0; + } + else + m_uiJudgmentTimer -= uiDiff; + } + + DoMeleeAttackIfReady(); + } +}; + +/*###### +## boss_high_nethermancer_zerevor +######*/ + +struct boss_high_nethermancer_zerevorAI : public boss_illidari_councilAI +{ + boss_high_nethermancer_zerevorAI(Creature* pCreature) : boss_illidari_councilAI(pCreature) { Reset(); } + + uint32 m_uiBlizzardTimer; + uint32 m_uiFlamestrikeTimer; + uint32 m_uiArcaneBoltTimer; + uint32 m_uiDampenMagicTimer; + uint32 m_uiArcaneExplosionTimer; + + void Reset() override + { + m_uiBlizzardTimer = urand(10000, 20000); + m_uiFlamestrikeTimer = urand(10000, 20000); + m_uiArcaneBoltTimer = 3000; + m_uiDampenMagicTimer = 2000; + m_uiArcaneExplosionTimer = 13000; + } + + void AttackStart(Unit* pWho) override + { + if (m_creature->Attack(pWho, true)) + { + m_creature->AddThreat(pWho); + m_creature->SetInCombatWith(pWho); + pWho->SetInCombatWith(m_creature); + DoStartMovement(pWho, 20.0f); + } + } + + void KilledUnit(Unit* /*pVictim*/) override + { + DoScriptText(SAY_ZERE_SLAY, m_creature); + } + + void JustDied(Unit* pKiller) override + { + DoScriptText(SAY_ZERE_DEATH, m_creature); + + boss_illidari_councilAI::JustDied(pKiller); + } + + void UpdateAI(const uint32 uiDiff) override + { + if (!m_creature->SelectHostileTarget() || !m_creature->getVictim()) + return; + + if (m_uiDampenMagicTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature, SPELL_DAMPEN_MAGIC) == CAST_OK) + m_uiDampenMagicTimer = 110000; // Almost 2 minutes + } + else + m_uiDampenMagicTimer -= uiDiff; + + if (m_uiArcaneExplosionTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature, SPELL_ARCANE_EXPLOSION) == CAST_OK) + m_uiArcaneExplosionTimer = urand(5000, 15000); + } + else + m_uiArcaneExplosionTimer -= uiDiff; + + if (m_uiArcaneBoltTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature->getVictim(), SPELL_ARCANE_BOLT) == CAST_OK) + m_uiArcaneBoltTimer = 3000; + } + else + m_uiArcaneBoltTimer -= uiDiff; + + if (m_uiBlizzardTimer < uiDiff) + { + if (Unit* pTarget = m_creature->SelectAttackingTarget(ATTACKING_TARGET_RANDOM, 0)) + { + if (DoCastSpellIfCan(pTarget, SPELL_BLIZZARD) == CAST_OK) + { + m_uiBlizzardTimer = urand(5000, 15000); + m_uiFlamestrikeTimer += 5000; + } + } + } + else + m_uiBlizzardTimer -= uiDiff; + + if (m_uiFlamestrikeTimer < uiDiff) + { + if (Unit* pTarget = m_creature->SelectAttackingTarget(ATTACKING_TARGET_RANDOM, 0)) + { + if (DoCastSpellIfCan(pTarget, SPELL_FLAMESTRIKE) == CAST_OK) + { + m_uiFlamestrikeTimer = urand(5000, 15000); + m_uiBlizzardTimer += 5000; + } + } + } + else + m_uiFlamestrikeTimer -= uiDiff; + } +}; + +/*###### +## boss_lady_malande +######*/ + +struct boss_lady_malandeAI : public boss_illidari_councilAI +{ + boss_lady_malandeAI(Creature* pCreature) : boss_illidari_councilAI(pCreature) { Reset(); } + + uint32 m_uiEmpoweredSmiteTimer; + uint32 m_uiCircleOfHealingTimer; + uint32 m_uiDivineWrathTimer; + uint32 m_uiReflectiveShieldTimer; + + void Reset() override + { + m_uiEmpoweredSmiteTimer = 10000; + m_uiCircleOfHealingTimer = 20000; + m_uiDivineWrathTimer = 5000; + m_uiReflectiveShieldTimer = 0; + } + + void AttackStart(Unit* pWho) override + { + if (m_creature->Attack(pWho, true)) + { + m_creature->AddThreat(pWho); + m_creature->SetInCombatWith(pWho); + pWho->SetInCombatWith(m_creature); + DoStartMovement(pWho, 20.0f); + } + } + + void KilledUnit(Unit* /*pVictim*/) override + { + DoScriptText(SAY_MALA_SLAY, m_creature); + } + + void JustDied(Unit* pKiller) override + { + DoScriptText(SAY_MALA_DEATH, m_creature); + + boss_illidari_councilAI::JustDied(pKiller); + } + + void UpdateAI(const uint32 uiDiff) override + { + if (!m_creature->SelectHostileTarget() || !m_creature->getVictim()) + return; + + if (m_uiEmpoweredSmiteTimer < uiDiff) + { + if (Unit* pTarget = m_creature->SelectAttackingTarget(ATTACKING_TARGET_RANDOM, 0)) + { + if (DoCastSpellIfCan(pTarget, SPELL_EMPOWERED_SMITE) == CAST_OK) + m_uiEmpoweredSmiteTimer = urand(5000, 15000); + } + } + else + m_uiEmpoweredSmiteTimer -= uiDiff; + + if (m_uiCircleOfHealingTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature, SPELL_CIRCLE_OF_HEALING) == CAST_OK) + m_uiCircleOfHealingTimer = 20000; + } + else + m_uiCircleOfHealingTimer -= uiDiff; + + if (m_uiDivineWrathTimer < uiDiff) + { + if (Unit* pTarget = m_creature->SelectAttackingTarget(ATTACKING_TARGET_RANDOM, 0)) + { + if (DoCastSpellIfCan(pTarget, SPELL_DIVINE_WRATH) == CAST_OK) + m_uiDivineWrathTimer = urand(2000, 5000); + } + } + else + m_uiDivineWrathTimer -= uiDiff; + + if (m_uiReflectiveShieldTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature, SPELL_REFLECTIVE_SHIELD) == CAST_OK) + m_uiReflectiveShieldTimer = urand(30000, 40000); + } + else + m_uiReflectiveShieldTimer -= uiDiff; + + DoMeleeAttackIfReady(); + } +}; + +/*###### +## boss_veras_darkshadow +######*/ + +struct boss_veras_darkshadowAI : public boss_illidari_councilAI +{ + boss_veras_darkshadowAI(Creature* pCreature) : boss_illidari_councilAI(pCreature) { Reset(); } + + uint32 m_uiDeadlyPoisonTimer; + uint32 m_uiVanishTimer; + uint32 m_uiVanishEndtimer; + uint32 m_uiEnvenomTimer; + + void Reset() override + { + m_uiDeadlyPoisonTimer = 1000; + m_uiVanishTimer = urand(30000, 40000); + m_uiEnvenomTimer = 5000; + m_uiVanishEndtimer = 0; + } + + void KilledUnit(Unit* /*pVictim*/) override + { + DoScriptText(SAY_VERA_SLAY, m_creature); + } + + void JustDied(Unit* pKiller) override + { + DoScriptText(SAY_VERA_DEATH, m_creature); + + boss_illidari_councilAI::JustDied(pKiller); + } + + void EnterEvadeMode() override + { + if (m_uiVanishEndtimer) + return; + + ScriptedAI::EnterEvadeMode(); + } + + void UpdateAI(const uint32 uiDiff) override + { + if (!m_creature->SelectHostileTarget() || !m_creature->getVictim()) + return; + + if (m_uiVanishEndtimer) + { + if (m_uiVanishEndtimer <= uiDiff) + { + if (DoCastSpellIfCan(m_creature, SPELL_VANISH_TELEPORT) == CAST_OK) + { + DoResetThreat(); + m_uiVanishEndtimer = 0; + } + } + else + m_uiVanishEndtimer -= uiDiff; + + // no more abilities during vanish + return; + } + + if (m_uiDeadlyPoisonTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature->getVictim(), SPELL_DEADLY_POISON) == CAST_OK) + m_uiDeadlyPoisonTimer = urand(4000, 7000); + } + else + m_uiDeadlyPoisonTimer -= uiDiff; + + if (m_uiEnvenomTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature->getVictim(), SPELL_ENVENOM) == CAST_OK) + m_uiEnvenomTimer = 5000; + } + else + m_uiEnvenomTimer -= uiDiff; + + if (m_uiVanishTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature, SPELL_VANISH) == CAST_OK) + { + m_uiVanishTimer = urand(30000, 40000); + m_uiVanishEndtimer = 1000; + } + } + else + m_uiVanishTimer -= uiDiff; + + DoMeleeAttackIfReady(); + } +}; + +CreatureAI* GetAI_mob_blood_elf_council_voice_trigger(Creature* pCreature) +{ + return new mob_blood_elf_council_voice_triggerAI(pCreature); +} + +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* pNewScript; + + pNewScript = new Script; + pNewScript->Name = "mob_illidari_council"; + pNewScript->GetAI = &GetAI_mob_illidari_council; + pNewScript->RegisterSelf(); + + pNewScript = new Script; + pNewScript->Name = "mob_blood_elf_council_voice_trigger"; + pNewScript->GetAI = &GetAI_mob_blood_elf_council_voice_trigger; + pNewScript->RegisterSelf(); + + pNewScript = new Script; + pNewScript->Name = "boss_gathios_the_shatterer"; + pNewScript->GetAI = &GetAI_boss_gathios_the_shatterer; + pNewScript->RegisterSelf(); + + pNewScript = new Script; + pNewScript->Name = "boss_lady_malande"; + pNewScript->GetAI = &GetAI_boss_lady_malande; + pNewScript->RegisterSelf(); + + pNewScript = new Script; + pNewScript->Name = "boss_veras_darkshadow"; + pNewScript->GetAI = &GetAI_boss_veras_darkshadow; + pNewScript->RegisterSelf(); + + pNewScript = new Script; + pNewScript->Name = "boss_high_nethermancer_zerevor"; + pNewScript->GetAI = &GetAI_boss_high_nethermancer_zerevor; + pNewScript->RegisterSelf(); +} diff --git a/src/modules/SD2/scripts/outland/black_temple/instance_black_temple.cpp b/src/modules/SD2/scripts/outland/black_temple/instance_black_temple.cpp new file mode 100644 index 000000000..f7f835592 --- /dev/null +++ b/src/modules/SD2/scripts/outland/black_temple/instance_black_temple.cpp @@ -0,0 +1,305 @@ +/* Copyright (C) 2006 - 2013 ScriptDev2 + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along 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 "black_temple.h" + +/* Black Temple encounters: +0 - High Warlord Naj'entus event +1 - Supremus Event +2 - Shade of Akama Event +3 - Teron Gorefiend Event +4 - Gurtogg Bloodboil Event +5 - Reliquary Of Souls Event +6 - Mother Shahraz Event +7 - Illidari Council Event +8 - Illidan Stormrage Event +*/ + +instance_black_temple::instance_black_temple(Map* pMap) : ScriptedInstance(pMap) +{ + Initialize(); +}; + +void instance_black_temple::Initialize() +{ + memset(&m_auiEncounter, 0, sizeof(m_auiEncounter)); +} + +void instance_black_temple::OnPlayerEnter(Player* /*pPlayer*/) +{ + DoSpawnAkamaIfCan(); +} + +bool instance_black_temple::IsEncounterInProgress() const +{ + for (uint8 i = 0; i < MAX_ENCOUNTER; ++i) + { + if (m_auiEncounter[i] == IN_PROGRESS) + return true; + } + + return false; +} + +void instance_black_temple::OnCreatureCreate(Creature* pCreature) +{ + switch (pCreature->GetEntry()) + { + case NPC_SPIRIT_OF_OLUM: + case NPC_SPIRIT_OF_UDALO: + // Use only the summoned versions + if (!pCreature->IsTemporarySummon()) + break; + case NPC_AKAMA: + case NPC_ILLIDAN_STORMRAGE: + case NPC_MAIEV_SHADOWSONG: + case NPC_AKAMA_SHADE: + case NPC_SHADE_OF_AKAMA: + case NPC_RELIQUARY_OF_SOULS: + case NPC_GATHIOS: + case NPC_ZEREVOR: + case NPC_LADY_MALANDE: + case NPC_VERAS: + case NPC_ILLIDARI_COUNCIL: + case NPC_COUNCIL_VOICE: + case NPC_ILLIDAN_DOOR_TRIGGER: + m_mNpcEntryGuidStore[pCreature->GetEntry()] = pCreature->GetObjectGuid(); + break; + case NPC_ASH_CHANNELER: + m_lChannelersGuidList.push_back(pCreature->GetObjectGuid()); + break; + case NPC_CREATURE_GENERATOR: + m_vCreatureGeneratorGuidVector.push_back(pCreature->GetObjectGuid()); + break; + case NPC_GLAIVE_TARGET: + m_vGlaiveTargetGuidVector.push_back(pCreature->GetObjectGuid()); + break; + } +} + +void instance_black_temple::OnObjectCreate(GameObject* pGo) +{ + switch (pGo->GetEntry()) + { + case GO_NAJENTUS_GATE: // Gate past Naj'entus (at the entrance to Supermoose's courtyards) + if (m_auiEncounter[TYPE_NAJENTUS] == DONE) + pGo->SetGoState(GO_STATE_ACTIVE); + break; + case GO_SUPREMUS_DOORS: // Main Temple Doors - right past Supermoose (Supremus) + if (m_auiEncounter[TYPE_SUPREMUS] == DONE) + pGo->SetGoState(GO_STATE_ACTIVE); + break; + case GO_SHADE_OF_AKAMA: // Door close during encounter + case GO_GOREFIEND_DOOR: // Door close during encounter + break; + case GO_GURTOGG_DOOR: // Door opens after encounter + if (m_auiEncounter[TYPE_BLOODBOIL] == DONE) + pGo->SetGoState(GO_STATE_ACTIVE); + break; + case GO_PRE_SHAHRAZ_DOOR: // Door leading to Mother Shahraz + if (m_auiEncounter[TYPE_SHADE] == DONE && m_auiEncounter[TYPE_GOREFIEND] == DONE && m_auiEncounter[TYPE_BLOODBOIL] == DONE && m_auiEncounter[TYPE_RELIQUIARY] == DONE) + pGo->SetGoState(GO_STATE_ACTIVE); + break; + case GO_POST_SHAHRAZ_DOOR: // Door after shahraz + if (m_auiEncounter[TYPE_SHAHRAZ] == DONE) + pGo->SetGoState(GO_STATE_ACTIVE); + break; + case GO_PRE_COUNCIL_DOOR: // Door leading to the Council (grand promenade) + case GO_COUNCIL_DOOR: // Door leading to the Council (inside) + case GO_ILLIDAN_GATE: // Gate leading to Temple Summit + case GO_ILLIDAN_DOOR_R: // Right door at Temple Summit + case GO_ILLIDAN_DOOR_L: // Left door at Temple Summit + break; + + default: + return; + } + m_mGoEntryGuidStore[pGo->GetEntry()] = pGo->GetObjectGuid(); +} + +void instance_black_temple::SetData(uint32 uiType, uint32 uiData) +{ + switch (uiType) + { + case TYPE_NAJENTUS: + m_auiEncounter[uiType] = uiData; + if (uiData == DONE) + DoUseDoorOrButton(GO_NAJENTUS_GATE); + break; + case TYPE_SUPREMUS: + m_auiEncounter[uiType] = uiData; + if (uiData == DONE) + DoUseDoorOrButton(GO_SUPREMUS_DOORS); + break; + case TYPE_SHADE: + m_auiEncounter[uiType] = uiData; + // combat door + DoUseDoorOrButton(GO_SHADE_OF_AKAMA); + if (uiData == FAIL) + { + // Reset channelers on fail + for (GuidList::const_iterator itr = m_lChannelersGuidList.begin(); itr != m_lChannelersGuidList.end(); ++itr) + { + if (Creature* pChanneler = instance->GetCreature(*itr)) + { + if (!pChanneler->IsAlive()) + pChanneler->Respawn(); + else + pChanneler->AI()->EnterEvadeMode(); + } + } + } + if (uiData == DONE) + DoOpenPreMotherDoor(); + break; + case TYPE_GOREFIEND: + m_auiEncounter[uiType] = uiData; + DoUseDoorOrButton(GO_GOREFIEND_DOOR); + if (uiData == DONE) + DoOpenPreMotherDoor(); + break; + case TYPE_BLOODBOIL: + m_auiEncounter[uiType] = uiData; + if (uiData == DONE) + { + DoOpenPreMotherDoor(); + DoUseDoorOrButton(GO_GURTOGG_DOOR); + } + break; + case TYPE_RELIQUIARY: + m_auiEncounter[uiType] = uiData; + if (uiData == DONE) + DoOpenPreMotherDoor(); + break; + case TYPE_SHAHRAZ: + if (uiData == DONE) + DoUseDoorOrButton(GO_POST_SHAHRAZ_DOOR); + m_auiEncounter[uiType] = uiData; + break; + case TYPE_COUNCIL: + // Don't set the same data twice + if (m_auiEncounter[uiType] == uiData) + return; + DoUseDoorOrButton(GO_COUNCIL_DOOR); + m_auiEncounter[uiType] = uiData; + if (uiData == DONE) + DoSpawnAkamaIfCan(); + break; + case TYPE_ILLIDAN: + DoUseDoorOrButton(GO_ILLIDAN_DOOR_R); + DoUseDoorOrButton(GO_ILLIDAN_DOOR_L); + if (uiData == FAIL) + { + // Cleanup encounter + DoSpawnAkamaIfCan(); + DoUseDoorOrButton(GO_ILLIDAN_GATE); + } + m_auiEncounter[uiType] = uiData; + break; + default: + script_error_log("Instance Black Temple: ERROR SetData = %u for type %u does not exist/not implemented.", uiType, uiData); + return; + } + + 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_strInstData = saveStream.str(); + + SaveToDB(); + OUT_SAVE_INST_DATA_COMPLETE; + } +} + +uint32 instance_black_temple::GetData(uint32 uiType) const +{ + if (uiType < MAX_ENCOUNTER) + return m_auiEncounter[uiType]; + + return 0; +} + +void instance_black_temple::DoOpenPreMotherDoor() +{ + if (GetData(TYPE_SHADE) == DONE && GetData(TYPE_GOREFIEND) == DONE && GetData(TYPE_BLOODBOIL) == DONE && GetData(TYPE_RELIQUIARY) == DONE) + DoUseDoorOrButton(GO_PRE_SHAHRAZ_DOOR); +} + +void instance_black_temple::DoSpawnAkamaIfCan() +{ + if (GetData(TYPE_ILLIDAN) == DONE || GetData(TYPE_COUNCIL) != DONE) + return; + + // If already spawned return + if (GetSingleCreatureFromStorage(NPC_AKAMA, true)) + return; + + // Summon Akama after the council has been defeated + if (Player* pPlayer = GetPlayerInMap()) + pPlayer->SummonCreature(NPC_AKAMA, 617.754f, 307.768f, 271.735f, 6.197f, TEMPSUMMON_DEAD_DESPAWN, 0); +} + +void instance_black_temple::Load(const char* chrIn) +{ + if (!chrIn) + { + 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]; + + 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_black_temple(Map* pMap) +{ + return new instance_black_temple(pMap); +} + +void AddSC_instance_black_temple() +{ + Script* pNewScript; + + pNewScript = new Script; + pNewScript->Name = "instance_black_temple"; + pNewScript->GetInstanceData = &GetInstanceData_instance_black_temple; + pNewScript->RegisterSelf(); +} diff --git a/src/modules/SD2/scripts/outland/blades_edge_mountains.cpp b/src/modules/SD2/scripts/outland/blades_edge_mountains.cpp new file mode 100644 index 000000000..b24e50208 --- /dev/null +++ b/src/modules/SD2/scripts/outland/blades_edge_mountains.cpp @@ -0,0 +1,831 @@ +/** + * ScriptDev2 is an extension for mangos providing enhanced features for + * area triggers, creatures, game objects, instances, items, and spells beyond + * the default database scripting in mangos. + * + * Copyright (C) 2006-2013 ScriptDev2 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * World of Warcraft, and all World of Warcraft or Warcraft art, images, + * and lore are copyrighted by Blizzard Entertainment, Inc. + */ + +/* ScriptData +SDName: Blades_Edge_Mountains +SD%Complete: 90 +SDComment: Quest support: 10503, 10504, 10512, 10545, 10556, 10609, 11058, 11080. (npc_daranelle needs bit more work before consider complete) +SDCategory: Blade's Edge Mountains +EndScriptData */ + +/* ContentData +mobs_nether_drake +npc_daranelle +npc_bloodmaul_stout_trigger +npc_simon_game_bunny +EndContentData */ + +#include "precompiled.h" +#include "TemporarySummon.h" + +/*###### +## mobs_nether_drake +######*/ + +enum +{ + SAY_NIHIL_1 = -1000169, + SAY_NIHIL_2 = -1000170, + SAY_NIHIL_3 = -1000171, + SAY_NIHIL_4 = -1000172, + SAY_NIHIL_INTERRUPT = -1000173, + + MAX_ENTRIES = 4, + + NPC_PROTO = 21821, + NPC_ADOLESCENT = 21817, + NPC_MATURE = 21820, + NPC_NIHIL = 21823, + + SPELL_T_PHASE_MODULATOR = 37573, + + SPELL_ARCANE_BLAST = 38881, + SPELL_MANA_BURN = 38884, + SPELL_INTANGIBLE_PRESENCE = 36513, +}; + +static const uint32 aNetherDrakeEntries[MAX_ENTRIES] = {NPC_PROTO, NPC_ADOLESCENT, NPC_MATURE, NPC_NIHIL}; + +struct mobs_nether_drakeAI : public ScriptedAI +{ + mobs_nether_drakeAI(Creature* pCreature) : ScriptedAI(pCreature) { Reset(); } + + bool m_bIsNihil; + uint32 m_uiNihilSpeechTimer; + uint32 m_uiNihilSpeechPhase; + + uint32 m_uiArcaneBlastTimer; + uint32 m_uiManaBurnTimer; + uint32 m_uiIntangiblePresenceTimer; + + void Reset() override + { + m_bIsNihil = false; + m_uiNihilSpeechTimer = 3000; + m_uiNihilSpeechPhase = 0; + + m_uiArcaneBlastTimer = 7500; + m_uiManaBurnTimer = 10000; + m_uiIntangiblePresenceTimer = 15000; + } + + void MoveInLineOfSight(Unit* pWho) override + { + if (m_creature->HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE)) + { return; } + + ScriptedAI::MoveInLineOfSight(pWho); + } + + // in case creature was not summoned (not expected) + void MovementInform(uint32 uiMoveType, uint32 uiPointId) override + { + if (uiMoveType != POINT_MOTION_TYPE) + { return; } + + if (uiPointId) + { m_creature->ForcedDespawn(); } + } + + void SpellHit(Unit* pCaster, const SpellEntry* pSpell) override + { + if (pSpell->Id == SPELL_T_PHASE_MODULATOR && pCaster->GetTypeId() == TYPEID_PLAYER) + { + // we are nihil, so say before transform + if (m_creature->GetEntry() == NPC_NIHIL) + { + DoScriptText(SAY_NIHIL_INTERRUPT, m_creature); + m_creature->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE); + m_bIsNihil = false; + } + + // choose a new entry + uint8 uiIndex = urand(0, MAX_ENTRIES - 1); + + // If we choose the same entry, try again + while (aNetherDrakeEntries[uiIndex] == m_creature->GetEntry()) + { uiIndex = urand(0, MAX_ENTRIES - 1); } + + if (m_creature->UpdateEntry(aNetherDrakeEntries[uiIndex])) + { + // Nihil does only dialogue + if (aNetherDrakeEntries[uiIndex] == NPC_NIHIL) + { + EnterEvadeMode(); + m_creature->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE); + m_bIsNihil = true; + } + else + { AttackStart(pCaster); } + } + } + } + + void UpdateAI(const uint32 uiDiff) override + { + if (m_bIsNihil) + { + if (m_uiNihilSpeechTimer < uiDiff) + { + switch (m_uiNihilSpeechPhase) + { + case 0: + DoScriptText(SAY_NIHIL_1, m_creature); + break; + case 1: + DoScriptText(SAY_NIHIL_2, m_creature); + break; + case 2: + DoScriptText(SAY_NIHIL_3, m_creature); + break; + case 3: + DoScriptText(SAY_NIHIL_4, m_creature); + break; + case 4: + m_creature->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE); + // take off to location above + m_creature->SetLevitate(true); + m_creature->SetByteFlag(UNIT_FIELD_BYTES_1, 3, UNIT_BYTE1_FLAG_ALWAYS_STAND | UNIT_BYTE1_FLAG_UNK_2); + m_creature->GetMotionMaster()->MovePoint(1, m_creature->GetPositionX() + 50.0f, m_creature->GetPositionY(), m_creature->GetPositionZ() + 50.0f); + break; + } + ++m_uiNihilSpeechPhase; + m_uiNihilSpeechTimer = 5000; + } + else + { m_uiNihilSpeechTimer -= uiDiff; } + + // anything below here is not interesting for Nihil, so skip it + return; + } + + if (!m_creature->SelectHostileTarget() || !m_creature->getVictim()) + { return; } + + if (m_uiIntangiblePresenceTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature, SPELL_INTANGIBLE_PRESENCE) == CAST_OK) + { m_uiIntangiblePresenceTimer = urand(15000, 30000); } + } + else + { m_uiIntangiblePresenceTimer -= uiDiff; } + + if (m_uiManaBurnTimer < uiDiff) + { + if (Unit* pTarget = m_creature->SelectAttackingTarget(ATTACKING_TARGET_RANDOM, 0, SPELL_MANA_BURN, SELECT_FLAG_POWER_MANA)) + { + if (DoCastSpellIfCan(pTarget, SPELL_MANA_BURN) == CAST_OK) + { m_uiManaBurnTimer = urand(8000, 16000); } + } + } + else + { m_uiManaBurnTimer -= uiDiff; } + + if (m_uiArcaneBlastTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature->getVictim(), SPELL_ARCANE_BLAST) == CAST_OK) + { m_uiArcaneBlastTimer = urand(2500, 7500); } + } + else + { m_uiArcaneBlastTimer -= uiDiff; } + + DoMeleeAttackIfReady(); + } +}; + +CreatureAI* GetAI_mobs_nether_drake(Creature* pCreature) +{ + return new mobs_nether_drakeAI(pCreature); +} + +/*###### +## npc_daranelle +######*/ + +enum +{ + SAY_SPELL_INFLUENCE = -1000174, + NPC_KALIRI_AURA_DISPEL = 21511, + SPELL_LASHHAN_CHANNEL = 36904 +}; + +struct npc_daranelleAI : public ScriptedAI +{ + npc_daranelleAI(Creature* pCreature) : ScriptedAI(pCreature) {Reset();} + + void Reset() override { } + + void MoveInLineOfSight(Unit* pWho) override + { + if (pWho->GetTypeId() == TYPEID_PLAYER) + { + if (pWho->HasAura(SPELL_LASHHAN_CHANNEL, EFFECT_INDEX_0) && m_creature->IsWithinDistInMap(pWho, 10.0f)) + { + DoScriptText(SAY_SPELL_INFLUENCE, m_creature, pWho); + + // TODO: Move the below to updateAI and run if this statement == true + ((Player*)pWho)->KilledMonsterCredit(NPC_KALIRI_AURA_DISPEL, m_creature->GetObjectGuid()); + pWho->RemoveAurasDueToSpell(SPELL_LASHHAN_CHANNEL); + } + } + + ScriptedAI::MoveInLineOfSight(pWho); + } +}; + +CreatureAI* GetAI_npc_daranelle(Creature* pCreature) +{ + return new npc_daranelleAI(pCreature); +} + +/*###### +## npc_bloodmaul_stout_trigger +######*/ + +enum +{ + SAY_BREW_1 = -1000156, + SAY_BREW_2 = -1000207, + SAY_BREW_3 = -1000208, + + SPELL_INTOXICATION = 35240, + SPELL_INTOXICATION_VISUAL = 35777, +}; + +static const uint32 aOgreEntries[] = {19995, 19998, 20334, 20723, 20726, 20730, 20731, 20732, 21296}; + +struct npc_bloodmaul_stout_triggerAI : public ScriptedAI +{ + npc_bloodmaul_stout_triggerAI(Creature* pCreature) : ScriptedAI(pCreature) { Reset(); } + + uint32 m_uiStartTimer; + bool m_bHasValidOgre; + + ObjectGuid m_selectedOgreGuid; + + void Reset() override + { + m_uiStartTimer = 1000; + m_bHasValidOgre = false; + } + + void MoveInLineOfSight(Unit* pWho) override + { + if (m_bHasValidOgre && pWho->GetObjectGuid() == m_selectedOgreGuid && m_creature->IsWithinDistInMap(pWho, 3.5f)) + { + // This part it's not 100% accurate - most of it is guesswork + // Some animations or spells may be missing + pWho->CastSpell(pWho, SPELL_INTOXICATION_VISUAL, true); + pWho->CastSpell(pWho, SPELL_INTOXICATION, true); + + // Handle evade after some time with EAI + m_creature->AI()->SendAIEvent(AI_EVENT_CUSTOM_EVENTAI_A, m_creature, (Creature*)pWho); + + // Give kill credit to the summoner player + if (m_creature->IsTemporarySummon()) + { + TemporarySummon* pTemporary = (TemporarySummon*)m_creature; + + if (Player* pSummoner = m_creature->GetMap()->GetPlayer(pTemporary->GetSummonerGuid())) + { pSummoner->KilledMonsterCredit(m_creature->GetEntry(), m_creature->GetObjectGuid()); } + } + + m_bHasValidOgre = false; + } + } + + void UpdateAI(const uint32 uiDiff) override + { + if (m_uiStartTimer) + { + if (m_uiStartTimer <= uiDiff) + { + // get all the ogres in range + std::list lOgreList; + for (uint8 i = 0; i < countof(aOgreEntries); ++i) + { GetCreatureListWithEntryInGrid(lOgreList, m_creature, aOgreEntries[i], 30.0f); } + + if (lOgreList.empty()) + { + m_uiStartTimer = 5000; + return; + } + + // sort by distance and get only the closest + lOgreList.sort(ObjectDistanceOrder(m_creature)); + + std::list::const_iterator ogreItr = lOgreList.begin(); + Creature* pOgre = NULL; + + do + { + if ((*ogreItr)->IsAlive() && !(*ogreItr)->HasAura(SPELL_INTOXICATION)) + { pOgre = *ogreItr; } + + ++ogreItr; + } + while (!pOgre && ogreItr != lOgreList.end()); + + if (!pOgre) + { + m_uiStartTimer = 5000; + return; + } + + // Move ogre to the point + float fX, fY, fZ; + pOgre->GetMotionMaster()->MoveIdle(); + m_creature->GetContactPoint(pOgre, fX, fY, fZ); + pOgre->GetMotionMaster()->MovePoint(0, fX, fY, fZ); + + switch (urand(0, 2)) + { + case 0: DoScriptText(SAY_BREW_1, pOgre); break; + case 1: DoScriptText(SAY_BREW_2, pOgre); break; + case 2: DoScriptText(SAY_BREW_3, pOgre); break; + } + + m_selectedOgreGuid = pOgre->GetObjectGuid(); + m_uiStartTimer = 0; + m_bHasValidOgre = true; + } + else + { m_uiStartTimer -= uiDiff; } + } + } +}; + +CreatureAI* GetAI_npc_bloodmaul_stout_trigger(Creature* pCreature) +{ + return new npc_bloodmaul_stout_triggerAI(pCreature); +} + +/*###### +## npc_simon_game_bunny +######*/ + +enum +{ + // sounds + SOUND_ID_BLUE = 11588, + SOUND_ID_GREEN = 11589, + SOUND_ID_RED = 11590, + SOUND_ID_YELLOW = 11591, + SOUND_ID_DISABLE_NODE = 11758, + + // generic spells + SPELL_SIMON_GAME_START = 39993, // aura used to prepare the AI game + SPELL_PRE_EVENT_TIMER = 40041, // aura used to handle the color sequence + + // stage prepare spells (summons the colored auras) + SPELL_PRE_GAME_BLUE_AURA = 40176, + SPELL_PRE_GAME_GREEN_AURA = 40177, + SPELL_PRE_GAME_RED_AURA = 40178, + SPELL_PRE_GAME_YELLOW_AURA = 40179, + + // stage prepare spells large + SPELL_PRE_GAME_YELLOW_LARGE = 41110, + SPELL_PRE_GAME_RED_LARGE = 41111, + SPELL_PRE_GAME_GREEN_LARGE = 41112, + SPELL_PRE_GAME_BLUE_LARGE = 41113, + + // visual spells which define which buttons are pressed + SPELL_BUTTON_PUSH_BLUE = 40244, + SPELL_BUTTON_PUSH_GREEN = 40245, + SPELL_BUTTON_PUSH_RED = 40246, + SPELL_BUTTON_PUSH_YELLOW = 40247, + + // allow the clusters to be used and despawns the visual auras + SPELL_GAME_START_RED = 40169, + SPELL_GAME_START_BLUE = 40170, + SPELL_GAME_START_GREEN = 40171, + SPELL_GAME_START_YELLOW = 40172, + + // locks the clusters after a stage is completed + SPELL_GAME_END_BLUE = 40283, + SPELL_GAME_END_GREEN = 40284, + SPELL_GAME_END_RED = 40285, + SPELL_GAME_END_YELLOW = 40286, + + // other spells + // SPELL_SWITCHED_ON_OFF = 40512, // decharger lock (not used) + // SPELL_SWITCHED_ON_OFF_2 = 40499, // decharger unlock (not used) + SPELL_SWITCHED_ON = 40494, // apexis lock spell + SPELL_SWITCHED_OFF = 40495, // apexis unlock spell + + // misc visual spells + SPELL_VISUAL_LEVEL_START = 40436, // on Player game begin + SPELL_VISUAL_GAME_FAILED = 40437, // on Player game fail + SPELL_VISUAL_GAME_START = 40387, // on AI game begin + SPELL_VISUAL_GAME_TICK = 40391, // game tick (sound) + SPELL_VISUAL_GAME_TICK_LARGE = 42019, // game tick large (sound) + + // spells used by the player on GO press + SPELL_INTROSPECTION_GREEN = 40055, + SPELL_INTROSPECTION_BLUE = 40165, + SPELL_INTROSPECTION_RED = 40166, + SPELL_INTROSPECTION_YELLOW = 40167, + + // button press results + SPELL_SIMON_BUTTON_PRESSED = 39999, + SPELL_GOOD_PRESS = 40063, + SPELL_BAD_PRESS = 41241, // single player punishment + SPELL_SIMON_GROUP_REWARD = 41952, // group punishment + + // quest rewards + SPELL_APEXIS_VIBRATIONS = 40310, // quest complete spell + SPELL_APEXIS_EMANATIONS = 40311, // quest complete spell + SPELL_APEXIS_ENLIGHTENMENT = 40312, // quest complete spell + + // other + NPC_SIMON_GAME_BUNNY = 22923, + + GO_APEXIS_RELIC = 185890, + GO_APEXIS_MONUMENT = 185944, + + QUEST_AN_APEXIS_RELIC = 11058, + QUEST_RELICS_EMANATION = 11080, + + // colors + COLOR_IDX_BLUE = 0, + COLOR_IDX_GREEN = 1, + COLOR_IDX_RED = 2, + COLOR_IDX_YELLOW = 3, + + // phases + PHASE_LEVEL_PREPARE = 1, + PHASE_AI_GAME = 2, + PHASE_PLAYER_PREPARE = 3, + PHASE_PLAYER_GAME = 4, + PHASE_LEVEL_FINISHED = 5, + + MAX_SIMON_LEVELS = 8, // counts the max levels of the game + MAX_SIMON_FAIL_TIMER = 5, // counts the delay in which the player is allowed to click +}; + +struct SimonGame +{ + uint8 m_uiColor; + uint32 m_uiVisual, m_uiIntrospection, m_uiSoundId; +}; + +static const SimonGame aApexisGameData[4] = +{ + {COLOR_IDX_BLUE, SPELL_BUTTON_PUSH_BLUE, SPELL_INTROSPECTION_BLUE, SOUND_ID_BLUE}, + {COLOR_IDX_GREEN, SPELL_BUTTON_PUSH_GREEN, SPELL_INTROSPECTION_GREEN, SOUND_ID_GREEN}, + {COLOR_IDX_RED, SPELL_BUTTON_PUSH_RED, SPELL_INTROSPECTION_RED, SOUND_ID_RED}, + {COLOR_IDX_YELLOW, SPELL_BUTTON_PUSH_YELLOW, SPELL_INTROSPECTION_YELLOW, SOUND_ID_YELLOW} +}; + +struct npc_simon_game_bunnyAI : public ScriptedAI +{ + npc_simon_game_bunnyAI(Creature* pCreature) : ScriptedAI(pCreature) { Reset(); } + + uint8 m_uiGamePhase; + + uint32 m_uiLevelCount; + uint32 m_uiLevelStage; + uint32 m_uiPlayerStage; + + std::vector m_vColors; + bool m_bIsLargeEvent; + bool m_bIsEventStarted; + + ObjectGuid m_masterPlayerGuid; + + void Reset() override + { + m_uiGamePhase = PHASE_LEVEL_PREPARE; + m_bIsEventStarted = false; + + m_uiLevelCount = 0; + m_uiLevelStage = 0; + m_uiPlayerStage = 0; + } + + void GetAIInformation(ChatHandler& reader) override + { + reader.PSendSysMessage("Simon Game Bunny, current game phase = %u, current level = %u", m_uiGamePhase, m_uiLevelCount); + } + + // Prepare levels + void DoPrepareLevel() + { + // this visual is cast only after the first level + if (m_uiLevelCount) + { DoCastSpellIfCan(m_creature, SPELL_VISUAL_GAME_START, CAST_TRIGGERED); } + // this part is done only on the first tick + else + { + // lock apexis + DoCastSpellIfCan(m_creature, SPELL_SWITCHED_ON, CAST_TRIGGERED); + DoCastSpellIfCan(m_creature, SPELL_PRE_EVENT_TIMER, CAST_TRIGGERED | CAST_AURA_NOT_PRESENT); + + // Get original summoner + if (m_creature->IsTemporarySummon()) + { m_masterPlayerGuid = ((TemporarySummon*)m_creature)->GetSummonerGuid(); } + + // Get closest apexis + if (GameObject* pGo = GetClosestGameObjectWithEntry(m_creature, GO_APEXIS_RELIC, 5.0f)) + { m_bIsLargeEvent = false; } + else if (GameObject* pGo = GetClosestGameObjectWithEntry(m_creature, GO_APEXIS_MONUMENT, 17.0f)) + { m_bIsLargeEvent = true; } + } + + // prepare the buttons and summon the visual auras + DoCastSpellIfCan(m_creature, m_bIsLargeEvent ? SPELL_PRE_GAME_BLUE_LARGE : SPELL_PRE_GAME_BLUE_AURA, CAST_TRIGGERED); + DoCastSpellIfCan(m_creature, m_bIsLargeEvent ? SPELL_PRE_GAME_GREEN_LARGE : SPELL_PRE_GAME_GREEN_AURA, CAST_TRIGGERED); + DoCastSpellIfCan(m_creature, m_bIsLargeEvent ? SPELL_PRE_GAME_RED_LARGE : SPELL_PRE_GAME_RED_AURA, CAST_TRIGGERED); + DoCastSpellIfCan(m_creature, m_bIsLargeEvent ? SPELL_PRE_GAME_YELLOW_LARGE : SPELL_PRE_GAME_YELLOW_AURA, CAST_TRIGGERED); + + m_vColors.clear(); + ++m_uiLevelCount; + } + + // Setup the color sequence + void DoSetupLevel() + { + uint8 uiIndex = urand(COLOR_IDX_BLUE, COLOR_IDX_YELLOW); + m_vColors.push_back(uiIndex); + + DoCastSpellIfCan(m_creature, aApexisGameData[uiIndex].m_uiVisual, CAST_TRIGGERED); + DoPlaySoundToSet(m_creature, aApexisGameData[uiIndex].m_uiSoundId); + } + + // Setup the player level - called at the beginning at each player level + void DoSetupPlayerLevel() + { + // allow the buttons to be used and despawn the visual auras + DoCastSpellIfCan(m_creature, SPELL_GAME_START_RED, CAST_TRIGGERED); + DoCastSpellIfCan(m_creature, SPELL_GAME_START_BLUE, CAST_TRIGGERED); + DoCastSpellIfCan(m_creature, SPELL_GAME_START_GREEN, CAST_TRIGGERED); + DoCastSpellIfCan(m_creature, SPELL_GAME_START_YELLOW, CAST_TRIGGERED); + } + + // Complete level - called when one level is completed succesfully + void DoCompleteLevel() + { + // lock the buttons + DoCastSpellIfCan(m_creature, SPELL_GAME_END_RED, CAST_TRIGGERED); + DoCastSpellIfCan(m_creature, SPELL_GAME_END_BLUE, CAST_TRIGGERED); + DoCastSpellIfCan(m_creature, SPELL_GAME_END_GREEN, CAST_TRIGGERED); + DoCastSpellIfCan(m_creature, SPELL_GAME_END_YELLOW, CAST_TRIGGERED); + + // Complete game if all the levels + if (m_uiLevelCount == MAX_SIMON_LEVELS) + { DoCompleteGame(); } + } + + // Complete event - called when the game has been completed succesfully + void DoCompleteGame() + { + // ToDo: not sure if the quest reward spells are implemented right. They all give the same buff but with a different duration + if (Player* pPlayer = m_creature->GetMap()->GetPlayer(m_masterPlayerGuid)) + { + if (Group* pGroup = pPlayer->GetGroup()) + { + for (GroupReference* pRef = pGroup->GetFirstMember(); pRef != NULL; pRef = pRef->next()) + { + if (Player* pMember = pRef->getSource()) + { + // distance check - they need to be close to the Apexis + if (!pMember->IsWithinDistInMap(m_creature, 20.0f)) + { continue; } + + // on group event cast Enlightment on daily quest and Emanations on normal quest + if (pMember->GetQuestStatus(QUEST_AN_APEXIS_RELIC) == QUEST_STATUS_INCOMPLETE) + { DoCastSpellIfCan(pMember, SPELL_APEXIS_EMANATIONS, CAST_TRIGGERED); } + else if (pMember->GetQuestStatus(QUEST_RELICS_EMANATION) == QUEST_STATUS_INCOMPLETE) + { DoCastSpellIfCan(pMember, SPELL_APEXIS_ENLIGHTENMENT, CAST_TRIGGERED); } + } + } + } + else + { + // solo event - cast Emanations on daily quest and vibrations on normal quest + if (pPlayer->GetQuestStatus(QUEST_AN_APEXIS_RELIC) == QUEST_STATUS_INCOMPLETE) + { DoCastSpellIfCan(pPlayer, SPELL_APEXIS_VIBRATIONS, CAST_TRIGGERED); } + else if (pPlayer->GetQuestStatus(QUEST_RELICS_EMANATION) == QUEST_STATUS_INCOMPLETE) + { DoCastSpellIfCan(pPlayer, SPELL_APEXIS_EMANATIONS, CAST_TRIGGERED); } + } + } + + // cleanup event after quest is finished + DoCastSpellIfCan(m_creature, SPELL_SWITCHED_OFF, CAST_TRIGGERED); + DoPlaySoundToSet(m_creature, SOUND_ID_DISABLE_NODE); + m_creature->ForcedDespawn(); + } + + // Cleanup event - called when event fails + void DoCleanupGame() + { + // lock the buttons + DoCastSpellIfCan(m_creature, SPELL_GAME_END_RED, CAST_TRIGGERED); + DoCastSpellIfCan(m_creature, SPELL_GAME_END_BLUE, CAST_TRIGGERED); + DoCastSpellIfCan(m_creature, SPELL_GAME_END_GREEN, CAST_TRIGGERED); + DoCastSpellIfCan(m_creature, SPELL_GAME_END_YELLOW, CAST_TRIGGERED); + + // unlock apexis and despawn + DoCastSpellIfCan(m_creature, SPELL_SWITCHED_OFF, CAST_TRIGGERED); + DoPlaySoundToSet(m_creature, SOUND_ID_DISABLE_NODE); + m_creature->ForcedDespawn(); + } + + void ReceiveAIEvent(AIEventType eventType, Creature* /*pSender*/, Unit* pInvoker, uint32 uiMiscValue) override + { + switch (m_uiGamePhase) + { + case PHASE_LEVEL_PREPARE: + // delay before each level - handled by big timer aura + if (eventType == AI_EVENT_CUSTOM_A) + { m_uiGamePhase = PHASE_AI_GAME; } + break; + case PHASE_AI_GAME: + // AI game - handled by small timer aura + if (eventType == AI_EVENT_CUSTOM_B) + { + // Move to next phase if the level is setup + if (m_uiLevelStage == m_uiLevelCount) + { + m_uiGamePhase = PHASE_PLAYER_PREPARE; + m_uiLevelStage = 0; + return; + } + + DoSetupLevel(); + ++m_uiLevelStage; + } + break; + case PHASE_PLAYER_PREPARE: + // Player prepare - handled by small timer aura + if (eventType == AI_EVENT_CUSTOM_B) + { + DoCastSpellIfCan(m_creature, SPELL_VISUAL_LEVEL_START, CAST_TRIGGERED); + DoSetupPlayerLevel(); + + m_uiGamePhase = PHASE_PLAYER_GAME; + m_uiPlayerStage = 0; + } + break; + case PHASE_PLAYER_GAME: + // Player game - listen to the player moves + if (eventType == AI_EVENT_CUSTOM_C) + { + // good button pressed + if (uiMiscValue == aApexisGameData[m_vColors[m_uiLevelStage]].m_uiIntrospection) + { + DoCastSpellIfCan(m_creature, SPELL_GOOD_PRESS, CAST_TRIGGERED); + DoCastSpellIfCan(m_creature, aApexisGameData[m_vColors[m_uiLevelStage]].m_uiVisual, CAST_TRIGGERED); + + DoPlaySoundToSet(m_creature, aApexisGameData[m_vColors[m_uiLevelStage]].m_uiSoundId); + + // increase the level stage and reset the event counter + ++m_uiLevelStage; + m_uiPlayerStage = 0; + + // if all buttons were pressed succesfully, then move to next level + if (m_uiLevelStage == m_vColors.size()) + { + DoCompleteLevel(); + + m_uiLevelStage = 0; + m_uiGamePhase = PHASE_LEVEL_FINISHED; + } + // cast tick sound + else + { DoCastSpellIfCan(pInvoker, m_bIsLargeEvent ? SPELL_VISUAL_GAME_TICK_LARGE : SPELL_VISUAL_GAME_TICK, CAST_TRIGGERED); } + } + // bad button pressed + else + { + DoCastSpellIfCan(pInvoker, m_bIsLargeEvent ? SPELL_SIMON_GROUP_REWARD : SPELL_BAD_PRESS, CAST_TRIGGERED); + DoCastSpellIfCan(m_creature, SPELL_VISUAL_GAME_FAILED, CAST_TRIGGERED); + DoCleanupGame(); + } + } + // AI ticks which handle the player timeout + else if (eventType == AI_EVENT_CUSTOM_B) + { + // if it takes too much time, the event will fail + if (m_uiPlayerStage == MAX_SIMON_FAIL_TIMER) + { + DoCastSpellIfCan(m_creature, SPELL_VISUAL_GAME_FAILED, CAST_TRIGGERED); + DoCleanupGame(); + } + + // Not sure if this is right, but we need to keep the buttons unlocked on every tick + DoSetupPlayerLevel(); + ++m_uiPlayerStage; + } + break; + case PHASE_LEVEL_FINISHED: + // small delay until the next level + if (eventType == AI_EVENT_CUSTOM_A) + { + DoPrepareLevel(); + m_uiGamePhase = PHASE_LEVEL_PREPARE; + } + break; + } + } + + void AttackStart(Unit* /*pWho*/) override { } + void MoveInLineOfSight(Unit* /*pWho*/) override { } + + void UpdateAI(const uint32 /*uiDiff*/) override + { + // Start game on first update tick - don't wait for dummy auras + if (!m_bIsEventStarted) + { + DoPrepareLevel(); + m_bIsEventStarted = true; + } + } +}; + +CreatureAI* GetAI_npc_simon_game_bunny(Creature* pCreature) +{ + return new npc_simon_game_bunnyAI(pCreature); +} + +bool EffectDummyCreature_npc_simon_game_bunny(Unit* pCaster, uint32 uiSpellId, SpellEffectIndex uiEffIndex, Creature* pCreatureTarget, ObjectGuid /*originalCasterGuid*/) +{ + if (pCreatureTarget->GetEntry() != NPC_SIMON_GAME_BUNNY) + { return false; } + + if (uiSpellId == SPELL_SIMON_GAME_START && uiEffIndex == EFFECT_INDEX_0) + { + pCreatureTarget->AI()->SendAIEvent(AI_EVENT_CUSTOM_A, pCaster, pCreatureTarget); + return true; + } + else if (uiSpellId == SPELL_PRE_EVENT_TIMER && uiEffIndex == EFFECT_INDEX_0) + { + pCreatureTarget->AI()->SendAIEvent(AI_EVENT_CUSTOM_B, pCaster, pCreatureTarget); + return true; + } + + return false; +} + +bool EffectScriptEffectCreature_npc_simon_game_bunny(Unit* pCaster, uint32 uiSpellId, SpellEffectIndex uiEffIndex, Creature* pCreatureTarget, ObjectGuid originalCasterGuid) +{ + if ((uiSpellId == SPELL_INTROSPECTION_BLUE || uiSpellId == SPELL_INTROSPECTION_GREEN || uiSpellId == SPELL_INTROSPECTION_RED || + uiSpellId == SPELL_INTROSPECTION_YELLOW) && uiEffIndex == EFFECT_INDEX_1) + { + if (pCreatureTarget->GetEntry() == NPC_SIMON_GAME_BUNNY && pCaster->GetTypeId() == TYPEID_PLAYER && originalCasterGuid.IsGameObject()) + { pCreatureTarget->AI()->SendAIEvent(AI_EVENT_CUSTOM_C, pCaster, pCreatureTarget, uiSpellId); } + + return true; + } + + return false; +} + +void AddSC_blades_edge_mountains() +{ + Script* pNewScript; + + pNewScript = new Script; + pNewScript->Name = "mobs_nether_drake"; + pNewScript->GetAI = &GetAI_mobs_nether_drake; + pNewScript->RegisterSelf(); + + pNewScript = new Script; + pNewScript->Name = "npc_daranelle"; + pNewScript->GetAI = &GetAI_npc_daranelle; + pNewScript->RegisterSelf(); + + pNewScript = new Script; + pNewScript->Name = "npc_bloodmaul_stout_trigger"; + pNewScript->GetAI = &GetAI_npc_bloodmaul_stout_trigger; + pNewScript->RegisterSelf(); + + pNewScript = new Script; + pNewScript->Name = "npc_simon_game_bunny"; + pNewScript->GetAI = &GetAI_npc_simon_game_bunny; + pNewScript->pEffectDummyNPC = &EffectDummyCreature_npc_simon_game_bunny; + pNewScript->pEffectScriptEffectNPC = &EffectScriptEffectCreature_npc_simon_game_bunny; + pNewScript->RegisterSelf(); +} diff --git a/src/modules/SD2/scripts/outland/boss_doomlord_kazzak.cpp b/src/modules/SD2/scripts/outland/boss_doomlord_kazzak.cpp new file mode 100644 index 000000000..e8e2ce7ef --- /dev/null +++ b/src/modules/SD2/scripts/outland/boss_doomlord_kazzak.cpp @@ -0,0 +1,226 @@ +/** + * ScriptDev2 is an extension for mangos providing enhanced features for + * area triggers, creatures, game objects, instances, items, and spells beyond + * the default database scripting in mangos. + * + * Copyright (C) 2006-2013 ScriptDev2 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * World of Warcraft, and all World of Warcraft or Warcraft art, images, + * and lore are copyrighted by Blizzard Entertainment, Inc. + */ + +/* ScriptData +SDName: Boss_Doomlord_Kazzak +SD%Complete: 90 +SDComment: Timers +SDCategory: Hellfire Peninsula +EndScriptData */ + +#include "precompiled.h" + +enum +{ + SAY_INTRO = -1000147, + SAY_AGGRO1 = -1000148, + SAY_AGGRO2 = -1000149, + SAY_SURPREME1 = -1000150, + SAY_SURPREME2 = -1000151, + SAY_KILL1 = -1000152, + SAY_KILL2 = -1000153, + SAY_KILL3 = -1000154, + SAY_DEATH = -1000155, + EMOTE_GENERIC_FRENZY = -1000002, + SAY_RAND1 = -1000157, + SAY_RAND2 = -1000158, + + SPELL_SHADOW_VOLLEY = 32963, + SPELL_CLEAVE = 31779, + SPELL_THUNDERCLAP = 36706, + SPELL_VOID_BOLT = 39329, + SPELL_MARK_OF_KAZZAK = 32960, + SPELL_FRENZY = 32964, // triggers 32963 + SPELL_CAPTURE_SOUL = 48473, // procs 32966 on player kill + SPELL_TWISTED_REFLECTION = 21063, + SPELL_BERSERK = 32965, // triggers 32963 +}; + +struct boss_doomlordkazzakAI : public ScriptedAI +{ + boss_doomlordkazzakAI(Creature* pCreature) : ScriptedAI(pCreature) { Reset(); } + + uint32 m_uiShadowVolleyTimer; + uint32 m_uiCleaveTimer; + uint32 m_uiThunderClapTimer; + uint32 m_uiVoidBoltTimer; + uint32 m_uiMarkOfKazzakTimer; + uint32 m_uiEnrageTimer; + uint32 m_uiGreatEnrageTimer; + uint32 m_uiTwistedReflectionTimer; + + void Reset() override + { + m_uiShadowVolleyTimer = urand(6000, 10000); + m_uiCleaveTimer = 7000; + m_uiThunderClapTimer = urand(14000, 18000); + m_uiVoidBoltTimer = 30000; + m_uiMarkOfKazzakTimer = 25000; + m_uiEnrageTimer = 60000; + m_uiGreatEnrageTimer = 3 * MINUTE * IN_MILLISECONDS; + m_uiTwistedReflectionTimer = 33000; // Timer may be incorrect + } + + void JustRespawned() override + { + DoScriptText(SAY_INTRO, m_creature); + } + + void Aggro(Unit* /*pWho*/) override + { + DoScriptText(urand(0, 1) ? SAY_AGGRO1 : SAY_AGGRO2, m_creature); + DoCastSpellIfCan(m_creature, SPELL_CAPTURE_SOUL, CAST_TRIGGERED); + } + + void KilledUnit(Unit* pVictim) override + { + // When Kazzak kills a player (not pets/totems), he regens some health + if (pVictim->GetTypeId() != TYPEID_PLAYER) + { return; } + + switch (urand(0, 2)) + { + case 0: DoScriptText(SAY_KILL1, m_creature); break; + case 1: DoScriptText(SAY_KILL2, m_creature); break; + case 2: DoScriptText(SAY_KILL3, m_creature); break; + } + } + + void JustDied(Unit* /*pKiller*/) override + { + DoScriptText(SAY_DEATH, m_creature); + } + + void UpdateAI(const uint32 uiDiff) override + { + // Return since we have no target + if (!m_creature->SelectHostileTarget() || !m_creature->getVictim()) + { return; } + + // ShadowVolley_Timer + if (m_uiShadowVolleyTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature, SPELL_SHADOW_VOLLEY) == CAST_OK) + { m_uiShadowVolleyTimer = urand(10000, 30000); } + } + else + { m_uiShadowVolleyTimer -= uiDiff; } + + // Cleave_Timer + if (m_uiCleaveTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature->getVictim(), SPELL_CLEAVE) == CAST_OK) + { m_uiCleaveTimer = urand(8000, 12000); } + } + else + { m_uiCleaveTimer -= uiDiff; } + + // ThunderClap_Timer + if (m_uiThunderClapTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature, SPELL_THUNDERCLAP) == CAST_OK) + { m_uiThunderClapTimer = urand(10000, 14000); } + } + else + { m_uiThunderClapTimer -= uiDiff; } + + // VoidBolt_Timer + if (m_uiVoidBoltTimer < uiDiff) + { + if (Unit* pTarget = m_creature->SelectAttackingTarget(ATTACKING_TARGET_RANDOM, 0)) + { + if (DoCastSpellIfCan(pTarget, SPELL_VOID_BOLT) == CAST_OK) + { m_uiVoidBoltTimer = urand(15000, 18000); } + } + } + else + { m_uiVoidBoltTimer -= uiDiff; } + + // MarkOfKazzak_Timer + if (m_uiMarkOfKazzakTimer < uiDiff) + { + if (Unit* pTarget = m_creature->SelectAttackingTarget(ATTACKING_TARGET_RANDOM, 0, SPELL_MARK_OF_KAZZAK, SELECT_FLAG_POWER_MANA)) + { + if (DoCastSpellIfCan(pTarget, SPELL_MARK_OF_KAZZAK) == CAST_OK) + { m_uiMarkOfKazzakTimer = 20000; } + } + } + else + { m_uiMarkOfKazzakTimer -= uiDiff; } + + // Enrage_Timer + if (m_uiEnrageTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature, SPELL_FRENZY) == CAST_OK) + { + DoScriptText(EMOTE_GENERIC_FRENZY, m_creature); + m_uiEnrageTimer = 60000; + } + } + else + { m_uiEnrageTimer -= uiDiff; } + + // Great_Enrage_Timer + if (m_uiGreatEnrageTimer) + { + if (m_uiGreatEnrageTimer <= uiDiff) + { + if (DoCastSpellIfCan(m_creature, SPELL_BERSERK) == CAST_OK) + { m_uiGreatEnrageTimer = 0; } + } + else + { m_uiGreatEnrageTimer -= uiDiff; } + } + + // Twisted Reflection + if (m_uiTwistedReflectionTimer < uiDiff) + { + if (Unit* pTarget = m_creature->SelectAttackingTarget(ATTACKING_TARGET_RANDOM, 0)) + { + if (DoCastSpellIfCan(pTarget, SPELL_TWISTED_REFLECTION) == CAST_OK) + { m_uiTwistedReflectionTimer = 15000; } + } + } + else + { m_uiTwistedReflectionTimer -= uiDiff; } + + DoMeleeAttackIfReady(); + } +}; + +CreatureAI* GetAI_boss_doomlordkazzak(Creature* pCreature) +{ + return new boss_doomlordkazzakAI(pCreature); +} + +void AddSC_boss_doomlordkazzak() +{ + Script* pNewScript; + + pNewScript = new Script; + pNewScript->Name = "boss_doomlord_kazzak"; + pNewScript->GetAI = &GetAI_boss_doomlordkazzak; + pNewScript->RegisterSelf(); +} diff --git a/src/modules/SD2/scripts/outland/boss_doomwalker.cpp b/src/modules/SD2/scripts/outland/boss_doomwalker.cpp new file mode 100644 index 000000000..d871971b5 --- /dev/null +++ b/src/modules/SD2/scripts/outland/boss_doomwalker.cpp @@ -0,0 +1,184 @@ +/** + * ScriptDev2 is an extension for mangos providing enhanced features for + * area triggers, creatures, game objects, instances, items, and spells beyond + * the default database scripting in mangos. + * + * Copyright (C) 2006-2013 ScriptDev2 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * World of Warcraft, and all World of Warcraft or Warcraft art, images, + * and lore are copyrighted by Blizzard Entertainment, Inc. + */ + +/* ScriptData +SDName: Boss_Doomwalker +SD%Complete: 100 +SDComment: +SDCategory: Shadowmoon Valley +EndScriptData */ + +#include "precompiled.h" + +enum +{ + SAY_AGGRO = -1000159, + SAY_EARTHQUAKE_1 = -1000160, + SAY_EARTHQUAKE_2 = -1000161, + SAY_OVERRUN_1 = -1000162, + SAY_OVERRUN_2 = -1000163, + SAY_SLAY_1 = -1000164, + SAY_SLAY_2 = -1000165, + SAY_SLAY_3 = -1000166, + SAY_DEATH = -1000167, + + SPELL_EARTHQUAKE = 32686, + SPELL_CRUSH_ARMOR = 33661, + SPELL_LIGHTNING_WRATH = 33665, + SPELL_OVERRUN = 32636, + SPELL_ENRAGE = 33653, + SPELL_MARK_OF_DEATH_PLAYER = 37128, + SPELL_MARK_OF_DEATH_AURA = 37125, // triggers 37131 on target if it has aura 37128 +}; + +struct boss_doomwalkerAI : public ScriptedAI +{ + boss_doomwalkerAI(Creature* pCreature) : ScriptedAI(pCreature) { Reset(); } + + uint32 m_uiChainTimer; + uint32 m_uiOverrunTimer; + uint32 m_uiQuakeTimer; + uint32 m_uiArmorTimer; + + bool m_bHasEnrage; + + void Reset() override + { + m_uiArmorTimer = urand(5000, 13000); + m_uiChainTimer = urand(10000, 30000); + m_uiQuakeTimer = urand(25000, 35000); + m_uiOverrunTimer = urand(30000, 45000); + + m_bHasEnrage = false; + } + + void KilledUnit(Unit* pVictim) override + { + if (pVictim->GetTypeId() != TYPEID_PLAYER) + { return; } + + pVictim->CastSpell(pVictim, SPELL_MARK_OF_DEATH_PLAYER, true, NULL, NULL, m_creature->GetObjectGuid()); + + if (urand(0, 4)) + { return; } + + switch (urand(0, 2)) + { + case 0: DoScriptText(SAY_SLAY_1, m_creature); break; + case 1: DoScriptText(SAY_SLAY_2, m_creature); break; + case 2: DoScriptText(SAY_SLAY_3, m_creature); break; + } + } + + void JustDied(Unit* /*pKiller*/) override + { + DoScriptText(SAY_DEATH, m_creature); + } + + void Aggro(Unit* /*pWho*/) override + { + DoCastSpellIfCan(m_creature, SPELL_MARK_OF_DEATH_AURA, CAST_TRIGGERED); + DoScriptText(SAY_AGGRO, m_creature); + } + + void UpdateAI(const uint32 uiDiff) override + { + if (!m_creature->SelectHostileTarget() || !m_creature->getVictim()) + { return; } + + // Spell Enrage, when hp <= 20% gain enrage + if (m_creature->GetHealthPercent() <= 20.0f && !m_bHasEnrage) + { + if (DoCastSpellIfCan(m_creature, SPELL_ENRAGE) == CAST_OK) + { m_bHasEnrage = true; } + } + + // Spell Overrun + if (m_uiOverrunTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature, SPELL_OVERRUN) == CAST_OK) + { + DoScriptText(urand(0, 1) ? SAY_OVERRUN_1 : SAY_OVERRUN_2, m_creature); + m_uiOverrunTimer = urand(25000, 40000); + } + } + else + { m_uiOverrunTimer -= uiDiff; } + + // Spell Earthquake + if (m_uiQuakeTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature, SPELL_EARTHQUAKE) == CAST_OK) + { + DoScriptText(urand(0, 1) ? SAY_EARTHQUAKE_1 : SAY_EARTHQUAKE_2, m_creature); + m_uiQuakeTimer = urand(30000, 55000); + } + } + else + { m_uiQuakeTimer -= uiDiff; } + + // Spell Chain Lightning + if (m_uiChainTimer < uiDiff) + { + Unit* pTarget = m_creature->SelectAttackingTarget(ATTACKING_TARGET_RANDOM, 1); + if (!pTarget) + { pTarget = m_creature->getVictim(); } + + if (pTarget) + { + if (DoCastSpellIfCan(pTarget, SPELL_LIGHTNING_WRATH) == CAST_OK) + { m_uiChainTimer = urand(7000, 27000); } + } + } + else + { m_uiChainTimer -= uiDiff; } + + // Spell Sunder Armor + if (m_uiArmorTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature->getVictim(), SPELL_CRUSH_ARMOR) == CAST_OK) + { m_uiArmorTimer = urand(10000, 25000); } + } + else + { m_uiArmorTimer -= uiDiff; } + + DoMeleeAttackIfReady(); + } +}; + +CreatureAI* GetAI_boss_doomwalker(Creature* pCreature) +{ + return new boss_doomwalkerAI(pCreature); +} + +void AddSC_boss_doomwalker() +{ + Script* pNewScript; + + pNewScript = new Script; + pNewScript->Name = "boss_doomwalker"; + pNewScript->GetAI = &GetAI_boss_doomwalker; + pNewScript->RegisterSelf(); +} diff --git a/src/modules/SD2/scripts/outland/coilfang_reservoir/serpent_shrine/boss_fathomlord_karathress.cpp b/src/modules/SD2/scripts/outland/coilfang_reservoir/serpent_shrine/boss_fathomlord_karathress.cpp new file mode 100644 index 000000000..03a3d6b20 --- /dev/null +++ b/src/modules/SD2/scripts/outland/coilfang_reservoir/serpent_shrine/boss_fathomlord_karathress.cpp @@ -0,0 +1,514 @@ +/* Copyright (C) 2006 - 2013 ScriptDev2 + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with 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: 95 +SDComment: Timers may need adjustments. +SDCategory: Coilfang Resevoir, Serpent Shrine Cavern +EndScriptData */ + +#include "precompiled.h" +#include "serpent_shrine.h" + +enum +{ + SAY_AGGRO = -1548021, + SAY_GAIN_BLESSING = -1548022, + SAY_GAIN_ABILITY1 = -1548023, + SAY_GAIN_ABILITY2 = -1548024, + SAY_GAIN_ABILITY3 = -1548025, + SAY_SLAY1 = -1548026, + SAY_SLAY2 = -1548027, + SAY_SLAY3 = -1548028, + SAY_DEATH = -1548029, + + // Karathress spells + SPELL_CATACLYSMIC_BOLT = 38441, + SPELL_ENRAGE = 24318, // ToDo: spell needs to be confirmed + SPELL_SEAR_NOVA = 38445, + SPELL_BLESSING_OF_THE_TIDES = 38449, // cast by each of the advisors when the boss reaches 75% hp + + // Sharkkis spells + SPELL_LEECHING_THROW = 29436, + SPELL_THE_BEAST_WITHIN = 38373, + SPELL_HURL_TRIDENT = 38374, + SPELL_MULTI_TOSS = 38366, + SPELL_SUMMON_FATHOM_LURKER = 38433, + SPELL_SUMMON_FATHOM_SPOREBAT = 38431, + SPELL_POWER_OF_SHARKKIS = 38455, // cast on Karathress, on death + + // Tidalvess spells + SPELL_FROST_SHOCK = 38234, + SPELL_SPITFIRE_TOTEM = 38236, + SPELL_POISON_CLEANSING_TOTEM = 38306, + SPELL_EARTHBIND_TOTEM = 38304, + SPELL_WINDFURY_WEAPON = 32911, // triggers spell 32912 (Windfury) + SPELL_POWER_OF_TIDALVESS = 38452, // cast on Karathress, on death + + // Caribdis Spells + SPELL_WATER_BOLT_VOLLEY = 38335, + SPELL_TIDAL_SURGE = 38358, // triggers 38353 which then triggers 38357 + SPELL_HEALING_WAVE = 38330, + SPELL_SUMMON_CYCLONE = 38337, // summons creature 22104 which uses spell 29538 + SPELL_POWER_OF_CARIBDIS = 38451, // cast on Karathress, on death + + SPELL_CYCLONE = 29538, + + MAX_ADVISORS = 3, + + NPC_CYCLONE = 22104, + NPC_SEER_OLUM = 22820 +}; + +// position for Seer Olum +static const float afCoordsOlum[4] = {446.78f, -542.76f, -7.547f, 0.401f}; + +static const uint32 aAdvisors[MAX_ADVISORS] = {NPC_SHARKKIS, NPC_TIDALVESS, NPC_CARIBDIS}; + +/*###### +## boss_fathomlord_karathress +######*/ + +struct boss_fathomlord_karathressAI : public ScriptedAI +{ + boss_fathomlord_karathressAI(Creature* pCreature) : ScriptedAI(pCreature) + { + m_pInstance = (ScriptedInstance*)pCreature->GetInstanceData(); + Reset(); + } + + ScriptedInstance* m_pInstance; + + uint32 m_uiCataclysmicBoltTimer; + uint32 m_uiSearingNovaTimer; + uint32 m_uiEnrageTimer; + + bool m_bBlessingOfTides; + + void Reset() override + { + m_uiCataclysmicBoltTimer = 10000; + m_uiSearingNovaTimer = urand(20000, 30000); + m_uiEnrageTimer = 10 * MINUTE * IN_MILLISECONDS; + m_bBlessingOfTides = false; + } + + void SpellHit(Unit* /*pCaster*/, const SpellEntry* pSpell) override + { + switch (pSpell->Id) + { + case SPELL_POWER_OF_SHARKKIS: + DoScriptText(SAY_GAIN_ABILITY1, m_creature); + break; + case SPELL_POWER_OF_TIDALVESS: + DoScriptText(SAY_GAIN_ABILITY1, m_creature); + break; + case SPELL_POWER_OF_CARIBDIS: + DoScriptText(SAY_GAIN_ABILITY3, m_creature); + break; + } + } + + void Aggro(Unit* /*pWho*/) override + { + if (m_pInstance) + m_pInstance->SetData(TYPE_KARATHRESS_EVENT, IN_PROGRESS); + + DoScriptText(SAY_AGGRO, m_creature); + } + + void KilledUnit(Unit* /*pVictim*/) override + { + switch (urand(0, 2)) + { + case 0: DoScriptText(SAY_SLAY1, m_creature); break; + case 1: DoScriptText(SAY_SLAY2, m_creature); break; + case 2: DoScriptText(SAY_SLAY3, m_creature); break; + } + } + + void JustDied(Unit* /*pKiller*/) override + { + DoScriptText(SAY_DEATH, m_creature); + + if (m_pInstance) + m_pInstance->SetData(TYPE_KARATHRESS_EVENT, DONE); + + // support for quest 10944 + m_creature->SummonCreature(NPC_SEER_OLUM, afCoordsOlum[0], afCoordsOlum[1], afCoordsOlum[2], afCoordsOlum[3], TEMPSUMMON_TIMED_DESPAWN, 1 * HOUR * IN_MILLISECONDS); + } + + void JustReachedHome() override + { + if (m_pInstance) + m_pInstance->SetData(TYPE_KARATHRESS_EVENT, FAIL); + } + + void UpdateAI(const uint32 uiDiff) override + { + if (!m_creature->SelectHostileTarget() || !m_creature->getVictim()) + return; + + if (m_uiCataclysmicBoltTimer < uiDiff) + { + // select a random unit other than the main tank + Unit* pTarget = m_creature->SelectAttackingTarget(ATTACKING_TARGET_RANDOM, 1); + + // if there aren't other units, cast on the tank + if (!pTarget) + pTarget = m_creature->getVictim(); + + if (pTarget) + { + if (DoCastSpellIfCan(pTarget, SPELL_CATACLYSMIC_BOLT) == CAST_OK) + m_uiCataclysmicBoltTimer = 10000; + } + } + else + m_uiCataclysmicBoltTimer -= uiDiff; + + if (!m_bBlessingOfTides && m_creature->GetHealthPercent() < 75.0f) + { + for (uint8 i = 0; i < MAX_ADVISORS; ++i) + { + if (Creature* pAdvisor = m_pInstance->GetSingleCreatureFromStorage(aAdvisors[i])) + { + // stack max three times (one for each alive) + if (pAdvisor->IsAlive()) + { + pAdvisor->InterruptNonMeleeSpells(false); + pAdvisor->CastSpell(m_creature, SPELL_BLESSING_OF_THE_TIDES, true); + } + } + } + + // yell if we now have the aura + if (m_creature->HasAura(SPELL_BLESSING_OF_THE_TIDES)) + DoScriptText(SAY_GAIN_BLESSING, m_creature); + + m_bBlessingOfTides = true; + } + + if (m_uiSearingNovaTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature, SPELL_SEAR_NOVA) == CAST_OK) + m_uiSearingNovaTimer = urand(20000, 30000); + } + else + m_uiSearingNovaTimer -= uiDiff; + + if (m_uiEnrageTimer) + { + if (m_uiEnrageTimer <= uiDiff) + { + if (DoCastSpellIfCan(m_creature, SPELL_ENRAGE) == CAST_OK) + m_uiEnrageTimer = 0; + } + else + m_uiEnrageTimer -= uiDiff; + } + + DoMeleeAttackIfReady(); + } +}; + +/*###### +## boss_fathomguard_sharkkis +######*/ + +struct boss_fathomguard_sharkkisAI : public ScriptedAI +{ + boss_fathomguard_sharkkisAI(Creature* pCreature) : ScriptedAI(pCreature) { Reset(); } + + uint32 m_uiHurlTridentTimer; + uint32 m_uiLeechingThrowTimer; + uint32 m_uiTheBeastWithinTimer; + uint32 m_uiMultiTossTimer; + uint32 m_uiPetTimer; + + void Reset() override + { + m_uiHurlTridentTimer = 2500; + m_uiLeechingThrowTimer = 20000; + m_uiTheBeastWithinTimer = 30000; + m_uiMultiTossTimer = urand(7000, 11000); + if (!m_creature->GetPet()) + m_uiPetTimer = 10000; + } + + void JustDied(Unit* /*pKiller*/) override + { + DoCastSpellIfCan(m_creature, SPELL_POWER_OF_SHARKKIS, CAST_TRIGGERED); + } + + void JustSummoned(Creature* pSummoned) override + { + if (m_creature->getVictim()) + pSummoned->AI()->AttackStart(m_creature->getVictim()); + } + + void SummonedCreatureJustDied(Creature* pSummoned) override + { + // resummon the pet in 10 secs + if (pSummoned->IsPet()) + m_uiPetTimer = 10000; + } + + void UpdateAI(const uint32 uiDiff) override + { + if (!m_creature->SelectHostileTarget() || !m_creature->getVictim()) + return; + + if (m_uiPetTimer) + { + if (m_uiPetTimer <= uiDiff) + { + if (DoCastSpellIfCan(m_creature, urand(0, 1) ? SPELL_SUMMON_FATHOM_LURKER : SPELL_SUMMON_FATHOM_SPOREBAT) == CAST_OK) + m_uiPetTimer = 0; + } + else + m_uiPetTimer -= uiDiff; + } + + if (m_uiHurlTridentTimer < uiDiff) + { + if (Unit* pTarget = m_creature->SelectAttackingTarget(ATTACKING_TARGET_RANDOM, 0, SPELL_HURL_TRIDENT, 0)) + { + if (DoCastSpellIfCan(pTarget, SPELL_HURL_TRIDENT) == CAST_OK) + m_uiHurlTridentTimer = 5000; + } + } + else + m_uiHurlTridentTimer -= uiDiff; + + if (m_uiLeechingThrowTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature->getVictim(), SPELL_LEECHING_THROW) == CAST_OK) + m_uiLeechingThrowTimer = 20000; + } + else + m_uiLeechingThrowTimer -= uiDiff; + + if (m_uiTheBeastWithinTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature, SPELL_THE_BEAST_WITHIN) == CAST_OK) + m_uiTheBeastWithinTimer = 30000; + } + else + m_uiTheBeastWithinTimer -= uiDiff; + + if (m_uiMultiTossTimer < uiDiff) + { + if (Unit* pTarget = m_creature->SelectAttackingTarget(ATTACKING_TARGET_RANDOM, 0)) + { + if (DoCastSpellIfCan(pTarget, SPELL_HURL_TRIDENT) == CAST_OK) + m_uiMultiTossTimer = urand(7000, 12000); + } + } + else + m_uiMultiTossTimer -= uiDiff; + + DoMeleeAttackIfReady(); + } +}; + +/*###### +## boss_fathomguard_tidalvess +######*/ + +struct boss_fathomguard_tidalvessAI : public ScriptedAI +{ + boss_fathomguard_tidalvessAI(Creature* pCreature) : ScriptedAI(pCreature) { Reset(); } + + uint32 m_uiFrostShockTimer; + uint32 m_uiWindfuryTimer; + uint32 m_uiTotemTimer; + + void Reset() override + { + m_uiFrostShockTimer = 25000; + m_uiWindfuryTimer = 0; + m_uiTotemTimer = urand(2000, 5000); + } + + void JustDied(Unit* /*pKiller*/) override + { + DoCastSpellIfCan(m_creature, SPELL_POWER_OF_TIDALVESS, CAST_TRIGGERED); + } + + void UpdateAI(const uint32 uiDiff) override + { + if (!m_creature->SelectHostileTarget() || !m_creature->getVictim()) + return; + + if (m_uiFrostShockTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature->getVictim(), SPELL_FROST_SHOCK) == CAST_OK) + m_uiFrostShockTimer = urand(25000, 30000); + } + else + m_uiFrostShockTimer -= uiDiff; + + if (m_uiWindfuryTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature, SPELL_WINDFURY_WEAPON) == CAST_OK) + m_uiWindfuryTimer = urand(90000, 100000); + } + else + m_uiWindfuryTimer -= uiDiff; + + if (m_uiTotemTimer < uiDiff) + { + if (m_creature->IsNonMeleeSpellCasted(false)) + { + switch (urand(0, 2)) + { + case 0: DoCastSpellIfCan(m_creature, SPELL_SPITFIRE_TOTEM); + case 1: DoCastSpellIfCan(m_creature, SPELL_POISON_CLEANSING_TOTEM); + case 2: DoCastSpellIfCan(m_creature, SPELL_EARTHBIND_TOTEM); + } + m_uiTotemTimer = urand(30000, 60000); + } + } + else + m_uiTotemTimer -= uiDiff; + + DoMeleeAttackIfReady(); + } +}; + +/*###### +## boss_fathomguard_caribdis +######*/ + +struct boss_fathomguard_caribdisAI : public ScriptedAI +{ + boss_fathomguard_caribdisAI(Creature* pCreature) : ScriptedAI(pCreature) { Reset(); } + + uint32 m_uiWaterBoltVolleyTimer; + uint32 m_uiTidalSurgeTimer; + uint32 m_uiHealTimer; + uint32 m_uiCycloneTimer; + + void Reset() override + { + m_uiWaterBoltVolleyTimer = 35000; + m_uiTidalSurgeTimer = urand(15000, 20000); + m_uiHealTimer = 55000; + m_uiCycloneTimer = urand(10000, 15000); + } + + void JustDied(Unit* /*pKiller*/) override + { + DoCastSpellIfCan(m_creature, SPELL_POWER_OF_CARIBDIS, CAST_TRIGGERED); + } + + void JustSummoned(Creature* pSummoned) override + { + // ToDo: research if this creature should follow the summoner or a random target + if (pSummoned->GetEntry() == NPC_CYCLONE) + pSummoned->CastSpell(pSummoned, SPELL_CYCLONE, true); + } + + void UpdateAI(const uint32 uiDiff) override + { + if (!m_creature->SelectHostileTarget() || !m_creature->getVictim()) + return; + + if (m_uiWaterBoltVolleyTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature, SPELL_WATER_BOLT_VOLLEY) == CAST_OK) + m_uiWaterBoltVolleyTimer = 30000; + } + else + m_uiWaterBoltVolleyTimer -= uiDiff; + + if (m_uiTidalSurgeTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature, SPELL_TIDAL_SURGE) == CAST_OK) + m_uiTidalSurgeTimer = urand(15000, 20000); + } + else + m_uiTidalSurgeTimer -= uiDiff; + + if (m_uiCycloneTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature, SPELL_SUMMON_CYCLONE) == CAST_OK) + m_uiCycloneTimer = urand(45000, 60000); + } + else + m_uiCycloneTimer -= uiDiff; + + if (m_uiHealTimer < uiDiff) + { + if (Unit* pTarget = DoSelectLowestHpFriendly(50.0f)) + { + if (DoCastSpellIfCan(pTarget, SPELL_HEALING_WAVE) == CAST_OK) + m_uiHealTimer = 60000; + } + } + else + m_uiHealTimer -= uiDiff; + + DoMeleeAttackIfReady(); + } +}; + +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* pNewScript; + + pNewScript = new Script; + pNewScript->Name = "boss_fathomlord_karathress"; + pNewScript->GetAI = &GetAI_boss_fathomlord_karathress; + pNewScript->RegisterSelf(); + + pNewScript = new Script; + pNewScript->Name = "boss_fathomguard_sharkkis"; + pNewScript->GetAI = &GetAI_boss_fathomguard_sharkkis; + pNewScript->RegisterSelf(); + + pNewScript = new Script; + pNewScript->Name = "boss_fathomguard_tidalvess"; + pNewScript->GetAI = &GetAI_boss_fathomguard_tidalvess; + pNewScript->RegisterSelf(); + + pNewScript = new Script; + pNewScript->Name = "boss_fathomguard_caribdis"; + pNewScript->GetAI = &GetAI_boss_fathomguard_caribdis; + pNewScript->RegisterSelf(); +} diff --git a/src/modules/SD2/scripts/outland/coilfang_reservoir/serpent_shrine/boss_hydross_the_unstable.cpp b/src/modules/SD2/scripts/outland/coilfang_reservoir/serpent_shrine/boss_hydross_the_unstable.cpp new file mode 100644 index 000000000..a0cd756e2 --- /dev/null +++ b/src/modules/SD2/scripts/outland/coilfang_reservoir/serpent_shrine/boss_hydross_the_unstable.cpp @@ -0,0 +1,371 @@ +/* Copyright (C) 2006 - 2013 ScriptDev2 + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with 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: 95 +SDComment: Timers may need improvemets. +SDCategory: Coilfang Resevoir, Serpent Shrine Cavern +EndScriptData */ + +#include "precompiled.h" +#include "serpent_shrine.h" + +enum +{ + SAY_AGGRO = -1548000, + SAY_SWITCH_TO_CLEAN = -1548001, + SAY_CLEAN_SLAY1 = -1548002, + SAY_CLEAN_SLAY2 = -1548003, + SAY_CLEAN_DEATH = -1548004, + SAY_SWITCH_TO_CORRUPT = -1548005, + SAY_CORRUPT_SLAY1 = -1548006, + SAY_CORRUPT_SLAY2 = -1548007, + SAY_CORRUPT_DEATH = -1548008, + + SPELL_WATER_TOMB = 38235, + SPELL_VILE_SLUDGE = 38246, + SPELL_CORRUPTION = 37961, // transform spell + SPELL_ENRAGE = 27680, // ToDo: this spell need verification + SPELL_BLUE_BEAM = 38015, + SPELL_SUMMON_WATER_ELEMENT = 36459, // spawn elemental on OOC timer + // SPELL_ELEMENTAL_SPAWNIN = 25035, // already handled in eventAI + SPELL_PURIFY_ELEMENTAL = 36461, // purify elemental on OOC timer + + NPC_PURE_SPAWN = 22035, + NPC_TAINTED_SPAWN = 22036, + NPC_PURIFIED_ELEMENTAL = 21260, + NPC_TAINTED_ELEMENTAL = 21253, + + POINT_ID_ELEMENTAL_CLEAN = 1, + POINT_ID_ELEMENTAL_EXIT = 2, + + SWITCH_RADIUS = 18, + MAX_HYDROSS_ADDS = 4, + MAX_HYDROSS_MARKS = 6, +}; + +static const uint32 aMarkHydross[MAX_HYDROSS_MARKS] = {38215, 38216, 38217, 38218, 38231, 40584}; +static const uint32 aMarkCorruption[MAX_HYDROSS_MARKS] = {38219, 38220, 38221, 38222, 38230, 40583}; + +static const float aElementalCleanPoint[3] = { -231.48f, -343.05f, -1.58f}; +static const float aElementalExitPoint[3] = { -177.41f, -395.72f, -1.60f}; + +struct boss_hydross_the_unstableAI : public ScriptedAI +{ + boss_hydross_the_unstableAI(Creature* pCreature) : ScriptedAI(pCreature) + { + m_pInstance = (instance_serpentshrine_cavern*)pCreature->GetInstanceData(); + Reset(); + } + + instance_serpentshrine_cavern* m_pInstance; + + uint32 m_uiBeamInitTimer; + uint32 m_uiElementalTimer; + uint32 m_uiPosCheckTimer; + uint32 m_uiMarkTimer; + uint32 m_uiWaterTombTimer; + uint32 m_uiVileSludgeTimer; + uint32 m_uiEnrageTimer; + uint8 m_uiMarkCount; + bool m_bCorruptedForm; + + void Reset() override + { + m_uiBeamInitTimer = 5000; + m_uiElementalTimer = 20000; + m_uiPosCheckTimer = 2000; + m_uiMarkTimer = 15000; + m_uiWaterTombTimer = 7000; + m_uiVileSludgeTimer = 7000; + m_uiMarkCount = 0; + m_uiEnrageTimer = 10 * MINUTE * IN_MILLISECONDS; + + m_bCorruptedForm = 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); + } + + void Aggro(Unit* /*pWho*/) override + { + DoScriptText(SAY_AGGRO, m_creature); + + if (m_pInstance) + m_pInstance->SetData(TYPE_HYDROSS_EVENT, IN_PROGRESS); + } + + void KilledUnit(Unit* /*pVictim*/) override + { + if (m_bCorruptedForm) + DoScriptText(urand(0, 1) ? SAY_CORRUPT_SLAY1 : SAY_CORRUPT_SLAY2, m_creature); + else + DoScriptText(urand(0, 1) ? SAY_CLEAN_SLAY1 : SAY_CLEAN_SLAY2, m_creature); + } + + void JustDied(Unit* /*pKiller*/) override + { + DoScriptText(m_bCorruptedForm ? SAY_CORRUPT_DEATH : SAY_CLEAN_DEATH, m_creature); + + if (m_pInstance) + m_pInstance->SetData(TYPE_HYDROSS_EVENT, DONE); + } + + void JustReachedHome() override + { + if (m_pInstance) + m_pInstance->SetData(TYPE_HYDROSS_EVENT, FAIL); + } + + void JustSummoned(Creature* pSummoned) override + { + switch (pSummoned->GetEntry()) + { + case NPC_PURE_SPAWN: + pSummoned->ApplySpellImmune(0, IMMUNITY_SCHOOL, SPELL_SCHOOL_MASK_FROST, true); + break; + case NPC_TAINTED_SPAWN: + pSummoned->ApplySpellImmune(0, IMMUNITY_SCHOOL, SPELL_SCHOOL_MASK_NATURE, true); + break; + case NPC_TAINTED_ELEMENTAL: + pSummoned->GetMotionMaster()->MovePoint(POINT_ID_ELEMENTAL_CLEAN, aElementalCleanPoint[0], aElementalCleanPoint[1], aElementalCleanPoint[2]); + break; + } + + // Attack only in combat + if (m_creature->getVictim()) + pSummoned->AI()->AttackStart(m_creature->getVictim()); + } + + void SummonedMovementInform(Creature* pSummoned, uint32 uiMotionType, uint32 uiPointId) override + { + if (uiMotionType != POINT_MOTION_TYPE) + return; + + if (uiPointId == POINT_ID_ELEMENTAL_CLEAN) + { + pSummoned->SetFacingToObject(m_creature); + DoCastSpellIfCan(pSummoned, SPELL_PURIFY_ELEMENTAL); + } + else if (uiPointId == POINT_ID_ELEMENTAL_EXIT) + pSummoned->ForcedDespawn(); + } + + void SpellHitTarget(Unit* pTarget, const SpellEntry* pSpell) override + { + // Purify elementals and make them go to exit + if (pSpell->Id == SPELL_PURIFY_ELEMENTAL) + { + ((Creature*)pTarget)->UpdateEntry(NPC_PURIFIED_ELEMENTAL); + pTarget->GetMotionMaster()->MovePoint(POINT_ID_ELEMENTAL_EXIT, aElementalExitPoint[0], aElementalExitPoint[1], aElementalExitPoint[2]); + } + } + + // Adds summon during phase switch + void DoSpawnAdds() + { + float fX, fY, fZ; + for (uint8 i = 0; i < MAX_HYDROSS_ADDS; ++i) + { + m_creature->GetNearPoint(m_creature, fX, fY, fZ, 0, 10, M_PI_F / 2 * i); + m_creature->SummonCreature(m_bCorruptedForm ? NPC_PURE_SPAWN : NPC_TAINTED_SPAWN, fX, fY, fZ, 0, TEMPSUMMON_DEAD_DESPAWN, 0); + } + } + + // Wrapper to handle the blue beams animation + void DoHandleBeamHelpers(bool bReset) + { + if (!m_pInstance) + return; + + GuidList lBeamHelpersGuid; + m_pInstance->GetBeamHelpersGUIDList(lBeamHelpersGuid); + + for (GuidList::const_iterator itr = lBeamHelpersGuid.begin(); itr != lBeamHelpersGuid.end(); ++itr) + { + if (Creature* pBeam = m_creature->GetMap()->GetCreature(*itr)) + { + if (bReset) + pBeam->InterruptNonMeleeSpells(false); + else + pBeam->CastSpell(m_creature, SPELL_BLUE_BEAM, false); + } + } + } + + void UpdateAI(const uint32 uiDiff) override + { + if (!m_creature->SelectHostileTarget() || !m_creature->getVictim()) + { + // handle elementals on OOC timer + if (m_uiElementalTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature, SPELL_SUMMON_WATER_ELEMENT) == CAST_OK) + m_uiElementalTimer = 20000; + } + else + m_uiElementalTimer -= uiDiff; + + return; + } + + if (m_uiBeamInitTimer) + { + if (m_uiBeamInitTimer <= uiDiff) + { + DoHandleBeamHelpers(false); + m_uiBeamInitTimer = 0; + } + else + m_uiBeamInitTimer -= uiDiff; + } + + // corrupted form + if (m_bCorruptedForm) + { + if (m_uiVileSludgeTimer < uiDiff) + { + if (Unit* pTarget = m_creature->SelectAttackingTarget(ATTACKING_TARGET_RANDOM, 0)) + { + if (DoCastSpellIfCan(pTarget, SPELL_VILE_SLUDGE) == CAST_OK) + m_uiVileSludgeTimer = 15000; + } + } + else + m_uiVileSludgeTimer -= uiDiff; + + // Change to clean + if (m_uiPosCheckTimer < uiDiff) + { + float fPosX, fPosY, fPosZ; + m_creature->GetCombatStartPosition(fPosX, fPosY, fPosZ); + + if (m_creature->IsWithinDist2d(fPosX, fPosY, SWITCH_RADIUS)) + { + DoScriptText(SAY_SWITCH_TO_CLEAN, m_creature); + m_creature->RemoveAurasDueToSpell(SPELL_CORRUPTION); + m_uiMarkCount = 0; + + DoHandleBeamHelpers(false); + DoResetThreat(); + DoSpawnAdds(); + + 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_bCorruptedForm = false; + m_uiMarkTimer = 15000; + } + + m_uiPosCheckTimer = 2000; + } + else + m_uiPosCheckTimer -= uiDiff; + } + // clean form + else + { + if (m_uiWaterTombTimer < uiDiff) + { + if (Unit* pTarget = m_creature->SelectAttackingTarget(ATTACKING_TARGET_RANDOM, 0)) + { + if (DoCastSpellIfCan(pTarget, SPELL_WATER_TOMB) == CAST_OK) + m_uiWaterTombTimer = 7000; + } + } + else + m_uiWaterTombTimer -= uiDiff; + + // Change to corrupt + if (m_uiPosCheckTimer < uiDiff) + { + float fPosX, fPosY, fPosZ; + m_creature->GetCombatStartPosition(fPosX, fPosY, fPosZ); + + if (!m_creature->IsWithinDist2d(fPosX, fPosY, SWITCH_RADIUS)) + { + if (DoCastSpellIfCan(m_creature, SPELL_CORRUPTION) == CAST_OK) + { + DoScriptText(SAY_SWITCH_TO_CORRUPT, m_creature); + m_uiMarkCount = 0; + + DoHandleBeamHelpers(true); + DoResetThreat(); + DoSpawnAdds(); + + 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); + + m_bCorruptedForm = true; + m_uiMarkTimer = 15000; + } + } + + m_uiPosCheckTimer = 2000; + } + else + m_uiPosCheckTimer -= uiDiff; + } + + // Apply mark debuff + if (m_uiMarkTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature, m_bCorruptedForm ? aMarkCorruption[m_uiMarkCount] : aMarkHydross[m_uiMarkCount]) == CAST_OK) + { + ++m_uiMarkCount; + m_uiMarkTimer = 15000; + + // limit the mark counter to 6 + if (m_uiMarkCount == MAX_HYDROSS_MARKS) + m_uiMarkCount = MAX_HYDROSS_MARKS - 1; + } + } + else + m_uiMarkTimer -= uiDiff; + + if (m_uiEnrageTimer) + { + if (m_uiEnrageTimer <= uiDiff) + { + if (DoCastSpellIfCan(m_creature, SPELL_ENRAGE) == CAST_OK) + m_uiEnrageTimer = 0; + } + else + m_uiEnrageTimer -= uiDiff; + } + + DoMeleeAttackIfReady(); + } +}; + +CreatureAI* GetAI_boss_hydross_the_unstable(Creature* pCreature) +{ + return new boss_hydross_the_unstableAI(pCreature); +} + +void AddSC_boss_hydross_the_unstable() +{ + Script* pNewScript; + + pNewScript = new Script; + pNewScript->Name = "boss_hydross_the_unstable"; + pNewScript->GetAI = &GetAI_boss_hydross_the_unstable; + pNewScript->RegisterSelf(); +} diff --git a/src/modules/SD2/scripts/outland/coilfang_reservoir/serpent_shrine/boss_lady_vashj.cpp b/src/modules/SD2/scripts/outland/coilfang_reservoir/serpent_shrine/boss_lady_vashj.cpp new file mode 100644 index 000000000..eb5937e15 --- /dev/null +++ b/src/modules/SD2/scripts/outland/coilfang_reservoir/serpent_shrine/boss_lady_vashj.cpp @@ -0,0 +1,518 @@ +/* Copyright (C) 2006 - 2013 ScriptDev2 + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA02111-1307USA + */ + +/* ScriptData +SDName: Boss_Lady_Vashj +SD%Complete: 80 +SDComment: Some details are not very clear: the usage of Shoot and Multishot spells; the summons positions. Tainted Core paralize NYI. Timers need improvements. +SDCategory: Coilfang Resevoir, Serpent Shrine Cavern +EndScriptData */ + +#include "precompiled.h" +#include "serpent_shrine.h" + +enum +{ + // yells + SAY_INTRO = -1548042, + SAY_AGGRO1 = -1548043, + SAY_AGGRO2 = -1548044, + SAY_AGGRO3 = -1548045, + SAY_AGGRO4 = -1548046, + SAY_PHASE1 = -1548047, + SAY_PHASE2 = -1548048, + SAY_PHASE3 = -1548049, + SAY_BOWSHOT1 = -1548050, + SAY_BOWSHOT2 = -1548051, + SAY_SLAY1 = -1548052, + SAY_SLAY2 = -1548053, + SAY_SLAY3 = -1548054, + SAY_DEATH = -1548055, + + // spells + SPELL_MULTI_SHOT = 38310, + SPELL_SHOCK_BLAST = 38509, + SPELL_ENTANGLE = 38316, + SPELL_STATIC_CHARGE = 38280, + SPELL_FORKED_LIGHTNING = 38145, + SPELL_SHOOT = 38295, + + SPELL_MAGIC_BARRIER = 38112, + SPELL_SURGE = 38044, + SPELL_SUMMON_TAINTED_ELEM = 38139, // maybe also related to spell 38494 + SPELL_PARALIZE = 38132, // aura which should apply to the player which picked the tainted core + + // summons + NPC_ENCHANTED_ELEMENTAL = 21958, + NPC_TAINTED_ELEMENTAL = 22009, + NPC_COILFANG_STRIDER = 22056, + NPC_COILFANG_ELITE = 22055, + NPC_TOXIC_SPOREBAT = 22140, + + // other + POINT_MOVE_CENTER = 1, + + PHASE_1 = 1, + PHASE_2 = 2, + PHASE_3 = 3, + + MAX_SHIELD_GEN = 4, +}; + +static const float afMiddlePos[3] = {30.134f, -923.65f, 42.9f}; + +// ToDo: all the following mobs are probably summoned by trigger npcs. +// Remove the hardcoded coords and set the right summoning when then DB will suppot this! + +static const float afSporebatPos[4] = {30.977156f, -925.297761f, 77.176567f, 5.223932f}; + +static const float afElementPos[8][4] = +{ + {8.3f , -835.3f , 21.9f, 5.0f}, + {53.4f , -835.3f , 21.9f, 4.5f}, + {96.0f , -861.9f , 21.8f, 4.0f}, + {96.0f , -986.4f , 21.4f, 2.5f}, + {54.4f , -1010.6f, 22.0f, 1.8f}, + {9.8f , -1012.0f, 21.7f, 1.4f}, + { -35.0f, -987.6f , 21.5f, 0.8f}, + { -58.9f, -901.6f , 21.5f, 6.0f} +}; + +const float afCoilfangElitePos[3][4] = +{ + {28.84f , -923.28f , 42.9f , 6.0f }, + {31.183281f, -953.502625f, 41.523602f, 1.640957f}, + {58.895180f, -923.124268f, 41.545307f, 3.152848f} +}; + +const float afCoilfangStriderPos[3][4] = +{ + {66.427f, -948.778f, 41.262245f, 2.584f}, + {7.513f , -959.538f, 41.300422f, 1.0346f}, + { -12.843f, -907.798f, 41.239620f, 6.087f} +}; + +struct boss_lady_vashjAI : public ScriptedAI +{ + boss_lady_vashjAI(Creature* pCreature) : ScriptedAI(pCreature) + { + m_pInstance = (instance_serpentshrine_cavern*)pCreature->GetInstanceData(); + Reset(); + } + + instance_serpentshrine_cavern* m_pInstance; + + uint32 m_uiShockBlastTimer; + uint32 m_uiEntangleTimer; + uint32 m_uiStaticChargeTimer; + uint32 m_uiRangedCheckTimer; + uint32 m_uiForkedLightningTimer; + uint32 m_uiEnchantedElementalTimer; + uint32 m_uiTaintedElementalTimer; + uint32 m_uiCoilfangEliteTimer; + uint32 m_uiCoilfangStriderTimer; + uint32 m_uiSummonSporebatTimer; + uint32 m_uiSummonSporebatStaticTimer; + + uint8 m_uiPhase; + uint8 m_uiGeneratorsUsed; + + bool m_bEntangle; + + void Reset() override + { + SetCombatMovement(true); + + m_uiPhase = PHASE_1; + m_uiGeneratorsUsed = 0; + + m_uiShockBlastTimer = urand(1000, 60000); + m_uiEntangleTimer = 30000; + m_uiStaticChargeTimer = urand(10000, 25000); + m_uiRangedCheckTimer = 2000; + m_bEntangle = false; + + m_uiForkedLightningTimer = urand(3000, 5000); + m_uiEnchantedElementalTimer = 10000; + m_uiTaintedElementalTimer = 53000; + m_uiCoilfangEliteTimer = 47000; + m_uiCoilfangStriderTimer = 60000; + + m_uiSummonSporebatTimer = 10000; + m_uiSummonSporebatStaticTimer = 30000; + } + + void Aggro(Unit* /*pWho*/) override + { + switch (urand(0, 3)) + { + case 0: DoScriptText(SAY_AGGRO1, m_creature); break; + case 1: DoScriptText(SAY_AGGRO2, m_creature); break; + case 2: DoScriptText(SAY_AGGRO3, m_creature); break; + case 3: DoScriptText(SAY_AGGRO4, m_creature); break; + } + + if (m_pInstance) + m_pInstance->SetData(TYPE_LADYVASHJ_EVENT, IN_PROGRESS); + } + + void MovementInform(uint32 uiType, uint32 uiPointId) override + { + if (uiType != POINT_MOTION_TYPE) + return; + + if (uiPointId == POINT_MOVE_CENTER) + { + // Initialize all the shield generators + if (m_pInstance) + { + GuidList lShieldGeneratorsGuid; + m_pInstance->GetShieldGeneratorsGUIDList(lShieldGeneratorsGuid); + + for (GuidList::const_iterator itr = lShieldGeneratorsGuid.begin(); itr != lShieldGeneratorsGuid.end(); ++itr) + { + if (Creature* pGenerator = m_creature->GetMap()->GetCreature(*itr)) + pGenerator->CastSpell(m_creature, SPELL_MAGIC_BARRIER, false); + } + } + + m_uiPhase = PHASE_2; + } + } + + void JustSummoned(Creature* pSummoned) override + { + switch (pSummoned->GetEntry()) + { + case NPC_COILFANG_STRIDER: + case NPC_COILFANG_ELITE: + case NPC_TOXIC_SPOREBAT: + if (Unit* pTarget = m_creature->SelectAttackingTarget(ATTACKING_TARGET_RANDOM, 0)) + pSummoned->AI()->AttackStart(pTarget); + break; + case NPC_ENCHANTED_ELEMENTAL: + pSummoned->GetMotionMaster()->MoveFollow(m_creature, 0, 0); + break; + } + } + + void SummonedCreatureJustDied(Creature* pSummoned) override + { + // Set the timer when summoned killed + if (pSummoned->GetEntry() == NPC_TAINTED_ELEMENTAL) + m_uiTaintedElementalTimer = 50000; + } + + void SummonedCreatureDespawn(Creature* pSummoned) override + { + // Set the timer when summoned despawned, if not already killed + if (pSummoned->GetEntry() == NPC_TAINTED_ELEMENTAL) + { + if (!m_uiTaintedElementalTimer) + m_uiTaintedElementalTimer = 50000; + } + } + + void KilledUnit(Unit* /*pVictim*/) override + { + switch (urand(0, 2)) + { + case 0: DoScriptText(SAY_SLAY1, m_creature); break; + case 1: DoScriptText(SAY_SLAY2, m_creature); break; + case 2: DoScriptText(SAY_SLAY3, m_creature); break; + } + } + + void JustDied(Unit* /*pKiller*/) override + { + DoScriptText(SAY_DEATH, m_creature); + + if (m_pInstance) + m_pInstance->SetData(TYPE_LADYVASHJ_EVENT, DONE); + } + + void JustReachedHome() override + { + if (m_pInstance) + m_pInstance->SetData(TYPE_LADYVASHJ_EVENT, FAIL); + } + + bool CanCastShootOrMultishot() + { + // It's not very clear how this should work - requires additional research! + if (DoCastSpellIfCan(m_creature->getVictim(), urand(0, 1) ? SPELL_SHOOT : SPELL_MULTI_SHOT) == CAST_OK) + { + if (urand(0, 2)) + DoScriptText(urand(0, 1) ? SAY_BOWSHOT1 : SAY_BOWSHOT2, m_creature); + + return true; + } + + return false; + } + + // Wrapper to inform the boss that a generator has been deactivated + void DoInformGeneratorStopped() + { + ++m_uiGeneratorsUsed; + + // Remove 5% of health on each generator used + // ToDo: research if this should be done by spell + m_creature->SetHealth(m_creature->GetHealth() - m_creature->GetMaxHealth()*.05f); + + // Check if all generators have been deactivated, or the creature doesn't have the spell barrier aura (in order to avoid eventual aura stacking bugs) + if (m_uiGeneratorsUsed == MAX_SHIELD_GEN || !m_creature->HasAura(SPELL_MAGIC_BARRIER)) + { + DoScriptText(SAY_PHASE3, m_creature); + SetCombatMovement(true); + + if (m_creature->getVictim()) + m_creature->GetMotionMaster()->MoveChase(m_creature->getVictim()); + + m_uiPhase = PHASE_3; + m_uiRangedCheckTimer = 3000; + } + } + + void UpdateAI(const uint32 uiDiff) override + { + if (!m_creature->SelectHostileTarget() || !m_creature->getVictim()) + return; + + if (m_uiPhase == PHASE_1 || m_uiPhase == PHASE_3) + { + if (m_uiShockBlastTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature->getVictim(), SPELL_SHOCK_BLAST) == CAST_OK) + m_uiShockBlastTimer = urand(1000, 15000); + } + else + m_uiShockBlastTimer -= uiDiff; + + if (m_uiStaticChargeTimer < uiDiff) + { + if (Unit* pTarget = m_creature->SelectAttackingTarget(ATTACKING_TARGET_RANDOM, 0, SPELL_STATIC_CHARGE, SELECT_FLAG_IN_MELEE_RANGE)) + { + if (DoCastSpellIfCan(pTarget, SPELL_STATIC_CHARGE) == CAST_OK) + m_uiStaticChargeTimer = urand(10000, 30000); + } + } + else + m_uiStaticChargeTimer -= uiDiff; + + if (m_uiEntangleTimer < uiDiff) + { + if (!m_bEntangle) + { + if (DoCastSpellIfCan(m_creature, SPELL_ENTANGLE) == CAST_OK) + { + m_bEntangle = true; + m_uiEntangleTimer = 5000; + } + } + else + { + // Cast Shoot or Multishot after Entangle + if (CanCastShootOrMultishot()) + { + m_bEntangle = false; + m_uiEntangleTimer = urand(20000, 25000); + } + } + } + else + m_uiEntangleTimer -= uiDiff; + + // Phase 1 abilities + if (m_uiPhase == PHASE_1) + { + // m_uiPhase 2 begins when Vashj hits 70%. She will run to the middle of her platform and surround herself in a shield making her invulerable. + if (m_creature->GetHealthPercent() <= 70.0f) + { + DoScriptText(SAY_PHASE2, m_creature); + + SetCombatMovement(false); + + m_creature->GetMotionMaster()->Clear(); + m_creature->GetMotionMaster()->MovePoint(POINT_MOVE_CENTER, afMiddlePos[0], afMiddlePos[1], afMiddlePos[2]); + + m_uiPhase = PHASE_2; + m_uiRangedCheckTimer = 10000; + } + } + // Phase 3 abilities + else + { + // ToDo: this is not very clear how it should work - requires additional research! + if (m_uiSummonSporebatTimer < uiDiff) + { + m_creature->SummonCreature(NPC_TOXIC_SPOREBAT, afSporebatPos[0], afSporebatPos[1], afSporebatPos[2], afSporebatPos[3], TEMPSUMMON_DEAD_DESPAWN, 0); + + // summon sporebats faster and faster + if (m_uiSummonSporebatStaticTimer > 1000) + m_uiSummonSporebatStaticTimer -= 1000; + + m_uiSummonSporebatTimer = m_uiSummonSporebatStaticTimer; + } + else + m_uiSummonSporebatTimer -= uiDiff; + } + + // If we are within range melee the target + if (m_creature->CanReachWithMeleeAttack(m_creature->getVictim())) + DoMeleeAttackIfReady(); + else + { + // Cast Shoot or Multishot when nobody in melee range + if (m_uiRangedCheckTimer < uiDiff) + { + if (CanCastShootOrMultishot()) + m_uiRangedCheckTimer = urand(1000, 2000); + } + else + m_uiRangedCheckTimer -= uiDiff; + } + } + // Phase 2 only + else + { + if (m_uiForkedLightningTimer < uiDiff) + { + if (Unit* pTarget = m_creature->SelectAttackingTarget(ATTACKING_TARGET_RANDOM, 0)) + { + if (DoCastSpellIfCan(pTarget, SPELL_FORKED_LIGHTNING) == CAST_OK) + m_uiForkedLightningTimer = urand(3000, 6000); + } + } + else + m_uiForkedLightningTimer -= uiDiff; + + if (m_uiEnchantedElementalTimer < uiDiff) + { + uint8 uiPos = urand(0, 7); + m_creature->SummonCreature(NPC_ENCHANTED_ELEMENTAL, afElementPos[uiPos][0], afElementPos[uiPos][1], afElementPos[uiPos][2], afElementPos[uiPos][3], TEMPSUMMON_DEAD_DESPAWN, 0); + + m_uiEnchantedElementalTimer = urand(5000, 10000); + } + else + m_uiEnchantedElementalTimer -= uiDiff; + + if (m_uiTaintedElementalTimer) + { + if (m_uiTaintedElementalTimer <= uiDiff) + { + uint8 uiPos = urand(0, 7); + + m_creature->SummonCreature(NPC_TAINTED_ELEMENTAL, afElementPos[uiPos][0], afElementPos[uiPos][1], afElementPos[uiPos][2], afElementPos[uiPos][3], TEMPSUMMON_DEAD_DESPAWN, 0); + m_uiTaintedElementalTimer = 0; + } + else + m_uiTaintedElementalTimer -= uiDiff; + } + + if (m_uiCoilfangEliteTimer < uiDiff) + { + uint8 uiPos = urand(0, 2); + + m_creature->SummonCreature(NPC_COILFANG_ELITE, afCoilfangElitePos[uiPos][0], afCoilfangElitePos[uiPos][1], afCoilfangElitePos[uiPos][2], afCoilfangElitePos[uiPos][3], TEMPSUMMON_DEAD_DESPAWN, 0); + m_uiCoilfangEliteTimer = urand(45000, 50000); + } + else + m_uiCoilfangEliteTimer -= uiDiff; + + if (m_uiCoilfangStriderTimer < uiDiff) + { + uint8 uiPos = urand(0, 2); + + m_creature->SummonCreature(NPC_COILFANG_STRIDER, afCoilfangStriderPos[uiPos][0], afCoilfangStriderPos[uiPos][1], afCoilfangStriderPos[uiPos][2], afCoilfangStriderPos[uiPos][3], TEMPSUMMON_DEAD_DESPAWN, 0); + m_uiCoilfangStriderTimer = urand(60000, 70000); + } + else + m_uiCoilfangStriderTimer -= uiDiff; + } + } +}; + +struct mob_enchanted_elementalAI : public ScriptedAI +{ + mob_enchanted_elementalAI(Creature* pCreature) : ScriptedAI(pCreature) + { + SetCombatMovement(false); + Reset(); + } + + void Reset() override { } + + void MoveInLineOfSight(Unit* pWho) override + { + // Buff Lady Vashj on range check - spell has script target + if (pWho->GetEntry() == NPC_LADYVASHJ && pWho->IsWithinDistInMap(m_creature, INTERACTION_DISTANCE) && pWho->IsWithinLOSInMap(m_creature)) + DoCastSpellIfCan(m_creature, SPELL_SURGE, CAST_TRIGGERED); + } + + void AttackStart(Unit* /*pWho*/) override { } + void UpdateAI(const uint32 /*uiDiff*/) override { } +}; + +bool GOUse_go_shield_generator(Player* /*pPlayer*/, GameObject* pGo) +{ + // Interrupt Magic barrier spell casting, inform the boss and make the GO unusable + if (ScriptedInstance* pInstance = (ScriptedInstance*)pGo->GetInstanceData()) + { + if (Creature* pGenerator = GetClosestCreatureWithEntry(pGo, NPC_SHIELD_GENERATOR, 5.0f)) + pGenerator->InterruptNonMeleeSpells(false); + + if (Creature* pVashj = pInstance->GetSingleCreatureFromStorage(NPC_LADYVASHJ)) + { + if (boss_lady_vashjAI* pLadyAI = dynamic_cast(pVashj->AI())) + pLadyAI->DoInformGeneratorStopped(); + } + + pGo->SetFlag(GAMEOBJECT_FLAGS, GO_FLAG_NO_INTERACT); + } + + return false; +} + +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); +} + +void AddSC_boss_lady_vashj() +{ + Script* pNewScript; + + pNewScript = new Script; + pNewScript->Name = "boss_lady_vashj"; + pNewScript->GetAI = &GetAI_boss_lady_vashj; + pNewScript->RegisterSelf(); + + pNewScript = new Script; + pNewScript->Name = "mob_enchanted_elemental"; + pNewScript->GetAI = &GetAI_mob_enchanted_elemental; + pNewScript->RegisterSelf(); + + pNewScript = new Script; + pNewScript->Name = "go_shield_generator"; + pNewScript->pGOUse = &GOUse_go_shield_generator; + pNewScript->RegisterSelf(); +} diff --git a/src/modules/SD2/scripts/outland/coilfang_reservoir/serpent_shrine/boss_leotheras_the_blind.cpp b/src/modules/SD2/scripts/outland/coilfang_reservoir/serpent_shrine/boss_leotheras_the_blind.cpp new file mode 100644 index 000000000..fce60b630 --- /dev/null +++ b/src/modules/SD2/scripts/outland/coilfang_reservoir/serpent_shrine/boss_leotheras_the_blind.cpp @@ -0,0 +1,339 @@ +/* Copyright (C) 2006 - 2013 ScriptDev2 + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with 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: 70 +SDComment: Inner Demons NYI; Transition to final phase needs more work. +SDCategory: Coilfang Resevoir, Serpent Shrine Cavern +EndScriptData */ + +#include "precompiled.h" +#include "serpent_shrine.h" + +enum +{ + SAY_AGGRO = -1548009, + SAY_SWITCH_TO_DEMON = -1548010, + SAY_INNER_DEMONS = -1548011, + SAY_DEMON_SLAY1 = -1548012, + SAY_DEMON_SLAY2 = -1548013, + SAY_DEMON_SLAY3 = -1548014, + SAY_NIGHTELF_SLAY1 = -1548015, + SAY_NIGHTELF_SLAY2 = -1548016, + SAY_NIGHTELF_SLAY3 = -1548017, + SAY_FINAL_FORM = -1548018, + SAY_FREE = -1548019, + SAY_DEATH = -1548020, + + SPELL_BERSERK = 27680, + SPELL_WHIRLWIND = 37640, + SPELL_CHAOS_BLAST = 37674, // triggers 37675 + SPELL_INSIDIOUS_WHISPER = 37676, + SPELL_WHISPER_CLEAR = 37922, // purpose unk - probably clear the demons on evade + SPELL_CONS_MADNESS = 37749, // charm spell for the players which didn't kill the inner demons during the demon phase + SPELL_METAMORPHOSIS = 37673, // demon transform spell + + // Inner demons already scripted in eventAI + // SPELL_DEMON_ALIGNMENT = 37713, + // SPELL_SHADOW_BOLT = 39309, + // SPELL_DEMON_LINK = 37716, + + // FACTION_DEMON_1 = 1829, + // FACTION_DEMON_2 = 1830, + // FACTION_DEMON_3 = 1831, + // FACTION_DEMON_4 = 1832, + // FACTION_DEMON_5 = 1833, + + NPC_INNER_DEMON = 21857, + NPC_SHADOW_LEO = 21875 +}; + +struct boss_leotheras_the_blindAI : public ScriptedAI +{ + boss_leotheras_the_blindAI(Creature* pCreature) : ScriptedAI(pCreature) + { + m_pInstance = (ScriptedInstance*)pCreature->GetInstanceData(); + Reset(); + } + + ScriptedInstance* m_pInstance; + + uint32 m_uiBanishTimer; + uint32 m_uiWhirlwindTimer; + uint32 m_uiInnerDemonTimer; + uint32 m_uiSwitchTimer; + uint32 m_uiChaosBlastTimer; + uint32 m_uiFinalFormTimer; + uint32 m_uiEnrageTimer; + + bool m_bDemonForm; + bool m_bIsFinalForm; + + void Reset() override + { + m_uiBanishTimer = 10000; + m_uiWhirlwindTimer = 18500; + m_uiInnerDemonTimer = 27500; + m_uiSwitchTimer = 60000; + m_uiChaosBlastTimer = 0; + m_uiFinalFormTimer = 0; + m_uiEnrageTimer = 10 * MINUTE * IN_MILLISECONDS; + + m_bDemonForm = false; + m_bIsFinalForm = false; + + m_creature->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE); + SetCombatMovement(true); + } + + void Aggro(Unit* /*pWho*/) override + { + DoScriptText(SAY_AGGRO, m_creature); + + if (m_pInstance) + m_pInstance->SetData(TYPE_LEOTHERAS_EVENT, IN_PROGRESS); + } + + void AttackStart(Unit* pWho) override + { + // Don't attack while banished + if (m_creature->HasAura(SPELL_LEOTHERAS_BANISH)) + return; + + ScriptedAI::AttackStart(pWho); + } + + void MoveInLineOfSight(Unit* pWho) override + { + // Don't attack while banished + if (m_creature->HasAura(SPELL_LEOTHERAS_BANISH)) + return; + + ScriptedAI::MoveInLineOfSight(pWho); + } + + void KilledUnit(Unit* pVictim) override + { + if (pVictim->GetTypeId() != TYPEID_PLAYER) + return; + + switch (urand(0, 2)) + { + case 0: DoScriptText(m_bDemonForm ? SAY_DEMON_SLAY1 : SAY_NIGHTELF_SLAY1, m_creature); break; + case 1: DoScriptText(m_bDemonForm ? SAY_DEMON_SLAY2 : SAY_NIGHTELF_SLAY2, m_creature); break; + case 2: DoScriptText(m_bDemonForm ? SAY_DEMON_SLAY3 : SAY_NIGHTELF_SLAY3, m_creature); break; + } + } + + void JustSummoned(Creature* pSummoned) override + { + if (pSummoned->GetEntry() == NPC_SHADOW_LEO) + { + pSummoned->AI()->AttackStart(m_creature->getVictim()); + pSummoned->GetMotionMaster()->MoveFollow(m_creature, 0, 0); + } + } + + void JustDied(Unit* /*pKiller*/) override + { + DoScriptText(SAY_DEATH, m_creature); + + if (m_pInstance) + m_pInstance->SetData(TYPE_LEOTHERAS_EVENT, DONE); + } + + void JustReachedHome() override + { + if (m_pInstance) + m_pInstance->SetData(TYPE_LEOTHERAS_EVENT, FAIL); + } + + void UpdateAI(const uint32 uiDiff) override + { + // Banish the boss before combat + if (m_uiBanishTimer) + { + if (m_uiBanishTimer <= uiDiff) + { + if (DoCastSpellIfCan(m_creature, SPELL_LEOTHERAS_BANISH) == CAST_OK) + m_uiBanishTimer = 0; + } + else + m_uiBanishTimer -= uiDiff; + } + + // Return since we have no target + if (!m_creature->SelectHostileTarget() || !m_creature->getVictim()) + return; + + if (m_uiFinalFormTimer) + { + if (m_uiFinalFormTimer <= uiDiff) + { + DoSpawnCreature(NPC_SHADOW_LEO, 0, 0, 0, 0, TEMPSUMMON_CORPSE_DESPAWN, 0); + m_creature->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE); + + SetCombatMovement(true); + DoStartMovement(m_creature->getVictim()); + m_uiFinalFormTimer = 0; + } + else + m_uiFinalFormTimer -= uiDiff; + + // Wait until we finish the transition + return; + } + + // Human form spells + if (!m_bDemonForm) + { + if (m_uiWhirlwindTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature, SPELL_WHIRLWIND) == CAST_OK) + m_uiWhirlwindTimer = 32000; + } + else + m_uiWhirlwindTimer -= uiDiff; + + if (!m_bIsFinalForm) + { + if (m_uiSwitchTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature, SPELL_METAMORPHOSIS) == CAST_OK) + { + DoScriptText(SAY_SWITCH_TO_DEMON, m_creature); + + SetCombatMovement(false); + m_creature->GetMotionMaster()->Clear(); + m_creature->GetMotionMaster()->MoveIdle(); + + DoResetThreat(); + m_bDemonForm = true; + + m_uiInnerDemonTimer = 27500; + m_uiSwitchTimer = 60000; + } + } + else + m_uiSwitchTimer -= uiDiff; + } + + DoMeleeAttackIfReady(); + } + // Demon form spells + else + { + if (m_uiInnerDemonTimer) + { + if (m_uiInnerDemonTimer <= uiDiff) + { + if (DoCastSpellIfCan(m_creature, SPELL_INSIDIOUS_WHISPER, CAST_INTERRUPT_PREVIOUS) == CAST_OK) + { + DoScriptText(SAY_INNER_DEMONS, m_creature); + m_uiInnerDemonTimer = 0; + } + } + else + m_uiInnerDemonTimer -= uiDiff; + } + + if (m_uiChaosBlastTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature->getVictim(), SPELL_CHAOS_BLAST) == CAST_OK) + m_uiChaosBlastTimer = urand(2000, 3000); + } + else + m_uiChaosBlastTimer -= uiDiff; + + if (m_uiSwitchTimer < uiDiff) + { + if (m_creature->IsNonMeleeSpellCasted(false)) + m_creature->InterruptNonMeleeSpells(false); + + // switch to nightelf form + m_creature->RemoveAurasDueToSpell(SPELL_METAMORPHOSIS); + + SetCombatMovement(true); + DoStartMovement(m_creature->getVictim()); + + DoResetThreat(); + m_bDemonForm = false; + + m_uiWhirlwindTimer = 18500; + m_uiSwitchTimer = 45000; + } + else + m_uiSwitchTimer -= uiDiff; + } + + // Prepare to summon the Shadow of Leotheras + if (!m_bIsFinalForm && m_creature->GetHealthPercent() < 15.0f) + { + DoScriptText(SAY_FINAL_FORM, m_creature); + m_uiFinalFormTimer = 10000; + + // reset him to human form if necessary + if (m_bDemonForm) + { + if (m_creature->IsNonMeleeSpellCasted(false)) + m_creature->InterruptNonMeleeSpells(false); + + // switch to nightelf form + m_creature->RemoveAurasDueToSpell(SPELL_METAMORPHOSIS); + + DoResetThreat(); + m_bDemonForm = false; + } + + m_creature->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE); + m_creature->HandleEmote(EMOTE_ONESHOT_KNEEL); + + SetCombatMovement(false); + m_creature->GetMotionMaster()->Clear(); + m_creature->GetMotionMaster()->MoveIdle(); + + m_bIsFinalForm = true; + } + + // Hard enrage timer + if (m_uiEnrageTimer) + { + if (m_uiEnrageTimer <= uiDiff) + { + if (DoCastSpellIfCan(m_creature, SPELL_BERSERK) == CAST_OK) + m_uiEnrageTimer = 0; + } + else + m_uiEnrageTimer -= uiDiff; + } + } +}; + +CreatureAI* GetAI_boss_leotheras_the_blind(Creature* pCreature) +{ + return new boss_leotheras_the_blindAI(pCreature); +} + +void AddSC_boss_leotheras_the_blind() +{ + Script* pNewScript; + + pNewScript = new Script; + pNewScript->Name = "boss_leotheras_the_blind"; + pNewScript->GetAI = &GetAI_boss_leotheras_the_blind; + pNewScript->RegisterSelf(); +} diff --git a/src/modules/SD2/scripts/outland/coilfang_reservoir/serpent_shrine/boss_morogrim_tidewalker.cpp b/src/modules/SD2/scripts/outland/coilfang_reservoir/serpent_shrine/boss_morogrim_tidewalker.cpp new file mode 100644 index 000000000..53718f6ca --- /dev/null +++ b/src/modules/SD2/scripts/outland/coilfang_reservoir/serpent_shrine/boss_morogrim_tidewalker.cpp @@ -0,0 +1,294 @@ +/* Copyright (C) 2006 - 2013 ScriptDev2 + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with 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 Globule script is not complete - requires additional research. +SDCategory: Coilfang Resevoir, Serpent Shrine Cavern +EndScriptData */ + +#include "precompiled.h" +#include "serpent_shrine.h" + +enum +{ + SAY_AGGRO = -1548030, + SAY_SUMMON1 = -1548031, + SAY_SUMMON2 = -1548032, + SAY_SUMMON_BUBL1 = -1548033, + SAY_SUMMON_BUBL2 = -1548034, + SAY_SLAY1 = -1548035, + SAY_SLAY2 = -1548036, + SAY_SLAY3 = -1548037, + SAY_DEATH = -1548038, + EMOTE_WATERY_GRAVE = -1548039, + EMOTE_EARTHQUAKE = -1548040, + EMOTE_WATERY_GLOBULES = -1548041, + + SPELL_DOUBLE_ATTACK = 18943, + SPELL_TIDAL_WAVE = 37730, + SPELL_EARTHQUAKE = 37764, + SPELL_WATERY_GRAVE = 38028, + // SPELL_WATERY_GRAVE_EXPLOSION = 38049, // spell purpose unk + + SPELL_SUMMON_MURLOC_A6 = 39813, + SPELL_SUMMON_MURLOC_A7 = 39814, + SPELL_SUMMON_MURLOC_A8 = 39815, + SPELL_SUMMON_MURLOC_A9 = 39816, + SPELL_SUMMON_MURLOC_A10 = 39817, + + SPELL_SUMMON_MURLOC_B6 = 39818, + SPELL_SUMMON_MURLOC_B7 = 39819, + SPELL_SUMMON_MURLOC_B8 = 39820, + SPELL_SUMMON_MURLOC_B9 = 39821, + SPELL_SUMMON_MURLOC_B10 = 39822, + + SPELL_SUMMON_GLOBULE_1 = 37854, + SPELL_SUMMON_GLOBULE_2 = 37858, + SPELL_SUMMON_GLOBULE_3 = 37860, + SPELL_SUMMON_GLOBULE_4 = 37861, + + SPELL_WATER_GLOBULE_NEW_TARGET = 39848, // spell requires additional research and probably core or script support + + NPC_WATER_GLOBULE = 21913, + NPC_TIDEWALKER_LURKER = 21920 +}; + +static const uint32 m_auiSpellWateryGraveTeleport[] = { 37850, 38023, 38024, 38025 }; + +struct boss_morogrim_tidewalkerAI : public ScriptedAI +{ + boss_morogrim_tidewalkerAI(Creature* pCreature) : ScriptedAI(pCreature) + { + m_pInstance = (ScriptedInstance*)pCreature->GetInstanceData(); + Reset(); + } + + ScriptedInstance* m_pInstance; + + uint32 m_uiTidalWaveTimer; + uint32 m_uiWateryGraveTimer; + uint32 m_uiEarthquakeTimer; + uint32 m_uiWateryGlobulesTimer; + uint8 m_uiGraveIndex; + + bool m_bIsPhase2; + + void Reset() override + { + m_uiTidalWaveTimer = 10000; + m_uiWateryGraveTimer = 30000; + m_uiEarthquakeTimer = 40000; + m_uiWateryGlobulesTimer = 0; + m_uiGraveIndex = 0; + + m_bIsPhase2 = false; + + DoCastSpellIfCan(m_creature, SPELL_DOUBLE_ATTACK); + } + + void Aggro(Unit* /*pWho*/) override + { + DoScriptText(SAY_AGGRO, m_creature); + + if (m_pInstance) + m_pInstance->SetData(TYPE_MOROGRIM_EVENT, IN_PROGRESS); + } + + void KilledUnit(Unit* /*pVictim*/) override + { + switch (urand(0, 2)) + { + case 0: DoScriptText(SAY_SLAY1, m_creature); break; + case 1: DoScriptText(SAY_SLAY2, m_creature); break; + case 2: DoScriptText(SAY_SLAY3, m_creature); break; + } + } + + void JustDied(Unit* /*pKiller*/) override + { + DoScriptText(SAY_DEATH, m_creature); + + if (m_pInstance) + m_pInstance->SetData(TYPE_MOROGRIM_EVENT, DONE); + } + + void JustReachedHome() override + { + if (m_pInstance) + m_pInstance->SetData(TYPE_MOROGRIM_EVENT, FAIL); + } + + void JustSummoned(Creature* pSummoned) override + { + if (pSummoned->GetEntry() == NPC_TIDEWALKER_LURKER) + { + if (Unit* pTarget = m_creature->SelectAttackingTarget(ATTACKING_TARGET_RANDOM, 0)) + pSummoned->AI()->AttackStart(pTarget); + } + else if (pSummoned->GetEntry() == NPC_WATER_GLOBULE) + { + if (Unit* pTarget = m_creature->SelectAttackingTarget(ATTACKING_TARGET_RANDOM, 0)) + pSummoned->GetMotionMaster()->MoveFollow(pTarget, 0.0f, 0.0f); + } + } + + void SpellHitTarget(Unit* pTarget, const SpellEntry* pSpell) override + { + // Handle watery grave teleport - each player hit has his own teleport spell + if (pSpell->Id == SPELL_WATERY_GRAVE && pTarget->GetTypeId() == TYPEID_PLAYER) + { + DoCastSpellIfCan(pTarget, m_auiSpellWateryGraveTeleport[m_uiGraveIndex], CAST_TRIGGERED); + ++m_uiGraveIndex; + } + } + + void UpdateAI(const uint32 uiDiff) override + { + if (!m_creature->SelectHostileTarget() || !m_creature->getVictim()) + return; + + if (m_uiEarthquakeTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature, SPELL_EARTHQUAKE) == CAST_OK) + { + DoScriptText(EMOTE_EARTHQUAKE, m_creature); + DoScriptText(urand(0, 1) ? SAY_SUMMON1 : SAY_SUMMON2, m_creature); + + // summon murlocs - north + DoCastSpellIfCan(m_creature, SPELL_SUMMON_MURLOC_A6, CAST_TRIGGERED); + DoCastSpellIfCan(m_creature, SPELL_SUMMON_MURLOC_A7, CAST_TRIGGERED); + DoCastSpellIfCan(m_creature, SPELL_SUMMON_MURLOC_A8, CAST_TRIGGERED); + DoCastSpellIfCan(m_creature, SPELL_SUMMON_MURLOC_A9, CAST_TRIGGERED); + DoCastSpellIfCan(m_creature, SPELL_SUMMON_MURLOC_A10, CAST_TRIGGERED); + + // summon murlocs - south + DoCastSpellIfCan(m_creature, SPELL_SUMMON_MURLOC_B6, CAST_TRIGGERED); + DoCastSpellIfCan(m_creature, SPELL_SUMMON_MURLOC_B7, CAST_TRIGGERED); + DoCastSpellIfCan(m_creature, SPELL_SUMMON_MURLOC_B8, CAST_TRIGGERED); + DoCastSpellIfCan(m_creature, SPELL_SUMMON_MURLOC_B9, CAST_TRIGGERED); + DoCastSpellIfCan(m_creature, SPELL_SUMMON_MURLOC_B10, CAST_TRIGGERED); + + m_uiEarthquakeTimer = 50000; + } + } + else + m_uiEarthquakeTimer -= uiDiff; + + if (m_uiTidalWaveTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature, SPELL_TIDAL_WAVE) == CAST_OK) + m_uiTidalWaveTimer = urand(20000, 25000); + } + else + m_uiTidalWaveTimer -= uiDiff; + + // Phase one specific spells + if (!m_bIsPhase2) + { + if (m_uiWateryGraveTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature, SPELL_WATERY_GRAVE) == CAST_OK) + { + DoScriptText(EMOTE_WATERY_GRAVE, m_creature); + m_uiWateryGraveTimer = 30000; + m_uiGraveIndex = 0; + } + } + else + m_uiWateryGraveTimer -= uiDiff; + + // Start Phase2 below 25% hp + if (m_creature->GetHealthPercent() < 25.0f) + m_bIsPhase2 = true; + } + else + { + if (m_uiWateryGlobulesTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature, SPELL_SUMMON_GLOBULE_1) == CAST_OK) + { + DoScriptText(EMOTE_WATERY_GLOBULES, m_creature); + DoScriptText(urand(0, 1) ? SAY_SUMMON_BUBL1 : SAY_SUMMON_BUBL2, m_creature); + + DoCastSpellIfCan(m_creature, SPELL_SUMMON_GLOBULE_2, CAST_TRIGGERED); + DoCastSpellIfCan(m_creature, SPELL_SUMMON_GLOBULE_3, CAST_TRIGGERED); + DoCastSpellIfCan(m_creature, SPELL_SUMMON_GLOBULE_4, CAST_TRIGGERED); + + m_uiWateryGlobulesTimer = 25000; + } + } + else + m_uiWateryGlobulesTimer -= uiDiff; + } + + DoMeleeAttackIfReady(); + } +}; + +struct mob_water_globuleAI : public ScriptedAI +{ + mob_water_globuleAI(Creature* pCreature) : ScriptedAI(pCreature) { Reset(); } + + uint32 m_uiTargetTimer; + + void Reset() override + { + m_uiTargetTimer = 10000; + } + + void MoveInLineOfSight(Unit* /*pWho*/) override + { + // ToDo: cast damage spell here, after proper checks are done + } + + void UpdateAI(const uint32 uiDiff) override + { + if (m_uiTargetTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature, SPELL_WATER_GLOBULE_NEW_TARGET) == CAST_OK) + m_uiTargetTimer = 10000; + } + else + m_uiTargetTimer -= uiDiff; + } +}; + +CreatureAI* GetAI_boss_morogrim_tidewalker(Creature* pCreature) +{ + return new boss_morogrim_tidewalkerAI(pCreature); +} + +CreatureAI* GetAI_mob_water_globule(Creature* pCreature) +{ + return new mob_water_globuleAI(pCreature); +} + +void AddSC_boss_morogrim_tidewalker() +{ + Script* pNewScript; + + pNewScript = new Script; + pNewScript->Name = "boss_morogrim_tidewalker"; + pNewScript->GetAI = &GetAI_boss_morogrim_tidewalker; + pNewScript->RegisterSelf(); + + pNewScript = new Script; + pNewScript->Name = "mob_water_globule"; + pNewScript->GetAI = &GetAI_mob_water_globule; + pNewScript->RegisterSelf(); +} diff --git a/src/modules/SD2/scripts/outland/coilfang_reservoir/serpent_shrine/boss_the_lurker_below.cpp b/src/modules/SD2/scripts/outland/coilfang_reservoir/serpent_shrine/boss_the_lurker_below.cpp new file mode 100644 index 000000000..0df5ccc2d --- /dev/null +++ b/src/modules/SD2/scripts/outland/coilfang_reservoir/serpent_shrine/boss_the_lurker_below.cpp @@ -0,0 +1,310 @@ +/* Copyright (C) 2006 - 2013 ScriptDev2 + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with 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: 90 +SDComment: Spawn animation NYI; Timers may need adjustments. +SDCategory: Coilfang Resevoir, Serpent Shrine Cavern +EndScriptData */ + +#include "precompiled.h" +#include "serpent_shrine.h" + +enum +{ + EMOTE_DEEP_BREATH = -1548056, + + SPELL_LURKER_SPAWN_TRIGGER = 54587, + SPELL_WHIRL = 37660, + SPELL_GEYSER = 37478, + SPELL_SPOUT = 37431, // trigger spells 37429, 37430 + SPELL_SPOUT_LEFT = 37429, + SPELL_SPOUT_RIGHT = 37430, + SPELL_WATERBOLT = 37138, + SPELL_SUBMERGE = 37550, + + NPC_COILFANG_AMBUSHER = 21865, + NPC_COILFANG_GUARDIAN = 21873, + + MAX_SUBMERGE_ADDS = 9, +}; + +enum Phases +{ + PHASE_EMERGEING = 0, // TODO unused for now + PHASE_NORMAL = 1, + PHASE_SPOUT = 2, + PHASE_SUBMERGED = 3, +}; + +struct AddsLocations +{ + uint32 uiEntry; + float fX, fY, fZ; +}; + +// Coords are guesswork +static const AddsLocations aLurkerLoc[MAX_SUBMERGE_ADDS] = +{ + {NPC_COILFANG_AMBUSHER, 2.855f, -459.823f, -19.18f}, + {NPC_COILFANG_AMBUSHER, 12.458f, -466.042f, -19.18f}, + {NPC_COILFANG_AMBUSHER, 51.366f, -460.836f, -19.18f}, + {NPC_COILFANG_AMBUSHER, 62.597f, -457.433f, -19.18f}, + {NPC_COILFANG_AMBUSHER, 77.607f, -384.302f, -19.18f}, + {NPC_COILFANG_AMBUSHER, 63.897f, -378.984f, -19.18f}, + {NPC_COILFANG_GUARDIAN, 34.447f, -387.333f, -19.18f}, + {NPC_COILFANG_GUARDIAN, 14.388f, -423.468f, -19.62f}, + {NPC_COILFANG_GUARDIAN, 42.471f, -445.115f, -19.76f}, +}; + +struct boss_the_lurker_belowAI : public Scripted_NoMovementAI +{ + boss_the_lurker_belowAI(Creature* pCreature) : Scripted_NoMovementAI(pCreature) + { + m_pInstance = (ScriptedInstance*)pCreature->GetInstanceData(); + Reset(); + } + + ScriptedInstance* m_pInstance; + + Phases m_uiPhase; + uint32 m_uiPhaseChangeTimer; + + uint32 m_uiWhirlTimer; + uint32 m_uiGeyserTimer; + uint32 m_uiSpoutTimer; + uint32 m_uiSpoutEndTimer; + + void Reset() override + { + m_uiPhase = PHASE_NORMAL; + m_uiPhaseChangeTimer = 90000; + + DoResetCombatTimers(); + } + + void DoResetCombatTimers() + { + m_uiWhirlTimer = 18000; + m_uiGeyserTimer = 50000; + m_uiSpoutTimer = 42000; + m_uiSpoutEndTimer = 23000; + } + + void JustReachedHome() override + { + if (m_pInstance) + m_pInstance->SetData(TYPE_THELURKER_EVENT, FAIL); + + m_creature->ForcedDespawn(); + } + + void JustDied(Unit* /*pVictim*/) override + { + if (m_pInstance) + m_pInstance->SetData(TYPE_THELURKER_EVENT, DONE); + } + + void JustSummoned(Creature* pSummoned) override + { + // Allow the adds to attack + pSummoned->SetInCombatWithZone(); + } + + // Wrapper to summon adds in phase 2 + void DoSummonCoilfangNaga() + { + for (uint8 i = 0; i < MAX_SUBMERGE_ADDS; ++i) + m_creature->SummonCreature(aLurkerLoc[i].uiEntry, aLurkerLoc[i].fX, aLurkerLoc[i].fY, aLurkerLoc[i].fZ, 0, TEMPSUMMON_DEAD_DESPAWN, 0); + } + + // Custom threat management + bool SelectHostileTarget() + { + Unit* pTarget = NULL; + Unit* pOldTarget = m_creature->getVictim(); + + if (!m_creature->GetThreatManager().isThreatListEmpty()) + pTarget = m_creature->GetThreatManager().getHostileTarget(); + + if (pTarget) + { + if (pOldTarget != pTarget && m_uiPhase != PHASE_SPOUT) + AttackStart(pTarget); + + // Set victim to old target (if not while Spout) + if (pOldTarget && pOldTarget->IsAlive() && m_uiPhase != PHASE_SPOUT) + { + m_creature->SetTargetGuid(pOldTarget->GetObjectGuid()); + m_creature->SetInFront(pOldTarget); + } + + return true; + } + + // Will call EnterEvadeMode if fit + return m_creature->SelectHostileTarget(); + } + + void UpdateAI(const uint32 uiDiff) override + { + if (!SelectHostileTarget()) + return; + + switch (m_uiPhase) + { + case PHASE_SPOUT: + + if (m_uiSpoutEndTimer < uiDiff) + { + // Remove rotation auras + m_creature->RemoveAurasDueToSpell(SPELL_SPOUT_LEFT); + m_creature->RemoveAurasDueToSpell(SPELL_SPOUT_RIGHT); + + m_uiPhase = PHASE_NORMAL; + m_uiSpoutEndTimer = 23000; + } + else + m_uiSpoutEndTimer -= uiDiff; + + // no break; + case PHASE_NORMAL: + + // Count the first phase during Spout too + if (m_uiPhaseChangeTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature, SPELL_SUBMERGE) == CAST_OK) + { + DoSummonCoilfangNaga(); + m_creature->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE); + m_uiPhase = PHASE_SUBMERGED; + m_uiPhaseChangeTimer = MINUTE * IN_MILLISECONDS; + } + } + else + m_uiPhaseChangeTimer -= uiDiff; + + // Combat spells are only in normal phase + if (m_uiPhase == PHASE_NORMAL) + { + if (m_uiSpoutTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature, SPELL_SPOUT) == CAST_OK) + { + DoScriptText(EMOTE_DEEP_BREATH, m_creature); + + // Remove the target focus but allow the boss to face the current victim + m_creature->SetTargetGuid(ObjectGuid()); + m_creature->SetFacingToObject(m_creature->getVictim()); + + m_uiPhase = PHASE_SPOUT; + m_uiSpoutTimer = 30000; + } + } + else + m_uiSpoutTimer -= uiDiff; + + if (m_uiWhirlTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature, SPELL_WHIRL) == CAST_OK) + m_uiWhirlTimer = 18000; + } + else + m_uiWhirlTimer -= uiDiff; + + if (m_uiGeyserTimer < uiDiff) + { + if (Unit* pTarget = m_creature->SelectAttackingTarget(ATTACKING_TARGET_RANDOM, 0)) + { + if (DoCastSpellIfCan(pTarget, SPELL_GEYSER) == CAST_OK) + m_uiGeyserTimer = urand(50000, 60000); + } + } + else + m_uiGeyserTimer -= uiDiff; + + // If we are within range melee the target + if (m_creature->CanReachWithMeleeAttack(m_creature->getVictim())) + DoMeleeAttackIfReady(); + // Spam Waterbolt spell when not tanked + else + { + if (!m_creature->IsNonMeleeSpellCasted(false)) + { + if (Unit* pTarget = m_creature->SelectAttackingTarget(ATTACKING_TARGET_RANDOM, 0)) + DoCastSpellIfCan(pTarget, SPELL_WATERBOLT); + } + } + } + + break; + case PHASE_SUBMERGED: + + if (m_uiPhaseChangeTimer < uiDiff) + { + DoResetCombatTimers(); + m_uiPhase = PHASE_NORMAL; + m_creature->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE); + m_creature->RemoveAurasDueToSpell(SPELL_SUBMERGE); + m_uiPhaseChangeTimer = 2 * MINUTE * IN_MILLISECONDS; + } + else + m_uiPhaseChangeTimer -= uiDiff; + + break; + } + } +}; + +CreatureAI* GetAI_boss_the_lurker_below(Creature* pCreature) +{ + return new boss_the_lurker_belowAI(pCreature); +} + +// Cast the spell that should summon the Lurker-Below +bool GOUse_go_strange_pool(Player* pPlayer, GameObject* pGo) +{ + // There is some chance to fish The Lurker Below, sources are from 20s to 10minutes, average 5min => 20 tries, hence 5% + if (urand(0, 99) < 5) + { + if (ScriptedInstance* pInstance = (ScriptedInstance*)pGo->GetInstanceData()) + { + if (pInstance->GetData(TYPE_THELURKER_EVENT) == NOT_STARTED || pInstance->GetData(TYPE_THELURKER_EVENT) == FAIL) + { + pPlayer->CastSpell(pPlayer, SPELL_LURKER_SPAWN_TRIGGER, true); + pInstance->SetData(TYPE_THELURKER_EVENT, IN_PROGRESS); + return true; + } + } + } + return false; +} + +void AddSC_boss_the_lurker_below() +{ + Script* pNewScript; + + pNewScript = new Script; + pNewScript->Name = "boss_the_lurker_below"; + pNewScript->GetAI = &GetAI_boss_the_lurker_below; + pNewScript->RegisterSelf(); + + pNewScript = new Script; + pNewScript->Name = "go_strange_pool"; + pNewScript->pGOUse = &GOUse_go_strange_pool; + pNewScript->RegisterSelf(); +} diff --git a/src/modules/SD2/scripts/outland/coilfang_reservoir/serpent_shrine/instance_serpent_shrine.cpp b/src/modules/SD2/scripts/outland/coilfang_reservoir/serpent_shrine/instance_serpent_shrine.cpp new file mode 100644 index 000000000..c6c626452 --- /dev/null +++ b/src/modules/SD2/scripts/outland/coilfang_reservoir/serpent_shrine/instance_serpent_shrine.cpp @@ -0,0 +1,265 @@ +/* Copyright (C) 2006 - 2013 ScriptDev2 + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along 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: 90 +SDComment: +SDCategory: Coilfang Resevoir, Serpent Shrine Cavern +EndScriptData */ + +#include "precompiled.h" +#include "serpent_shrine.h" + +/* Serpentshrine cavern encounters: +0 - Hydross The Unstable event +1 - Leotheras The Blind Event +2 - The Lurker Below Event +3 - Fathom-Lord Karathress Event +4 - Morogrim Tidewalker Event +5 - Lady Vashj Event +*/ + +instance_serpentshrine_cavern::instance_serpentshrine_cavern(Map* pMap) : ScriptedInstance(pMap), + m_uiSpellBinderCount(0) +{ + Initialize(); +} + +void instance_serpentshrine_cavern::Initialize() +{ + memset(&m_auiEncounter, 0, sizeof(m_auiEncounter)); +} + +bool instance_serpentshrine_cavern::IsEncounterInProgress() const +{ + for (uint8 i = 0; i < MAX_ENCOUNTER; ++i) + { + if (m_auiEncounter[i] == IN_PROGRESS) + return true; + } + + return false; +} + +void instance_serpentshrine_cavern::OnCreatureCreate(Creature* pCreature) +{ + switch (pCreature->GetEntry()) + { + case NPC_LADYVASHJ: + case NPC_SHARKKIS: + case NPC_TIDALVESS: + case NPC_CARIBDIS: + case NPC_LEOTHERAS: + m_mNpcEntryGuidStore[pCreature->GetEntry()] = pCreature->GetObjectGuid(); + break; + case NPC_GREYHEART_SPELLBINDER: + m_lSpellBindersGUIDList.push_back(pCreature->GetObjectGuid()); + break; + case NPC_HYDROSS_BEAM_HELPER: + m_lBeamHelpersGUIDList.push_back(pCreature->GetObjectGuid()); + break; + case NPC_SHIELD_GENERATOR: + m_lShieldGeneratorGUIDList.push_back(pCreature->GetObjectGuid()); + break; + case NPC_COILFANG_PRIESTESS: + case NPC_COILFANG_SHATTERER: + case NPC_VASHJIR_HONOR_GUARD: + case NPC_GREYHEART_TECHNICIAN: + // Filter only the mobs spawned on the platforms + if (pCreature->GetPositionZ() > 0) + m_sPlatformMobsGUIDSet.insert(pCreature->GetObjectGuid()); + break; + } +} + +void instance_serpentshrine_cavern::OnObjectCreate(GameObject* pGo) +{ + switch (pGo->GetEntry()) + { + case GO_SHIELD_GENERATOR_1: + case GO_SHIELD_GENERATOR_2: + case GO_SHIELD_GENERATOR_3: + case GO_SHIELD_GENERATOR_4: + m_mGoEntryGuidStore[pGo->GetEntry()] = pGo->GetObjectGuid(); + break; + } +} + +void instance_serpentshrine_cavern::SetData(uint32 uiType, uint32 uiData) +{ + switch (uiType) + { + case TYPE_HYDROSS_EVENT: + m_auiEncounter[uiType] = uiData; + break; + case TYPE_LEOTHERAS_EVENT: + m_auiEncounter[uiType] = uiData; + if (uiData == FAIL) + { + for (GuidList::const_iterator itr = m_lSpellBindersGUIDList.begin(); itr != m_lSpellBindersGUIDList.end(); ++itr) + { + if (Creature* pSpellBinder = instance->GetCreature(*itr)) + pSpellBinder->Respawn(); + } + + m_uiSpellBinderCount = 0; + } + break; + case TYPE_THELURKER_EVENT: + case TYPE_KARATHRESS_EVENT: + case TYPE_MOROGRIM_EVENT: + m_auiEncounter[uiType] = uiData; + break; + case TYPE_LADYVASHJ_EVENT: + m_auiEncounter[uiType] = uiData; + if (uiData == FAIL) + { + // interrupt the shield + for (GuidList::const_iterator itr = m_lShieldGeneratorGUIDList.begin(); itr != m_lShieldGeneratorGUIDList.end(); ++itr) + { + if (Creature* pGenerator = instance->GetCreature(*itr)) + pGenerator->InterruptNonMeleeSpells(false); + } + + // reset generators + DoToggleGameObjectFlags(GO_SHIELD_GENERATOR_1, GO_FLAG_NO_INTERACT, false); + DoToggleGameObjectFlags(GO_SHIELD_GENERATOR_2, GO_FLAG_NO_INTERACT, false); + DoToggleGameObjectFlags(GO_SHIELD_GENERATOR_3, GO_FLAG_NO_INTERACT, false); + DoToggleGameObjectFlags(GO_SHIELD_GENERATOR_4, GO_FLAG_NO_INTERACT, false); + } + 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_strInstData = saveStream.str(); + + SaveToDB(); + OUT_SAVE_INST_DATA_COMPLETE; + } +} + +void instance_serpentshrine_cavern::Load(const char* chrIn) +{ + if (!chrIn) + { + 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]; + + for (uint8 i = 0; i < MAX_ENCOUNTER; ++i) + { + if (m_auiEncounter[i] == IN_PROGRESS) + m_auiEncounter[i] = NOT_STARTED; + } + + OUT_LOAD_INST_DATA_COMPLETE; +} + +uint32 instance_serpentshrine_cavern::GetData(uint32 uiType) const +{ + if (uiType < MAX_ENCOUNTER) + return m_auiEncounter[uiType]; + + return 0; +} + +void instance_serpentshrine_cavern::SetData64(uint32 uiData, uint64 uiGuid) +{ + // Note: this is handled in Acid. The purpose is check which npc from the platform set is alive + // The function is triggered by eventAI on generic timer + if (uiData == DATA_WATERSTATE_EVENT) + { + if (m_sPlatformMobsGUIDSet.find(ObjectGuid(uiGuid)) != m_sPlatformMobsGUIDSet.end()) + m_sPlatformMobsAliveGUIDSet.insert(ObjectGuid(uiGuid)); + } +} + +bool instance_serpentshrine_cavern::CheckConditionCriteriaMeet(Player const* pPlayer, uint32 uiInstanceConditionId, WorldObject const* pConditionSource, uint32 conditionSourceType) const +{ + switch (uiInstanceConditionId) + { + case INSTANCE_CONDITION_ID_LURKER: + return GetData(TYPE_THELURKER_EVENT) != DONE; + case INSTANCE_CONDITION_ID_SCALDING_WATER: + return m_sPlatformMobsAliveGUIDSet.empty(); + } + + script_error_log("instance_serpentshrine_cavern::CheckConditionCriteriaMeet called with unsupported Id %u. Called with param plr %s, src %s, condition source type %u", + uiInstanceConditionId, pPlayer ? pPlayer->GetGuidStr().c_str() : "NULL", pConditionSource ? pConditionSource->GetGuidStr().c_str() : "NULL", conditionSourceType); + return false; +} + +void instance_serpentshrine_cavern::OnCreatureEnterCombat(Creature* pCreature) +{ + // Interrupt spell casting on aggro + if (pCreature->GetEntry() == NPC_GREYHEART_SPELLBINDER) + pCreature->InterruptNonMeleeSpells(false); +} + +void instance_serpentshrine_cavern::OnCreatureDeath(Creature* pCreature) +{ + switch (pCreature->GetEntry()) + { + case NPC_GREYHEART_SPELLBINDER: + ++m_uiSpellBinderCount; + + if (m_uiSpellBinderCount == MAX_SPELLBINDERS) + { + if (Creature* pLeotheras = GetSingleCreatureFromStorage(NPC_LEOTHERAS)) + { + pLeotheras->RemoveAurasDueToSpell(SPELL_LEOTHERAS_BANISH); + pLeotheras->SetInCombatWithZone(); + } + } + break; + case NPC_COILFANG_PRIESTESS: + case NPC_COILFANG_SHATTERER: + case NPC_VASHJIR_HONOR_GUARD: + case NPC_GREYHEART_TECHNICIAN: + if (m_sPlatformMobsGUIDSet.find(pCreature->GetObjectGuid()) != m_sPlatformMobsGUIDSet.end()) + m_sPlatformMobsAliveGUIDSet.erase(pCreature->GetObjectGuid()); + break; + } +} + +InstanceData* GetInstanceData_instance_serpentshrine_cavern(Map* pMap) +{ + return new instance_serpentshrine_cavern(pMap); +} + +void AddSC_instance_serpentshrine_cavern() +{ + Script* pNewScript; + + pNewScript = new Script; + pNewScript->Name = "instance_serpent_shrine"; + pNewScript->GetInstanceData = &GetInstanceData_instance_serpentshrine_cavern; + pNewScript->RegisterSelf(); +} diff --git a/src/modules/SD2/scripts/outland/coilfang_reservoir/serpent_shrine/serpent_shrine.h b/src/modules/SD2/scripts/outland/coilfang_reservoir/serpent_shrine/serpent_shrine.h new file mode 100644 index 000000000..0ca8298c3 --- /dev/null +++ b/src/modules/SD2/scripts/outland/coilfang_reservoir/serpent_shrine/serpent_shrine.h @@ -0,0 +1,96 @@ +/* Copyright (C) 2006 - 2013 ScriptDev2 + * This program is free software licensed under GPL version 2 + * Please see the included DOCS/LICENSE.TXT for more information */ + +#ifndef DEF_SERPENT_SHRINE_H +#define DEF_SERPENT_SHRINE_H + +enum +{ + MAX_ENCOUNTER = 6, + MAX_SPELLBINDERS = 3, + + TYPE_HYDROSS_EVENT = 0, + TYPE_KARATHRESS_EVENT = 1, + TYPE_LADYVASHJ_EVENT = 2, + TYPE_LEOTHERAS_EVENT = 3, + TYPE_MOROGRIM_EVENT = 4, + TYPE_THELURKER_EVENT = 5, + + DATA_WATERSTATE_EVENT = 1, // DO NOT CHANGE! Used by Acid. - used to check the mobs for the water event. + + // NPC_KARATHRESS = 21214, + NPC_CARIBDIS = 21964, + NPC_SHARKKIS = 21966, + NPC_TIDALVESS = 21965, + NPC_LEOTHERAS = 21215, + NPC_LADYVASHJ = 21212, + NPC_GREYHEART_SPELLBINDER = 21806, + NPC_HYDROSS_BEAM_HELPER = 21933, + NPC_SHIELD_GENERATOR = 19870, + + // waterstate event related + NPC_COILFANG_PRIESTESS = 21220, + NPC_COILFANG_SHATTERER = 21301, + NPC_VASHJIR_HONOR_GUARD = 21218, + NPC_GREYHEART_TECHNICIAN = 21263, + + GO_SHIELD_GENERATOR_1 = 185051, + GO_SHIELD_GENERATOR_2 = 185052, + GO_SHIELD_GENERATOR_3 = 185053, + GO_SHIELD_GENERATOR_4 = 185054, + + // Objects and doors no longer used since 2.4.0 + // GO_CONSOLE_HYDROSS = 185117, + // GO_CONSOLE_LURKER = 185118, + // GO_CONSOLE_LEOTHERAS = 185115, + // GO_CONSOLE_KARATHRESS = 185114, + // GO_CONSOLE_MOROGRIM = 185116, + // GO_CONSOLE_VASHJ = 184568, + // GO_BRIDGE_PART_1 = 184203, + // GO_BRIDGE_PART_2 = 184204, + // GO_BRIDGE_PART_3 = 184205, + + SPELL_LEOTHERAS_BANISH = 37546, +}; + +class instance_serpentshrine_cavern : public ScriptedInstance +{ + public: + instance_serpentshrine_cavern(Map* pMap); + + void Initialize() override; + bool IsEncounterInProgress() const override; + + void OnCreatureCreate(Creature* pCreature) override; + void OnCreatureEnterCombat(Creature* pCreature) override; + void OnCreatureDeath(Creature* pCreature) override; + void OnObjectCreate(GameObject* pGo) override; + + void SetData(uint32 uiType, uint32 uiData) override; + uint32 GetData(uint32 uiType) const override; + + void SetData64(uint32 uiType, uint64 uiGuid) override; + + bool CheckConditionCriteriaMeet(Player const* pPlayer, uint32 uiInstanceConditionId, WorldObject const* pConditionSource, uint32 conditionSourceType) const override; + + void GetBeamHelpersGUIDList(GuidList& lList) { lList = m_lBeamHelpersGUIDList; } + void GetShieldGeneratorsGUIDList(GuidList& lList) { lList = m_lShieldGeneratorGUIDList; } + + const char* Save() const override { return m_strInstData.c_str(); } + void Load(const char* chrIn) override; + + private: + uint32 m_auiEncounter[MAX_ENCOUNTER]; + std::string m_strInstData; + + uint32 m_uiSpellBinderCount; + + GuidList m_lSpellBindersGUIDList; + GuidList m_lBeamHelpersGUIDList; + GuidList m_lShieldGeneratorGUIDList; + GuidSet m_sPlatformMobsGUIDSet; + GuidSet m_sPlatformMobsAliveGUIDSet; +}; + +#endif diff --git a/src/modules/SD2/scripts/outland/coilfang_reservoir/slave_pens/boss_ahune.cpp b/src/modules/SD2/scripts/outland/coilfang_reservoir/slave_pens/boss_ahune.cpp new file mode 100644 index 000000000..79a336c61 --- /dev/null +++ b/src/modules/SD2/scripts/outland/coilfang_reservoir/slave_pens/boss_ahune.cpp @@ -0,0 +1,412 @@ +/* Copyright (C) 2006 - 2013 ScriptDev2 + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +/* ScriptData +SDName: boss_ahune +SD%Complete: 75 +SDComment: Submerged phase visual spells NYI; they require additional research. +SDCategory: Slave Pens +EndScriptData */ + +#include "precompiled.h" +#include "TemporarySummon.h" + +enum +{ + // general spells + SPELL_AHUNES_SHIELD = 45954, + SPELL_SPANKY_HANDS = 46146, // procs on melee hit; should proc 46430 but currently is not used because of the invalid proc flag + SPELL_SYNCH_HEALTH = 46430, + SPELL_SUICIDE = 45254, + SPELL_AHUNE_LOOT = 45941, + SPELL_AHUNE_LOOT_H = 46623, + SPELL_ISDEAD_CHECK = 61976, // purpose unk + SPELL_AHUNE_DIES_ACHIEV = 62043, + + // ground phase spells + SPELL_SUMMON_HAILSTONE = 45951, + SPELL_SUMMON_COLDWAVE = 45952, + SPELL_SUMMON_FROSTWIND = 45953, + + // submerged phase spells + SPELL_BIRTH = 37745, // spawn animation - not confirmed + SPELL_SUBMERGE = 37550, // submerge animation - not confirmed + SPELL_STAY_SUBMERGED = 46981, // triggers 37751; this should keep the boss submerged + SPELL_AHUNE_SELF_STUN = 46416, + SPELL_ICE_BOMBARD = 46397, // cast on phase 2 end; related to the fire opening visuals + SPELL_CLOSE_OPENING_VISUAL = 46236, // same as above + SPELL_STAND = 37752, // purpose unk + + // frozen core spells + SPELL_ICE_SPEAR_AURA = 46371, + SPELL_FROZEN_CORE_HIT = 46810, // procs on melee hit; should summon npc 26239 for a 2 seconds + SPELL_GHOST_DISGUISE = 46786, // triggered by spell 46809 + + // ice spear spells + SPELL_SUMMON_ICE_SPEAR_GO = 46369, + SPELL_ICE_SPEAR_DELAY = 46878, + SPELL_ICE_SPEAR_KNOCKBACK = 46360, + SPELL_ICE_SPEAR_VISUAL = 75498, + + // npcs and GOs + NPC_FROZEN_CORE = 25865, + NPC_GHOST_OF_AHUNE = 26239, + NPC_AHUNITE_HAILSTONE = 25755, + NPC_AHUNITE_COLDWAVE = 25756, + NPC_AHUNITE_FROSTWIND = 25757, + NPC_ICE_SPEAR_BUNNY = 25985, + GO_ICE_SPEAR = 188077, + + PHASE_GROUND = 1, + PHASE_SUBMERGED = 2, +}; + +/*###### +## boss_ahune +######*/ + +struct boss_ahuneAI : public Scripted_NoMovementAI +{ + boss_ahuneAI(Creature* pCreature) : Scripted_NoMovementAI(pCreature) + { + m_bHasCombatStarted = false; + Reset(); + } + + bool m_bHasCombatStarted; + + uint8 m_uiPhase; + uint8 m_uiPhaseChangeCount; + uint32 m_uiPhaseChangeTimer; + + uint32 m_uiHailstoneTimer; + uint32 m_uiColdwaveTimer; + uint32 m_uiFrostwindTimer; + + ObjectGuid m_frozenCoreGuid; + + void Reset() override + { + m_uiPhase = PHASE_GROUND; + m_uiPhaseChangeTimer = 90000; + m_uiPhaseChangeCount = 0; + + m_uiHailstoneTimer = 1000; + m_uiColdwaveTimer = urand(5000, 10000); + m_uiFrostwindTimer = urand(20000, 25000); + } + + void Aggro(Unit* /*pWho*/) override + { + DoCastSpellIfCan(m_creature, SPELL_BIRTH); + DoCastSpellIfCan(m_creature, SPELL_AHUNES_SHIELD, CAST_TRIGGERED | CAST_AURA_NOT_PRESENT); + DoCastSpellIfCan(m_creature, SPELL_SPANKY_HANDS, CAST_TRIGGERED | CAST_AURA_NOT_PRESENT); + } + + void JustDied(Unit* /*pKiller*/) override + { + DoCastSpellIfCan(m_creature, SPELL_AHUNE_DIES_ACHIEV, CAST_TRIGGERED); + DoCastSpellIfCan(m_creature, m_creature->GetMap()->IsRegularDifficulty() ? SPELL_AHUNE_LOOT : SPELL_AHUNE_LOOT_H, CAST_TRIGGERED); + } + + void JustReachedHome() override + { + // Cleanup on evade is done by creature_linking + m_creature->ForcedDespawn(); + } + + void DamageTaken(Unit* pDoneBy, uint32& uiDamage) override + { + // it's not clear whether this should work like this or should be handled by the proc aura + if (Creature* pCore = m_creature->GetMap()->GetCreature(m_frozenCoreGuid)) + DoCastSpellIfCan(pCore, SPELL_SYNCH_HEALTH, CAST_TRIGGERED); + } + + void SpellHit(Unit* /*pSource*/, const SpellEntry* pSpell) override + { + if (pSpell->Id == SPELL_SUBMERGE) + { + // Note: the following spell breaks the visual. Needs to be fixed! + // DoCastSpellIfCan(m_creature, SPELL_AHUNE_SELF_STUN, CAST_TRIGGERED); + + m_creature->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE); + + if (Creature* pCore = m_creature->GetMap()->GetCreature(m_frozenCoreGuid)) + pCore->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE | UNIT_FLAG_OOC_NOT_ATTACKABLE); + } + } + + void JustSummoned(Creature* pSummoned) override + { + switch (pSummoned->GetEntry()) + { + case NPC_AHUNITE_HAILSTONE: + case NPC_AHUNITE_COLDWAVE: + case NPC_AHUNITE_FROSTWIND: + if (Unit* pTarget = m_creature->SelectAttackingTarget(ATTACKING_TARGET_RANDOM, 0)) + pSummoned->AI()->AttackStart(pTarget); + break; + case NPC_FROZEN_CORE: + m_frozenCoreGuid = pSummoned->GetObjectGuid(); + break; + } + } + + void SummonedCreatureJustDied(Creature* pSummoned) override + { + // When the core dies, commit suicide + if (pSummoned->GetEntry() == NPC_FROZEN_CORE) + DoCastSpellIfCan(m_creature, SPELL_SUICIDE, CAST_TRIGGERED); + } + + void UpdateAI(const uint32 uiDiff) override + { + // Attack on first update tick, in order to properly handle the spawn animation + if (!m_bHasCombatStarted) + { + if (m_creature->IsTemporarySummon()) + { + TemporarySummon* pTemporary = (TemporarySummon*)m_creature; + + if (Player* pSummoner = m_creature->GetMap()->GetPlayer(pTemporary->GetSummonerGuid())) + AttackStart(pSummoner); + } + + m_bHasCombatStarted = true; + } + + if (!m_creature->SelectHostileTarget() || !m_creature->getVictim()) + return; + + if (m_uiPhase == PHASE_GROUND) + { + // only once at the beginning of the phase + if (m_uiHailstoneTimer) + { + if (m_uiHailstoneTimer <= uiDiff) + { + if (DoCastSpellIfCan(m_creature, SPELL_SUMMON_HAILSTONE) == CAST_OK) + m_uiHailstoneTimer = 0; + } + else + m_uiHailstoneTimer -= uiDiff; + } + + if (m_uiColdwaveTimer < uiDiff) + { + for (uint8 i = 0; i < 2; ++i) + DoCastSpellIfCan(m_creature, SPELL_SUMMON_COLDWAVE); + + m_uiColdwaveTimer = urand(5000, 10000); + } + else + m_uiColdwaveTimer -= uiDiff; + + // starts only after the first phase change + if (m_uiPhaseChangeCount) + { + if (m_uiFrostwindTimer < uiDiff) + { + for (uint8 i = 0; i < m_uiPhaseChangeCount; ++i) + DoCastSpellIfCan(m_creature, SPELL_SUMMON_FROSTWIND); + + m_uiFrostwindTimer = urand(5000, 10000); + } + else + m_uiFrostwindTimer -= uiDiff; + } + + if (m_uiPhaseChangeTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature, SPELL_SUBMERGE) == CAST_OK) + { + m_uiPhaseChangeTimer = 40000; + m_uiPhase = PHASE_SUBMERGED; + ++m_uiPhaseChangeCount; + } + } + else + m_uiPhaseChangeTimer -= uiDiff; + + DoMeleeAttackIfReady(); + } + else if (m_uiPhase == PHASE_SUBMERGED) + { + if (m_uiPhaseChangeTimer < uiDiff) + { + m_creature->RemoveAurasDueToSpell(SPELL_SUBMERGE); + m_creature->RemoveAurasDueToSpell(SPELL_AHUNE_SELF_STUN); + m_creature->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE); + + DoCastSpellIfCan(m_creature, SPELL_BIRTH); + + if (Creature* pCore = m_creature->GetMap()->GetCreature(m_frozenCoreGuid)) + pCore->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE | UNIT_FLAG_OOC_NOT_ATTACKABLE); + + m_uiPhase = PHASE_GROUND; + m_uiHailstoneTimer = 1000; + m_uiPhaseChangeTimer = 90000; + } + else + m_uiPhaseChangeTimer -= uiDiff; + } + } +}; + +CreatureAI* GetAI_boss_ahune(Creature* pCreature) +{ + return new boss_ahuneAI(pCreature); +} + +/*###### +## npc_frozen_core +######*/ + +struct npc_frozen_coreAI : public Scripted_NoMovementAI +{ + npc_frozen_coreAI(Creature* pCreature) : Scripted_NoMovementAI(pCreature) { Reset(); } + + ObjectGuid m_ahuheGuid; + + void Reset() override + { + if (m_creature->IsTemporarySummon()) + m_ahuheGuid = ((TemporarySummon*)m_creature)->GetSummonerGuid(); + + DoCastSpellIfCan(m_creature, SPELL_FROZEN_CORE_HIT, CAST_TRIGGERED | CAST_AURA_NOT_PRESENT); + DoCastSpellIfCan(m_creature, SPELL_ICE_SPEAR_AURA, CAST_TRIGGERED | CAST_AURA_NOT_PRESENT); + } + + void DamageTaken(Unit* pDoneBy, uint32& uiDamage) override + { + // it's not clear whether this should work like this or should be handled by the proc aura + if (Creature* pAhune = m_creature->GetMap()->GetCreature(m_ahuheGuid)) + DoCastSpellIfCan(pAhune, SPELL_SYNCH_HEALTH, CAST_TRIGGERED); + } + + void JustSummoned(Creature* pSummoned) override + { + if (pSummoned->GetEntry() == NPC_ICE_SPEAR_BUNNY) + { + pSummoned->CastSpell(pSummoned, SPELL_ICE_SPEAR_VISUAL, true); + pSummoned->CastSpell(pSummoned, SPELL_SUMMON_ICE_SPEAR_GO, true); + pSummoned->CastSpell(pSummoned, SPELL_ICE_SPEAR_DELAY, true); + } + } + + void AttackStart(Unit* /*pWho*/) override { } + void MoveInLineOfSight(Unit* /*pWho*/) override { } + void UpdateAI(const uint32 /*uiDiff*/) override { } +}; + +CreatureAI* GetAI_npc_frozen_core(Creature* pCreature) +{ + return new npc_frozen_coreAI(pCreature); +} + +/*###### +## npc_ice_spear_bunny +######*/ + +struct npc_ice_spear_bunnyAI : public Scripted_NoMovementAI +{ + npc_ice_spear_bunnyAI(Creature* pCreature) : Scripted_NoMovementAI(pCreature) { Reset(); } + + ObjectGuid m_iceSpearGuid; + + uint8 m_uiEventCount; + + void Reset() override + { + m_uiEventCount = 0; + } + + void JustSummoned(GameObject* pGo) override + { + if (pGo->GetEntry() == GO_ICE_SPEAR) + m_iceSpearGuid = pGo->GetObjectGuid(); + } + + void ReceiveAIEvent(AIEventType eventType, Creature* /*pSender*/, Unit* /*pInvoker*/, uint32 /*uiMiscValue*/) override + { + if (eventType == AI_EVENT_CUSTOM_A) + { + ++m_uiEventCount; + + // Knockback at 4 aura stacks (2 seconds) + if (m_uiEventCount == 4) + { + DoCastSpellIfCan(m_creature, SPELL_ICE_SPEAR_KNOCKBACK); + + if (GameObject* pSpear = m_creature->GetMap()->GetGameObject(m_iceSpearGuid)) + pSpear->Use(m_creature); + } + // Cleanup at 10 aura stacks (5 seconds) + else if (m_uiEventCount == 10) + { + if (GameObject* pSpear = m_creature->GetMap()->GetGameObject(m_iceSpearGuid)) + pSpear->SetLootState(GO_JUST_DEACTIVATED); + + m_creature->ForcedDespawn(); + } + } + } + + void AttackStart(Unit* /*pWho*/) override { } + void MoveInLineOfSight(Unit* /*pWho*/) override { } + void UpdateAI(const uint32 /*uiDiff*/) override { } +}; + +CreatureAI* GetAI_npc_ice_spear_bunny(Creature* pCreature) +{ + return new npc_ice_spear_bunnyAI(pCreature); +} + +bool EffectDummyCreature_npc_ice_spear_bunny(Unit* pCaster, uint32 uiSpellId, SpellEffectIndex uiEffIndex, Creature* pCreatureTarget, ObjectGuid /*originalCasterGuid*/) +{ + // always check spellid and effectindex + if (uiSpellId == SPELL_ICE_SPEAR_DELAY && uiEffIndex == EFFECT_INDEX_0) + { + if (pCreatureTarget->GetEntry() == NPC_ICE_SPEAR_BUNNY) + pCreatureTarget->AI()->SendAIEvent(AI_EVENT_CUSTOM_A, pCaster, pCreatureTarget); + + // always return true when we are handling this spell and effect + return true; + } + + return false; +} + +void AddSC_boss_ahune() +{ + Script* pNewScript; + + pNewScript = new Script; + pNewScript->Name = "boss_ahune"; + pNewScript->GetAI = &GetAI_boss_ahune; + pNewScript->RegisterSelf(); + + pNewScript = new Script; + pNewScript->Name = "npc_frozen_core"; + pNewScript->GetAI = &GetAI_npc_frozen_core; + pNewScript->RegisterSelf(); + + pNewScript = new Script; + pNewScript->Name = "npc_ice_spear_bunny"; + pNewScript->GetAI = &GetAI_npc_ice_spear_bunny; + pNewScript->pEffectDummyNPC = &EffectDummyCreature_npc_ice_spear_bunny; + pNewScript->RegisterSelf(); +} diff --git a/src/modules/SD2/scripts/outland/coilfang_reservoir/steam_vault/boss_hydromancer_thespia.cpp b/src/modules/SD2/scripts/outland/coilfang_reservoir/steam_vault/boss_hydromancer_thespia.cpp new file mode 100644 index 000000000..17f0b132a --- /dev/null +++ b/src/modules/SD2/scripts/outland/coilfang_reservoir/steam_vault/boss_hydromancer_thespia.cpp @@ -0,0 +1,166 @@ +/* Copyright (C) 2006 - 2013 ScriptDev2 + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with 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: Timers may need small adjustments; Elementals summon needs further research +SDCategory: Coilfang Resevoir, The Steamvault +EndScriptData */ + +/* ContentData +boss_hydromancer_thespia +mob_coilfang_waterelemental +EndContentData */ + +#include "precompiled.h" +#include "steam_vault.h" + +enum +{ + SAY_SUMMON = -1545000, + SAY_CLOUD = -1545024, + SAY_AGGRO_1 = -1545001, + SAY_AGGRO_2 = -1545002, + SAY_AGGRO_3 = -1545003, + SAY_SLAY_1 = -1545004, + SAY_SLAY_2 = -1545005, + SAY_DEAD = -1545006, + + SPELL_LIGHTNING_CLOUD = 25033, + SPELL_LUNG_BURST = 31481, + SPELL_ENVELOPING_WINDS = 31718, + SPELL_SUMMON_ELEMENTALS = 31476, // not sure where to use this +}; + +struct boss_thespiaAI : public ScriptedAI +{ + boss_thespiaAI(Creature* pCreature) : ScriptedAI(pCreature) + { + m_pInstance = (ScriptedInstance*)pCreature->GetInstanceData(); + m_bIsRegularMode = pCreature->GetMap()->IsRegularDifficulty(); + Reset(); + } + + ScriptedInstance* m_pInstance; + bool m_bIsRegularMode; + + uint32 m_uiLightningCloudTimer; + uint32 m_uiLungBurstTimer; + uint32 m_uiEnvelopingWindsTimer; + + void Reset() override + { + m_uiLightningCloudTimer = 15000; + m_uiLungBurstTimer = urand(15000, 18000); + m_uiEnvelopingWindsTimer = urand(20000, 25000); + } + + void JustReachedHome() override + { + if (m_pInstance) + m_pInstance->SetData(TYPE_HYDROMANCER_THESPIA, FAIL); + } + + void JustDied(Unit* /*pKiller*/) override + { + DoScriptText(SAY_DEAD, m_creature); + + if (m_pInstance) + m_pInstance->SetData(TYPE_HYDROMANCER_THESPIA, DONE); + } + + void KilledUnit(Unit* /*pVictim*/) override + { + DoScriptText(urand(0, 1) ? SAY_SLAY_1 : SAY_SLAY_2, m_creature); + } + + void Aggro(Unit* /*pWho*/) override + { + switch (urand(0, 2)) + { + case 0: DoScriptText(SAY_AGGRO_1, m_creature); break; + case 1: DoScriptText(SAY_AGGRO_2, m_creature); break; + case 2: DoScriptText(SAY_AGGRO_3, m_creature); break; + } + + if (m_pInstance) + m_pInstance->SetData(TYPE_HYDROMANCER_THESPIA, IN_PROGRESS); + } + + void UpdateAI(const uint32 uiDiff) override + { + if (!m_creature->SelectHostileTarget() || !m_creature->getVictim()) + return; + + // LightningCloud_Timer + if (m_uiLightningCloudTimer < uiDiff) + { + if (Unit* pTarget = m_creature->SelectAttackingTarget(ATTACKING_TARGET_RANDOM, 0)) + { + if (DoCastSpellIfCan(pTarget, SPELL_LIGHTNING_CLOUD) == CAST_OK) + { + if (urand(0, 1)) + DoScriptText(SAY_CLOUD, m_creature); + m_uiLightningCloudTimer = m_bIsRegularMode ? 30000 : 10000; + } + } + } + else + m_uiLightningCloudTimer -= uiDiff; + + // LungBurst_Timer + if (m_uiLungBurstTimer < uiDiff) + { + if (Unit* pTarget = m_creature->SelectAttackingTarget(ATTACKING_TARGET_RANDOM, 0)) + { + if (DoCastSpellIfCan(pTarget, SPELL_LUNG_BURST) == CAST_OK) + m_uiLungBurstTimer = urand(7000, 12000); + } + } + else + m_uiLungBurstTimer -= uiDiff; + + // EnvelopingWinds_Timer + if (m_uiEnvelopingWindsTimer < uiDiff) + { + if (Unit* pTarget = m_creature->SelectAttackingTarget(ATTACKING_TARGET_RANDOM, 0)) + { + if (DoCastSpellIfCan(pTarget, SPELL_ENVELOPING_WINDS) == CAST_OK) + m_uiEnvelopingWindsTimer = m_bIsRegularMode ? 10000 : 15000; + } + } + else + m_uiEnvelopingWindsTimer -= uiDiff; + + DoMeleeAttackIfReady(); + } +}; + +CreatureAI* GetAI_boss_thespiaAI(Creature* pCreature) +{ + return new boss_thespiaAI(pCreature); +} + +void AddSC_boss_hydromancer_thespia() +{ + Script* pNewScript; + + pNewScript = new Script; + pNewScript->Name = "boss_hydromancer_thespia"; + pNewScript->GetAI = &GetAI_boss_thespiaAI; + pNewScript->RegisterSelf(); +} diff --git a/src/modules/SD2/scripts/outland/coilfang_reservoir/steam_vault/boss_mekgineer_steamrigger.cpp b/src/modules/SD2/scripts/outland/coilfang_reservoir/steam_vault/boss_mekgineer_steamrigger.cpp new file mode 100644 index 000000000..f6ca80412 --- /dev/null +++ b/src/modules/SD2/scripts/outland/coilfang_reservoir/steam_vault/boss_mekgineer_steamrigger.cpp @@ -0,0 +1,291 @@ +/* Copyright (C) 2006 - 2013 ScriptDev2 + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with 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: 80 +SDComment: Enrage on heroic NYI +SDCategory: Coilfang Resevoir, The Steamvault +EndScriptData */ + +/* ContentData +boss_mekgineer_steamrigger +mob_steamrigger_mechanic +EndContentData */ + +#include "precompiled.h" +#include "steam_vault.h" + +enum +{ + SAY_MECHANICS = -1545007, + SAY_AGGRO_1 = -1545008, + SAY_AGGRO_2 = -1545009, + SAY_AGGRO_3 = -1545010, + SAY_AGGRO_4 = -1545011, + SAY_SLAY_1 = -1545012, + SAY_SLAY_2 = -1545013, + SAY_SLAY_3 = -1545014, + SAY_DEATH = -1545015, + + SPELL_SUPER_SHRINK_RAY = 31485, + SPELL_SAW_BLADE = 31486, + SPELL_ELECTRIFIED_NET = 35107, + // SPELL_ENRAGE_H = 1, // current enrage spell not known + + NPC_STEAMRIGGER_MECHANIC = 17951, + + // Mechanic spells + SPELL_DISPEL_MAGIC = 17201, + SPELL_REPAIR = 31532, + SPELL_REPAIR_H = 37936, +}; + +struct SummonLocation +{ + float m_fX, m_fY, m_fZ; +}; + +// Spawn locations +static const SummonLocation aSteamriggerSpawnLocs[] = +{ + { -316.101f, -166.444f, -7.66f}, + { -348.497f, -161.718f, -7.66f}, + { -331.161f, -112.212f, -7.66f}, +}; + +struct boss_mekgineer_steamriggerAI : public ScriptedAI +{ + boss_mekgineer_steamriggerAI(Creature* pCreature) : ScriptedAI(pCreature) + { + m_pInstance = (ScriptedInstance*)pCreature->GetInstanceData(); + m_bIsRegularMode = pCreature->GetMap()->IsRegularDifficulty(); + Reset(); + } + + ScriptedInstance* m_pInstance; + bool m_bIsRegularMode; + + uint32 m_uiShrinkTimer; + uint32 m_uiSawBladeTimer; + uint32 m_uiElectrifiedNetTimer; + uint32 m_uiMechanicTimer; + uint8 m_uiMechanicPhaseCount; + + void Reset() override + { + m_uiShrinkTimer = 20000; + m_uiSawBladeTimer = 15000; + m_uiElectrifiedNetTimer = 10000; + m_uiMechanicTimer = 20000; + m_uiMechanicPhaseCount = 1; + } + + void JustReachedHome() override + { + if (m_pInstance) + m_pInstance->SetData(TYPE_MEKGINEER_STEAMRIGGER, FAIL); + } + + void JustDied(Unit* /*pKiller*/) override + { + DoScriptText(SAY_DEATH, m_creature); + + if (m_pInstance) + m_pInstance->SetData(TYPE_MEKGINEER_STEAMRIGGER, DONE); + } + + void KilledUnit(Unit* /*pVictim*/) override + { + switch (urand(0, 2)) + { + case 0: DoScriptText(SAY_SLAY_1, m_creature); break; + case 1: DoScriptText(SAY_SLAY_2, m_creature); break; + case 2: DoScriptText(SAY_SLAY_3, m_creature); break; + } + } + + void Aggro(Unit* /*pWho*/) override + { + switch (urand(0, 2)) + { + case 0: DoScriptText(SAY_AGGRO_1, m_creature); break; + case 1: DoScriptText(SAY_AGGRO_2, m_creature); break; + case 2: DoScriptText(SAY_AGGRO_3, m_creature); break; + } + + if (m_pInstance) + m_pInstance->SetData(TYPE_MEKGINEER_STEAMRIGGER, IN_PROGRESS); + } + + void JustSummoned(Creature* pSummoned) override + { + if (pSummoned->GetEntry() == NPC_STEAMRIGGER_MECHANIC) + pSummoned->GetMotionMaster()->MoveFollow(m_creature, 0, 0); + } + + // Wrapper to summon three Mechanics + void SummonMechanichs() + { + DoScriptText(SAY_MECHANICS, m_creature); + + for (uint8 i = 0; i < 3; ++i) + m_creature->SummonCreature(NPC_STEAMRIGGER_MECHANIC, aSteamriggerSpawnLocs[i].m_fX, aSteamriggerSpawnLocs[i].m_fY, aSteamriggerSpawnLocs[i].m_fZ, 0, TEMPSUMMON_TIMED_OOC_OR_DEAD_DESPAWN, 240000); + } + + void UpdateAI(const uint32 uiDiff) override + { + if (!m_creature->SelectHostileTarget() || !m_creature->getVictim()) + return; + + if (m_uiShrinkTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature, SPELL_SUPER_SHRINK_RAY) == CAST_OK) + m_uiShrinkTimer = 20000; + } + else + m_uiShrinkTimer -= uiDiff; + + if (m_uiSawBladeTimer < uiDiff) + { + Unit* pTarget = m_creature->SelectAttackingTarget(ATTACKING_TARGET_RANDOM, 1); + if (!pTarget) + pTarget = m_creature->getVictim(); + + if (pTarget) + { + if (DoCastSpellIfCan(pTarget, SPELL_SAW_BLADE) == CAST_OK) + m_uiSawBladeTimer = 15000; + } + } + else + m_uiSawBladeTimer -= uiDiff; + + if (m_uiElectrifiedNetTimer < uiDiff) + { + if (Unit* pTarget = m_creature->SelectAttackingTarget(ATTACKING_TARGET_RANDOM, 0)) + { + if (DoCastSpellIfCan(pTarget, SPELL_ELECTRIFIED_NET) == CAST_OK) + m_uiElectrifiedNetTimer = 10000; + } + } + else + m_uiElectrifiedNetTimer -= uiDiff; + + // On Heroic mode summon a mechanic at each 20 secs + if (!m_bIsRegularMode) + { + if (m_uiMechanicTimer < uiDiff) + { + m_creature->SummonCreature(NPC_STEAMRIGGER_MECHANIC, aSteamriggerSpawnLocs[2].m_fX, aSteamriggerSpawnLocs[2].m_fY, aSteamriggerSpawnLocs[2].m_fZ, 0, TEMPSUMMON_TIMED_OOC_OR_DEAD_DESPAWN, 240000); + m_uiMechanicTimer = 20000; + } + else + m_uiMechanicTimer -= uiDiff; + } + + if (m_creature->GetHealthPercent() < (100 - 25 * m_uiMechanicPhaseCount)) + { + SummonMechanichs(); + ++m_uiMechanicPhaseCount; + } + + DoMeleeAttackIfReady(); + } +}; + +CreatureAI* GetAI_boss_mekgineer_steamrigger(Creature* pCreature) +{ + return new boss_mekgineer_steamriggerAI(pCreature); +} + +struct mob_steamrigger_mechanicAI : public ScriptedAI +{ + mob_steamrigger_mechanicAI(Creature* pCreature) : ScriptedAI(pCreature) + { + m_pInstance = (ScriptedInstance*)pCreature->GetInstanceData(); + m_bIsRegularMode = pCreature->GetMap()->IsRegularDifficulty(); + Reset(); + } + + ScriptedInstance* m_pInstance; + bool m_bIsRegularMode; + + bool m_bCanStartAttack; + + void Reset() override + { + m_bCanStartAttack = false; + } + + void AttackStart(Unit* pWho) override + { + // Trigger attack only for players + if (pWho->GetTypeId() != TYPEID_PLAYER) + return; + + m_creature->InterruptNonMeleeSpells(false); + ScriptedAI::AttackStart(pWho); + m_bCanStartAttack = true; + } + + void MoveInLineOfSight(Unit* pWho) override + { + // Return if already in combat + if (m_bCanStartAttack) + return; + + // Don't attack players unless attacked + if (pWho->GetEntry() == NPC_STEAMRIGGER) + { + if (m_pInstance->GetData(TYPE_MEKGINEER_STEAMRIGGER) == IN_PROGRESS) + { + // Channel the repair spell on Steamrigger + // This will also stop creature movement and will allow them to continue to follow the boss after channeling is finished or the boss is out of range + if (m_creature->IsWithinDistInMap(pWho, 2 * INTERACTION_DISTANCE)) + DoCastSpellIfCan(m_creature, m_bIsRegularMode ? SPELL_REPAIR : SPELL_REPAIR_H); + } + } + } + + void UpdateAI(const uint32 /*uiDiff*/) override + { + if (!m_creature->SelectHostileTarget() || !m_creature->getVictim()) + return; + + DoMeleeAttackIfReady(); + } +}; + +CreatureAI* GetAI_mob_steamrigger_mechanic(Creature* pCreature) +{ + return new mob_steamrigger_mechanicAI(pCreature); +} + +void AddSC_boss_mekgineer_steamrigger() +{ + Script* pNewScript; + + pNewScript = new Script; + pNewScript->Name = "boss_mekgineer_steamrigger"; + pNewScript->GetAI = &GetAI_boss_mekgineer_steamrigger; + pNewScript->RegisterSelf(); + + pNewScript = new Script; + pNewScript->Name = "mob_steamrigger_mechanic"; + pNewScript->GetAI = &GetAI_mob_steamrigger_mechanic; + pNewScript->RegisterSelf(); +} diff --git a/src/modules/SD2/scripts/outland/coilfang_reservoir/steam_vault/boss_warlord_kalithresh.cpp b/src/modules/SD2/scripts/outland/coilfang_reservoir/steam_vault/boss_warlord_kalithresh.cpp new file mode 100644 index 000000000..2e7570bd1 --- /dev/null +++ b/src/modules/SD2/scripts/outland/coilfang_reservoir/steam_vault/boss_warlord_kalithresh.cpp @@ -0,0 +1,252 @@ +/* Copyright (C) 2006 - 2013 ScriptDev2 + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with 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: 90 +SDComment: Timers may need some fine adjustments +SDCategory: Coilfang Resevoir, The Steamvault +EndScriptData */ + +#include "precompiled.h" +#include "steam_vault.h" + +enum +{ + SAY_INTRO = -1545016, + SAY_REGEN = -1545017, + SAY_AGGRO1 = -1545018, + SAY_AGGRO2 = -1545019, + SAY_AGGRO3 = -1545020, + SAY_SLAY1 = -1545021, + SAY_SLAY2 = -1545022, + SAY_DEATH = -1545023, + + SPELL_SPELL_REFLECTION = 31534, + SPELL_IMPALE = 39061, + SPELL_WARLORDS_RAGE = 37081, // triggers 36453 + SPELL_WARLORDS_RAGE_NAGA = 31543, // triggers 37076 +}; + +struct boss_warlord_kalithreshAI : public ScriptedAI +{ + boss_warlord_kalithreshAI(Creature* pCreature) : ScriptedAI(pCreature) + { + m_pInstance = (instance_steam_vault*)pCreature->GetInstanceData(); + m_bHasTaunted = false; + Reset(); + } + + instance_steam_vault* m_pInstance; + + uint32 m_uiReflectionTimer; + uint32 m_uiImpaleTimer; + uint32 m_uiRageTimer; + uint32 m_uiRageCastTimer; + + ObjectGuid m_distillerGuid; + + bool m_bHasTaunted; + + void Reset() override + { + m_uiReflectionTimer = 15000; + m_uiImpaleTimer = urand(7000, 14000); + m_uiRageTimer = urand(15000, 20000); + m_uiRageCastTimer = 0; + } + + void JustReachedHome() override + { + if (m_pInstance) + m_pInstance->SetData(TYPE_WARLORD_KALITHRESH, FAIL); + } + + void Aggro(Unit* /*pWho*/) override + { + switch (urand(0, 2)) + { + case 0: DoScriptText(SAY_AGGRO1, m_creature); break; + case 1: DoScriptText(SAY_AGGRO2, m_creature); break; + case 2: DoScriptText(SAY_AGGRO3, m_creature); break; + } + + if (m_pInstance) + m_pInstance->SetData(TYPE_WARLORD_KALITHRESH, IN_PROGRESS); + } + + void KilledUnit(Unit* /*pVictim*/) override + { + DoScriptText(urand(0, 1) ? SAY_SLAY1 : SAY_SLAY2, m_creature); + } + + void JustDied(Unit* /*pKiller*/) override + { + DoScriptText(SAY_DEATH, m_creature); + + if (m_pInstance) + m_pInstance->SetData(TYPE_WARLORD_KALITHRESH, DONE); + } + + void MoveInLineOfSight(Unit* pWho) override + { + if (!m_bHasTaunted && m_creature->IsWithinDistInMap(pWho, 40.0f)) + { + DoScriptText(SAY_INTRO, m_creature); + m_bHasTaunted = true; + } + + ScriptedAI::MoveInLineOfSight(pWho); + } + + void MovementInform(uint32 uiMoveType, uint32 uiPointId) override + { + if (uiMoveType != POINT_MOTION_TYPE || !uiPointId) + return; + + // There is a small delay between the point reach and the channeling start + m_uiRageCastTimer = 1000; + } + + void UpdateAI(const uint32 uiDiff) override + { + if (!m_creature->SelectHostileTarget() || !m_creature->getVictim()) + return; + + if (m_uiRageCastTimer) + { + if (m_uiRageCastTimer <= uiDiff) + { + if (DoCastSpellIfCan(m_creature, SPELL_WARLORDS_RAGE) == CAST_OK) + { + DoScriptText(SAY_REGEN, m_creature); + SetCombatMovement(true); + m_uiRageCastTimer = 0; + + // Also make the distiller cast + if (Creature* pDistiller = m_creature->GetMap()->GetCreature(m_distillerGuid)) + { + pDistiller->CastSpell(pDistiller, SPELL_WARLORDS_RAGE_NAGA, true); + pDistiller->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE); + } + } + } + else + m_uiRageCastTimer -= uiDiff; + } + + // Move to closest distiller + if (m_uiRageTimer < uiDiff) + { + if (Creature* pDistiller = GetClosestCreatureWithEntry(m_creature, NPC_NAGA_DISTILLER, 100.0f)) + { + float fX, fY, fZ; + pDistiller->GetContactPoint(m_creature, fX, fY, fZ, INTERACTION_DISTANCE); + m_creature->GetMotionMaster()->MovePoint(1, fX, fY, fZ); + SetCombatMovement(false); + m_distillerGuid = pDistiller->GetObjectGuid(); + } + + m_uiRageTimer = urand(35000, 45000); + } + else + m_uiRageTimer -= uiDiff; + + // Reflection_Timer + if (m_uiReflectionTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature, SPELL_SPELL_REFLECTION) == CAST_OK) + m_uiReflectionTimer = 30000; + } + else + m_uiReflectionTimer -= uiDiff; + + // Impale_Timer + if (m_uiImpaleTimer < uiDiff) + { + if (Unit* pTarget = m_creature->SelectAttackingTarget(ATTACKING_TARGET_RANDOM, 0)) + { + if (DoCastSpellIfCan(pTarget, SPELL_IMPALE) == CAST_OK) + m_uiImpaleTimer = urand(7500, 12500); + } + } + else + m_uiImpaleTimer -= uiDiff; + + DoMeleeAttackIfReady(); + } +}; + +bool EffectAuraDummy_spell_aura_dummy_warlord_rage(const Aura* pAura, bool bApply) +{ + if (pAura->GetId() == SPELL_WARLORDS_RAGE && pAura->GetEffIndex() == EFFECT_INDEX_0) + { + if (Creature* pTarget = (Creature*)pAura->GetTarget()) + { + // Resume combat when the cast is finished or interrupted + if (!bApply) + { + if (pTarget->getVictim()) + { + pTarget->GetMotionMaster()->MovementExpired(); + pTarget->GetMotionMaster()->MoveChase(pTarget->getVictim()); + } + } + } + } + + return true; +} + +struct mob_naga_distillerAI : public Scripted_NoMovementAI +{ + mob_naga_distillerAI(Creature* pCreature) : Scripted_NoMovementAI(pCreature) { Reset(); } + + void Reset() override + { + m_creature->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE); + } + + void MoveInLineOfSight(Unit* /*pWho*/) override { } + void AttackStart(Unit* /*pWho*/) override { } + void UpdateAI(const uint32 /*uiDiff*/) override { } +}; + +CreatureAI* GetAI_boss_warlord_kalithresh(Creature* pCreature) +{ + return new boss_warlord_kalithreshAI(pCreature); +} + +CreatureAI* GetAI_mob_naga_distiller(Creature* pCreature) +{ + return new mob_naga_distillerAI(pCreature); +} + +void AddSC_boss_warlord_kalithresh() +{ + Script* pNewScript; + + pNewScript = new Script; + pNewScript->Name = "boss_warlord_kalithresh"; + pNewScript->GetAI = &GetAI_boss_warlord_kalithresh; + pNewScript->pEffectAuraDummy = &EffectAuraDummy_spell_aura_dummy_warlord_rage; + pNewScript->RegisterSelf(); + + pNewScript = new Script; + pNewScript->Name = "mob_naga_distiller"; + pNewScript->GetAI = &GetAI_mob_naga_distiller; + pNewScript->RegisterSelf(); +} diff --git a/src/modules/SD2/scripts/outland/coilfang_reservoir/steam_vault/instance_steam_vault.cpp b/src/modules/SD2/scripts/outland/coilfang_reservoir/steam_vault/instance_steam_vault.cpp new file mode 100644 index 000000000..ccdbdc663 --- /dev/null +++ b/src/modules/SD2/scripts/outland/coilfang_reservoir/steam_vault/instance_steam_vault.cpp @@ -0,0 +1,208 @@ +/* Copyright (C) 2006 - 2013 ScriptDev2 + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along 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 "steam_vault.h" + +/* Steam Vaults encounters: +1 - Hydromancer Thespia Event +2 - Mekgineer Steamrigger Event +3 - Warlord Kalithresh Event +*/ + +bool GOUse_go_main_chambers_access_panel(Player* /*pPlayer*/, GameObject* pGo) +{ + ScriptedInstance* pInstance = (ScriptedInstance*)pGo->GetInstanceData(); + + if (!pInstance) + return true; + + if (pGo->GetEntry() == GO_ACCESS_PANEL_HYDRO) + pInstance->SetData(TYPE_HYDROMANCER_THESPIA, SPECIAL); + else if (pGo->GetEntry() == GO_ACCESS_PANEL_MEK) + pInstance->SetData(TYPE_MEKGINEER_STEAMRIGGER, SPECIAL); + + return false; +} + +instance_steam_vault::instance_steam_vault(Map* pMap) : ScriptedInstance(pMap) +{ + Initialize(); +} + +void instance_steam_vault::Initialize() +{ + memset(&m_auiEncounter, 0, sizeof(m_auiEncounter)); +} + +void instance_steam_vault::OnCreatureCreate(Creature* pCreature) +{ + switch (pCreature->GetEntry()) + { + case NPC_STEAMRIGGER: + case NPC_KALITHRESH: + m_mNpcEntryGuidStore[pCreature->GetEntry()] = pCreature->GetObjectGuid(); + break; + case NPC_NAGA_DISTILLER: + m_lNagaDistillerGuidList.push_back(pCreature->GetObjectGuid()); + break; + } +} + +void instance_steam_vault::OnObjectCreate(GameObject* pGo) +{ + switch (pGo->GetEntry()) + { + case GO_MAIN_CHAMBERS_DOOR: + if (m_auiEncounter[TYPE_HYDROMANCER_THESPIA] == SPECIAL && m_auiEncounter[TYPE_MEKGINEER_STEAMRIGGER] == SPECIAL) + pGo->SetGoState(GO_STATE_ACTIVE); + break; + case GO_ACCESS_PANEL_HYDRO: + if (m_auiEncounter[TYPE_HYDROMANCER_THESPIA] == DONE) + pGo->RemoveFlag(GAMEOBJECT_FLAGS, GO_FLAG_NO_INTERACT); + break; + case GO_ACCESS_PANEL_MEK: + if (m_auiEncounter[TYPE_MEKGINEER_STEAMRIGGER] == DONE) + pGo->RemoveFlag(GAMEOBJECT_FLAGS, GO_FLAG_NO_INTERACT); + break; + default: + return; + } + m_mGoEntryGuidStore[pGo->GetEntry()] = pGo->GetObjectGuid(); +} + +void instance_steam_vault::OnCreatureDeath(Creature* pCreature) +{ + // Break the Warlord spell on the Distiller death + if (pCreature->GetEntry() == NPC_NAGA_DISTILLER) + { + if (Creature* pWarlord = GetSingleCreatureFromStorage(NPC_KALITHRESH)) + pWarlord->InterruptNonMeleeSpells(false); + } +} + +void instance_steam_vault::SetData(uint32 uiType, uint32 uiData) +{ + switch (uiType) + { + case TYPE_HYDROMANCER_THESPIA: + if (uiData == DONE) + DoToggleGameObjectFlags(GO_ACCESS_PANEL_HYDRO, GO_FLAG_NO_INTERACT, false); + if (uiData == SPECIAL) + { + if (GetData(TYPE_MEKGINEER_STEAMRIGGER) == SPECIAL) + DoUseDoorOrButton(GO_MAIN_CHAMBERS_DOOR); + } + m_auiEncounter[uiType] = uiData; + break; + case TYPE_MEKGINEER_STEAMRIGGER: + if (uiData == DONE) + DoToggleGameObjectFlags(GO_ACCESS_PANEL_MEK, GO_FLAG_NO_INTERACT, false); + if (uiData == SPECIAL) + { + if (GetData(TYPE_HYDROMANCER_THESPIA) == SPECIAL) + DoUseDoorOrButton(GO_MAIN_CHAMBERS_DOOR); + } + m_auiEncounter[uiType] = uiData; + break; + case TYPE_WARLORD_KALITHRESH: + DoUseDoorOrButton(GO_MAIN_CHAMBERS_DOOR); + if (uiData == FAIL) + { + // Reset Distiller flags - respawn is handled by DB + for (GuidList::const_iterator itr = m_lNagaDistillerGuidList.begin(); itr != m_lNagaDistillerGuidList.end(); ++itr) + { + if (Creature* pDistiller = instance->GetCreature(*itr)) + { + if (!pDistiller->HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE)) + pDistiller->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE); + } + } + } + m_auiEncounter[uiType] = uiData; + break; + } + + if (uiData == DONE || uiData == SPECIAL) + { + OUT_SAVE_INST_DATA; + + std::ostringstream saveStream; + saveStream << m_auiEncounter[0] << " " << m_auiEncounter[1] << " " << m_auiEncounter[2]; + + m_strInstData = saveStream.str(); + + SaveToDB(); + OUT_SAVE_INST_DATA_COMPLETE; + } +} + +uint32 instance_steam_vault::GetData(uint32 uiType) const +{ + if (uiType < MAX_ENCOUNTER) + return m_auiEncounter[uiType]; + + return 0; +} + +void instance_steam_vault::Load(const char* chrIn) +{ + if (!chrIn) + { + 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]; + + 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* pNewScript; + + pNewScript = new Script; + pNewScript->Name = "go_main_chambers_access_panel"; + pNewScript->pGOUse = &GOUse_go_main_chambers_access_panel; + pNewScript->RegisterSelf(); + + pNewScript = new Script; + pNewScript->Name = "instance_steam_vault"; + pNewScript->GetInstanceData = &GetInstanceData_instance_steam_vault; + pNewScript->RegisterSelf(); +} diff --git a/src/modules/SD2/scripts/outland/coilfang_reservoir/steam_vault/steam_vault.h b/src/modules/SD2/scripts/outland/coilfang_reservoir/steam_vault/steam_vault.h new file mode 100644 index 000000000..8992cc5b6 --- /dev/null +++ b/src/modules/SD2/scripts/outland/coilfang_reservoir/steam_vault/steam_vault.h @@ -0,0 +1,51 @@ +/* Copyright (C) 2006 - 2013 ScriptDev2 + * This program is free software licensed under GPL version 2 + * Please see the included DOCS/LICENSE.TXT for more information */ + +#ifndef DEF_STEAM_VAULT_H +#define DEF_STEAM_VAULT_H + +enum +{ + MAX_ENCOUNTER = 3, + + TYPE_HYDROMANCER_THESPIA = 0, + TYPE_MEKGINEER_STEAMRIGGER = 1, + TYPE_WARLORD_KALITHRESH = 2, + + NPC_NAGA_DISTILLER = 17954, + NPC_STEAMRIGGER = 17796, + NPC_KALITHRESH = 17798, + // NPC_THESPIA = 17797, + + GO_MAIN_CHAMBERS_DOOR = 183049, + GO_ACCESS_PANEL_HYDRO = 184125, + GO_ACCESS_PANEL_MEK = 184126, +}; + +class instance_steam_vault : public ScriptedInstance +{ + public: + instance_steam_vault(Map* pMap); + + void Initialize() override; + + void OnCreatureCreate(Creature* pCreature) override; + void OnObjectCreate(GameObject* pGo) override; + + void OnCreatureDeath(Creature* pCreature) override; + + void SetData(uint32 uiType, uint32 uiData) override; + uint32 GetData(uint32 uiType) const override; + + const char* Save() const override { return m_strInstData.c_str(); } + void Load(const char* chrIn) override; + + private: + uint32 m_auiEncounter[MAX_ENCOUNTER]; + std::string m_strInstData; + + GuidList m_lNagaDistillerGuidList; +}; + +#endif diff --git a/src/modules/SD2/scripts/outland/coilfang_reservoir/underbog/boss_hungarfen.cpp b/src/modules/SD2/scripts/outland/coilfang_reservoir/underbog/boss_hungarfen.cpp new file mode 100644 index 000000000..be6652c2c --- /dev/null +++ b/src/modules/SD2/scripts/outland/coilfang_reservoir/underbog/boss_hungarfen.cpp @@ -0,0 +1,190 @@ +/* Copyright (C) 2006 - 2013 ScriptDev2 + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with 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: 80 +SDComment: Need confirmation if spell data are same in both modes; The Underbog Mushroom may need some more research +SDCategory: Coilfang Resevoir, Underbog +EndScriptData */ + +#include "precompiled.h" + +enum +{ + SPELL_FOUL_SPORES = 31673, + SPELL_ACID_GEYSER = 38739, + SPELL_DESPAWN_MUSHROOMS = 34874, + + // Mushroom spells + SPELL_SPORE_CLOUD = 34168, + SPELL_PUTRID_MUSHROOM = 31690, + SPELL_GROW = 31698, + + NPC_UNDERBOG_MUSHROOM = 17990, +}; + +struct boss_hungarfenAI : public ScriptedAI +{ + boss_hungarfenAI(Creature* pCreature) : ScriptedAI(pCreature) + { + m_bIsRegularMode = pCreature->GetMap()->IsRegularDifficulty(); + Reset(); + } + + bool m_bIsRegularMode; + bool m_bHasSpores; + uint32 m_uiMushroomTimer; + uint32 m_uiAcidGeyserTimer; + + void Reset() override + { + m_bHasSpores = false; + m_uiMushroomTimer = 5000; // 1 mushroom after 5s, then one per 10s. This should be different in heroic mode + m_uiAcidGeyserTimer = 10000; + } + + void JustDied(Unit* /*pKiller*/) override + { + DoCastSpellIfCan(m_creature, SPELL_DESPAWN_MUSHROOMS, CAST_TRIGGERED); + } + + void JustReachedHome() override + { + DoCastSpellIfCan(m_creature, SPELL_DESPAWN_MUSHROOMS, CAST_TRIGGERED); + } + + void UpdateAI(const uint32 uiDiff) override + { + if (!m_creature->SelectHostileTarget() || !m_creature->getVictim()) + return; + + if (m_creature->GetHealthPercent() <= 20.0f && !m_bHasSpores) + { + if (DoCastSpellIfCan(m_creature, SPELL_FOUL_SPORES) == CAST_OK) + m_bHasSpores = true; + } + + if (m_uiMushroomTimer < uiDiff) + { + // Summon a mushroom exactly on target position + if (Unit* pTarget = m_creature->SelectAttackingTarget(ATTACKING_TARGET_RANDOM, 0)) + m_creature->SummonCreature(NPC_UNDERBOG_MUSHROOM, pTarget->GetPositionX(), pTarget->GetPositionY(), pTarget->GetPositionZ(), 0, TEMPSUMMON_CORPSE_DESPAWN, 0); + + m_uiMushroomTimer = m_bIsRegularMode ? 10000 : 5000; + } + else + m_uiMushroomTimer -= uiDiff; + + if (m_uiAcidGeyserTimer < uiDiff) + { + if (Unit* pTarget = m_creature->SelectAttackingTarget(ATTACKING_TARGET_RANDOM, 0)) + { + if (DoCastSpellIfCan(pTarget, SPELL_ACID_GEYSER) == CAST_OK) + m_uiAcidGeyserTimer = urand(10000, 17500); + } + } + else + m_uiAcidGeyserTimer -= uiDiff; + + DoMeleeAttackIfReady(); + } +}; + +CreatureAI* GetAI_boss_hungarfen(Creature* pCreature) +{ + return new boss_hungarfenAI(pCreature); +} + +struct mob_underbog_mushroomAI : public ScriptedAI +{ + mob_underbog_mushroomAI(Creature* pCreature) : ScriptedAI(pCreature) { Reset(); } + + uint32 m_uiGrowTimer; + uint32 m_uiShrinkTimer; + uint32 m_uiSporeTimer; + + void Reset() override + { + m_uiGrowTimer = 1000; + m_uiSporeTimer = 15000; + m_uiShrinkTimer = 20000; + + DoCastSpellIfCan(m_creature, SPELL_PUTRID_MUSHROOM); + } + + void MoveInLineOfSight(Unit* /*pWho*/) override { return; } + void AttackStart(Unit* /*pWho*/) override { return; } + + void UpdateAI(const uint32 uiDiff) override + { + if (m_uiSporeTimer) + { + if (m_uiSporeTimer <= uiDiff) + { + if (DoCastSpellIfCan(m_creature, SPELL_SPORE_CLOUD) == CAST_OK) + { + m_uiGrowTimer = 0; + m_uiSporeTimer = 0; + } + } + else + m_uiSporeTimer -= uiDiff; + } + + if (m_uiGrowTimer) + { + if (m_uiGrowTimer <= uiDiff) + { + if (DoCastSpellIfCan(m_creature, SPELL_GROW) == CAST_OK) + m_uiGrowTimer = 3000; + } + else + m_uiGrowTimer -= uiDiff; + } + + if (m_uiShrinkTimer) + { + if (m_uiShrinkTimer <= uiDiff) + { + m_creature->RemoveAurasDueToSpell(SPELL_GROW); + m_uiShrinkTimer = 0; + } + else + m_uiShrinkTimer -= uiDiff; + } + } +}; + +CreatureAI* GetAI_mob_underbog_mushroom(Creature* pCreature) +{ + return new mob_underbog_mushroomAI(pCreature); +} + +void AddSC_boss_hungarfen() +{ + Script* pNewScript; + + pNewScript = new Script; + pNewScript->Name = "boss_hungarfen"; + pNewScript->GetAI = &GetAI_boss_hungarfen; + pNewScript->RegisterSelf(); + + pNewScript = new Script; + pNewScript->Name = "mob_underbog_mushroom"; + pNewScript->GetAI = &GetAI_mob_underbog_mushroom; + pNewScript->RegisterSelf(); +} diff --git a/src/modules/SD2/scripts/outland/gruuls_lair/boss_gruul.cpp b/src/modules/SD2/scripts/outland/gruuls_lair/boss_gruul.cpp new file mode 100644 index 000000000..bf103c58d --- /dev/null +++ b/src/modules/SD2/scripts/outland/gruuls_lair/boss_gruul.cpp @@ -0,0 +1,267 @@ +/* Copyright (C) 2006 - 2013 ScriptDev2 + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with 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 "gruuls_lair.h" + +enum +{ + SAY_AGGRO = -1565010, + SAY_SLAM1 = -1565011, + SAY_SLAM2 = -1565012, + SAY_SHATTER1 = -1565013, + SAY_SHATTER2 = -1565014, + SAY_SLAY1 = -1565015, + 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 boss_gruulAI : public ScriptedAI +{ + boss_gruulAI(Creature* pCreature) : ScriptedAI(pCreature) + { + m_pInstance = (ScriptedInstance*)pCreature->GetInstanceData(); + Reset(); + } + + ScriptedInstance* m_pInstance; + + uint32 m_uiGrowthTimer; + uint32 m_uiCaveInTimer; + uint32 m_uiCaveInStaticTimer; + uint32 m_uiGroundSlamTimer; + uint32 m_uiHurtfulStrikeTimer; + uint32 m_uiReverberationTimer; + + bool m_bPerformingGroundSlam; + + void Reset() override + { + m_uiGrowthTimer = 30000; + m_uiCaveInTimer = 27000; + m_uiCaveInStaticTimer = 30000; + m_uiGroundSlamTimer = 35000; + m_uiHurtfulStrikeTimer = 8000; + m_uiReverberationTimer = 60000 + 45000; + m_bPerformingGroundSlam = false; + } + + void Aggro(Unit* /*pWho*/) override + { + DoScriptText(SAY_AGGRO, m_creature); + + if (m_pInstance) + m_pInstance->SetData(TYPE_GRUUL_EVENT, IN_PROGRESS); + } + + void JustReachedHome() override + { + if (m_pInstance) + m_pInstance->SetData(TYPE_GRUUL_EVENT, FAIL); + } + + void KilledUnit(Unit* /*pVictim*/) override + { + switch (urand(0, 2)) + { + case 0: DoScriptText(SAY_SLAY1, m_creature); break; + case 1: DoScriptText(SAY_SLAY2, m_creature); break; + case 2: DoScriptText(SAY_SLAY3, m_creature); break; + } + } + + void JustDied(Unit* /*pKiller*/) override + { + DoScriptText(SAY_DEATH, m_creature); + + if (m_pInstance) + m_pInstance->SetData(TYPE_GRUUL_EVENT, DONE); + } + + void SpellHitTarget(Unit* pTarget, const SpellEntry* pSpell) override + { + // This to emulate effect1 (77) of SPELL_GROUND_SLAM, knock back to any direction + // It's initially wrong, since this will cause fall damage, which is by comments, not intended. + if (pSpell->Id == SPELL_GROUND_SLAM) + { + if (pTarget->GetTypeId() == TYPEID_PLAYER) + { + switch (urand(0, 1)) + { + case 0: pTarget->CastSpell(pTarget, SPELL_MAGNETIC_PULL, true, NULL, NULL, m_creature->GetObjectGuid()); break; + case 1: pTarget->CastSpell(pTarget, SPELL_KNOCK_BACK, true, NULL, NULL, m_creature->GetObjectGuid()); break; + } + } + } + + // 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() != CHASE_MOTION_TYPE) + { + if (m_creature->getVictim()) + m_creature->GetMotionMaster()->MoveChase(m_creature->getVictim()); + } + } + } + } + + void UpdateAI(const uint32 uiDiff) override + { + if (!m_creature->SelectHostileTarget() || !m_creature->getVictim()) + return; + + // Gruul can cast this spell up to 30 times + if (m_uiGrowthTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature, SPELL_GROWTH) == CAST_OK) + { + DoScriptText(EMOTE_GROW, m_creature); + m_uiGrowthTimer = 30000; + } + } + else + m_uiGrowthTimer -= uiDiff; + + if (m_bPerformingGroundSlam) + { + if (m_uiGroundSlamTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature, SPELL_SHATTER) == CAST_OK) + { + DoScriptText(urand(0, 1) ? SAY_SHATTER1 : SAY_SHATTER2, m_creature); + m_uiGroundSlamTimer = 120000; + m_uiHurtfulStrikeTimer = 8000; + + // Give a little time to the players to undo the damage from shatter + if (m_uiReverberationTimer < 10000) + m_uiReverberationTimer += 10000; + } + } + else + m_uiGroundSlamTimer -= uiDiff; + } + else + { + // Hurtful Strike + if (m_uiHurtfulStrikeTimer < uiDiff) + { + if (Unit* pTarget = m_creature->SelectAttackingTarget(ATTACKING_TARGET_TOPAGGRO, 1, SPELL_HURTFUL_STRIKE, SELECT_FLAG_PLAYER)) + DoCastSpellIfCan(pTarget, SPELL_HURTFUL_STRIKE); + else + DoCastSpellIfCan(m_creature->getVictim(), SPELL_HURTFUL_STRIKE); + + m_uiHurtfulStrikeTimer = 8000; + } + else + m_uiHurtfulStrikeTimer -= uiDiff; + + // Reverberation + if (m_uiReverberationTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature, SPELL_REVERBERATION) == CAST_OK) + m_uiReverberationTimer = urand(15000, 25000); + } + else + m_uiReverberationTimer -= uiDiff; + + // Cave In + if (m_uiCaveInTimer < uiDiff) + { + if (Unit* pTarget = m_creature->SelectAttackingTarget(ATTACKING_TARGET_RANDOM, 0)) + { + if (DoCastSpellIfCan(pTarget, SPELL_CAVE_IN) == CAST_OK) + { + if (m_uiCaveInStaticTimer >= 4000) + m_uiCaveInStaticTimer -= 2000; + + m_uiCaveInTimer = m_uiCaveInStaticTimer; + } + } + } + else + m_uiCaveInTimer -= uiDiff; + + // Ground Slam, Gronn Lord's Grasp, Stoned, Shatter + if (m_uiGroundSlamTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature, SPELL_GROUND_SLAM) == CAST_OK) + { + DoScriptText(urand(0, 1) ? SAY_SLAM1 : SAY_SLAM2, m_creature); + m_creature->GetMotionMaster()->Clear(); + m_creature->GetMotionMaster()->MoveIdle(); + + m_bPerformingGroundSlam = true; + m_uiGroundSlamTimer = 10000; + } + } + else + m_uiGroundSlamTimer -= uiDiff; + + DoMeleeAttackIfReady(); + } + } +}; + +CreatureAI* GetAI_boss_gruul(Creature* pCreature) +{ + return new boss_gruulAI(pCreature); +} + +void AddSC_boss_gruul() +{ + Script* pNewScript; + + pNewScript = new Script; + pNewScript->Name = "boss_gruul"; + pNewScript->GetAI = &GetAI_boss_gruul; + pNewScript->RegisterSelf(); +} diff --git a/src/modules/SD2/scripts/outland/gruuls_lair/boss_high_king_maulgar.cpp b/src/modules/SD2/scripts/outland/gruuls_lair/boss_high_king_maulgar.cpp new file mode 100644 index 000000000..05eaa4c9b --- /dev/null +++ b/src/modules/SD2/scripts/outland/gruuls_lair/boss_high_king_maulgar.cpp @@ -0,0 +1,554 @@ +/* Copyright (C) 2006 - 2013 ScriptDev2 + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +/* ScriptData +SDName: Boss_High_King_Maulgar +SD%Complete: 80 +SDComment: Verify that the script is working properly +SDCategory: Gruul's Lair +EndScriptData */ + +#include "precompiled.h" +#include "gruuls_lair.h" + +enum +{ + SAY_AGGRO = -1565000, + SAY_ENRAGE = -1565001, + SAY_OGRE_DEATH1 = -1565002, + SAY_OGRE_DEATH2 = -1565003, + SAY_OGRE_DEATH3 = -1565004, + SAY_OGRE_DEATH4 = -1565005, + SAY_SLAY1 = -1565006, + SAY_SLAY2 = -1565007, + SAY_SLAY3 = -1565008, + SAY_DEATH = -1565009, + + // High King Maulgar Spells + SPELL_ARCING_SMASH = 39144, + SPELL_MIGHTY_BLOW = 33230, + SPELL_WHIRLWIND = 33238, + SPELL_FLURRY = 33232, + SPELL_CHARGE = 26561, + SPELL_FEAR = 16508, + + // Olm the Summoner Spells + SPELL_DARK_DECAY = 33129, + SPELL_DEATH_COIL = 33130, + SPELL_SUMMON_WILD_FELHUNTER = 33131, + + // Kiggler the Crazed Spells + SPELL_GREATER_POLYMORPH = 33173, + SPELL_LIGHTNING_BOLT = 36152, + SPELL_ARCANE_SHOCK = 33175, + SPELL_ARCANE_EXPLOSION = 33237, + + // Blindeye the Seer Spells + SPELL_GREATER_PW_SHIELD = 33147, + SPELL_HEAL = 33144, + SPELL_PRAYEROFHEALING = 33152, + + // Krosh Firehand Spells + SPELL_GREATER_FIREBALL = 33051, + SPELL_SPELLSHIELD = 33054, + SPELL_BLAST_WAVE = 33061, +}; + +// High King Maulgar AI +struct boss_high_king_maulgarAI : public ScriptedAI +{ + boss_high_king_maulgarAI(Creature* pCreature) : ScriptedAI(pCreature) + { + m_pInstance = (ScriptedInstance*)pCreature->GetInstanceData(); + Reset(); + } + + ScriptedInstance* m_pInstance; + + uint32 m_uiArcingSmashTimer; + uint32 m_uiMightyBlowTimer; + uint32 m_uiWhirlwindTimer; + uint32 m_uiChargeTimer; + uint32 m_uiFearTimer; + uint32 m_uiCouncilDeathCount; + + bool m_bPhase2; + + void Reset() override + { + m_uiArcingSmashTimer = urand(8000, 14000); + m_uiMightyBlowTimer = urand(15000, 25000); + m_uiWhirlwindTimer = 30000; + m_uiChargeTimer = 2000; + m_uiFearTimer = urand(10000, 25000); + m_uiCouncilDeathCount = 0; + m_bPhase2 = false; + } + + void JustReachedHome() override + { + if (m_pInstance) + m_pInstance->SetData(TYPE_MAULGAR_EVENT, FAIL); + } + + void KilledUnit(Unit* /*pVictim*/) override + { + switch (urand(0, 2)) + { + case 0: DoScriptText(SAY_SLAY1, m_creature); break; + case 1: DoScriptText(SAY_SLAY2, m_creature); break; + case 2: DoScriptText(SAY_SLAY3, m_creature); break; + } + } + + void JustDied(Unit* /*pKiller*/) override + { + DoScriptText(SAY_DEATH, m_creature); + + // Set data to Special on Death + if (m_pInstance) + m_pInstance->SetData(TYPE_MAULGAR_EVENT, SPECIAL); + } + + void Aggro(Unit* /*pWho*/) override + { + DoScriptText(SAY_AGGRO, m_creature); + + if (m_pInstance) + m_pInstance->SetData(TYPE_MAULGAR_EVENT, IN_PROGRESS); + } + + void EventCouncilDeath() + { + switch (++m_uiCouncilDeathCount) + { + case 1: DoScriptText(SAY_OGRE_DEATH1, m_creature); break; + case 2: DoScriptText(SAY_OGRE_DEATH2, m_creature); break; + case 3: DoScriptText(SAY_OGRE_DEATH3, m_creature); break; + case 4: DoScriptText(SAY_OGRE_DEATH4, m_creature); break; + } + } + + void UpdateAI(const uint32 uiDiff) override + { + if (!m_creature->SelectHostileTarget() || !m_creature->getVictim()) + return; + + if (m_uiArcingSmashTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature, SPELL_ARCING_SMASH) == CAST_OK) + m_uiArcingSmashTimer = urand(8000, 12000); + } + else + m_uiArcingSmashTimer -= uiDiff; + + if (m_uiWhirlwindTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature, SPELL_WHIRLWIND) == CAST_OK) + m_uiWhirlwindTimer = urand(30000, 40000); + } + else + m_uiWhirlwindTimer -= uiDiff; + + if (m_uiMightyBlowTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature->getVictim(), SPELL_MIGHTY_BLOW) == CAST_OK) + m_uiMightyBlowTimer = urand(20000, 35000); + } + else + m_uiMightyBlowTimer -= uiDiff; + + if (!m_bPhase2 && m_creature->GetHealthPercent() < 50.0f) + { + if (DoCastSpellIfCan(m_creature, SPELL_FLURRY) == CAST_OK) + { + DoScriptText(SAY_ENRAGE, m_creature); + m_bPhase2 = true; + } + } + + if (m_bPhase2) + { + if (m_uiChargeTimer < uiDiff) + { + if (Unit* pTarget = m_creature->SelectAttackingTarget(ATTACKING_TARGET_RANDOM, 1)) + { + if (DoCastSpellIfCan(pTarget, SPELL_CHARGE) == CAST_OK) + m_uiChargeTimer = urand(14000, 20000); + } + } + else + m_uiChargeTimer -= uiDiff; + + if (m_uiFearTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature, SPELL_FEAR) == CAST_OK) + m_uiFearTimer = urand(20000, 35000); + } + else + m_uiFearTimer -= uiDiff; + } + + DoMeleeAttackIfReady(); + } +}; + +// Base AI for every council member +struct Council_Base_AI : public ScriptedAI +{ + Council_Base_AI(Creature* pCreature) : ScriptedAI(pCreature) + { + m_pInstance = (ScriptedInstance*)pCreature->GetInstanceData(); + } + + ScriptedInstance* m_pInstance; + + void JustDied(Unit* /*pVictim*/) override + { + if (!m_pInstance) + return; + + Creature* pMaulgar = m_pInstance->GetSingleCreatureFromStorage(NPC_MAULGAR); + if (pMaulgar && pMaulgar->IsAlive()) + { + if (boss_high_king_maulgarAI* pMaulgarAI = dynamic_cast(pMaulgar->AI())) + pMaulgarAI->EventCouncilDeath(); + } + + // Set data to Special on Death + m_pInstance->SetData(TYPE_MAULGAR_EVENT, SPECIAL); + } +}; + +// Olm The Summoner AI +struct boss_olm_the_summonerAI : public Council_Base_AI +{ + boss_olm_the_summonerAI(Creature* pCreature) : Council_Base_AI(pCreature) {Reset();} + + uint32 m_uiDarkDecayTimer; + uint32 m_uiDeathCoilTimer; + uint32 m_uiSummonTimer; + + void Reset() override + { + m_uiDarkDecayTimer = 18000; + m_uiDeathCoilTimer = 14000; + m_uiSummonTimer = 10000; + } + + void UpdateAI(const uint32 uiDiff) override + { + if (!m_creature->SelectHostileTarget() || !m_creature->getVictim()) + return; + + if (m_uiDarkDecayTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature->getVictim(), SPELL_DARK_DECAY) == CAST_OK) + m_uiDarkDecayTimer = 20000; + } + else + m_uiDarkDecayTimer -= uiDiff; + + if (m_uiDeathCoilTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature->getVictim(), SPELL_DEATH_COIL) == CAST_OK) + m_uiDeathCoilTimer = urand(8000, 13000); + } + else + m_uiDeathCoilTimer -= uiDiff; + + if (m_uiSummonTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature, SPELL_SUMMON_WILD_FELHUNTER) == CAST_OK) + m_uiSummonTimer = urand(25000, 35000); + } + else + m_uiSummonTimer -= uiDiff; + + DoMeleeAttackIfReady(); + } +}; + +// Kiggler The Crazed AI +struct boss_kiggler_the_crazedAI : public Council_Base_AI +{ + boss_kiggler_the_crazedAI(Creature* pCreature) : Council_Base_AI(pCreature) {Reset();} + + uint32 m_uiGreatherPolymorphTimer; + uint32 m_uiLightningBoltTimer; + uint32 m_uiArcaneShockTimer; + uint32 m_uiArcaneExplosionTimer; + + void Reset() override + { + m_uiGreatherPolymorphTimer = 15000; + m_uiLightningBoltTimer = 10000; + m_uiArcaneShockTimer = 20000; + m_uiArcaneExplosionTimer = 30000; + } + + void SpellHitTarget(Unit* pVictim, const SpellEntry* pSpell) override + { + // Spell currently not supported by core. Knock back effect should lower threat + // Workaround in script: + if (pSpell->Id == SPELL_ARCANE_EXPLOSION) + { + if (pVictim->GetTypeId() != TYPEID_PLAYER) + return; + + m_creature->GetThreatManager().modifyThreatPercent(pVictim, -75); + } + } + + void AttackStart(Unit* pWho) override + { + if (!pWho) + return; + + if (m_creature->Attack(pWho, false)) + { + m_creature->AddThreat(pWho); + m_creature->SetInCombatWith(pWho); + pWho->SetInCombatWith(m_creature); + + m_creature->GetMotionMaster()->MoveChase(pWho, 20.0f); + } + } + + void UpdateAI(const uint32 uiDiff) override + { + if (!m_creature->SelectHostileTarget() || !m_creature->getVictim()) + return; + + if (m_uiGreatherPolymorphTimer < uiDiff) + { + if (Unit* pTarget = m_creature->SelectAttackingTarget(ATTACKING_TARGET_RANDOM, 0)) + { + if (DoCastSpellIfCan(pTarget, SPELL_GREATER_POLYMORPH) == CAST_OK) + m_uiGreatherPolymorphTimer = urand(15000, 20000); + } + } + else + m_uiGreatherPolymorphTimer -= uiDiff; + + if (m_uiLightningBoltTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature->getVictim(), SPELL_LIGHTNING_BOLT) == CAST_OK) + m_uiLightningBoltTimer = urand(2500, 4000); + } + else + m_uiLightningBoltTimer -= uiDiff; + + if (m_uiArcaneShockTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature->getVictim(), SPELL_ARCANE_SHOCK) == CAST_OK) + m_uiArcaneShockTimer = urand(15000, 20000); + } + else + m_uiArcaneShockTimer -= uiDiff; + + if (m_uiArcaneExplosionTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature, SPELL_ARCANE_EXPLOSION) == CAST_OK) + m_uiArcaneExplosionTimer = 30000; + } + else + m_uiArcaneExplosionTimer -= uiDiff; + + DoMeleeAttackIfReady(); + } +}; + +// Blindeye The Seer AI +struct boss_blindeye_the_seerAI : public Council_Base_AI +{ + boss_blindeye_the_seerAI(Creature* pCreature) : Council_Base_AI(pCreature) {Reset();} + + uint32 m_uiGreaterPowerWordShieldTimer; + uint32 m_uiHealTimer; + uint32 m_uiPrayerofHealingTimer; + + void Reset() override + { + m_uiGreaterPowerWordShieldTimer = 5000; + m_uiHealTimer = urand(25000, 40000); + m_uiPrayerofHealingTimer = urand(45000, 55000); + } + + void UpdateAI(const uint32 uiDiff) override + { + if (!m_creature->SelectHostileTarget() || !m_creature->getVictim()) + return; + + if (m_uiGreaterPowerWordShieldTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature, SPELL_GREATER_PW_SHIELD) == CAST_OK) + m_uiGreaterPowerWordShieldTimer = urand(30000, 40000); + } + else + m_uiGreaterPowerWordShieldTimer -= uiDiff; + + if (m_uiHealTimer < uiDiff) + { + if (Unit* pTarget = DoSelectLowestHpFriendly(50.0f)) + { + if (DoCastSpellIfCan(pTarget, SPELL_HEAL) == CAST_OK) + m_uiHealTimer = urand(15000, 40000); + } + } + else + m_uiHealTimer -= uiDiff; + + if (m_uiPrayerofHealingTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature, SPELL_PRAYEROFHEALING) == CAST_OK) + m_uiPrayerofHealingTimer = urand(35000, 50000); + } + else + m_uiPrayerofHealingTimer -= uiDiff; + + DoMeleeAttackIfReady(); + } +}; + +// Krosh Firehand AI +struct boss_krosh_firehandAI : public Council_Base_AI +{ + boss_krosh_firehandAI(Creature* pCreature) : Council_Base_AI(pCreature) {Reset();} + + uint32 m_uiGreaterFireballTimer; + uint32 m_uiSpellShieldTimer; + uint32 m_uiBlastWaveTimer; + + void Reset() override + { + m_uiGreaterFireballTimer = 4000; + m_uiSpellShieldTimer = 1000; + m_uiBlastWaveTimer = 12000; + } + + void AttackStart(Unit* pWho) override + { + if (!pWho) + return; + + if (m_creature->Attack(pWho, true)) + { + m_creature->AddThreat(pWho); + m_creature->SetInCombatWith(pWho); + pWho->SetInCombatWith(m_creature); + + m_creature->GetMotionMaster()->MoveChase(pWho, 30.0f); + } + } + + void UpdateAI(const uint32 uiDiff) override + { + if (!m_creature->SelectHostileTarget() || !m_creature->getVictim()) + return; + + if (m_uiGreaterFireballTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature->getVictim(), SPELL_GREATER_FIREBALL) == CAST_OK) + m_uiGreaterFireballTimer = 3200; + } + else + m_uiGreaterFireballTimer -= uiDiff; + + if (m_uiSpellShieldTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature, SPELL_SPELLSHIELD) == CAST_OK) + m_uiSpellShieldTimer = 30000; + } + else + m_uiSpellShieldTimer -= uiDiff; + + if (m_uiBlastWaveTimer < uiDiff) + { + GuidVector vGuids; + m_creature->FillGuidsListFromThreatList(vGuids); + for (GuidVector::const_iterator i = vGuids.begin(); i != vGuids.end(); ++i) + { + Unit* pUnit = m_creature->GetMap()->GetUnit(*i); + + if (pUnit && pUnit->IsWithinDistInMap(m_creature, 15.0f)) + { + DoCastSpellIfCan(m_creature, SPELL_BLAST_WAVE, CAST_INTERRUPT_PREVIOUS); + break; + } + } + + m_uiBlastWaveTimer = 6000; + } + else + m_uiBlastWaveTimer -= uiDiff; + } +}; + +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* pNewScript; + + pNewScript = new Script; + pNewScript->Name = "boss_high_king_maulgar"; + pNewScript->GetAI = &GetAI_boss_high_king_maulgar; + pNewScript->RegisterSelf(); + + pNewScript = new Script; + pNewScript->Name = "boss_kiggler_the_crazed"; + pNewScript->GetAI = &GetAI_boss_kiggler_the_crazed; + pNewScript->RegisterSelf(); + + pNewScript = new Script; + pNewScript->Name = "boss_blindeye_the_seer"; + pNewScript->GetAI = &GetAI_boss_blindeye_the_seer; + pNewScript->RegisterSelf(); + + pNewScript = new Script; + pNewScript->Name = "boss_olm_the_summoner"; + pNewScript->GetAI = &GetAI_boss_olm_the_summoner; + pNewScript->RegisterSelf(); + + pNewScript = new Script; + pNewScript->Name = "boss_krosh_firehand"; + pNewScript->GetAI = &GetAI_boss_krosh_firehand; + pNewScript->RegisterSelf(); +} diff --git a/src/modules/SD2/scripts/outland/gruuls_lair/gruuls_lair.h b/src/modules/SD2/scripts/outland/gruuls_lair/gruuls_lair.h new file mode 100644 index 000000000..47f4359bc --- /dev/null +++ b/src/modules/SD2/scripts/outland/gruuls_lair/gruuls_lair.h @@ -0,0 +1,52 @@ +/* Copyright (C) 2006 - 2013 ScriptDev2 + * This program is free software licensed under GPL version 2 + * Please see the included DOCS/LICENSE.TXT for more information */ + +#ifndef DEF_GRUULS_LAIR_H +#define DEF_GRUULS_LAIR_H + +enum +{ + MAX_ENCOUNTER = 2, + MAX_COUNCIL = 5, + + // Encounter Status + TYPE_MAULGAR_EVENT = 0, + TYPE_GRUUL_EVENT = 1, + + GO_PORT_GRONN_1 = 183817, // 184468 not in use + GO_PORT_GRONN_2 = 184662, + + // NPC GUIDs + NPC_MAULGAR = 18831, + // NPC_BLINDEYE = 18836, + // NPC_KIGGLER = 18835, + // NPC_KROSH = 18832, + // NPC_OLM = 18834, +}; + +class instance_gruuls_lair : public ScriptedInstance +{ + public: + instance_gruuls_lair(Map* pMap); + + void Initialize() override; + bool IsEncounterInProgress() const override; + + void OnCreatureCreate(Creature* pCreature) override; + void OnObjectCreate(GameObject* pGo) override; + + void SetData(uint32 uiType, uint32 uiData) override; + uint32 GetData(uint32 uiType) const override; + + const char* Save() const override { return m_strSaveData.c_str(); } + void Load(const char* chrIn) override; + + private: + uint32 m_auiEncounter[MAX_ENCOUNTER]; + std::string m_strSaveData; + + uint8 m_uiCouncilMembersDied; +}; + +#endif diff --git a/src/modules/SD2/scripts/outland/gruuls_lair/instance_gruuls_lair.cpp b/src/modules/SD2/scripts/outland/gruuls_lair/instance_gruuls_lair.cpp new file mode 100644 index 000000000..b620907ab --- /dev/null +++ b/src/modules/SD2/scripts/outland/gruuls_lair/instance_gruuls_lair.cpp @@ -0,0 +1,157 @@ +/* Copyright (C) 2006 - 2013 ScriptDev2 + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along 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 "gruuls_lair.h" + +/* Gruuls Lair encounters: +1 - High King Maulgar event +2 - Gruul event +*/ + +instance_gruuls_lair::instance_gruuls_lair(Map* pMap) : ScriptedInstance(pMap), + m_uiCouncilMembersDied(0) +{ + Initialize(); +} + +void instance_gruuls_lair::Initialize() +{ + memset(&m_auiEncounter, 0, sizeof(m_auiEncounter)); +} + +bool instance_gruuls_lair::IsEncounterInProgress() const +{ + for (uint8 i = 0; i < MAX_ENCOUNTER; ++i) + if (m_auiEncounter[i] == IN_PROGRESS) + return true; + + return false; +} + +void instance_gruuls_lair::OnCreatureCreate(Creature* pCreature) +{ + if (pCreature->GetEntry() == NPC_MAULGAR) + m_mNpcEntryGuidStore[NPC_MAULGAR] = pCreature->GetObjectGuid(); +} + +void instance_gruuls_lair::OnObjectCreate(GameObject* pGo) +{ + switch (pGo->GetEntry()) + { + case GO_PORT_GRONN_1: + if (m_auiEncounter[TYPE_MAULGAR_EVENT] == DONE) + pGo->SetGoState(GO_STATE_ACTIVE); + break; + case GO_PORT_GRONN_2: + break; + + default: + return; + } + m_mGoEntryGuidStore[pGo->GetEntry()] = pGo->GetObjectGuid(); +} + +void instance_gruuls_lair::SetData(uint32 uiType, uint32 uiData) +{ + switch (uiType) + { + case TYPE_MAULGAR_EVENT: + if (uiData == SPECIAL) + { + ++m_uiCouncilMembersDied; + + if (m_uiCouncilMembersDied == MAX_COUNCIL) + SetData(TYPE_MAULGAR_EVENT, DONE); + // Don't store special data + break; + } + if (uiData == FAIL) + m_uiCouncilMembersDied = 0; + if (uiData == DONE) + DoUseDoorOrButton(GO_PORT_GRONN_1); + m_auiEncounter[uiType] = uiData; + break; + case TYPE_GRUUL_EVENT: + DoUseDoorOrButton(GO_PORT_GRONN_2); + m_auiEncounter[uiType] = uiData; + break; + } + + if (uiData == DONE) + { + OUT_SAVE_INST_DATA; + + std::ostringstream saveStream; + saveStream << m_auiEncounter[0] << " " << m_auiEncounter[1]; + + m_strSaveData = saveStream.str(); + + SaveToDB(); + OUT_SAVE_INST_DATA_COMPLETE; + } +} + +uint32 instance_gruuls_lair::GetData(uint32 uiType) const +{ + if (uiType < MAX_ENCOUNTER) + return m_auiEncounter[uiType]; + + return 0; +} + +void instance_gruuls_lair::Load(const char* chrIn) +{ + if (!chrIn) + { + OUT_LOAD_INST_DATA_FAIL; + return; + } + + OUT_LOAD_INST_DATA(chrIn); + + std::istringstream loadStream(chrIn); + + loadStream >> m_auiEncounter[0] >> m_auiEncounter[1]; + + 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_gruuls_lair(Map* pMap) +{ + return new instance_gruuls_lair(pMap); +} + +void AddSC_instance_gruuls_lair() +{ + Script* pNewScript; + + pNewScript = new Script; + pNewScript->Name = "instance_gruuls_lair"; + pNewScript->GetInstanceData = &GetInstanceData_instance_gruuls_lair; + pNewScript->RegisterSelf(); +} diff --git a/src/modules/SD2/scripts/outland/hellfire_citadel/blood_furnace/blood_furnace.h b/src/modules/SD2/scripts/outland/hellfire_citadel/blood_furnace/blood_furnace.h new file mode 100644 index 000000000..f4e982acf --- /dev/null +++ b/src/modules/SD2/scripts/outland/hellfire_citadel/blood_furnace/blood_furnace.h @@ -0,0 +1,99 @@ +/* Copyright (C) 2006 - 2013 ScriptDev2 + * 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 + +enum +{ + MAX_ENCOUNTER = 3, + MAX_ORC_WAVES = 4, + + TYPE_THE_MAKER_EVENT = 0, + TYPE_BROGGOK_EVENT = 1, + TYPE_KELIDAN_EVENT = 2, + + // NPC_THE_MAKER = 17381, + NPC_BROGGOK = 17380, + NPC_KELIDAN_THE_BREAKER = 17377, + NPC_NASCENT_FEL_ORC = 17398, // Used in the Broggok event + NPC_MAGTHERIDON = 21174, + NPC_SHADOWMOON_CHANNELER = 17653, + + GO_DOOR_FINAL_EXIT = 181766, + GO_DOOR_MAKER_FRONT = 181811, + GO_DOOR_MAKER_REAR = 181812, + GO_DOOR_BROGGOK_FRONT = 181822, + GO_DOOR_BROGGOK_REAR = 181819, + GO_DOOR_KELIDAN_EXIT = 181823, + + // GO_PRISON_CELL_MAKER1 = 181813, // The maker cell front right + // GO_PRISON_CELL_MAKER2 = 181814, // The maker cell back right + // GO_PRISON_CELL_MAKER3 = 181816, // The maker cell front left + // GO_PRISON_CELL_MAKER4 = 181815, // The maker cell back left + + GO_PRISON_CELL_BROGGOK_1 = 181817, // Broggok cell back left (NE) + GO_PRISON_CELL_BROGGOK_2 = 181818, // Broggok cell back right (SE) + GO_PRISON_CELL_BROGGOK_3 = 181820, // Broggok cell front left (NW) + GO_PRISON_CELL_BROGGOK_4 = 181821, // Broggok cell front right (SW) + + SAY_BROGGOK_INTRO = -1542015, +}; + +// Random Magtheridon taunt +static const int32 aRandomTaunt[] = { -1544000, -1544001, -1544002, -1544003, -1544004, -1544005}; + +struct BroggokEventInfo +{ + BroggokEventInfo() : m_bIsCellOpened(false), m_uiKilledOrcCount(0) {} + + ObjectGuid m_cellGuid; + bool m_bIsCellOpened; + uint8 m_uiKilledOrcCount; + GuidSet m_sSortedOrcGuids; +}; + +class instance_blood_furnace : public ScriptedInstance +{ + public: + instance_blood_furnace(Map* pMap); + + void Initialize() override; + + void OnCreatureCreate(Creature* pCreature) override; + void OnObjectCreate(GameObject* pGo) override; + + void OnCreatureDeath(Creature* pCreature) override; + void OnCreatureEvade(Creature* pCreature); + + void SetData(uint32 uiType, uint32 uiData) override; + uint32 GetData(uint32 uiType) const override; + + void Update(uint32 uiDiff) override; + + void Load(const char* chrIn) override; + const char* Save() const override { return m_strInstData.c_str(); } + + void GetMovementDistanceForIndex(uint32 uiIndex, float& dx, float& dy); + + void GetKelidanAddList(GuidList& lList) { lList = m_lChannelersGuids; m_lChannelersGuids.clear(); } + + private: + void DoSortBroggokOrcs(); + void DoNextBroggokEventPhase(); + + uint32 m_auiEncounter[MAX_ENCOUNTER]; + std::string m_strInstData; + + BroggokEventInfo m_aBroggokEvent[MAX_ORC_WAVES]; + + uint32 m_uiBroggokEventTimer; // Timer for opening the event cages; only on heroic mode = 30 secs + uint32 m_uiBroggokEventPhase; + uint32 m_uiRandYellTimer; // Random yell for Magtheridon + + GuidList m_luiNascentOrcGuids; + GuidList m_lChannelersGuids; +}; + +#endif diff --git a/src/modules/SD2/scripts/outland/hellfire_citadel/blood_furnace/boss_broggok.cpp b/src/modules/SD2/scripts/outland/hellfire_citadel/blood_furnace/boss_broggok.cpp new file mode 100644 index 000000000..7d202a963 --- /dev/null +++ b/src/modules/SD2/scripts/outland/hellfire_citadel/blood_furnace/boss_broggok.cpp @@ -0,0 +1,189 @@ +/* Copyright (C) 2006 - 2013 ScriptDev2 + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with 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 "blood_furnace.h" + +enum +{ + SAY_AGGRO = -1542008, + + SPELL_SLIME_SPRAY = 30913, + SPELL_SLIME_SPRAY_H = 38458, + SPELL_POISON_CLOUD = 30916, + SPELL_POISON_BOLT = 30917, + SPELL_POISON_BOLT_H = 38459, + + SPELL_POISON = 30914, + + POINT_EVENT_COMBAT = 1, +}; + +struct boss_broggokAI : public ScriptedAI +{ + boss_broggokAI(Creature* pCreature) : ScriptedAI(pCreature) + { + m_pInstance = (instance_blood_furnace*)pCreature->GetInstanceData(); + m_bIsRegularMode = pCreature->GetMap()->IsRegularDifficulty(); + Reset(); + } + + instance_blood_furnace* m_pInstance; + bool m_bIsRegularMode; + + uint32 m_uiAcidSprayTimer; + uint32 m_uiPoisonSpawnTimer; + uint32 m_uiPoisonBoltTimer; + + void Reset() override + { + m_uiAcidSprayTimer = 10000; + m_uiPoisonSpawnTimer = 5000; + m_uiPoisonBoltTimer = 7000; + } + + void Aggro(Unit* /*pWho*/) override + { + DoScriptText(SAY_AGGRO, m_creature); + + if (m_pInstance) + m_pInstance->SetData(TYPE_BROGGOK_EVENT, IN_PROGRESS); + } + + void JustSummoned(Creature* pSummoned) override + { + // ToDo: set correct flags and data in DB!!! + pSummoned->setFaction(16); + pSummoned->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE); + pSummoned->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE); + pSummoned->CastSpell(pSummoned, SPELL_POISON, false, NULL, NULL, m_creature->GetObjectGuid()); + } + + void JustDied(Unit* /*pWho*/) override + { + if (m_pInstance) + m_pInstance->SetData(TYPE_BROGGOK_EVENT, DONE); + } + + void EnterEvadeMode() override + { + m_creature->RemoveAllAurasOnEvade(); + m_creature->DeleteThreatList(); + m_creature->CombatStop(true); + m_creature->LoadCreatureAddon(true); + + m_creature->SetLootRecipient(NULL); + + Reset(); + + if (!m_creature->IsAlive()) + return; + + if (m_pInstance) + { + float dx, dy; + float fRespX, fRespY, fRespZ; + m_creature->GetRespawnCoord(fRespX, fRespY, fRespZ); + m_pInstance->GetMovementDistanceForIndex(4, dx, dy); + m_creature->GetMotionMaster()->MovePoint(POINT_EVENT_COMBAT, dx, dy, fRespZ); + } + else + m_creature->GetMotionMaster()->MoveTargetedHome(); + } + + // Reset Orientation + void MovementInform(uint32 uiMotionType, uint32 uiPointId) override + { + if (uiMotionType != POINT_MOTION_TYPE || uiPointId != POINT_EVENT_COMBAT) + return; + + if (GameObject* pFrontDoor = m_pInstance->GetSingleGameObjectFromStorage(GO_DOOR_BROGGOK_FRONT)) + m_creature->SetFacingToObject(pFrontDoor); + } + + void UpdateAI(const uint32 uiDiff) override + { + if (!m_creature->SelectHostileTarget() || !m_creature->getVictim()) + return; + + if (m_uiAcidSprayTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature, m_bIsRegularMode ? SPELL_SLIME_SPRAY : SPELL_SLIME_SPRAY_H) == CAST_OK) + m_uiAcidSprayTimer = urand(4000, 12000); + } + else + m_uiAcidSprayTimer -= uiDiff; + + if (m_uiPoisonBoltTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature, m_bIsRegularMode ? SPELL_POISON_BOLT : SPELL_POISON_BOLT_H) == CAST_OK) + m_uiPoisonBoltTimer = urand(4000, 12000); + } + else + m_uiPoisonBoltTimer -= uiDiff; + + if (m_uiPoisonSpawnTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature, SPELL_POISON_CLOUD) == CAST_OK) + m_uiPoisonSpawnTimer = 20000; + } + else + m_uiPoisonSpawnTimer -= uiDiff; + + DoMeleeAttackIfReady(); + } +}; + +struct mob_broggok_poisoncloudAI : public ScriptedAI +{ + mob_broggok_poisoncloudAI(Creature* pCreature) : ScriptedAI(pCreature) {Reset();} + + void Reset() override { } + void MoveInLineOfSight(Unit* /*who*/) override { } + void AttackStart(Unit* /*who*/) override { } +}; + +CreatureAI* GetAI_boss_broggok(Creature* pCreature) +{ + return new boss_broggokAI(pCreature); +} + +CreatureAI* GetAI_mob_broggok_poisoncloud(Creature* pCreature) +{ + return new mob_broggok_poisoncloudAI(pCreature); +} + +void AddSC_boss_broggok() +{ + Script* pNewScript; + + pNewScript = new Script; + pNewScript->Name = "boss_broggok"; + pNewScript->GetAI = &GetAI_boss_broggok; + pNewScript->RegisterSelf(); + + pNewScript = new Script; + pNewScript->Name = "mob_broggok_poisoncloud"; + pNewScript->GetAI = &GetAI_mob_broggok_poisoncloud; + pNewScript->RegisterSelf(); +} diff --git a/src/modules/SD2/scripts/outland/hellfire_citadel/blood_furnace/boss_kelidan_the_breaker.cpp b/src/modules/SD2/scripts/outland/hellfire_citadel/blood_furnace/boss_kelidan_the_breaker.cpp new file mode 100644 index 000000000..d86d1e8d7 --- /dev/null +++ b/src/modules/SD2/scripts/outland/hellfire_citadel/blood_furnace/boss_kelidan_the_breaker.cpp @@ -0,0 +1,416 @@ +/* Copyright (C) 2006 - 2013 ScriptDev2 + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with 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 "blood_furnace.h" + +enum +{ + MAX_ADDS = 5, + + SAY_MAGTHERIDON_INTRO = -1542016, // Yell by Magtheridon + SAY_WAKE = -1542000, + SAY_ADD_AGGRO_1 = -1542001, + SAY_ADD_AGGRO_2 = -1542002, + SAY_ADD_AGGRO_3 = -1542003, + SAY_KILL_1 = -1542004, + SAY_KILL_2 = -1542005, + SAY_NOVA = -1542006, + SAY_DIE = -1542007, + + SPELL_CORRUPTION = 30938, + SPELL_EVOCATION = 30935, + + SPELL_FIRE_NOVA = 33132, + SPELL_FIRE_NOVA_H = 37371, + + SPELL_SHADOW_BOLT_VOLLEY = 28599, + SPELL_SHADOW_BOLT_VOLLEY_H = 40070, + + SPELL_BURNING_NOVA = 30940, + SPELL_VORTEX = 37370, + + SPELL_CHANNELING = 39123, +}; + +struct SortByAngle +{ + SortByAngle(WorldObject const* pRef): m_pRef(pRef) {} + bool operator()(WorldObject* pLeft, WorldObject* pRight) + { + return m_pRef->GetAngle(pLeft) < m_pRef->GetAngle(pRight); + } + WorldObject const* m_pRef; +}; + +struct boss_kelidan_the_breakerAI : public ScriptedAI +{ + boss_kelidan_the_breakerAI(Creature* pCreature) : ScriptedAI(pCreature) + { + m_pInstance = (instance_blood_furnace*)pCreature->GetInstanceData(); + m_bIsRegularMode = pCreature->GetMap()->IsRegularDifficulty(); + m_uiSetupAddsTimer = 100; + m_bDidMagtheridonYell = false; + DoCastSpellIfCan(m_creature, SPELL_EVOCATION); + Reset(); + } + + instance_blood_furnace* m_pInstance; + + bool m_bIsRegularMode; + + uint32 m_uiShadowVolleyTimer; + uint32 m_uiBurningNovaTimer; + uint32 m_uiFirenovaTimer; + uint32 m_uiCorruptionTimer; + uint32 m_uiSetupAddsTimer; + uint8 m_uiKilledAdds; + bool m_bDidMagtheridonYell; + + GuidVector m_vAddGuids; + + void Reset() override + { + m_uiShadowVolleyTimer = 1000; + m_uiBurningNovaTimer = 15000; + m_uiCorruptionTimer = 5000; + m_uiFirenovaTimer = 0; + m_uiKilledAdds = 0; + } + + void MoveInLineOfSight(Unit* pWho) override + { + if (!m_bDidMagtheridonYell && pWho->GetTypeId() == TYPEID_PLAYER && !((Player*)pWho)->isGameMaster() && m_creature->_IsWithinDist(pWho, 73.0f, false)) + { + if (m_pInstance) + m_pInstance->DoOrSimulateScriptTextForThisInstance(SAY_MAGTHERIDON_INTRO, NPC_MAGTHERIDON); + + m_bDidMagtheridonYell = true; + } + + ScriptedAI::MoveInLineOfSight(pWho); + } + + void Aggro(Unit* /*pWho*/) override + { + DoScriptText(SAY_WAKE, m_creature); + } + + void KilledUnit(Unit* /*pVictim*/) override + { + if (urand(0, 1)) + return; + + DoScriptText(urand(0, 1) ? SAY_KILL_1 : SAY_KILL_2, m_creature); + } + + void JustDied(Unit* /*pKiller*/) override + { + DoScriptText(SAY_DIE, m_creature); + + if (m_pInstance) + m_pInstance->SetData(TYPE_KELIDAN_EVENT, DONE); + } + + void JustReachedHome() override + { + if (m_pInstance) + m_pInstance->SetData(TYPE_KELIDAN_EVENT, FAIL); + + DoCastSpellIfCan(m_creature, SPELL_EVOCATION); + m_uiSetupAddsTimer = 2000; + } + + void DoSetupAdds() + { + m_uiSetupAddsTimer = 0; + + if (!m_pInstance) + return; + + GuidList lAddGuids; + m_pInstance->GetKelidanAddList(lAddGuids); + + // Sort Adds to vector if not already done + if (!lAddGuids.empty()) + { + m_vAddGuids.reserve(lAddGuids.size()); + std::list lAdds; + for (GuidList::const_iterator itr = lAddGuids.begin(); itr != lAddGuids.end(); ++itr) + { + if (Creature* pAdd = m_pInstance->instance->GetCreature(*itr)) + lAdds.push_back(pAdd); + } + // Sort them by angle + lAdds.sort(SortByAngle(m_creature)); + for (std::list::const_iterator itr = lAdds.begin(); itr != lAdds.end(); ++itr) + m_vAddGuids.push_back((*itr)->GetObjectGuid()); + } + + // Respawn killed adds and reset counter + m_uiKilledAdds = 0; + for (GuidVector::const_iterator itr = m_vAddGuids.begin(); itr != m_vAddGuids.end(); ++itr) + { + Creature* pAdd = m_pInstance->instance->GetCreature(*itr); + if (pAdd && !pAdd->IsAlive()) + pAdd->Respawn(); + } + + // Cast pentagram + uint8 s = m_vAddGuids.size(); + for (uint8 i = 0; i < s; ++i) + { + Creature* pCaster = m_pInstance->instance->GetCreature(m_vAddGuids[i]); + Creature* pTarget = m_pInstance->instance->GetCreature(m_vAddGuids[(i + 2) % s]); + if (pCaster && pTarget) + pCaster->CastSpell(pTarget, SPELL_CHANNELING, false); + } + } + + void AddJustAggroed(Unit* pWho) + { + // Let all adds attack + for (GuidVector::const_iterator itr = m_vAddGuids.begin(); itr != m_vAddGuids.end(); ++itr) + { + Creature* pAdd = m_creature->GetMap()->GetCreature(*itr); + if (pAdd && !pAdd->getVictim()) + pAdd->AI()->AttackStart(pWho); + } + } + + void AddJustReachedHome() + { + m_uiSetupAddsTimer = 2000; + } + + void AddJustDied(Unit* pKiller) + { + ++m_uiKilledAdds; + if (m_uiKilledAdds == MAX_ADDS) + { + m_creature->InterruptNonMeleeSpells(true); + AttackStart(pKiller); + } + } + + void UpdateAI(const uint32 uiDiff) override + { + if (m_uiSetupAddsTimer) + { + if (m_uiSetupAddsTimer <= uiDiff) + DoSetupAdds(); + else + m_uiSetupAddsTimer -= uiDiff; + } + + if (!m_creature->SelectHostileTarget() || !m_creature->getVictim()) + return; + + if (m_uiFirenovaTimer) + { + if (m_uiFirenovaTimer <= uiDiff) + { + if (DoCastSpellIfCan(m_creature, m_bIsRegularMode ? SPELL_FIRE_NOVA : SPELL_FIRE_NOVA_H) == CAST_OK) + { + m_uiFirenovaTimer = 0; + m_uiShadowVolleyTimer = 2000; + } + } + else + m_uiFirenovaTimer -= uiDiff; + } + + if (m_uiShadowVolleyTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature, m_bIsRegularMode ? SPELL_SHADOW_BOLT_VOLLEY : SPELL_SHADOW_BOLT_VOLLEY_H) == CAST_OK) + m_uiShadowVolleyTimer = urand(5000, 13000); + } + else + m_uiShadowVolleyTimer -= uiDiff; + + if (m_uiCorruptionTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature, SPELL_CORRUPTION) == CAST_OK) + m_uiCorruptionTimer = urand(30000, 50000); + } + else + m_uiCorruptionTimer -= uiDiff; + + if (m_uiBurningNovaTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature, SPELL_BURNING_NOVA, CAST_TRIGGERED) == CAST_OK) + { + DoScriptText(SAY_NOVA, m_creature); + + if (!m_bIsRegularMode) + DoCastSpellIfCan(m_creature, SPELL_VORTEX, CAST_TRIGGERED); + + m_uiBurningNovaTimer = urand(20000, 28000); + m_uiFirenovaTimer = 5000; + } + } + else + m_uiBurningNovaTimer -= uiDiff; + + DoMeleeAttackIfReady(); + } +}; + +CreatureAI* GetAI_boss_kelidan_the_breaker(Creature* pCreature) +{ + return new boss_kelidan_the_breakerAI(pCreature); +} + +/*###### +## mob_shadowmoon_channeler +######*/ + +enum +{ + SPELL_SHADOW_BOLT = 12739, + SPELL_SHADOW_BOLT_H = 15472, + + SPELL_MARK_OF_SHADOW = 30937, +}; + +struct mob_shadowmoon_channelerAI : public ScriptedAI +{ + mob_shadowmoon_channelerAI(Creature* pCreature) : ScriptedAI(pCreature) + { + m_pInstance = (ScriptedInstance*)pCreature->GetInstanceData(); + m_bIsRegularMode = pCreature->GetMap()->IsRegularDifficulty(); + Reset(); + } + + ScriptedInstance* m_pInstance; + bool m_bIsRegularMode; + + uint32 m_uiShadowBoltTimer; + uint32 m_uiMarkOfShadowTimer; + + void Reset() override + { + m_uiShadowBoltTimer = urand(1000, 2000); + m_uiMarkOfShadowTimer = urand(5000, 7000); + } + + void Aggro(Unit* pWho) override + { + m_creature->InterruptNonMeleeSpells(false); + + switch (urand(0, 2)) + { + case 0: + DoScriptText(SAY_ADD_AGGRO_1, m_creature); + break; + case 1: + DoScriptText(SAY_ADD_AGGRO_2, m_creature); + break; + case 2: + DoScriptText(SAY_ADD_AGGRO_3, m_creature); + break; + } + + if (!m_pInstance) + return; + + if (Creature* pKelidan = m_pInstance->GetSingleCreatureFromStorage(NPC_KELIDAN_THE_BREAKER)) + if (boss_kelidan_the_breakerAI* pKelidanAI = dynamic_cast(pKelidan->AI())) + pKelidanAI->AddJustAggroed(pWho); + } + + void JustDied(Unit* pKiller) override + { + if (!m_pInstance) + return; + + if (Creature* pKelidan = m_pInstance->GetSingleCreatureFromStorage(NPC_KELIDAN_THE_BREAKER)) + if (boss_kelidan_the_breakerAI* pKelidanAI = dynamic_cast(pKelidan->AI())) + pKelidanAI->AddJustDied(pKiller); + } + + void JustReachedHome() override + { + if (!m_pInstance) + return; + + if (Creature* pKelidan = m_pInstance->GetSingleCreatureFromStorage(NPC_KELIDAN_THE_BREAKER)) + if (boss_kelidan_the_breakerAI* pKelidanAI = dynamic_cast(pKelidan->AI())) + pKelidanAI->AddJustReachedHome(); + } + + void UpdateAI(const uint32 uiDiff) override + { + if (!m_creature->SelectHostileTarget() || !m_creature->getVictim()) + return; + + if (m_uiMarkOfShadowTimer < uiDiff) + { + if (Unit* pTarget = m_creature->SelectAttackingTarget(ATTACKING_TARGET_RANDOM, 0)) + { + if (DoCastSpellIfCan(pTarget, SPELL_MARK_OF_SHADOW) == CAST_OK) + m_uiMarkOfShadowTimer = urand(15000, 20000); + } + } + else + m_uiMarkOfShadowTimer -= uiDiff; + + if (m_uiShadowBoltTimer < uiDiff) + { + if (Unit* pTarget = m_creature->SelectAttackingTarget(ATTACKING_TARGET_RANDOM, 0)) + { + if (DoCastSpellIfCan(pTarget, m_bIsRegularMode ? SPELL_SHADOW_BOLT : SPELL_SHADOW_BOLT_H) == CAST_OK) + m_uiShadowBoltTimer = urand(5000, 6000); + } + } + else + m_uiShadowBoltTimer -= uiDiff; + + DoMeleeAttackIfReady(); + } +}; + +CreatureAI* GetAI_mob_shadowmoon_channeler(Creature* pCreature) +{ + return new mob_shadowmoon_channelerAI(pCreature); +} + +void AddSC_boss_kelidan_the_breaker() +{ + Script* pNewScript; + + pNewScript = new Script; + pNewScript->Name = "boss_kelidan_the_breaker"; + pNewScript->GetAI = &GetAI_boss_kelidan_the_breaker; + pNewScript->RegisterSelf(); + + pNewScript = new Script; + pNewScript->Name = "mob_shadowmoon_channeler"; + pNewScript->GetAI = &GetAI_mob_shadowmoon_channeler; + pNewScript->RegisterSelf(); +} diff --git a/src/modules/SD2/scripts/outland/hellfire_citadel/blood_furnace/boss_the_maker.cpp b/src/modules/SD2/scripts/outland/hellfire_citadel/blood_furnace/boss_the_maker.cpp new file mode 100644 index 000000000..3a055fdf0 --- /dev/null +++ b/src/modules/SD2/scripts/outland/hellfire_citadel/blood_furnace/boss_the_maker.cpp @@ -0,0 +1,160 @@ +/* Copyright (C) 2006 - 2013 ScriptDev2 + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with 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 "blood_furnace.h" + +enum +{ + SAY_AGGRO_1 = -1542009, + SAY_AGGRO_2 = -1542010, + SAY_AGGRO_3 = -1542011, + SAY_KILL_1 = -1542012, + SAY_KILL_2 = -1542013, + SAY_DIE = -1542014, + + SPELL_ACID_SPRAY = 38153, // heroic 38973 ??? 38153 + SPELL_EXPLODING_BREAKER = 30925, + SPELL_EXPLODING_BREAKER_H = 40059, + SPELL_KNOCKDOWN = 20276, + SPELL_DOMINATION = 30923 +}; + +struct boss_the_makerAI : public ScriptedAI +{ + boss_the_makerAI(Creature* pCreature) : ScriptedAI(pCreature) + { + m_pInstance = (ScriptedInstance*)pCreature->GetInstanceData(); + m_bIsRegularMode = pCreature->GetMap()->IsRegularDifficulty(); + Reset(); + } + + ScriptedInstance* m_pInstance; + bool m_bIsRegularMode; + + uint32 m_uiAcidSprayTimer; + uint32 m_uiExplodingBreakerTimer; + uint32 m_uiDominationTimer; + uint32 m_uiKnockdownTimer; + + void Reset() override + { + m_uiAcidSprayTimer = 15000; + m_uiExplodingBreakerTimer = 6000; + m_uiDominationTimer = 20000; + m_uiKnockdownTimer = 10000; + } + + void Aggro(Unit* /*pWho*/) override + { + switch (urand(0, 2)) + { + case 0: DoScriptText(SAY_AGGRO_1, m_creature); break; + case 1: DoScriptText(SAY_AGGRO_2, m_creature); break; + case 2: DoScriptText(SAY_AGGRO_3, m_creature); break; + } + + if (m_pInstance) + m_pInstance->SetData(TYPE_THE_MAKER_EVENT, IN_PROGRESS); + } + + void JustReachedHome() override + { + if (m_pInstance) + m_pInstance->SetData(TYPE_THE_MAKER_EVENT, FAIL); + } + + void KilledUnit(Unit* /*pVictim*/) override + { + DoScriptText(urand(0, 1) ? SAY_KILL_1 : SAY_KILL_2, m_creature); + } + + void JustDied(Unit* /*pKiller*/) override + { + DoScriptText(SAY_DIE, m_creature); + + if (m_pInstance) + m_pInstance->SetData(TYPE_THE_MAKER_EVENT, DONE); + } + + void UpdateAI(const uint32 uiDiff) override + { + if (!m_creature->SelectHostileTarget() || !m_creature->getVictim()) + return; + + if (m_uiAcidSprayTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature, SPELL_ACID_SPRAY) == CAST_OK) + m_uiAcidSprayTimer = urand(15000, 23000); + } + else + m_uiAcidSprayTimer -= uiDiff; + + if (m_uiExplodingBreakerTimer < uiDiff) + { + if (Unit* pTarget = m_creature->SelectAttackingTarget(ATTACKING_TARGET_RANDOM, 0)) + { + if (DoCastSpellIfCan(pTarget, m_bIsRegularMode ? SPELL_EXPLODING_BREAKER : SPELL_EXPLODING_BREAKER_H) == CAST_OK) + m_uiExplodingBreakerTimer = urand(4000, 12000); + } + } + else + m_uiExplodingBreakerTimer -= uiDiff; + + if (m_uiDominationTimer < uiDiff) + { + if (Unit* pTarget = m_creature->SelectAttackingTarget(ATTACKING_TARGET_RANDOM, 1)) + { + if (DoCastSpellIfCan(pTarget, SPELL_DOMINATION) == CAST_OK) + m_uiDominationTimer = urand(15000, 25000); + } + } + else + m_uiDominationTimer -= uiDiff; + + if (m_uiKnockdownTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature->getVictim(), SPELL_KNOCKDOWN) == CAST_OK) + m_uiKnockdownTimer = urand(4000, 12000); + } + else + m_uiKnockdownTimer -= uiDiff; + + DoMeleeAttackIfReady(); + } +}; + +CreatureAI* GetAI_boss_the_makerAI(Creature* pCreature) +{ + return new boss_the_makerAI(pCreature); +} + +void AddSC_boss_the_maker() +{ + Script* pNewScript; + + pNewScript = new Script; + pNewScript->Name = "boss_the_maker"; + pNewScript->GetAI = &GetAI_boss_the_makerAI; + pNewScript->RegisterSelf(); +} diff --git a/src/modules/SD2/scripts/outland/hellfire_citadel/blood_furnace/instance_blood_furnace.cpp b/src/modules/SD2/scripts/outland/hellfire_citadel/blood_furnace/instance_blood_furnace.cpp new file mode 100644 index 000000000..16f870399 --- /dev/null +++ b/src/modules/SD2/scripts/outland/hellfire_citadel/blood_furnace/instance_blood_furnace.cpp @@ -0,0 +1,423 @@ +/* Copyright (C) 2006 - 2013 ScriptDev2 + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along 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: 75 +SDComment: +SDCategory: Blood Furnace +EndScriptData */ + +#include "precompiled.h" +#include "blood_furnace.h" + +instance_blood_furnace::instance_blood_furnace(Map* pMap) : ScriptedInstance(pMap), + m_uiBroggokEventTimer(30000), + m_uiBroggokEventPhase(0), + m_uiRandYellTimer(90000) +{ + Initialize(); +} + +void instance_blood_furnace::Initialize() +{ + memset(&m_auiEncounter, 0, sizeof(m_auiEncounter)); +} + +void instance_blood_furnace::OnCreatureCreate(Creature* pCreature) +{ + switch (pCreature->GetEntry()) + { + case NPC_BROGGOK: + case NPC_KELIDAN_THE_BREAKER: + case NPC_MAGTHERIDON: + m_mNpcEntryGuidStore[pCreature->GetEntry()] = pCreature->GetObjectGuid(); + break; + + case NPC_NASCENT_FEL_ORC: + m_luiNascentOrcGuids.push_back(pCreature->GetObjectGuid()); + break; + case NPC_SHADOWMOON_CHANNELER: + m_lChannelersGuids.push_back(pCreature->GetObjectGuid()); + break; + } +} + +void instance_blood_furnace::OnObjectCreate(GameObject* pGo) +{ + switch (pGo->GetEntry()) + { + case GO_DOOR_MAKER_FRONT: // the maker front door + break; + case GO_DOOR_MAKER_REAR: // the maker rear door + if (m_auiEncounter[TYPE_THE_MAKER_EVENT] == DONE) + pGo->SetGoState(GO_STATE_ACTIVE); + break; + case GO_DOOR_BROGGOK_FRONT: // broggok front door + break; + case GO_DOOR_BROGGOK_REAR: // broggok rear door + if (m_auiEncounter[TYPE_BROGGOK_EVENT] == DONE) + pGo->SetGoState(GO_STATE_ACTIVE); + break; + case GO_DOOR_KELIDAN_EXIT: // kelidan exit door + if (m_auiEncounter[TYPE_KELIDAN_EVENT] == DONE) + pGo->SetGoState(GO_STATE_ACTIVE); + break; + case GO_DOOR_FINAL_EXIT: // final exit door + if (m_auiEncounter[TYPE_KELIDAN_EVENT] == DONE) + pGo->SetGoState(GO_STATE_ACTIVE); + break; + + case GO_PRISON_CELL_BROGGOK_1: m_aBroggokEvent[0].m_cellGuid = pGo->GetObjectGuid(); return; + case GO_PRISON_CELL_BROGGOK_2: m_aBroggokEvent[1].m_cellGuid = pGo->GetObjectGuid(); return; + case GO_PRISON_CELL_BROGGOK_3: m_aBroggokEvent[2].m_cellGuid = pGo->GetObjectGuid(); return; + case GO_PRISON_CELL_BROGGOK_4: m_aBroggokEvent[3].m_cellGuid = pGo->GetObjectGuid(); return; + + default: + return; + } + m_mGoEntryGuidStore[pGo->GetEntry()] = pGo->GetObjectGuid(); +} + +void instance_blood_furnace::SetData(uint32 uiType, uint32 uiData) +{ + switch (uiType) + { + case TYPE_THE_MAKER_EVENT: + if (uiData == IN_PROGRESS) + DoUseDoorOrButton(GO_DOOR_MAKER_FRONT); + if (uiData == FAIL) + DoUseDoorOrButton(GO_DOOR_MAKER_FRONT); + if (uiData == DONE) + { + DoUseDoorOrButton(GO_DOOR_MAKER_FRONT); + DoUseDoorOrButton(GO_DOOR_MAKER_REAR); + } + m_auiEncounter[uiType] = uiData; + break; + case TYPE_BROGGOK_EVENT: + if (m_auiEncounter[uiType] == uiData) + return; + + // Combat door; the exit door is opened in event + DoUseDoorOrButton(GO_DOOR_BROGGOK_FRONT); + if (uiData == IN_PROGRESS) + { + if (m_uiBroggokEventPhase <= MAX_ORC_WAVES) + { + m_uiBroggokEventPhase = 0; + DoSortBroggokOrcs(); + // open first cage + DoNextBroggokEventPhase(); + } + } + else if (uiData == FAIL) + { + // On wipe we reset only the orcs; if the party wipes at the boss itself then the orcs don't reset + if (m_uiBroggokEventPhase <= MAX_ORC_WAVES) + { + for (uint8 i = 0; i < MAX_ORC_WAVES; ++i) + { + // Reset Orcs + if (!m_aBroggokEvent[i].m_bIsCellOpened) + continue; + + m_aBroggokEvent[i].m_uiKilledOrcCount = 0; + for (GuidSet::const_iterator itr = m_aBroggokEvent[i].m_sSortedOrcGuids.begin(); itr != m_aBroggokEvent[i].m_sSortedOrcGuids.end(); ++itr) + { + if (Creature* pOrc = instance->GetCreature(*itr)) + { + if (!pOrc->IsAlive()) + pOrc->Respawn(); + } + } + + // Close Door + DoUseDoorOrButton(m_aBroggokEvent[i].m_cellGuid); + m_aBroggokEvent[i].m_bIsCellOpened = false; + } + } + } + m_auiEncounter[uiType] = uiData; + break; + case TYPE_KELIDAN_EVENT: + if (uiData == DONE) + { + DoUseDoorOrButton(GO_DOOR_KELIDAN_EXIT); + DoUseDoorOrButton(GO_DOOR_FINAL_EXIT); + } + m_auiEncounter[uiType] = uiData; + break; + default: + script_error_log("Instance Blood Furnace SetData with Type %u Data %u, but this is not implemented.", uiType, uiData); + return; + } + + if (uiData == DONE) + { + OUT_SAVE_INST_DATA; + + std::ostringstream saveStream; + saveStream << m_auiEncounter[0] << " " << m_auiEncounter[1] << " " << m_auiEncounter[2]; + + m_strInstData = saveStream.str(); + + SaveToDB(); + OUT_SAVE_INST_DATA_COMPLETE; + } +} + +void instance_blood_furnace::DoNextBroggokEventPhase() +{ + // Get Movement Position + float dx, dy; + GetMovementDistanceForIndex(m_uiBroggokEventPhase, dx, dy); + + // Open door to the final boss now and move boss to the center of the room + if (m_uiBroggokEventPhase >= MAX_ORC_WAVES) + { + DoUseDoorOrButton(GO_DOOR_BROGGOK_REAR); + + if (Creature* pBroggok = GetSingleCreatureFromStorage(NPC_BROGGOK)) + { + pBroggok->SetWalk(false); + pBroggok->GetMotionMaster()->MovePoint(0, dx, dy, pBroggok->GetPositionZ()); + } + } + else + { + // Open cage door + if (!m_aBroggokEvent[m_uiBroggokEventPhase].m_bIsCellOpened) + DoUseDoorOrButton(m_aBroggokEvent[m_uiBroggokEventPhase].m_cellGuid); + + m_aBroggokEvent[m_uiBroggokEventPhase].m_bIsCellOpened = true; + + for (GuidSet::const_iterator itr = m_aBroggokEvent[m_uiBroggokEventPhase].m_sSortedOrcGuids.begin(); itr != m_aBroggokEvent[m_uiBroggokEventPhase].m_sSortedOrcGuids.end(); ++itr) + { + if (Creature* pOrc = instance->GetCreature(*itr)) + { + // Remove unit flags from npcs + pOrc->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE); + pOrc->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE); + + // Move them out of the cages + pOrc->SetWalk(false); + pOrc->GetMotionMaster()->MovePoint(0, pOrc->GetPositionX() + dx, pOrc->GetPositionY() + dy, pOrc->GetPositionZ()); + } + } + } + + // Prepare for further handling + m_uiBroggokEventTimer = 30000; + ++m_uiBroggokEventPhase; +} + +void instance_blood_furnace::OnCreatureEvade(Creature* pCreature) +{ + if (m_auiEncounter[TYPE_BROGGOK_EVENT] == FAIL) + return; + + if (pCreature->GetEntry() == NPC_BROGGOK) + SetData(TYPE_BROGGOK_EVENT, FAIL); + + else if (pCreature->GetEntry() == NPC_NASCENT_FEL_ORC) + { + for (uint8 i = 0; i < std::min(m_uiBroggokEventPhase, MAX_ORC_WAVES); ++i) + { + if (m_aBroggokEvent[i].m_sSortedOrcGuids.find(pCreature->GetObjectGuid()) != m_aBroggokEvent[i].m_sSortedOrcGuids.end()) + SetData(TYPE_BROGGOK_EVENT, FAIL); + } + } +} + +void instance_blood_furnace::OnCreatureDeath(Creature* pCreature) +{ + if (m_auiEncounter[TYPE_BROGGOK_EVENT] != IN_PROGRESS) + return; + + if (pCreature->GetEntry() == NPC_NASCENT_FEL_ORC) + { + uint8 uiClearedCells = 0; + for (uint8 i = 0; i < std::min(m_uiBroggokEventPhase, MAX_ORC_WAVES); ++i) + { + if (m_aBroggokEvent[i].m_sSortedOrcGuids.size() == m_aBroggokEvent[i].m_uiKilledOrcCount) + { + ++uiClearedCells; + continue; + } + + // Increase kill counter, if we found a mob of this cell + if (m_aBroggokEvent[i].m_sSortedOrcGuids.find(pCreature->GetObjectGuid()) != m_aBroggokEvent[i].m_sSortedOrcGuids.end()) + m_aBroggokEvent[i].m_uiKilledOrcCount++; + + if (m_aBroggokEvent[i].m_sSortedOrcGuids.size() == m_aBroggokEvent[i].m_uiKilledOrcCount) + ++uiClearedCells; + } + + // Increase phase when all opened cells are cleared + if (uiClearedCells == m_uiBroggokEventPhase) + DoNextBroggokEventPhase(); + } +} + +void instance_blood_furnace::Update(uint32 uiDiff) +{ + // Broggok Event: For the last wave we don't check the timer; the boss is released only when all mobs die, also the timer is only active on heroic + if (m_auiEncounter[TYPE_BROGGOK_EVENT] == IN_PROGRESS && m_uiBroggokEventPhase < MAX_ORC_WAVES && !instance->IsRegularDifficulty()) + { + if (m_uiBroggokEventTimer < uiDiff) + DoNextBroggokEventPhase(); + else + m_uiBroggokEventTimer -= uiDiff; + } + + if (m_uiRandYellTimer < uiDiff) + { + if (Creature* pMagtheridon = GetSingleCreatureFromStorage(NPC_MAGTHERIDON)) + { + DoScriptText(aRandomTaunt[urand(0, 5)], pMagtheridon); + m_uiRandYellTimer = 90000; + } + } + else + m_uiRandYellTimer -= uiDiff; +} + +uint32 instance_blood_furnace::GetData(uint32 uiType) const +{ + if (uiType < MAX_ENCOUNTER) + return m_auiEncounter[uiType]; + + return 0; +} + +void instance_blood_furnace::Load(const char* chrIn) +{ + if (!chrIn) + { + 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]; + + 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; +} + +// Sort all nascent orcs in the instance in order to get only those near broggok doors +void instance_blood_furnace::DoSortBroggokOrcs() +{ + for (GuidList::const_iterator itr = m_luiNascentOrcGuids.begin(); itr != m_luiNascentOrcGuids.end(); ++itr) + { + if (Creature* pOrc = instance->GetCreature(*itr)) + { + for (uint8 i = 0; i < MAX_ORC_WAVES; ++i) + { + if (GameObject* pDoor = instance->GetGameObject(m_aBroggokEvent[i].m_cellGuid)) + { + if (pOrc->IsWithinDistInMap(pDoor, 15.0f)) + { + m_aBroggokEvent[i].m_sSortedOrcGuids.insert(pOrc->GetObjectGuid()); + if (!pOrc->IsAlive()) + pOrc->Respawn(); + break; + } + } + } + } + } +} + +// Helper function to calculate the position to where the orcs should move +// For case of orc-indexes the difference, for Braggok his position +void instance_blood_furnace::GetMovementDistanceForIndex(uint32 uiIndex, float& dx, float& dy) +{ + GameObject* pDoor[2]; + + if (uiIndex < MAX_ORC_WAVES) + { + // Use doors 0, 1 for index 0 or 1; and use doors 2, 3 for index 2 or 3 + pDoor[0] = instance->GetGameObject(m_aBroggokEvent[(uiIndex / 2) * 2].m_cellGuid); + pDoor[1] = instance->GetGameObject(m_aBroggokEvent[(uiIndex / 2) * 2 + 1].m_cellGuid); + } + else + { + // Use doors 0 and 3 for Braggok case (which means the middle point is the center of the room) + pDoor[0] = instance->GetGameObject(m_aBroggokEvent[0].m_cellGuid); + pDoor[1] = instance->GetGameObject(m_aBroggokEvent[3].m_cellGuid); + } + + if (!pDoor[0] || !pDoor[1]) + return; + + if (uiIndex < MAX_ORC_WAVES) + { + dx = (pDoor[0]->GetPositionX() + pDoor[1]->GetPositionX()) / 2 - pDoor[uiIndex % 2]->GetPositionX(); + dy = (pDoor[0]->GetPositionY() + pDoor[1]->GetPositionY()) / 2 - pDoor[uiIndex % 2]->GetPositionY(); + } + else + { + dx = (pDoor[0]->GetPositionX() + pDoor[1]->GetPositionX()) / 2; + dy = (pDoor[0]->GetPositionY() + pDoor[1]->GetPositionY()) / 2; + } +} + +InstanceData* GetInstanceData_instance_blood_furnace(Map* pMap) +{ + return new instance_blood_furnace(pMap); +} + +bool GOUse_go_prison_cell_lever(Player* /*pPlayer*/, GameObject* pGo) +{ + ScriptedInstance* pInstance = (ScriptedInstance*)pGo->GetInstanceData(); + + if (!pInstance) + return false; + + // Set broggok event in progress + if (pInstance->GetData(TYPE_BROGGOK_EVENT) != DONE && pInstance->GetData(TYPE_BROGGOK_EVENT) != IN_PROGRESS) + { + pInstance->SetData(TYPE_BROGGOK_EVENT, IN_PROGRESS); + + // Yell intro + if (Creature* pBroggok = pInstance->GetSingleCreatureFromStorage(NPC_BROGGOK)) + DoScriptText(SAY_BROGGOK_INTRO, pBroggok); + } + + return false; +} + +void AddSC_instance_blood_furnace() +{ + Script* pNewScript; + + pNewScript = new Script; + pNewScript->Name = "instance_blood_furnace"; + pNewScript->GetInstanceData = &GetInstanceData_instance_blood_furnace; + pNewScript->RegisterSelf(); + + pNewScript = new Script; + pNewScript->Name = "go_prison_cell_lever"; + pNewScript->pGOUse = &GOUse_go_prison_cell_lever; + pNewScript->RegisterSelf(); +} diff --git a/src/modules/SD2/scripts/outland/hellfire_citadel/hellfire_ramparts/boss_nazan_and_vazruden.cpp b/src/modules/SD2/scripts/outland/hellfire_citadel/hellfire_ramparts/boss_nazan_and_vazruden.cpp new file mode 100644 index 000000000..764e16f6d --- /dev/null +++ b/src/modules/SD2/scripts/outland/hellfire_citadel/hellfire_ramparts/boss_nazan_and_vazruden.cpp @@ -0,0 +1,457 @@ +/* Copyright (C) 2006 - 2013 ScriptDev2 + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +/* ScriptData +SDName: Boss_Nazan_And_Vazruden +SD%Complete: 95 +SDComment: Bellowing Roar Timer (heroic) needs some love +SDCategory: Hellfire Citadel, Hellfire Ramparts +EndScriptData */ + +#include "precompiled.h" +#include "hellfire_ramparts.h" + +enum +{ + SAY_INTRO = -1543017, + SAY_AGGRO1 = -1543018, + SAY_AGGRO2 = -1543019, + SAY_AGGRO3 = -1543020, + SAY_TAUNT = -1543021, + SAY_KILL1 = -1543022, + SAY_KILL2 = -1543023, + SAY_DEATH = -1543024, + EMOTE_DESCEND = -1543025, + + SPELL_SUMMON_VAZRUDEN = 30717, + + // vazruden + SPELL_REVENGE = 19130, + SPELL_REVENGE_H = 40392, + + // nazan + SPELL_FIREBALL = 30691, // This and the next while flying (dmg values will change in cata) + SPELL_FIREBALL_B = 33793, + SPELL_FIREBALL_H = 32491, + SPELL_FIREBALL_B_H = 33794, + SPELL_FIREBALL_LAND = 34653, // While landed + SPELL_FIREBALL_LAND_H = 36920, + + SPELL_CONE_OF_FIRE = 30926, + SPELL_CONE_OF_FIRE_H = 36921, + + SPELL_BELLOW_ROAR_H = 39427, + + // misc + POINT_ID_CENTER = 100, + POINT_ID_FLYING = 101, + POINT_ID_COMBAT = 102, + + NPC_NAZAN = 17536, +}; + +const float afCenterPos[3] = { -1399.401f, 1736.365f, 87.008f}; // moves here to drop off nazan +const float afCombatPos[3] = { -1413.848f, 1754.019f, 83.146f}; // moves here when decending + +// This is the flying mob ("mounted" on dragon) spawned initially +// This npc will morph into the "unmounted" dragon (nazan) after vazruden is summoned and continue flying +// Descent after Vazruden reach 30% HP +struct boss_vazruden_heraldAI : public ScriptedAI +{ + boss_vazruden_heraldAI(Creature* pCreature) : ScriptedAI(pCreature) + { + pCreature->SetActiveObjectState(true); + m_pInstance = (ScriptedInstance*)pCreature->GetInstanceData(); + m_bIsRegularMode = pCreature->GetMap()->IsRegularDifficulty(); + Reset(); + } + + ScriptedInstance* m_pInstance; + bool m_bIsRegularMode; + + bool m_bIsEventInProgress; + bool m_bIsDescending; + uint32 m_uiMovementTimer; + uint32 m_uiFireballTimer; + uint32 m_uiFireballBTimer; + uint32 m_uiConeOfFireTimer; + uint32 m_uiBellowingRoarTimer; + + ObjectGuid m_lastSeenPlayerGuid; + ObjectGuid m_vazrudenGuid; + + void Reset() override + { + if (m_creature->GetEntry() != NPC_VAZRUDEN_HERALD) + m_creature->UpdateEntry(NPC_VAZRUDEN_HERALD); + + m_creature->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE); + + m_uiMovementTimer = 0; + m_bIsEventInProgress = false; + m_bIsDescending = false; + m_lastSeenPlayerGuid.Clear(); + m_vazrudenGuid.Clear(); + m_uiFireballTimer = 0; + m_uiFireballBTimer = 2100; + m_uiConeOfFireTimer = urand(8100, 19700); + m_uiBellowingRoarTimer = 100; // TODO Guesswork, though such an AoE fear soon after landing seems fitting + + // see boss_onyxia + // sort of a hack, it is unclear how this really work but the values appear to be valid + m_creature->SetByteValue(UNIT_FIELD_BYTES_1, 3, UNIT_BYTE1_FLAG_ALWAYS_STAND | UNIT_BYTE1_FLAG_UNK_2); + m_creature->SetLevitate(true); + } + + void MoveInLineOfSight(Unit* pWho) override + { + if (m_bIsEventInProgress && !m_lastSeenPlayerGuid && pWho->GetTypeId() == TYPEID_PLAYER && pWho->IsAlive() && !((Player*)pWho)->isGameMaster()) + { + if (m_creature->IsWithinDistInMap(pWho, 40.0f)) + m_lastSeenPlayerGuid = pWho->GetObjectGuid(); + } + + if (m_pInstance && m_pInstance->GetData(TYPE_NAZAN) != IN_PROGRESS) + return; + + ScriptedAI::MoveInLineOfSight(pWho); + } + + void AttackStart(Unit* pWho) override + { + if (m_pInstance && m_pInstance->GetData(TYPE_NAZAN) != IN_PROGRESS) + return; + + ScriptedAI::AttackStart(pWho); + } + + void MovementInform(uint32 uiType, uint32 uiPointId) override + { + if (!m_pInstance) + return; + + if (uiType == WAYPOINT_MOTION_TYPE) + { + if (m_uiMovementTimer || m_bIsEventInProgress) + return; + + if (m_pInstance->GetData(TYPE_NAZAN) == SPECIAL) + { + m_creature->SetCombatStartPosition(m_creature->GetPositionX(), m_creature->GetPositionY(), m_creature->GetPositionZ()); + m_uiMovementTimer = 1000; + m_bIsEventInProgress = true; + } + } + + if (uiType == POINT_MOTION_TYPE) + { + switch (uiPointId) + { + case POINT_ID_CENTER: + DoSplit(); + break; + case POINT_ID_COMBAT: + { + m_creature->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE); + m_pInstance->SetData(TYPE_NAZAN, IN_PROGRESS); + + // Landing + // undo flying + m_creature->SetByteValue(UNIT_FIELD_BYTES_1, 3, 0); + m_creature->SetLevitate(false); + + Player* pPlayer = m_creature->GetMap()->GetPlayer(m_lastSeenPlayerGuid); + if (pPlayer && pPlayer->IsAlive()) + AttackStart(pPlayer); + + // Initialize for combat + m_uiFireballTimer = urand(5200, 16500); + + break; + } + case POINT_ID_FLYING: + if (m_bIsEventInProgress) // Additional check for wipe case, while nazan is flying to this point + m_uiFireballTimer = 1; + break; + } + } + } + + void DoMoveToCenter() + { + DoScriptText(SAY_INTRO, m_creature); + m_creature->GetMotionMaster()->MovePoint(POINT_ID_CENTER, afCenterPos[0], afCenterPos[1], afCenterPos[2]); + } + + void DoSplit() + { + m_creature->UpdateEntry(NPC_NAZAN); + + DoCastSpellIfCan(m_creature, SPELL_SUMMON_VAZRUDEN); + + m_uiMovementTimer = 3000; + + // Let him idle for now + m_creature->GetMotionMaster()->MoveIdle(); + } + + void DoMoveToAir() + { + float fX, fY, fZ; + m_creature->GetCombatStartPosition(fX, fY, fZ); + + // Remove Idle MMGen + if (m_creature->GetMotionMaster()->GetCurrentMovementGeneratorType() == IDLE_MOTION_TYPE) + m_creature->GetMotionMaster()->MovementExpired(false); + + m_creature->GetMotionMaster()->MovePoint(POINT_ID_FLYING, fX, fY, fZ); + } + + void DoMoveToCombat() + { + if (m_bIsDescending || !m_pInstance || m_pInstance->GetData(TYPE_NAZAN) == IN_PROGRESS) + return; + + m_bIsDescending = true; + + m_creature->SetWalk(false); + m_creature->GetMotionMaster()->MovePoint(POINT_ID_COMBAT, afCombatPos[0], afCombatPos[1], afCombatPos[2]); + DoScriptText(EMOTE_DESCEND, m_creature); + } + + void JustSummoned(Creature* pSummoned) override + { + if (pSummoned->GetEntry() != NPC_VAZRUDEN) + return; + + if (Player* pPlayer = m_creature->GetMap()->GetPlayer(m_lastSeenPlayerGuid)) + pSummoned->AI()->AttackStart(pPlayer); + + m_vazrudenGuid = pSummoned->GetObjectGuid(); + + if (m_pInstance) + m_pInstance->SetData(TYPE_VAZRUDEN, IN_PROGRESS); + } + + void JustDied(Unit* /*pKiller*/) override + { + if (m_pInstance) + m_pInstance->SetData(TYPE_NAZAN, DONE); + } + + void JustReachedHome() override + { + if (m_pInstance) + m_pInstance->SetData(TYPE_NAZAN, FAIL); + } + + void UpdateAI(const uint32 uiDiff) override + { + if (!m_creature->SelectHostileTarget() || !m_creature->getVictim()) + { + if (m_uiMovementTimer) + { + if (m_uiMovementTimer <= uiDiff) + { + if (m_pInstance) + { + if (m_pInstance->GetData(TYPE_VAZRUDEN) == IN_PROGRESS) + DoMoveToAir(); + else + DoMoveToCenter(); + } + m_uiMovementTimer = 0; + } + else + m_uiMovementTimer -= uiDiff; + } + + if (m_vazrudenGuid && m_uiFireballTimer) + { + if (m_uiFireballTimer <= uiDiff) + { + if (Creature* pVazruden = m_creature->GetMap()->GetCreature(m_vazrudenGuid)) + { + if (Unit* pEnemy = pVazruden->SelectAttackingTarget(ATTACKING_TARGET_RANDOM, 0)) + { + if (DoCastSpellIfCan(pEnemy, m_bIsRegularMode ? SPELL_FIREBALL : SPELL_FIREBALL_H, 0, pVazruden->GetObjectGuid()) == CAST_OK) + m_uiFireballTimer = urand(2100, 7300); + } + } + } + else + m_uiFireballTimer -= uiDiff; + + if (m_uiFireballBTimer < uiDiff) + { + if (Creature* pVazruden = m_creature->GetMap()->GetCreature(m_vazrudenGuid)) + { + if (Unit* pEnemy = pVazruden->SelectAttackingTarget(ATTACKING_TARGET_RANDOM, 0)) + { + if (DoCastSpellIfCan(pEnemy, m_bIsRegularMode ? SPELL_FIREBALL_B : SPELL_FIREBALL_B_H, 0, pVazruden->GetObjectGuid()) == CAST_OK) + m_uiFireballBTimer = 15700; + } + } + } + else + m_uiFireballBTimer -= uiDiff; + } + + if (m_creature->GetHealthPercent() < 20.0f) + DoMoveToCombat(); + + return; + } + + // In Combat + if (m_uiFireballTimer < uiDiff) + { + if (Unit* pEnemy = m_creature->SelectAttackingTarget(ATTACKING_TARGET_RANDOM, 0)) + { + if (DoCastSpellIfCan(pEnemy, m_bIsRegularMode ? SPELL_FIREBALL_LAND : SPELL_FIREBALL_LAND_H) == CAST_OK) + m_uiFireballTimer = urand(7300, 13200); + } + } + else + m_uiFireballTimer -= uiDiff; + + if (m_uiConeOfFireTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature->getVictim(), m_bIsRegularMode ? SPELL_CONE_OF_FIRE : SPELL_CONE_OF_FIRE_H) == CAST_OK) + m_uiConeOfFireTimer = urand(7300, 13200); + } + else + m_uiConeOfFireTimer -= uiDiff; + + if (!m_bIsRegularMode) + { + if (m_uiBellowingRoarTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature, SPELL_BELLOW_ROAR_H) == CAST_OK) + m_uiBellowingRoarTimer = urand(8000, 12000); // TODO Guesswork, 8s cooldown + } + else + m_uiBellowingRoarTimer -= uiDiff; + } + + DoMeleeAttackIfReady(); + } +}; + +CreatureAI* GetAI_boss_vazruden_herald(Creature* pCreature) +{ + return new boss_vazruden_heraldAI(pCreature); +} + +// This is the summoned boss ("dismounted") that starts attacking the players +struct boss_vazrudenAI : public ScriptedAI +{ + boss_vazrudenAI(Creature* pCreature) : ScriptedAI(pCreature) + { + m_pInstance = (ScriptedInstance*)pCreature->GetInstanceData(); + m_bIsRegularMode = pCreature->GetMap()->IsRegularDifficulty(); + Reset(); + } + + ScriptedInstance* m_pInstance; + bool m_bIsRegularMode; + + uint32 m_uiRevengeTimer; + bool m_bHealthBelow; + + void Reset() override + { + m_bHealthBelow = false; + m_uiRevengeTimer = urand(5500, 8400); + } + + void Aggro(Unit* /*pWho*/) override + { + switch (urand(0, 2)) + { + case 0: DoScriptText(SAY_AGGRO1, m_creature); break; + case 1: DoScriptText(SAY_AGGRO2, m_creature); break; + case 2: DoScriptText(SAY_AGGRO3, m_creature); break; + } + } + + void JustDied(Unit* /*pKiller*/) override + { + DoScriptText(SAY_DEATH, m_creature); + + if (m_pInstance) + m_pInstance->SetData(TYPE_VAZRUDEN, DONE); + } + + void JustReachedHome() override + { + if (m_pInstance) + m_pInstance->SetData(TYPE_VAZRUDEN, FAIL); + } + + void KilledUnit(Unit* /*pVictim*/) override + { + DoScriptText(urand(0, 1) ? SAY_KILL1 : SAY_KILL2, m_creature); + } + + void DamageTaken(Unit* /*pDealer*/, uint32& uiDamage) override + { + if (!m_bHealthBelow && m_pInstance && (float(m_creature->GetHealth() - uiDamage) / m_creature->GetMaxHealth()) < 0.30f) + { + if (Creature* pNazan = m_pInstance->GetSingleCreatureFromStorage(NPC_VAZRUDEN_HERALD)) + if (boss_vazruden_heraldAI* pNazanAI = dynamic_cast(pNazan->AI())) + pNazanAI->DoMoveToCombat(); + + m_bHealthBelow = true; + } + } + + void UpdateAI(const uint32 uiDiff) override + { + if (!m_creature->SelectHostileTarget() || !m_creature->getVictim()) + return; + + if (m_uiRevengeTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature->getVictim(), m_bIsRegularMode ? SPELL_REVENGE : SPELL_REVENGE_H) == CAST_OK) + m_uiRevengeTimer = urand(11400, 14300); + } + else + m_uiRevengeTimer -= uiDiff; + + DoMeleeAttackIfReady(); + } +}; + +CreatureAI* GetAI_boss_vazruden(Creature* pCreature) +{ + return new boss_vazrudenAI(pCreature); +} + +void AddSC_boss_nazan_and_vazruden() +{ + Script* pNewScript; + + pNewScript = new Script; + pNewScript->Name = "boss_vazruden"; + pNewScript->GetAI = &GetAI_boss_vazruden; + pNewScript->RegisterSelf(); + + pNewScript = new Script; + pNewScript->Name = "boss_vazruden_herald"; + pNewScript->GetAI = &GetAI_boss_vazruden_herald; + pNewScript->RegisterSelf(); +} diff --git a/src/modules/SD2/scripts/outland/hellfire_citadel/hellfire_ramparts/boss_omor_the_unscarred.cpp b/src/modules/SD2/scripts/outland/hellfire_citadel/hellfire_ramparts/boss_omor_the_unscarred.cpp new file mode 100644 index 000000000..37688500b --- /dev/null +++ b/src/modules/SD2/scripts/outland/hellfire_citadel/hellfire_ramparts/boss_omor_the_unscarred.cpp @@ -0,0 +1,219 @@ +/* Copyright (C) 2006 - 2013 ScriptDev2 + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with 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" + +enum +{ + SAY_AGGRO_1 = -1543009, + SAY_AGGRO_2 = -1543010, + SAY_AGGRO_3 = -1543011, + SAY_SUMMON = -1543012, + SAY_CURSE = -1543013, + SAY_KILL_1 = -1543014, + SAY_DIE = -1543015, + SAY_WIPE = -1543016, + + SPELL_ORBITAL_STRIKE = 30637, + SPELL_SHADOW_WHIP = 30638, + SPELL_TREACHEROUS_AURA = 30695, + SPELL_BANE_OF_TREACHERY_H = 37566, + SPELL_DEMONIC_SHIELD = 31901, + SPELL_SHADOW_BOLT = 30686, + SPELL_SHADOW_BOLT_H = 39297, + SPELL_SUMMON_FIENDISH_HOUND = 30707, +}; + +struct boss_omor_the_unscarredAI : public ScriptedAI +{ + boss_omor_the_unscarredAI(Creature* pCreature) : ScriptedAI(pCreature) + { + SetCombatMovement(false); + m_bIsRegularMode = pCreature->GetMap()->IsRegularDifficulty(); + Reset(); + } + + bool m_bIsRegularMode; + + uint32 m_uiOrbitalStrikeTimer; + uint32 m_uiShadowWhipTimer; + uint32 m_uiAuraTimer; + uint32 m_uiDemonicShieldTimer; + uint32 m_uiShadowboltTimer; + uint32 m_uiSummonTimer; + ObjectGuid m_playerGuid; + bool m_bCanPullBack; + + void Reset() override + { + DoScriptText(SAY_WIPE, m_creature); + + m_uiOrbitalStrikeTimer = 25000; + m_uiShadowWhipTimer = 2000; + m_uiAuraTimer = urand(12300, 23300); + m_uiDemonicShieldTimer = 1000; + m_uiShadowboltTimer = urand(6600, 8900); + m_uiSummonTimer = urand(19600, 23100); + m_playerGuid.Clear(); + m_bCanPullBack = false; + } + + void Aggro(Unit* /*pWho*/) override + { + switch (urand(0, 2)) + { + case 0: DoScriptText(SAY_AGGRO_1, m_creature); break; + case 1: DoScriptText(SAY_AGGRO_2, m_creature); break; + case 2: DoScriptText(SAY_AGGRO_3, m_creature); break; + } + } + + void KilledUnit(Unit* /*pVictim*/) override + { + if (urand(0, 1)) + return; + + DoScriptText(SAY_KILL_1, m_creature); + } + + void JustSummoned(Creature* pSummoned) override + { + DoScriptText(SAY_SUMMON, m_creature); + + if (Unit* pTarget = m_creature->SelectAttackingTarget(ATTACKING_TARGET_RANDOM, 0)) + pSummoned->AI()->AttackStart(pTarget); + } + + void JustDied(Unit* /*pKiller*/) override + { + DoScriptText(SAY_DIE, m_creature); + } + + void UpdateAI(const uint32 uiDiff) override + { + if (!m_creature->SelectHostileTarget() || !m_creature->getVictim()) + return; + + if (m_uiSummonTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature, SPELL_SUMMON_FIENDISH_HOUND) == CAST_OK) + m_uiSummonTimer = urand(24100, 26900); + } + else + m_uiSummonTimer -= uiDiff; + + if (m_bCanPullBack) + { + if (m_uiShadowWhipTimer < uiDiff) + { + if (Player* pPlayer = m_creature->GetMap()->GetPlayer(m_playerGuid)) + { + // if unit dosen't have this flag, then no pulling back (script will attempt cast, even if orbital strike was resisted) + if (pPlayer->HasMovementFlag(MOVEFLAG_FALLING)) + DoCastSpellIfCan(pPlayer, SPELL_SHADOW_WHIP, CAST_INTERRUPT_PREVIOUS); + } + m_playerGuid.Clear(); + m_uiShadowWhipTimer = 2000; + m_bCanPullBack = false; + } + else + m_uiShadowWhipTimer -= uiDiff; + } + else if (m_uiOrbitalStrikeTimer < uiDiff) + { + Unit* pTemp = NULL; + if (m_creature->CanReachWithMeleeAttack(m_creature->getVictim())) + pTemp = m_creature->getVictim(); + else + pTemp = m_creature->SelectAttackingTarget(ATTACKING_TARGET_RANDOM, 0); + + if (pTemp && pTemp->GetTypeId() == TYPEID_PLAYER) + { + if (DoCastSpellIfCan(pTemp, SPELL_ORBITAL_STRIKE) == CAST_OK) + { + m_uiOrbitalStrikeTimer = urand(14000, 16000); + m_playerGuid = pTemp->GetObjectGuid(); + + m_bCanPullBack = true; + } + } + } + else + m_uiOrbitalStrikeTimer -= uiDiff; + + if (m_creature->GetHealthPercent() < 20.0f) + { + if (m_uiDemonicShieldTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature, SPELL_DEMONIC_SHIELD) == CAST_OK) + m_uiDemonicShieldTimer = 15000; + } + else + m_uiDemonicShieldTimer -= uiDiff; + } + + if (m_uiAuraTimer < uiDiff) + { + if (Unit* pTarget = m_creature->SelectAttackingTarget(ATTACKING_TARGET_RANDOM, 0)) + { + if (DoCastSpellIfCan(pTarget, m_bIsRegularMode ? SPELL_TREACHEROUS_AURA : SPELL_BANE_OF_TREACHERY_H) == CAST_OK) + { + m_uiAuraTimer = urand(8000, 16000); + DoScriptText(SAY_CURSE, m_creature); + } + } + } + else + m_uiAuraTimer -= uiDiff; + + if (m_uiShadowboltTimer < uiDiff) + { + if (Unit* pTarget = m_creature->SelectAttackingTarget(ATTACKING_TARGET_RANDOM, 1)) + { + if (DoCastSpellIfCan(pTarget, m_bIsRegularMode ? SPELL_SHADOW_BOLT : SPELL_SHADOW_BOLT_H) == CAST_OK) + m_uiShadowboltTimer = urand(4200, 7300); + } + else + m_uiShadowboltTimer = 2000; + } + else + m_uiShadowboltTimer -= uiDiff; + + DoMeleeAttackIfReady(); + } +}; + +CreatureAI* GetAI_boss_omor_the_unscarredAI(Creature* pCreature) +{ + return new boss_omor_the_unscarredAI(pCreature); +} + +void AddSC_boss_omor_the_unscarred() +{ + Script* pNewScript; + + pNewScript = new Script; + pNewScript->Name = "boss_omor_the_unscarred"; + pNewScript->GetAI = &GetAI_boss_omor_the_unscarredAI; + pNewScript->RegisterSelf(); +} diff --git a/src/modules/SD2/scripts/outland/hellfire_citadel/hellfire_ramparts/boss_watchkeeper_gargolmar.cpp b/src/modules/SD2/scripts/outland/hellfire_citadel/hellfire_ramparts/boss_watchkeeper_gargolmar.cpp new file mode 100644 index 000000000..eebc42e02 --- /dev/null +++ b/src/modules/SD2/scripts/outland/hellfire_citadel/hellfire_ramparts/boss_watchkeeper_gargolmar.cpp @@ -0,0 +1,177 @@ +/* Copyright (C) 2006 - 2013 ScriptDev2 + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with 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 pTarget furthest away, not random. +SDCategory: Hellfire Citadel, Hellfire Ramparts +EndScriptData */ + +#include "precompiled.h" + +enum +{ + SAY_TAUNT = -1543000, + SAY_HEAL = -1543001, + SAY_SURGE = -1543002, + SAY_AGGRO_1 = -1543003, + SAY_AGGRO_2 = -1543004, + SAY_AGGRO_3 = -1543005, + SAY_KILL_1 = -1543006, + SAY_KILL_2 = -1543007, + SAY_DIE = -1543008, + + SPELL_MORTAL_WOUND = 30641, + SPELL_MORTAL_WOUND_H = 36814, + SPELL_SURGE = 34645, + SPELL_RETALIATION = 22857, + SPELL_OVERPOWER = 32154, +}; + +struct boss_watchkeeper_gargolmarAI : public ScriptedAI +{ + boss_watchkeeper_gargolmarAI(Creature* pCreature) : ScriptedAI(pCreature) + { + m_bIsRegularMode = pCreature->GetMap()->IsRegularDifficulty(); + Reset(); + } + + bool m_bIsRegularMode; + + uint32 m_uiSurgeTimer; + uint32 m_uiMortalWoundTimer; + uint32 m_uiRetaliationTimer; + uint32 m_uiOverpowerTimer; + + bool m_bHasTaunted; + bool m_bYelledForHeal; + + void Reset() override + { + m_uiSurgeTimer = urand(2400, 6100); + m_uiMortalWoundTimer = urand(3500, 14400); + m_uiRetaliationTimer = 0; + m_uiOverpowerTimer = urand(3600, 14800); + + m_bHasTaunted = false; + m_bYelledForHeal = false; + } + + void Aggro(Unit* /*pWho*/) override + { + switch (urand(0, 2)) + { + case 0: DoScriptText(SAY_AGGRO_1, m_creature); break; + case 1: DoScriptText(SAY_AGGRO_2, m_creature); break; + case 2: DoScriptText(SAY_AGGRO_3, m_creature); break; + } + } + + void MoveInLineOfSight(Unit* pWho) override + { + if (!m_bHasTaunted && m_creature->IsWithinDistInMap(pWho, 60.0f)) + { + DoScriptText(SAY_TAUNT, m_creature); + m_bHasTaunted = true; + } + + ScriptedAI::MoveInLineOfSight(pWho); + } + + void KilledUnit(Unit* /*pVictim*/) override + { + DoScriptText(urand(0, 1) ? SAY_KILL_1 : SAY_KILL_2, m_creature); + } + + void JustDied(Unit* /*pKiller*/) override + { + DoScriptText(SAY_DIE, m_creature); + } + + void UpdateAI(const uint32 uiDiff) override + { + if (!m_creature->SelectHostileTarget() || !m_creature->getVictim()) + return; + + if (m_uiMortalWoundTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature->getVictim(), m_bIsRegularMode ? SPELL_MORTAL_WOUND : SPELL_MORTAL_WOUND_H) == CAST_OK) + m_uiMortalWoundTimer = urand(6100, 12200); + } + else + m_uiMortalWoundTimer -= uiDiff; + + if (m_uiSurgeTimer < uiDiff) + { + if (Unit* pTarget = m_creature->SelectAttackingTarget(ATTACKING_TARGET_RANDOM, 0)) + { + if (DoCastSpellIfCan(pTarget, SPELL_SURGE) == CAST_OK) + { + DoScriptText(SAY_SURGE, m_creature); + m_uiSurgeTimer = urand(12100, 21700); + } + } + } + else + m_uiSurgeTimer -= uiDiff; + + if (m_creature->GetHealthPercent() < 20.0f) + { + if (m_uiRetaliationTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature, SPELL_RETALIATION) == CAST_OK) + m_uiRetaliationTimer = 30000; + } + else + m_uiRetaliationTimer -= uiDiff; + } + + if (m_uiOverpowerTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature->getVictim(), SPELL_OVERPOWER) == CAST_OK) + m_uiOverpowerTimer = urand(18100, 33700); + } + else + m_uiOverpowerTimer -= uiDiff; + + if (!m_bYelledForHeal) + { + if (m_creature->GetHealthPercent() < 40.0f) + { + DoScriptText(SAY_HEAL, m_creature); + m_bYelledForHeal = true; + } + } + + DoMeleeAttackIfReady(); + } +}; + +CreatureAI* GetAI_boss_watchkeeper_gargolmarAI(Creature* pCreature) +{ + return new boss_watchkeeper_gargolmarAI(pCreature); +} + +void AddSC_boss_watchkeeper_gargolmar() +{ + Script* pNewScript; + + pNewScript = new Script; + pNewScript->Name = "boss_watchkeeper_gargolmar"; + pNewScript->GetAI = &GetAI_boss_watchkeeper_gargolmarAI; + pNewScript->RegisterSelf(); +} diff --git a/src/modules/SD2/scripts/outland/hellfire_citadel/hellfire_ramparts/hellfire_ramparts.h b/src/modules/SD2/scripts/outland/hellfire_citadel/hellfire_ramparts/hellfire_ramparts.h new file mode 100644 index 000000000..7e7c8d922 --- /dev/null +++ b/src/modules/SD2/scripts/outland/hellfire_citadel/hellfire_ramparts/hellfire_ramparts.h @@ -0,0 +1,47 @@ +/* Copyright (C) 2006 - 2013 ScriptDev2 + * 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 + +enum +{ + MAX_ENCOUNTER = 2, + + TYPE_VAZRUDEN = 1, + TYPE_NAZAN = 2, // Do not change, used in ACID (SetData(SPECIAL) on death of 17517 + + NPC_HELLFIRE_SENTRY = 17517, + NPC_VAZRUDEN_HERALD = 17307, + NPC_VAZRUDEN = 17537, + + GO_FEL_IRON_CHEST = 185168, + GO_FEL_IRON_CHEST_H = 185169, +}; + +class instance_ramparts : public ScriptedInstance +{ + public: + instance_ramparts(Map* pMap); + + void Initialize() override; + + void OnCreatureCreate(Creature* pCreature) override; + void OnObjectCreate(GameObject* pGo) override; + + void SetData(uint32 uiType, uint32 uiData) override; + uint32 GetData(uint32 uiType) const override; + + // No need to save and load this instance (only one encounter needs special handling, no doors used) + + private: + void DoFailVazruden(); + + uint32 m_auiEncounter[MAX_ENCOUNTER]; + + uint32 m_uiSentryCounter; + GuidList m_lSentryGUIDs; +}; + +#endif diff --git a/src/modules/SD2/scripts/outland/hellfire_citadel/hellfire_ramparts/instance_hellfire_ramparts.cpp b/src/modules/SD2/scripts/outland/hellfire_citadel/hellfire_ramparts/instance_hellfire_ramparts.cpp new file mode 100644 index 000000000..285ae00df --- /dev/null +++ b/src/modules/SD2/scripts/outland/hellfire_citadel/hellfire_ramparts/instance_hellfire_ramparts.cpp @@ -0,0 +1,158 @@ +/* Copyright (C) 2006 - 2013 ScriptDev2 + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along 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 "hellfire_ramparts.h" + +instance_ramparts::instance_ramparts(Map* pMap) : ScriptedInstance(pMap), + m_uiSentryCounter(0) +{ + Initialize(); +} + +void instance_ramparts::Initialize() +{ + memset(&m_auiEncounter, 0, sizeof(m_auiEncounter)); +} + +void instance_ramparts::OnCreatureCreate(Creature* pCreature) +{ + switch (pCreature->GetEntry()) + { + case NPC_VAZRUDEN_HERALD: + case NPC_VAZRUDEN: + m_mNpcEntryGuidStore[pCreature->GetEntry()] = pCreature->GetObjectGuid(); + break; + case NPC_HELLFIRE_SENTRY: + m_lSentryGUIDs.push_back(pCreature->GetObjectGuid()); + break; + } +} + +void instance_ramparts::OnObjectCreate(GameObject* pGo) +{ + switch (pGo->GetEntry()) + { + case GO_FEL_IRON_CHEST: + case GO_FEL_IRON_CHEST_H: + m_mGoEntryGuidStore[pGo->GetEntry()] = pGo->GetObjectGuid(); + break; + } +} + +void instance_ramparts::SetData(uint32 uiType, uint32 uiData) +{ + debug_log("SD2: Instance Ramparts: SetData received for type %u with data %u", uiType, uiData); + + switch (uiType) + { + case TYPE_VAZRUDEN: + if (m_auiEncounter[0] == uiData) + return; + if (uiData == DONE && m_auiEncounter[1] == DONE) + DoRespawnGameObject(instance->IsRegularDifficulty() ? GO_FEL_IRON_CHEST : GO_FEL_IRON_CHEST_H, HOUR); + if (uiData == FAIL && m_auiEncounter[0] != FAIL) + DoFailVazruden(); + m_auiEncounter[0] = uiData; + break; + case TYPE_NAZAN: + if (m_auiEncounter[1] == uiData) + return; + if (uiData == SPECIAL) // SPECIAL set via ACID + { + ++m_uiSentryCounter; + + if (m_uiSentryCounter == 2) + m_auiEncounter[1] = uiData; + + return; + } + if (uiData == DONE && m_auiEncounter[0] == DONE) + { + DoRespawnGameObject(instance->IsRegularDifficulty() ? GO_FEL_IRON_CHEST : GO_FEL_IRON_CHEST_H, HOUR); + DoToggleGameObjectFlags(instance->IsRegularDifficulty() ? GO_FEL_IRON_CHEST : GO_FEL_IRON_CHEST_H, GO_FLAG_NO_INTERACT, false); + } + if (uiData == FAIL && m_auiEncounter[1] != FAIL) + DoFailVazruden(); + + m_auiEncounter[1] = uiData; + break; + } +} + +uint32 instance_ramparts::GetData(uint32 uiType) const +{ + if (uiType == TYPE_VAZRUDEN) + return m_auiEncounter[0]; + + if (uiType == TYPE_NAZAN) + return m_auiEncounter[1]; + + return 0; +} + +void instance_ramparts::DoFailVazruden() +{ + // Store FAIL for both types + m_auiEncounter[0] = FAIL; + m_auiEncounter[1] = FAIL; + + // Restore Sentries (counter and respawn them) + m_uiSentryCounter = 0; + for (GuidList::const_iterator itr = m_lSentryGUIDs.begin(); itr != m_lSentryGUIDs.end(); ++itr) + { + if (Creature* pSentry = instance->GetCreature(*itr)) + pSentry->Respawn(); + } + + // Respawn or Reset Vazruden the herald + if (Creature* pVazruden = GetSingleCreatureFromStorage(NPC_VAZRUDEN_HERALD)) + { + if (!pVazruden->IsAlive()) + pVazruden->Respawn(); + else + { + if (ScriptedAI* pVazrudenAI = dynamic_cast(pVazruden->AI())) + pVazrudenAI->EnterEvadeMode(); + } + } + + // Despawn Vazruden + if (Creature* pVazruden = GetSingleCreatureFromStorage(NPC_VAZRUDEN)) + pVazruden->ForcedDespawn(); +} + +InstanceData* GetInstanceData_instance_ramparts(Map* pMap) +{ + return new instance_ramparts(pMap); +} + +void AddSC_instance_ramparts() +{ + Script* pNewScript; + + pNewScript = new Script; + pNewScript->Name = "instance_ramparts"; + pNewScript->GetInstanceData = &GetInstanceData_instance_ramparts; + pNewScript->RegisterSelf(); +} diff --git a/src/modules/SD2/scripts/outland/hellfire_citadel/magtheridons_lair/boss_magtheridon.cpp b/src/modules/SD2/scripts/outland/hellfire_citadel/magtheridons_lair/boss_magtheridon.cpp new file mode 100644 index 000000000..fe93c9201 --- /dev/null +++ b/src/modules/SD2/scripts/outland/hellfire_citadel/magtheridons_lair/boss_magtheridon.cpp @@ -0,0 +1,589 @@ +/* Copyright (C) 2006 - 2013 ScriptDev2 + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with 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: 80 +SDComment: Phase 3 transition requires additional research. The Manticron cubes require additional core support. Timers need to be revised. +SDCategory: Hellfire Citadel, Magtheridon's lair +EndScriptData */ + +#include "precompiled.h" +#include "magtheridons_lair.h" + +enum +{ + // yells + SAY_AGGRO_1 = -1544006, + SAY_AGGRO_2 = -1544007, + SAY_BANISH = -1544008, + SAY_CHAMBER_DESTROY = -1544009, + SAY_PLAYER_KILLED = -1544010, + SAY_DEATH = -1544011, + + EMOTE_GENERIC_ENRAGED = -1000003, + EMOTE_BLASTNOVA = -1544013, + EMOTE_FREED = -1544015, + + // Maghteridon spells + SPELL_SHADOW_CAGE_DUMMY = 30205, // dummy aura - in creature_template_addon + SPELL_BLASTNOVA = 30616, + SPELL_CLEAVE = 30619, + // SPELL_QUAKE = 30657, // spell may be related but probably used in the recent versions of the script + // SPELL_QUAKE_TRIGGER = 30576, // spell removed from DBC - triggers 30571 + SPELL_QUAKE_KNOCKBACK = 30571, + SPELL_BLAZE = 30541, // triggers 30542 + SPELL_BERSERK = 27680, + + // phase 3 spells + SPELL_CAMERA_SHAKE = 36455, + SPELL_DEBRIS_KNOCKDOWN = 36449, + SPELL_QUAKE_EFFECT = 30572, // sets the debris during phase 3 - triggers 30632 + SPELL_DEBRIS_DAMAGE = 30631, + SPELL_DEBRIS_VISUAL = 30632, + + // Cube spells + SPELL_SHADOW_CAGE = 30168, + SPELL_SHADOW_GRASP_VISUAL = 30166, + SPELL_SHADOW_GRASP = 30410, + SPELL_MIND_EXHAUSTION = 44032, + + // Hellfire channeler spells + SPELL_SHADOW_GRASP_DUMMY = 30207, // dummy spell - cast on OOC timer + SPELL_SHADOW_BOLT_VOLLEY = 30510, + SPELL_DARK_MENDING = 30528, + SPELL_FEAR = 30530, // 39176 + SPELL_BURNING_ABYSSAL = 30511, + SPELL_SOUL_TRANSFER = 30531, + + // Abyss spells + SPELL_FIRE_BLAST = 37110, + + // summons + // NPC_MAGS_ROOM = 17516, + NPC_BURNING_ABYSS = 17454, + NPC_RAID_TRIGGER = 17376, + + MAX_QUAKE_COUNT = 7, +}; + +/*###### +## boss_magtheridon +######*/ + +struct boss_magtheridonAI : public ScriptedAI +{ + boss_magtheridonAI(Creature* pCreature) : ScriptedAI(pCreature) + { + m_pInstance = (ScriptedInstance*)pCreature->GetInstanceData(); + Reset(); + } + + ScriptedInstance* m_pInstance; + + uint32 m_uiBerserkTimer; + uint32 m_uiQuakeTimer; + uint32 m_uiCleaveTimer; + uint32 m_uiBlastNovaTimer; + uint32 m_uiBlazeTimer; + uint32 m_uiDebrisTimer; + uint32 m_uiTransitionTimer; + uint8 m_uiTransitionCount; + uint8 m_uiQuakeCount; + + bool m_bIsPhase3; + + void Reset() override + { + m_uiBerserkTimer = 20 * MINUTE * IN_MILLISECONDS; + m_uiQuakeTimer = 30000; + m_uiBlazeTimer = urand(10000, 15000); + m_uiBlastNovaTimer = 60000; + m_uiCleaveTimer = 15000; + m_uiTransitionTimer = 0; + m_uiTransitionCount = 0; + m_uiDebrisTimer = urand(20000, 30000); + + m_bIsPhase3 = false; + + SetCombatMovement(true); + + m_creature->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE); + } + + void Aggro(Unit* /*pWho*/) override + { + DoScriptText(EMOTE_FREED, m_creature); + DoScriptText(urand(0, 1) ? SAY_AGGRO_1 : SAY_AGGRO_2, m_creature); + + m_creature->RemoveAurasDueToSpell(SPELL_SHADOW_CAGE_DUMMY); + } + + void KilledUnit(Unit* /*pVictim*/) override + { + DoScriptText(SAY_PLAYER_KILLED, m_creature); + } + + void JustDied(Unit* /*pKiller*/) override + { + if (m_pInstance) + m_pInstance->SetData(TYPE_MAGTHERIDON_EVENT, DONE); + + DoScriptText(SAY_DEATH, m_creature); + } + + void JustReachedHome() override + { + if (m_pInstance) + m_pInstance->SetData(TYPE_MAGTHERIDON_EVENT, FAIL); + } + + void SpellHit(Unit* /*pCaster*/, const SpellEntry* pSpell) override + { + // When banished by the cubes + if (pSpell->Id == SPELL_SHADOW_CAGE) + DoScriptText(SAY_BANISH, m_creature); + } + + void UpdateAI(const uint32 uiDiff) override + { + if (!m_creature->SelectHostileTarget() || !m_creature->getVictim()) + return; + + if (m_uiBerserkTimer) + { + if (m_uiBerserkTimer <= uiDiff) + { + if (DoCastSpellIfCan(m_creature, SPELL_BERSERK) == CAST_OK) + { + DoScriptText(EMOTE_GENERIC_ENRAGED, m_creature); + m_uiBerserkTimer = 0; + } + } + else + m_uiBerserkTimer -= uiDiff; + } + + // Transition to phase 3 + if (m_uiTransitionTimer) + { + if (m_uiTransitionTimer <= uiDiff) + { + switch (m_uiTransitionCount) + { + case 0: + // Shake the room + if (DoCastSpellIfCan(m_creature, SPELL_CAMERA_SHAKE) == CAST_OK) + { + if (m_pInstance) + m_pInstance->SetData(TYPE_MAGTHERIDON_EVENT, SPECIAL); + + m_uiTransitionTimer = 8000; + } + break; + case 1: + if (DoCastSpellIfCan(m_creature, SPELL_DEBRIS_KNOCKDOWN) == CAST_OK) + m_uiTransitionTimer = 0; + break; + } + + ++m_uiTransitionCount; + } + else + m_uiTransitionTimer -= uiDiff; + + // Workaround for missing spell: no other spells during transition + if (!m_uiTransitionCount) + return; + } + + if (m_uiQuakeTimer < uiDiff) + { + // Workaround for missing spell + // Note: this won't really stun the boss, but it won't allow him to use other spells + if (!m_uiQuakeCount) + { + SetCombatMovement(false); + m_creature->GetMotionMaster()->Clear(); + m_creature->GetMotionMaster()->MoveIdle(); + } + + if (m_uiQuakeCount < MAX_QUAKE_COUNT) + { + if (DoCastSpellIfCan(m_creature, SPELL_QUAKE_KNOCKBACK) == CAST_OK) + { + m_uiQuakeTimer = 1000; + ++m_uiQuakeCount; + } + } + else + { + SetCombatMovement(true); + m_creature->GetMotionMaster()->Clear(); + m_creature->GetMotionMaster()->MoveChase(m_creature->getVictim()); + + m_uiQuakeTimer = 43000; + m_uiQuakeCount = 0; + } + } + else + m_uiQuakeTimer -= uiDiff; + + // don't use other spells during quake + if (m_uiQuakeCount) + return; + + if (m_uiCleaveTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature->getVictim(), SPELL_CLEAVE) == CAST_OK) + m_uiCleaveTimer = 10000; + } + else + m_uiCleaveTimer -= uiDiff; + + if (m_uiBlastNovaTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature, SPELL_BLASTNOVA) == CAST_OK) + { + DoScriptText(EMOTE_BLASTNOVA, m_creature); + m_uiBlastNovaTimer = 60000; + } + } + else + m_uiBlastNovaTimer -= uiDiff; + + if (m_uiBlazeTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature, SPELL_BLAZE) == CAST_OK) + m_uiBlazeTimer = urand(10000, 15000); + } + else + m_uiBlazeTimer -= uiDiff; + + // Transition to phase 3 + if (!m_bIsPhase3 && m_creature->GetHealthPercent() < 30.0f) + { + // ToDo: maybe there is a spell here - requires additional research + DoScriptText(SAY_CHAMBER_DESTROY, m_creature); + m_uiTransitionTimer = 5000; + m_bIsPhase3 = true; + } + + // Debris fall in phase 3 + if (m_bIsPhase3) + { + if (m_uiDebrisTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature, SPELL_QUAKE_EFFECT) == CAST_OK) + m_uiDebrisTimer = urand(20000, 30000); + } + else + m_uiDebrisTimer -= uiDiff; + } + + DoMeleeAttackIfReady(); + } +}; + +/*###### +## mob_hellfire_channeler +######*/ + +struct mob_hellfire_channelerAI : public ScriptedAI +{ + mob_hellfire_channelerAI(Creature* pCreature) : ScriptedAI(pCreature) + { + m_pInstance = (ScriptedInstance*)pCreature->GetInstanceData(); + Reset(); + } + + ScriptedInstance* m_pInstance; + + uint32 m_uiShadowGraspTimer; + uint32 m_uiShadowBoltVolleyTimer; + uint32 m_uiDarkMendingTimer; + uint32 m_uiFearTimer; + uint32 m_uiInfernalTimer; + + void Reset() override + { + m_uiShadowGraspTimer = 10000; + m_uiShadowBoltVolleyTimer = urand(8000, 10000); + m_uiDarkMendingTimer = 10000; + m_uiFearTimer = urand(15000, 20000); + m_uiInfernalTimer = urand(10000, 50000); + } + + void Aggro(Unit* /*pWho*/) override + { + m_creature->InterruptNonMeleeSpells(false); + + if (m_pInstance) + m_pInstance->SetData(TYPE_CHANNELER_EVENT, IN_PROGRESS); + } + + // Don't attack on LoS check + void MoveInLineOfSight(Unit* /*pWho*/) override { } + + void JustDied(Unit* /*pKiller*/) override + { + DoCastSpellIfCan(m_creature, SPELL_SOUL_TRANSFER, CAST_TRIGGERED); + } + + void JustReachedHome() override + { + if (m_pInstance) + m_pInstance->SetData(TYPE_CHANNELER_EVENT, FAIL); + } + + void JustSummoned(Creature* pSummoned) override + { + if (m_creature->getVictim()) + pSummoned->AI()->AttackStart(m_creature->getVictim()); + } + + void SummonedCreatureDespawn(Creature* pSummoned) override + { + if (pSummoned->GetEntry() == NPC_BURNING_ABYSS) + m_uiInfernalTimer = urand(10000, 15000); + } + + void UpdateAI(const uint32 uiDiff) override + { + // Channel spell on Magtheridon, on OOC timer + if (m_uiShadowGraspTimer) + { + if (m_uiShadowGraspTimer <= uiDiff) + { + if (DoCastSpellIfCan(m_creature, SPELL_SHADOW_GRASP_DUMMY) == CAST_OK) + m_uiShadowGraspTimer = 0; + } + else + m_uiShadowGraspTimer -= uiDiff; + } + + if (!m_creature->SelectHostileTarget() || !m_creature->getVictim()) + return; + + if (m_uiShadowBoltVolleyTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature, SPELL_SHADOW_BOLT_VOLLEY) == CAST_OK) + m_uiShadowBoltVolleyTimer = urand(10000, 20000); + } + else + m_uiShadowBoltVolleyTimer -= uiDiff; + + if (m_uiDarkMendingTimer < uiDiff) + { + if (Unit* pTarget = DoSelectLowestHpFriendly(30.0f)) + { + if (DoCastSpellIfCan(pTarget, SPELL_DARK_MENDING) == CAST_OK) + m_uiDarkMendingTimer = urand(10000, 20000); + } + } + else + m_uiDarkMendingTimer -= uiDiff; + + if (m_uiFearTimer < uiDiff) + { + if (Unit* pTarget = m_creature->SelectAttackingTarget(ATTACKING_TARGET_RANDOM, 1)) + { + if (DoCastSpellIfCan(pTarget, SPELL_FEAR) == CAST_OK) + m_uiFearTimer = urand(25000, 40000); + } + } + else + m_uiFearTimer -= uiDiff; + + if (m_uiInfernalTimer) + { + if (m_uiInfernalTimer <= uiDiff) + { + if (Unit* pTarget = m_creature->SelectAttackingTarget(ATTACKING_TARGET_RANDOM, 0)) + { + if (DoCastSpellIfCan(pTarget, SPELL_BURNING_ABYSSAL) == CAST_OK) + m_uiInfernalTimer = 0; + } + } + else + m_uiInfernalTimer -= uiDiff; + } + + DoMeleeAttackIfReady(); + } +}; + +/*###### +## go_manticron_cube +######*/ + +bool GOUse_go_manticron_cube(Player* pPlayer, GameObject* pGo) +{ + // if exhausted or already channeling return + if (pPlayer->HasAura(SPELL_MIND_EXHAUSTION) || pPlayer->HasAura(SPELL_SHADOW_GRASP)) + return true; + + if (ScriptedInstance* pInstance = (ScriptedInstance*)pGo->GetInstanceData()) + { + if (pInstance->GetData(TYPE_MAGTHERIDON_EVENT) != IN_PROGRESS) + return true; + + if (Creature* pMagtheridon = pInstance->GetSingleCreatureFromStorage(NPC_MAGTHERIDON)) + { + if (!pMagtheridon->IsAlive()) + return true; + + // visual is cast by cube + if (Creature* pTrigger = GetClosestCreatureWithEntry(pGo, NPC_RAID_TRIGGER, 5.0f)) + pTrigger->CastSpell(pTrigger, SPELL_SHADOW_GRASP_VISUAL, false); + + // the real spell is cast by player + pPlayer->CastSpell(pPlayer, SPELL_SHADOW_GRASP, false, NULL, NULL, pGo->GetObjectGuid()); + } + } + + return true; +} + +// TODO Remove this 'script' when combat and persistent area auras can be proper prevented from core-side +struct npc_target_triggerAI : public Scripted_NoMovementAI +{ + npc_target_triggerAI(Creature* pCreature) : Scripted_NoMovementAI(pCreature) {Reset();} + + uint32 m_uiDebrisTimer; + + void Reset() override + { + m_uiDebrisTimer = 0; + } + + void AttackStart(Unit* /*pWho*/) override {} + void MoveInLineOfSight(Unit* /*pWho*/) override {} + + void SpellHit(Unit* /*pCaster*/, const SpellEntry* pSpell) override + { + // Workaround for missing core support for this type of dummy aura + if (pSpell->Id == SPELL_QUAKE_EFFECT) + { + if (DoCastSpellIfCan(m_creature, SPELL_DEBRIS_VISUAL) == CAST_OK) + m_uiDebrisTimer = 5000; + } + } + + void UpdateAI(const uint32 uiDiff) override + { + // Cast debris damage after 5 seconds (on visual removal) + if (m_uiDebrisTimer) + { + if (m_uiDebrisTimer <= uiDiff) + { + if (DoCastSpellIfCan(m_creature, SPELL_DEBRIS_DAMAGE) == CAST_OK) + m_uiDebrisTimer = 0; + } + else + m_uiDebrisTimer -= uiDiff; + } + } +}; + +// ToDo: move this script to eventAI +struct mob_abyssalAI : public ScriptedAI +{ + mob_abyssalAI(Creature* pCreature) : ScriptedAI(pCreature) { Reset(); } + + uint32 m_uiFireBlastTimer; + uint32 m_uiDespawnTimer; + + void Reset() override + { + m_uiDespawnTimer = 60000; + m_uiFireBlastTimer = 6000; + } + + void UpdateAI(const uint32 uiDiff) override + { + if (!m_creature->SelectHostileTarget() || !m_creature->getVictim()) + return; + + if (m_uiDespawnTimer < uiDiff) + { + m_creature->ForcedDespawn(); + m_uiDespawnTimer = 10000; + } + else + m_uiDespawnTimer -= uiDiff; + + if (m_uiFireBlastTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature->getVictim(), SPELL_FIRE_BLAST) == CAST_OK) + m_uiFireBlastTimer = urand(5000, 15000); + } + else + m_uiFireBlastTimer -= uiDiff; + + DoMeleeAttackIfReady(); + } +}; + +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_npc_target_triggerAI(Creature* pCreature) +{ + return new npc_target_triggerAI(pCreature); +} + +CreatureAI* GetAI_mob_abyssalAI(Creature* pCreature) +{ + return new mob_abyssalAI(pCreature); +} + +void AddSC_boss_magtheridon() +{ + Script* pNewScript; + + pNewScript = new Script; + pNewScript->Name = "boss_magtheridon"; + pNewScript->GetAI = &GetAI_boss_magtheridon; + pNewScript->RegisterSelf(); + + pNewScript = new Script; + pNewScript->Name = "mob_hellfire_channeler"; + pNewScript->GetAI = &GetAI_mob_hellfire_channeler; + pNewScript->RegisterSelf(); + + pNewScript = new Script; + pNewScript->Name = "go_manticron_cube"; + pNewScript->pGOUse = &GOUse_go_manticron_cube; + pNewScript->RegisterSelf(); + + pNewScript = new Script; + pNewScript->Name = "npc_target_trigger"; + pNewScript->GetAI = &GetAI_npc_target_triggerAI; + pNewScript->RegisterSelf(); + + pNewScript = new Script; + pNewScript->Name = "mob_abyssal"; + pNewScript->GetAI = &GetAI_mob_abyssalAI; + pNewScript->RegisterSelf(); +} diff --git a/src/modules/SD2/scripts/outland/hellfire_citadel/magtheridons_lair/instance_magtheridons_lair.cpp b/src/modules/SD2/scripts/outland/hellfire_citadel/magtheridons_lair/instance_magtheridons_lair.cpp new file mode 100644 index 000000000..4a5e51fc8 --- /dev/null +++ b/src/modules/SD2/scripts/outland/hellfire_citadel/magtheridons_lair/instance_magtheridons_lair.cpp @@ -0,0 +1,257 @@ +/* Copyright (C) 2006 - 2013 ScriptDev2 + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along 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 "magtheridons_lair.h" + +instance_magtheridons_lair::instance_magtheridons_lair(Map* pMap) : ScriptedInstance(pMap), + m_uiRandYellTimer(90000), + m_uiCageBreakTimer(0), + m_uiCageBreakStage(0) +{ + Initialize(); +} + +void instance_magtheridons_lair::Initialize() +{ + memset(&m_auiEncounter, 0, sizeof(m_auiEncounter)); +} + +bool instance_magtheridons_lair::IsEncounterInProgress() const +{ + for (uint8 i = 0; i < MAX_ENCOUNTER; ++i) + { + if (m_auiEncounter[i] == IN_PROGRESS) + return true; + } + + return false; +} + +void instance_magtheridons_lair::OnCreatureCreate(Creature* pCreature) +{ + switch (pCreature->GetEntry()) + { + case NPC_MAGTHERIDON: + m_mNpcEntryGuidStore[NPC_MAGTHERIDON] = pCreature->GetObjectGuid(); + break; + case NPC_CHANNELER: + m_lChannelerGuidList.push_back(pCreature->GetObjectGuid()); + break; + } +} + +void instance_magtheridons_lair::OnObjectCreate(GameObject* pGo) +{ + switch (pGo->GetEntry()) + { + case GO_DOODAD_HF_MAG_DOOR01: // event door + m_mGoEntryGuidStore[GO_DOODAD_HF_MAG_DOOR01] = pGo->GetObjectGuid(); + break; + case GO_DOODAD_HF_RAID_FX01: // hall + case GO_MAGTHERIDON_COLUMN_003: // six columns + case GO_MAGTHERIDON_COLUMN_002: + case GO_MAGTHERIDON_COLUMN_004: + case GO_MAGTHERIDON_COLUMN_005: + case GO_MAGTHERIDON_COLUMN_000: + case GO_MAGTHERIDON_COLUMN_001: + m_lColumnGuidList.push_back(pGo->GetObjectGuid()); + break; + case GO_MANTICRON_CUBE: + m_lCubeGuidList.push_back(pGo->GetObjectGuid()); + break; + } +} + +void instance_magtheridons_lair::SetData(uint32 uiType, uint32 uiData) +{ + switch (uiType) + { + case TYPE_MAGTHERIDON_EVENT: + switch (uiData) + { + case FAIL: + // Reset channelers + for (GuidList::const_iterator itr = m_lChannelerGuidList.begin(); itr != m_lChannelerGuidList.end(); ++itr) + { + if (Creature* pChanneler = instance->GetCreature(*itr)) + { + if (!pChanneler->IsAlive()) + pChanneler->Respawn(); + } + } + + // Reset columns + for (GuidList::const_iterator itr = m_lColumnGuidList.begin(); itr != m_lColumnGuidList.end(); ++itr) + { + if (GameObject* pColumn = instance->GetGameObject(*itr)) + pColumn->ResetDoorOrButton(); + } + + // Reset cubes + for (GuidList::const_iterator itr = m_lCubeGuidList.begin(); itr != m_lCubeGuidList.end(); ++itr) + DoToggleGameObjectFlags(*itr, GO_FLAG_NO_INTERACT, true); + + // Reset timers and doors + SetData(TYPE_CHANNELER_EVENT, NOT_STARTED); + m_uiCageBreakTimer = 0; + m_uiCageBreakStage = 0; + + // no break; + case DONE: + // Reset door on Fail or Done + if (GameObject* pDoor = GetSingleGameObjectFromStorage(GO_DOODAD_HF_MAG_DOOR01)) + pDoor->ResetDoorOrButton(); + break; + case IN_PROGRESS: + // Set boss in combat + if (Creature* pMagtheridon = GetSingleCreatureFromStorage(NPC_MAGTHERIDON)) + { + if (pMagtheridon->IsAlive()) + { + pMagtheridon->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE); + pMagtheridon->SetInCombatWithZone(); + } + } + // Enable cubes + for (GuidList::const_iterator itr = m_lCubeGuidList.begin(); itr != m_lCubeGuidList.end(); ++itr) + DoToggleGameObjectFlags(*itr, GO_FLAG_NO_INTERACT, false); + break; + case SPECIAL: + // Collapse the hall - don't store this value + for (GuidList::const_iterator itr = m_lColumnGuidList.begin(); itr != m_lColumnGuidList.end(); ++itr) + DoUseDoorOrButton(*itr); + // return, don't set encounter as special + return; + } + m_auiEncounter[uiType] = uiData; + break; + case TYPE_CHANNELER_EVENT: + // don't set the same data twice + if (m_auiEncounter[1] == uiData) + break; + // stop the event timer on fail + if (uiData == FAIL) + { + m_uiCageBreakTimer = 0; + m_uiCageBreakStage = 0; + + // Reset door on Fail + if (GameObject* pDoor = GetSingleGameObjectFromStorage(GO_DOODAD_HF_MAG_DOOR01)) + pDoor->ResetDoorOrButton(); + + // Reset Magtheridon + if (Creature* pMagtheridon = GetSingleCreatureFromStorage(NPC_MAGTHERIDON)) + { + if (pMagtheridon->IsAlive()) + pMagtheridon->AI()->EnterEvadeMode(); + } + } + // prepare Magtheridon for release + if (uiData == IN_PROGRESS) + { + if (Creature* pMagtheridon = GetSingleCreatureFromStorage(NPC_MAGTHERIDON)) + { + if (pMagtheridon->IsAlive()) + { + DoScriptText(EMOTE_EVENT_BEGIN, pMagtheridon); + m_uiCageBreakTimer = MINUTE * IN_MILLISECONDS; + } + } + + // combat door + DoUseDoorOrButton(GO_DOODAD_HF_MAG_DOOR01); + } + m_auiEncounter[uiType] = uiData; + break; + } + + // Instance save isn't needed for this one +} + +uint32 instance_magtheridons_lair::GetData(uint32 uiType) const +{ + if (uiType < MAX_ENCOUNTER) + return m_auiEncounter[uiType]; + + return 0; +} + +void instance_magtheridons_lair::Update(uint32 uiDiff) +{ + // Prepare to release Magtheridon + if (m_uiCageBreakTimer) + { + if (m_uiCageBreakTimer <= uiDiff) + { + switch (m_uiCageBreakStage) + { + case 0: + if (Creature* pMagtheridon = GetSingleCreatureFromStorage(NPC_MAGTHERIDON)) + { + if (pMagtheridon->IsAlive()) + { + DoScriptText(EMOTE_NEARLY_FREE, pMagtheridon); + m_uiCageBreakTimer = MINUTE * IN_MILLISECONDS; + } + } + break; + case 1: + SetData(TYPE_MAGTHERIDON_EVENT, IN_PROGRESS); + m_uiCageBreakTimer = 0; + break; + } + + ++m_uiCageBreakStage; + } + else + m_uiCageBreakTimer -= uiDiff; + } + + // no yell if event is in progress or finished + if (m_auiEncounter[TYPE_CHANNELER_EVENT] == IN_PROGRESS || m_auiEncounter[TYPE_MAGTHERIDON_EVENT] == DONE) + return; + + if (m_uiRandYellTimer < uiDiff) + { + DoOrSimulateScriptTextForThisInstance(aRandomTaunt[urand(0, 5)], NPC_MAGTHERIDON); + m_uiRandYellTimer = 90000; + } + else + m_uiRandYellTimer -= uiDiff; +} + +InstanceData* GetInstanceData_instance_magtheridons_lair(Map* pMap) +{ + return new instance_magtheridons_lair(pMap); +} + +void AddSC_instance_magtheridons_lair() +{ + Script* pNewScript; + + pNewScript = new Script; + pNewScript->Name = "instance_magtheridons_lair"; + pNewScript->GetInstanceData = &GetInstanceData_instance_magtheridons_lair; + pNewScript->RegisterSelf(); +} diff --git a/src/modules/SD2/scripts/outland/hellfire_citadel/magtheridons_lair/magtheridons_lair.h b/src/modules/SD2/scripts/outland/hellfire_citadel/magtheridons_lair/magtheridons_lair.h new file mode 100644 index 000000000..a41b47bc5 --- /dev/null +++ b/src/modules/SD2/scripts/outland/hellfire_citadel/magtheridons_lair/magtheridons_lair.h @@ -0,0 +1,63 @@ +/* Copyright (C) 2006 - 2013 ScriptDev2 + * This program is free software licensed under GPL version 2 + * Please see the included DOCS/LICENSE.TXT for more information */ + +#ifndef DEF_MAGTHERIDONS_LAIR_H +#define DEF_MAGTHERIDONS_LAIR_H + +enum +{ + MAX_ENCOUNTER = 2, + + TYPE_MAGTHERIDON_EVENT = 0, + TYPE_CHANNELER_EVENT = 1, + + NPC_MAGTHERIDON = 17257, + NPC_CHANNELER = 17256, + + GO_MANTICRON_CUBE = 181713, + GO_DOODAD_HF_MAG_DOOR01 = 183847, + GO_DOODAD_HF_RAID_FX01 = 184653, + GO_MAGTHERIDON_COLUMN_003 = 184634, + GO_MAGTHERIDON_COLUMN_002 = 184635, + GO_MAGTHERIDON_COLUMN_004 = 184636, + GO_MAGTHERIDON_COLUMN_005 = 184637, + GO_MAGTHERIDON_COLUMN_000 = 184638, + GO_MAGTHERIDON_COLUMN_001 = 184639, + + EMOTE_EVENT_BEGIN = -1544014, + EMOTE_NEARLY_FREE = -1544016, +}; + +static const int32 aRandomTaunt[] = { -1544000, -1544001, -1544002, -1544003, -1544004, -1544005}; + +class instance_magtheridons_lair : public ScriptedInstance +{ + public: + instance_magtheridons_lair(Map* pMap); + + void Initialize() override; + + bool IsEncounterInProgress() const override; + + void OnCreatureCreate(Creature* pCreature) override; + void OnObjectCreate(GameObject* pGo) override; + + void SetData(uint32 uiType, uint32 uiData) override; + uint32 GetData(uint32 uiType) const override; + + void Update(uint32 uiDiff) override; + + private: + uint32 m_auiEncounter[MAX_ENCOUNTER]; + + GuidList m_lChannelerGuidList; + GuidList m_lColumnGuidList; + GuidList m_lCubeGuidList; + + uint32 m_uiRandYellTimer; + uint32 m_uiCageBreakTimer; + uint8 m_uiCageBreakStage; +}; + +#endif diff --git a/src/modules/SD2/scripts/outland/hellfire_citadel/shattered_halls/boss_nethekurse.cpp b/src/modules/SD2/scripts/outland/hellfire_citadel/shattered_halls/boss_nethekurse.cpp new file mode 100644 index 000000000..b6f3dfee1 --- /dev/null +++ b/src/modules/SD2/scripts/outland/hellfire_citadel/shattered_halls/boss_nethekurse.cpp @@ -0,0 +1,455 @@ +/* Copyright (C) 2006 - 2013 ScriptDev2 + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with 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 "shattered_halls.h" + +struct Say +{ + int32 id; +}; + +static Say PeonAttacked[] = +{ + { -1540001}, + { -1540002}, + { -1540003}, + { -1540004}, +}; + +static Say PeonDies[] = +{ + { -1540005}, + { -1540006}, + { -1540007}, + { -1540008}, +}; + +enum +{ + SAY_INTRO = -1540000, + SAY_TAUNT_1 = -1540009, + SAY_TAUNT_2 = -1540010, + SAY_TAUNT_3 = -1540011, + SAY_AGGRO_1 = -1540012, + SAY_AGGRO_2 = -1540013, + SAY_AGGRO_3 = -1540014, + SAY_SLAY_1 = -1540015, + SAY_SLAY_2 = -1540016, + SAY_DIE = -1540017, + + SPELL_DEATH_COIL = 30500, + SPELL_DARK_SPIN = 30502, // core bug spell attack caster :D + SPELL_SHADOW_FISSURE = 30496, // Summon the ShadowFissure NPC + + SPELL_SHADOW_CLEAVE = 30495, + SPELL_SHADOW_SLAM_H = 35953, + + SPELL_SHADOW_SEAR = 30735, // On fel orcs - not sure yet how it is used + SPELL_HEMORRHAGE = 30478, + + SPELL_CONSUMPTION = 30497, // Cast by the shadow fissure + + NPC_FEL_ORC_CONVERT = 17083, +}; + +struct boss_grand_warlock_nethekurseAI : public ScriptedAI +{ + boss_grand_warlock_nethekurseAI(Creature* pCreature) : ScriptedAI(pCreature) + { + m_pInstance = (ScriptedInstance*)pCreature->GetInstanceData(); + m_bIsRegularMode = pCreature->GetMap()->IsRegularDifficulty(); + m_bIntroOnce = false; + Reset(); + } + + ScriptedInstance* m_pInstance; + bool m_bIsRegularMode; + + bool m_bIntroOnce; + bool m_bIsIntroEvent; + bool m_bIsMainEvent; + bool m_bSpinOnce; + // bool m_bHasTaunted; + bool m_bPhase; + + uint32 m_uiPeonEngagedCount; + uint32 m_uiPeonKilledCount; + + uint32 m_uiIntroEventTimer; + uint32 m_uiDeathCoilTimer; + uint32 m_uiShadowFissureTimer; + uint32 m_uiCleaveTimer; + + ObjectGuid m_lastEventInvokerGuid; + + void Reset() override + { + m_creature->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE); + + m_bIsIntroEvent = false; + m_bIsMainEvent = false; + // m_bHasTaunted = false; + m_bSpinOnce = false; + m_bPhase = false; + + m_uiPeonEngagedCount = 0; + m_uiPeonKilledCount = 0; + + m_uiIntroEventTimer = 90000; // how long before getting bored and kills his minions? + m_uiDeathCoilTimer = 20000; + m_uiShadowFissureTimer = 8000; + m_uiCleaveTimer = 5000; + + m_lastEventInvokerGuid.Clear(); + } + + void DoYellForPeonAggro(Unit* pWho) + { + if (m_uiPeonEngagedCount >= 4) + return; + + DoScriptText(PeonAttacked[m_uiPeonEngagedCount].id, m_creature); + ++m_uiPeonEngagedCount; + + if (pWho) + m_lastEventInvokerGuid = pWho->GetObjectGuid(); + } + + void DoYellForPeonDeath(Unit* pKiller) + { + if (m_uiPeonKilledCount >= 4) + return; + + DoScriptText(PeonDies[m_uiPeonKilledCount].id, m_creature); + ++m_uiPeonKilledCount; + + if (m_uiPeonKilledCount == 4) + { + m_bIsIntroEvent = false; + m_bIsMainEvent = true; + m_creature->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE); + + if (pKiller) + AttackStart(pKiller); + } + } + + void DoTauntPeons() + { + switch (urand(0, 2)) + { + case 0: DoScriptText(SAY_TAUNT_1, m_creature); break; + case 1: DoScriptText(SAY_TAUNT_2, m_creature); break; + case 2: DoScriptText(SAY_TAUNT_3, m_creature); break; + } + + std::list lFelConverts; + GetCreatureListWithEntryInGrid(lFelConverts, m_creature, NPC_FEL_ORC_CONVERT, 40.0f); + for (std::list::iterator itr = lFelConverts.begin(); itr != lFelConverts.end(); ++itr) + (*itr)->DealDamage(*itr, (*itr)->GetHealth(), NULL, DIRECT_DAMAGE, SPELL_SCHOOL_MASK_NORMAL, NULL, false); + + m_bIsIntroEvent = false; + m_uiPeonEngagedCount = 4; + m_uiPeonKilledCount = 4; + m_bIsMainEvent = true; + m_creature->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE); + + if (Unit* pEnemy = m_creature->GetMap()->GetUnit(m_lastEventInvokerGuid)) + AttackStart(pEnemy); + } + + void AttackStart(Unit* pWho) override + { + if (m_bIsIntroEvent || !m_bIsMainEvent) + return; + + if (m_creature->Attack(pWho, true)) + { + m_creature->AddThreat(pWho); + m_creature->SetInCombatWith(pWho); + pWho->SetInCombatWith(m_creature); + + if (m_bPhase) + DoStartNoMovement(pWho); + else + DoStartMovement(pWho); + } + } + + void MoveInLineOfSight(Unit* pWho) override + { + if (!m_bIntroOnce && pWho->GetTypeId() == TYPEID_PLAYER && !((Player*)pWho)->isGameMaster() && m_creature->IsWithinDistInMap(pWho, 50.0f) && m_creature->IsWithinLOSInMap(pWho)) + { + DoScriptText(SAY_INTRO, m_creature); + m_bIntroOnce = true; + m_bIsIntroEvent = true; + + m_lastEventInvokerGuid = pWho->GetObjectGuid(); + + if (m_pInstance) + m_pInstance->SetData(TYPE_NETHEKURSE, IN_PROGRESS); + } + + if (m_bIsIntroEvent || !m_bIsMainEvent) + return; + + ScriptedAI::MoveInLineOfSight(pWho); + } + + void Aggro(Unit* /*pWho*/) override + { + switch (urand(0, 2)) + { + case 0: DoScriptText(SAY_AGGRO_1, m_creature); break; + case 1: DoScriptText(SAY_AGGRO_2, m_creature); break; + case 2: DoScriptText(SAY_AGGRO_3, m_creature); break; + } + } + + void JustSummoned(Creature* pSummoned) override + { + // ToDo: this should be done in DB + pSummoned->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE); + pSummoned->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE); + + pSummoned->CastSpell(pSummoned, SPELL_CONSUMPTION, false, NULL, NULL, m_creature->GetObjectGuid()); + } + + void KilledUnit(Unit* /*pVictim*/) override + { + DoScriptText(urand(0, 1) ? SAY_SLAY_1 : SAY_SLAY_2, m_creature); + } + + void JustDied(Unit* /*pKiller*/) override + { + DoScriptText(SAY_DIE, m_creature); + + if (!m_pInstance) + return; + + m_pInstance->SetData(TYPE_NETHEKURSE, DONE); + } + + void JustReachedHome() override + { + if (m_pInstance) + m_pInstance->SetData(TYPE_NETHEKURSE, FAIL); + + std::list lFelConverts; + GetCreatureListWithEntryInGrid(lFelConverts, m_creature, NPC_FEL_ORC_CONVERT, 40.0f); + for (std::list::iterator itr = lFelConverts.begin(); itr != lFelConverts.end(); ++itr) + { + if (!(*itr)->IsAlive()) + (*itr)->Respawn(); + } + } + + void UpdateAI(const uint32 uiDiff) override + { + if (m_bIsIntroEvent) + { + if (!m_pInstance) + return; + + if (m_pInstance->GetData(TYPE_NETHEKURSE) == IN_PROGRESS) + { + if (m_uiIntroEventTimer < uiDiff) + DoTauntPeons(); + else + m_uiIntroEventTimer -= uiDiff; + } + } + + if (!m_creature->SelectHostileTarget() || !m_creature->getVictim()) + return; + + if (!m_bIsMainEvent) + return; + + if (m_bPhase) + { + if (!m_bSpinOnce) + { + DoCastSpellIfCan(m_creature->getVictim(), SPELL_DARK_SPIN); + m_bSpinOnce = true; + } + + if (m_uiCleaveTimer < uiDiff) + { + DoCastSpellIfCan(m_creature->getVictim(), m_bIsRegularMode ? SPELL_SHADOW_CLEAVE : SPELL_SHADOW_SLAM_H); + m_uiCleaveTimer = urand(6000, 8500); + } + else + m_uiCleaveTimer -= uiDiff; + } + else + { + if (m_uiShadowFissureTimer < uiDiff) + { + if (Unit* pTarget = m_creature->SelectAttackingTarget(ATTACKING_TARGET_RANDOM, 0)) + DoCastSpellIfCan(pTarget, SPELL_SHADOW_FISSURE); + m_uiShadowFissureTimer = urand(7500, 15000); + } + else + m_uiShadowFissureTimer -= uiDiff; + + if (m_uiDeathCoilTimer < uiDiff) + { + if (Unit* pTarget = m_creature->SelectAttackingTarget(ATTACKING_TARGET_RANDOM, 0)) + DoCastSpellIfCan(pTarget, SPELL_DEATH_COIL); + m_uiDeathCoilTimer = urand(15000, 20000); + } + else + m_uiDeathCoilTimer -= uiDiff; + + if (m_creature->GetHealthPercent() <= 20.0f) + m_bPhase = true; + + DoMeleeAttackIfReady(); + } + } +}; + +struct mob_fel_orc_convertAI : public ScriptedAI +{ + mob_fel_orc_convertAI(Creature* pCreature) : ScriptedAI(pCreature) + { + m_pInstance = (ScriptedInstance*)pCreature->GetInstanceData(); + Reset(); + } + + ScriptedInstance* m_pInstance; + uint32 m_uiHemorrhageTimer; + + void Reset() override + { + m_creature->SetNoCallAssistance(true); // we don't want any assistance (WE R HEROZ!) + m_uiHemorrhageTimer = 3000; + } + + void MoveInLineOfSight(Unit* /*pWho*/) override + { + return; + } + + void Aggro(Unit* pWho) override + { + if (m_pInstance) + { + Creature* pKurse = m_pInstance->GetSingleCreatureFromStorage(NPC_NETHEKURSE); + if (pKurse && m_creature->IsWithinDist(pKurse, 45.0f)) + { + if (boss_grand_warlock_nethekurseAI* pKurseAI = dynamic_cast(pKurse->AI())) + pKurseAI->DoYellForPeonAggro(pWho); + + if (m_pInstance->GetData(TYPE_NETHEKURSE) == IN_PROGRESS) + return; + else + m_pInstance->SetData(TYPE_NETHEKURSE, IN_PROGRESS); + } + } + } + + void JustDied(Unit* pKiller) override + { + if (m_pInstance) + { + if (m_pInstance->GetData(TYPE_NETHEKURSE) != IN_PROGRESS) + return; + + if (Creature* pKurse = m_pInstance->GetSingleCreatureFromStorage(NPC_NETHEKURSE)) + { + if (boss_grand_warlock_nethekurseAI* pKurseAI = dynamic_cast(pKurse->AI())) + pKurseAI->DoYellForPeonDeath(pKiller); + } + } + } + + void UpdateAI(const uint32 uiDiff) override + { + if (!m_creature->SelectHostileTarget() || !m_creature->getVictim()) + return; + + if (m_uiHemorrhageTimer < uiDiff) + { + DoCastSpellIfCan(m_creature->getVictim(), SPELL_HEMORRHAGE); + m_uiHemorrhageTimer = 15000; + } + else + m_uiHemorrhageTimer -= uiDiff; + + DoMeleeAttackIfReady(); + } +}; + +// NOTE: this creature are also summoned by other spells, for different creatures +struct mob_lesser_shadow_fissureAI : public Scripted_NoMovementAI +{ + mob_lesser_shadow_fissureAI(Creature* pCreature) : Scripted_NoMovementAI(pCreature) { Reset(); } + + void Reset() override { } + void MoveInLineOfSight(Unit* /*pWho*/) override { } + void AttackStart(Unit* /*pWho*/) override { } +}; + +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* pNewScript; + + pNewScript = new Script; + pNewScript->Name = "boss_grand_warlock_nethekurse"; + pNewScript->GetAI = &GetAI_boss_grand_warlock_nethekurse; + pNewScript->RegisterSelf(); + + pNewScript = new Script; + pNewScript->Name = "mob_fel_orc_convert"; + pNewScript->GetAI = &GetAI_mob_fel_orc_convert; + pNewScript->RegisterSelf(); + + pNewScript = new Script; + pNewScript->Name = "mob_lesser_shadow_fissure"; + pNewScript->GetAI = &GetAI_mob_lesser_shadow_fissure; + pNewScript->RegisterSelf(); +} diff --git a/src/modules/SD2/scripts/outland/hellfire_citadel/shattered_halls/boss_warbringer_omrogg.cpp b/src/modules/SD2/scripts/outland/hellfire_citadel/shattered_halls/boss_warbringer_omrogg.cpp new file mode 100644 index 000000000..70c69a852 --- /dev/null +++ b/src/modules/SD2/scripts/outland/hellfire_citadel/shattered_halls/boss_warbringer_omrogg.cpp @@ -0,0 +1,425 @@ +/* Copyright (C) 2006 - 2013 ScriptDev2 + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with 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 "shattered_halls.h" + +enum +{ + 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, + SPELL_BURNING_MAUL_H = 36056, + + NPC_LEFT_HEAD = 19523, + NPC_RIGHT_HEAD = 19524 +}; + +struct Yell +{ + int32 id; + uint32 creature; +}; + +static Yell GoCombat[] = +{ + { -1540018, NPC_LEFT_HEAD}, + { -1540019, NPC_LEFT_HEAD}, + { -1540020, NPC_LEFT_HEAD}, +}; +static Yell GoCombatDelay[] = +{ + { -1540021, NPC_RIGHT_HEAD}, + { -1540022, NPC_RIGHT_HEAD}, + { -1540023, NPC_RIGHT_HEAD}, +}; + +static Yell Threat[] = +{ + { -1540024, NPC_LEFT_HEAD}, + { -1540025, NPC_RIGHT_HEAD}, + { -1540026, NPC_LEFT_HEAD}, + { -1540027, NPC_LEFT_HEAD}, +}; +static Yell ThreatDelay1[] = +{ + { -1540028, NPC_RIGHT_HEAD}, + { -1540029, NPC_LEFT_HEAD}, + { -1540030, NPC_RIGHT_HEAD}, + { -1540031, NPC_RIGHT_HEAD}, +}; +static Yell ThreatDelay2[] = +{ + { -1540032, NPC_LEFT_HEAD}, + { -1540033, NPC_RIGHT_HEAD}, + { -1540034, NPC_LEFT_HEAD}, + { -1540035, NPC_LEFT_HEAD}, +}; + +static Yell Killing[] = +{ + { -1540036, NPC_LEFT_HEAD}, + { -1540037, NPC_RIGHT_HEAD}, +}; +static Yell KillingDelay[] = +{ + { -1540038, NPC_RIGHT_HEAD}, + { -1000000, NPC_LEFT_HEAD}, +}; + +struct mob_omrogg_headsAI : public ScriptedAI +{ + mob_omrogg_headsAI(Creature* pCreature) : ScriptedAI(pCreature) { Reset(); } + + uint32 m_uiDeathTimer; + bool m_bDeathYell; + + void Reset() override + { + m_uiDeathTimer = 2000; + m_bDeathYell = false; + } + + void DoDeathYell() + { + m_bDeathYell = true; + } + + void UpdateAI(const uint32 uiDiff) override + { + if (!m_bDeathYell) + return; + + if (m_uiDeathTimer < uiDiff) + { + DoScriptText(YELL_DIE_R, m_creature); + m_uiDeathTimer = 10000; + m_creature->ForcedDespawn(1000); + } + else + m_uiDeathTimer -= uiDiff; + } +}; + +struct boss_warbringer_omroggAI : public ScriptedAI +{ + boss_warbringer_omroggAI(Creature* pCreature) : ScriptedAI(pCreature) + { + m_pInstance = (ScriptedInstance*)pCreature->GetInstanceData(); + m_bIsRegularMode = pCreature->GetMap()->IsRegularDifficulty(); + Reset(); + } + + ScriptedInstance* m_pInstance; + bool m_bIsRegularMode; + + ObjectGuid m_leftHeadGuid; + ObjectGuid m_rightHeadGuid; + + int m_iAggro; + int m_iThreat; + int m_iKilling; + + bool m_bAggroYell; + bool m_bThreatYell; + bool m_bThreatYell2; + bool m_bKillingYell; + + uint32 m_uiDelayTimer; + uint32 m_uiBlastWaveTimer; + uint32 m_uiBlastCount; + uint32 m_uiFearTimer; + uint32 m_uiBurningMaulTimer; + uint32 m_uiThunderClapTimer; + uint32 m_uiResetThreatTimer; + + void Reset() override + { + m_bAggroYell = false; + m_bThreatYell = false; + m_bThreatYell2 = false; + m_bKillingYell = false; + + m_uiDelayTimer = 4000; + m_uiBlastWaveTimer = 0; + m_uiBlastCount = 0; + m_uiFearTimer = 8000; + m_uiBurningMaulTimer = 25000; + m_uiThunderClapTimer = 15000; + m_uiResetThreatTimer = 30000; + } + + void DoYellForThreat() + { + Creature* pLeftHead = m_creature->GetMap()->GetCreature(m_leftHeadGuid); + Creature* pRightHead = m_creature->GetMap()->GetCreature(m_rightHeadGuid); + + if (!pLeftHead || !pRightHead) + return; + + m_iThreat = irand(0, 3); + + Unit* pSource = (pLeftHead->GetEntry() == Threat[m_iThreat].creature ? pLeftHead : pRightHead); + + DoScriptText(Threat[m_iThreat].id, pSource); + + m_uiDelayTimer = 3500; + m_bThreatYell = true; + } + + void Aggro(Unit* /*pWho*/) override + { + 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 (Creature* pLeftHead = m_creature->GetMap()->GetCreature(m_leftHeadGuid)) + { + m_iAggro = irand(0, 2); + + DoScriptText(GoCombat[m_iAggro].id, pLeftHead); + + m_uiDelayTimer = 3500; + m_bAggroYell = true; + } + + if (m_pInstance) + m_pInstance->SetData(TYPE_OMROGG, IN_PROGRESS); + } + + void JustSummoned(Creature* pSummoned) override + { + if (pSummoned->GetEntry() == NPC_LEFT_HEAD) + m_leftHeadGuid = pSummoned->GetObjectGuid(); + else if (pSummoned->GetEntry() == NPC_RIGHT_HEAD) + m_rightHeadGuid = pSummoned->GetObjectGuid(); + } + + void KilledUnit(Unit* /*pVictim*/) override + { + Creature* pLeftHead = m_creature->GetMap()->GetCreature(m_leftHeadGuid); + Creature* pRightHead = m_creature->GetMap()->GetCreature(m_rightHeadGuid); + + if (!pLeftHead || !pRightHead) + return; + + m_iKilling = irand(0, 1); + + Creature* pSource = (pLeftHead->GetEntry() == Killing[m_iKilling].creature ? pLeftHead : pRightHead); + + switch (m_iKilling) + { + case 0: + DoScriptText(Killing[m_iKilling].id, pSource); + m_uiDelayTimer = 3500; + m_bKillingYell = true; + break; + case 1: + DoScriptText(Killing[m_iKilling].id, pSource); + m_bKillingYell = false; + break; + } + } + + void JustDied(Unit* /*pKiller*/) override + { + Creature* pLeftHead = m_creature->GetMap()->GetCreature(m_leftHeadGuid); + Creature* pRightHead = m_creature->GetMap()->GetCreature(m_rightHeadGuid); + + if (!pLeftHead || !pRightHead) + return; + + DoScriptText(YELL_DIE_L, pLeftHead); + pLeftHead->ForcedDespawn(1000); + + if (mob_omrogg_headsAI* pHeadAI = dynamic_cast(pRightHead->AI())) + pHeadAI->DoDeathYell(); + + if (m_pInstance) + m_pInstance->SetData(TYPE_OMROGG, DONE); + } + + void JustReachedHome() override + { + if (Creature* pLeftHead = m_creature->GetMap()->GetCreature(m_leftHeadGuid)) + { + pLeftHead->ForcedDespawn(); + m_leftHeadGuid.Clear(); + } + + if (Creature* pRightHead = m_creature->GetMap()->GetCreature(m_rightHeadGuid)) + { + pRightHead->ForcedDespawn(); + m_rightHeadGuid.Clear(); + } + + if (m_pInstance) + m_pInstance->SetData(TYPE_OMROGG, FAIL); + } + + void UpdateAI(const uint32 uiDiff) override + { + if (m_uiDelayTimer < uiDiff) + { + m_uiDelayTimer = 3500; + + Creature* pLeftHead = m_creature->GetMap()->GetCreature(m_leftHeadGuid); + Creature* pRightHead = m_creature->GetMap()->GetCreature(m_rightHeadGuid); + + if (!pLeftHead || !pRightHead) + return; + + if (m_bAggroYell) + { + DoScriptText(GoCombatDelay[m_iAggro].id, pRightHead); + m_bAggroYell = false; + } + + if (m_bThreatYell2) + { + Creature* pSource = (pLeftHead->GetEntry() == ThreatDelay2[m_iThreat].creature ? pLeftHead : pRightHead); + + DoScriptText(ThreatDelay2[m_iThreat].id, pSource); + m_bThreatYell2 = false; + } + + if (m_bThreatYell) + { + Creature* pSource = (pLeftHead->GetEntry() == ThreatDelay1[m_iThreat].creature ? pLeftHead : pRightHead); + + DoScriptText(ThreatDelay1[m_iThreat].id, pSource); + m_bThreatYell = false; + m_bThreatYell2 = true; + } + + if (m_bKillingYell) + { + Creature* pSource = (pLeftHead->GetEntry() == KillingDelay[m_iKilling].creature ? pLeftHead : pRightHead); + + DoScriptText(KillingDelay[m_iKilling].id, pSource); + m_bKillingYell = false; + } + } + else + m_uiDelayTimer -= uiDiff; + + if (!m_creature->SelectHostileTarget() || !m_creature->getVictim()) + return; + + if (m_uiBlastCount && m_uiBlastWaveTimer) + { + if (m_uiBlastWaveTimer <= uiDiff) + { + if (DoCastSpellIfCan(m_creature, SPELL_BLAST_WAVE) == CAST_OK) + { + m_uiBlastWaveTimer = 5000; + ++m_uiBlastCount; + + if (m_uiBlastCount == 3) + m_uiBlastCount = 0; + } + } + else + m_uiBlastWaveTimer -= uiDiff; + } + + if (m_uiBurningMaulTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature, m_bIsRegularMode ? SPELL_BURNING_MAUL : SPELL_BURNING_MAUL_H) == CAST_OK) + { + DoScriptText(EMOTE_ENRAGE, m_creature); + m_uiBurningMaulTimer = 40000; + m_uiBlastWaveTimer = 16000; + m_uiBlastCount = 1; + } + } + else + m_uiBurningMaulTimer -= uiDiff; + + if (m_uiResetThreatTimer < uiDiff) + { + if (Unit* pTarget = m_creature->SelectAttackingTarget(ATTACKING_TARGET_RANDOM, 0)) + { + DoYellForThreat(); + DoResetThreat(); + AttackStart(pTarget); + } + m_uiResetThreatTimer = urand(25000, 40000); + } + else + m_uiResetThreatTimer -= uiDiff; + + if (m_uiFearTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature, SPELL_FEAR) == CAST_OK) + m_uiFearTimer = urand(15000, 35000); + } + else + m_uiFearTimer -= uiDiff; + + if (m_uiThunderClapTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature, SPELL_THUNDERCLAP) == CAST_OK) + m_uiThunderClapTimer = urand(15000, 30000); + } + else + m_uiThunderClapTimer -= uiDiff; + + 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* pNewScript; + + pNewScript = new Script; + pNewScript->Name = "boss_warbringer_omrogg"; + pNewScript->GetAI = &GetAI_boss_warbringer_omrogg; + pNewScript->RegisterSelf(); + + pNewScript = new Script; + pNewScript->Name = "mob_omrogg_heads"; + pNewScript->GetAI = &GetAI_mob_omrogg_heads; + pNewScript->RegisterSelf(); +} diff --git a/src/modules/SD2/scripts/outland/hellfire_citadel/shattered_halls/boss_warchief_kargath_bladefist.cpp b/src/modules/SD2/scripts/outland/hellfire_citadel/shattered_halls/boss_warchief_kargath_bladefist.cpp new file mode 100644 index 000000000..d7f122027 --- /dev/null +++ b/src/modules/SD2/scripts/outland/hellfire_citadel/shattered_halls/boss_warchief_kargath_bladefist.cpp @@ -0,0 +1,318 @@ +/* Copyright (C) 2006 - 2013 ScriptDev2 + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with 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" +#include "shattered_halls.h" + +enum +{ + SAY_AGGRO1 = -1540042, + SAY_AGGRO2 = -1540043, + SAY_AGGRO3 = -1540044, + SAY_SLAY1 = -1540045, + SAY_SLAY2 = -1540046, + SAY_DEATH = -1540047, + SAY_EVADE = -1540048, + + SPELL_BLADE_DANCE = 30739, + SPELL_CHARGE_H = 25821, + + TARGET_NUM = 5, + + NPC_SHATTERED_ASSASSIN = 17695, + NPC_HEARTHEN_GUARD = 17621, + NPC_SHARPSHOOTER_GUARD = 17622, + NPC_REAVER_GUARD = 17623, +}; + +float AssassEntrance[3] = {275.136f, -84.29f, 2.3f}; // y -8 +float AssassExit[3] = {184.233f, -84.29f, 2.3f}; // y -8 +float AddsEntrance[3] = {306.036f, -84.29f, 1.93f}; + +struct boss_warchief_kargath_bladefistAI : public ScriptedAI +{ + boss_warchief_kargath_bladefistAI(Creature* pCreature) : ScriptedAI(pCreature) + { + m_pInstance = (ScriptedInstance*)pCreature->GetInstanceData(); + m_bIsRegularMode = pCreature->GetMap()->IsRegularDifficulty(); + Reset(); + } + + ScriptedInstance* m_pInstance; + bool m_bIsRegularMode; + + GuidVector m_vAddGuids; + GuidVector m_vAssassinGuids; + + uint32 m_uiChargeTimer; + uint32 m_uiBladeDanceTimer; + uint32 m_uiSummonAssistantTimer; + uint32 m_uiWaitTimer; + + uint32 m_uiAssassinsTimer; + + uint32 m_uiSummoned; + bool m_bInBlade; + + uint32 m_uiTargetNum; + + void Reset() override + { + m_creature->SetSpeedRate(MOVE_RUN, 2.0f); + + m_uiSummoned = 2; + m_bInBlade = false; + m_uiWaitTimer = 0; + + m_uiChargeTimer = 0; + m_uiBladeDanceTimer = 45000; + m_uiSummonAssistantTimer = 30000; + m_uiAssassinsTimer = 5000; + } + + void Aggro(Unit* /*pWho*/) override + { + switch (urand(0, 2)) + { + case 0: DoScriptText(SAY_AGGRO1, m_creature); break; + case 1: DoScriptText(SAY_AGGRO2, m_creature); break; + case 2: DoScriptText(SAY_AGGRO3, m_creature); break; + } + + if (m_pInstance) + m_pInstance->SetData(TYPE_BLADEFIST, IN_PROGRESS); + } + + void JustSummoned(Creature* pSummoned) override + { + switch (pSummoned->GetEntry()) + { + case NPC_HEARTHEN_GUARD: + case NPC_SHARPSHOOTER_GUARD: + case NPC_REAVER_GUARD: + if (Unit* pTarget = m_creature->SelectAttackingTarget(ATTACKING_TARGET_RANDOM, 0)) + pSummoned->AI()->AttackStart(pTarget); + + m_vAddGuids.push_back(pSummoned->GetObjectGuid()); + break; + case NPC_SHATTERED_ASSASSIN: + m_vAssassinGuids.push_back(pSummoned->GetObjectGuid()); + break; + } + } + + void KilledUnit(Unit* pVictim) override + { + if (pVictim->GetTypeId() == TYPEID_PLAYER) + DoScriptText(urand(0, 1) ? SAY_SLAY1 : SAY_SLAY2, m_creature); + } + + void JustDied(Unit* /*pKiller*/) override + { + DoScriptText(SAY_DEATH, m_creature); + DoDespawnAdds(); + + if (m_pInstance) + m_pInstance->SetData(TYPE_BLADEFIST, DONE); + } + + void JustReachedHome() override + { + DoDespawnAdds(); + + if (m_pInstance) + m_pInstance->SetData(TYPE_BLADEFIST, FAIL); + } + + void MovementInform(uint32 uiType, uint32 uiPointId) override + { + if (m_bInBlade) + { + if (uiType != POINT_MOTION_TYPE) + return; + + if (uiPointId != 1) + return; + + if (m_uiTargetNum > 0) // to prevent loops + { + m_uiWaitTimer = 1; + DoCastSpellIfCan(m_creature, SPELL_BLADE_DANCE, CAST_TRIGGERED); + --m_uiTargetNum; + } + } + } + + // Note: this should be done by creature linkin in core + void DoDespawnAdds() + { + for (GuidVector::const_iterator itr = m_vAddGuids.begin(); itr != m_vAddGuids.end(); ++itr) + { + if (Creature* pTemp = m_creature->GetMap()->GetCreature(*itr)) + pTemp->ForcedDespawn(); + } + + m_vAddGuids.clear(); + + for (GuidVector::const_iterator itr = m_vAssassinGuids.begin(); itr != m_vAssassinGuids.end(); ++itr) + { + if (Creature* pTemp = m_creature->GetMap()->GetCreature(*itr)) + pTemp->ForcedDespawn(); + } + + m_vAssassinGuids.clear(); + } + + void SpawnAssassin() + { + m_creature->SummonCreature(NPC_SHATTERED_ASSASSIN, AssassEntrance[0], AssassEntrance[1] + 8, AssassEntrance[2], 0, TEMPSUMMON_TIMED_OOC_DESPAWN, 24000); + m_creature->SummonCreature(NPC_SHATTERED_ASSASSIN, AssassEntrance[0], AssassEntrance[1] - 8, AssassEntrance[2], 0, TEMPSUMMON_TIMED_OOC_DESPAWN, 24000); + m_creature->SummonCreature(NPC_SHATTERED_ASSASSIN, AssassExit[0], AssassExit[1] + 8, AssassExit[2], 0, TEMPSUMMON_TIMED_OOC_DESPAWN, 24000); + m_creature->SummonCreature(NPC_SHATTERED_ASSASSIN, AssassExit[0], AssassExit[1] - 8, AssassExit[2], 0, TEMPSUMMON_TIMED_OOC_DESPAWN, 24000); + } + + void UpdateAI(const uint32 uiDiff) override + { + if (!m_creature->SelectHostileTarget() || !m_creature->getVictim()) + return; + + // Check if out of range + if (EnterEvadeIfOutOfCombatArea(uiDiff)) + { + DoScriptText(SAY_EVADE, m_creature); + return; + } + + if (m_uiAssassinsTimer) + { + if (m_uiAssassinsTimer <= uiDiff) + { + SpawnAssassin(); + m_uiAssassinsTimer = 0; + } + else + m_uiAssassinsTimer -= uiDiff; + } + + if (m_bInBlade) + { + if (m_uiWaitTimer) + { + if (m_uiWaitTimer <= uiDiff) + { + if (m_uiTargetNum == 0) + { + // stop bladedance + m_bInBlade = false; + m_creature->SetSpeedRate(MOVE_RUN, 2.0f); + m_creature->GetMotionMaster()->MoveChase(m_creature->getVictim()); + m_uiWaitTimer = 0; + if (!m_bIsRegularMode) + m_uiChargeTimer = 5000; + } + else + { + // move in bladedance + float x, y, randx, randy; + randx = (rand() % 40); + randy = (rand() % 40); + x = 210 + randx ; + y = -60 - randy ; + m_creature->GetMotionMaster()->MovePoint(1, x, y, m_creature->GetPositionZ()); + m_uiWaitTimer = 0; + } + } + else + m_uiWaitTimer -= uiDiff; + } + } + else // !m_bInBlade + { + if (m_uiBladeDanceTimer < uiDiff) + { + m_uiTargetNum = TARGET_NUM; + m_uiWaitTimer = 1; + m_bInBlade = true; + m_uiBladeDanceTimer = 30000; + m_creature->SetSpeedRate(MOVE_RUN, 4.0f); + return; + } + else + m_uiBladeDanceTimer -= uiDiff; + + if (m_uiChargeTimer) + { + if (m_uiChargeTimer <= uiDiff) + { + if (Unit* pTarget = m_creature->SelectAttackingTarget(ATTACKING_TARGET_RANDOM, 0)) + DoCastSpellIfCan(pTarget, SPELL_CHARGE_H); + + m_uiChargeTimer = 0; + } + else + m_uiChargeTimer -= uiDiff; + } + + if (m_uiSummonAssistantTimer < uiDiff) + { + for (uint32 i = 0; i < m_uiSummoned; ++i) + { + switch (urand(0, 2)) + { + case 0: m_creature->SummonCreature(NPC_HEARTHEN_GUARD, AddsEntrance[0], AddsEntrance[1], AddsEntrance[2], 0.0f, TEMPSUMMON_TIMED_OOC_DESPAWN, 20000); break; + case 1: m_creature->SummonCreature(NPC_SHARPSHOOTER_GUARD, AddsEntrance[0], AddsEntrance[1], AddsEntrance[2], 0.0f, TEMPSUMMON_TIMED_OOC_DESPAWN, 20000); break; + case 2: m_creature->SummonCreature(NPC_REAVER_GUARD, AddsEntrance[0], AddsEntrance[1], AddsEntrance[2], 0.0f, TEMPSUMMON_TIMED_OOC_DESPAWN, 20000); break; + } + } + + if (!urand(0, 4)) + ++m_uiSummoned; + + m_uiSummonAssistantTimer = urand(25000, 35000); + } + else + m_uiSummonAssistantTimer -= uiDiff; + + DoMeleeAttackIfReady(); + } + } +}; + +CreatureAI* GetAI_boss_warchief_kargath_bladefist(Creature* pCreature) +{ + return new boss_warchief_kargath_bladefistAI(pCreature); +} + +void AddSC_boss_warchief_kargath_bladefist() +{ + Script* pNewScript; + + pNewScript = new Script; + pNewScript->Name = "boss_warchief_kargath_bladefist"; + pNewScript->GetAI = &GetAI_boss_warchief_kargath_bladefist; + pNewScript->RegisterSelf(); +} diff --git a/src/modules/SD2/scripts/outland/hellfire_citadel/shattered_halls/instance_shattered_halls.cpp b/src/modules/SD2/scripts/outland/hellfire_citadel/shattered_halls/instance_shattered_halls.cpp new file mode 100644 index 000000000..dab52ff08 --- /dev/null +++ b/src/modules/SD2/scripts/outland/hellfire_citadel/shattered_halls/instance_shattered_halls.cpp @@ -0,0 +1,333 @@ +/* Copyright (C) 2006 - 2013 ScriptDev2 + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along 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 "shattered_halls.h" + +instance_shattered_halls::instance_shattered_halls(Map* pMap) : ScriptedInstance(pMap), + m_uiExecutionTimer(55 * MINUTE* IN_MILLISECONDS), + m_uiTeam(0), + m_uiExecutionStage(0) +{ + Initialize(); +} + +void instance_shattered_halls::Initialize() +{ + memset(&m_auiEncounter, 0, sizeof(m_auiEncounter)); +} + +void instance_shattered_halls::OnPlayerEnter(Player* pPlayer) +{ + // Only on heroic + if (instance->IsRegularDifficulty() || m_uiTeam) + return; + + m_uiTeam = pPlayer->GetTeam(); + + if (m_uiTeam == ALLIANCE) + pPlayer->SummonCreature(aSoldiersLocs[1].m_uiAllianceEntry, aSoldiersLocs[1].m_fX, aSoldiersLocs[1].m_fY, aSoldiersLocs[1].m_fZ, aSoldiersLocs[1].m_fO, TEMPSUMMON_DEAD_DESPAWN, 0); + else + pPlayer->SummonCreature(aSoldiersLocs[0].m_uiHordeEntry, aSoldiersLocs[0].m_fX, aSoldiersLocs[0].m_fY, aSoldiersLocs[0].m_fZ, aSoldiersLocs[0].m_fO, TEMPSUMMON_DEAD_DESPAWN, 0); +} + +void instance_shattered_halls::OnObjectCreate(GameObject* pGo) +{ + switch (pGo->GetEntry()) + { + case GO_NETHEKURSE_DOOR: + if (m_auiEncounter[TYPE_NETHEKURSE] == DONE) + pGo->SetGoState(GO_STATE_ACTIVE); + break; + case GO_NETHEKURSE_ENTER_DOOR: + if (m_auiEncounter[TYPE_NETHEKURSE] == DONE) + pGo->SetGoState(GO_STATE_ACTIVE); + break; + + default: + return; + } + + m_mGoEntryGuidStore[pGo->GetEntry()] = pGo->GetObjectGuid(); +} + +void instance_shattered_halls::OnCreatureCreate(Creature* pCreature) +{ + switch (pCreature->GetEntry()) + { + case NPC_NETHEKURSE: + case NPC_KARGATH_BLADEFIST: + case NPC_EXECUTIONER: + case NPC_SOLDIER_ALLIANCE_2: + case NPC_SOLDIER_ALLIANCE_3: + case NPC_OFFICER_ALLIANCE: + case NPC_SOLDIER_HORDE_2: + case NPC_SOLDIER_HORDE_3: + case NPC_OFFICER_HORDE: + m_mNpcEntryGuidStore[pCreature->GetEntry()] = pCreature->GetObjectGuid(); + break; + } +} + +void instance_shattered_halls::SetData(uint32 uiType, uint32 uiData) +{ + switch (uiType) + { + case TYPE_NETHEKURSE: + m_auiEncounter[uiType] = uiData; + if (uiData == DONE) + { + DoUseDoorOrButton(GO_NETHEKURSE_DOOR); + DoUseDoorOrButton(GO_NETHEKURSE_ENTER_DOOR); + } + break; + case TYPE_OMROGG: + m_auiEncounter[uiType] = uiData; + break; + case TYPE_BLADEFIST: + m_auiEncounter[uiType] = uiData; + if (uiData == DONE) + { + // Make executioner attackable only after the final boss is dead + if (Creature* pExecutioner = GetSingleCreatureFromStorage(NPC_EXECUTIONER, true)) + pExecutioner->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE | UNIT_FLAG_NON_ATTACKABLE); + } + break; + case TYPE_EXECUTION: + m_auiEncounter[uiType] = uiData; + if (uiData == IN_PROGRESS && !GetSingleCreatureFromStorage(NPC_EXECUTIONER, true)) + { + if (Player* pPlayer = GetPlayerInMap()) + { + // summon the 3 npcs for execution + for (uint8 i = 2; i < 5; ++i) + pPlayer->SummonCreature(m_uiTeam == ALLIANCE ? aSoldiersLocs[i].m_uiAllianceEntry : aSoldiersLocs[i].m_uiHordeEntry, aSoldiersLocs[i].m_fX, aSoldiersLocs[i].m_fY, aSoldiersLocs[i].m_fZ, aSoldiersLocs[i].m_fO, TEMPSUMMON_DEAD_DESPAWN, 0); + + // Summon the executioner; Note: according to wowhead he shouldn't be targetable until Kargath encounter is finished + if (Creature* pExecutioner = pPlayer->SummonCreature(NPC_EXECUTIONER, afExecutionerLoc[0], afExecutionerLoc[1], afExecutionerLoc[2], afExecutionerLoc[3], TEMPSUMMON_DEAD_DESPAWN, 0, true)) + pExecutioner->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE | UNIT_FLAG_NON_ATTACKABLE); + + // cast the execution spell + DoCastGroupDebuff(SPELL_KARGATH_EXECUTIONER_1); + } + } + if (uiData == DONE) + { + // If the officer is already killed, then skip the quest completion + if (m_uiExecutionStage) + break; + + // Complete quest 9524 or 9525 + if (Creature* pOfficer = GetSingleCreatureFromStorage(m_uiTeam == ALLIANCE ? NPC_OFFICER_ALLIANCE : NPC_OFFICER_HORDE)) + { + Map::PlayerList const& lPlayers = instance->GetPlayers(); + + for (Map::PlayerList::const_iterator itr = lPlayers.begin(); itr != lPlayers.end(); ++itr) + { + if (Player* pPlayer = itr->getSource()) + pPlayer->KilledMonsterCredit(pOfficer->GetEntry(), pOfficer->GetObjectGuid()); + } + } + } + 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_strInstData = saveStream.str(); + + SaveToDB(); + OUT_SAVE_INST_DATA_COMPLETE; + } +} + +void instance_shattered_halls::Load(const char* chrIn) +{ + if (!chrIn) + { + 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 < MAX_ENCOUNTER; ++i) + { + if (m_auiEncounter[i] == IN_PROGRESS) + m_auiEncounter[i] = NOT_STARTED; + } + + OUT_LOAD_INST_DATA_COMPLETE; +} + +uint32 instance_shattered_halls::GetData(uint32 uiType) const +{ + if (uiType < MAX_ENCOUNTER) + return m_auiEncounter[uiType]; + + return 0; +} + +void instance_shattered_halls::OnCreatureDeath(Creature* pCreature) +{ + if (pCreature->GetEntry() == NPC_EXECUTIONER) + SetData(TYPE_EXECUTION, DONE); +} + +void instance_shattered_halls::OnCreatureEnterCombat(Creature* pCreature) +{ + // Set data to special in order to pause the event timer + // This is according to the blizz comments which say that it is possible to complete the event if you engage the npc while you have only a few seconds left + if (pCreature->GetEntry() == NPC_EXECUTIONER) + SetData(TYPE_EXECUTION, SPECIAL); +} + +void instance_shattered_halls::OnCreatureEvade(Creature* pCreature) +{ + // If npc evades continue the counting + if (pCreature->GetEntry() == NPC_EXECUTIONER) + SetData(TYPE_EXECUTION, IN_PROGRESS); +} + +bool instance_shattered_halls::CheckConditionCriteriaMeet(Player const* pPlayer, uint32 uiInstanceConditionId, WorldObject const* pConditionSource, uint32 conditionSourceType) const +{ + switch (uiInstanceConditionId) + { + case INSTANCE_CONDITION_ID_NORMAL_MODE: // No soldier alive + case INSTANCE_CONDITION_ID_HARD_MODE: // One soldier alive + case INSTANCE_CONDITION_ID_HARD_MODE_2: // Two soldier alive + case INSTANCE_CONDITION_ID_HARD_MODE_3: // Three soldier alive + return uiInstanceConditionId == uint32(INSTANCE_CONDITION_ID_HARD_MODE_3 - m_uiExecutionStage); + } + + script_error_log("instance_shattered_halls::CheckConditionCriteriaMeet called with unsupported Id %u. Called with param plr %s, src %s, condition source type %u", + uiInstanceConditionId, pPlayer ? pPlayer->GetGuidStr().c_str() : "NULL", pConditionSource ? pConditionSource->GetGuidStr().c_str() : "NULL", conditionSourceType); + return false; +} + +void instance_shattered_halls::Update(uint32 uiDiff) +{ + if (m_auiEncounter[TYPE_EXECUTION] != IN_PROGRESS) + return; + + if (m_uiExecutionTimer < uiDiff) + { + switch (m_uiExecutionStage) + { + case 0: + // Kill the officer + if (Creature* pSoldier = GetSingleCreatureFromStorage(m_uiTeam == ALLIANCE ? NPC_OFFICER_ALLIANCE : NPC_OFFICER_HORDE)) + pSoldier->DealDamage(pSoldier, pSoldier->GetHealth(), NULL, DIRECT_DAMAGE, SPELL_SCHOOL_MASK_NORMAL, NULL, false); + + // Make Kargath yell + DoOrSimulateScriptTextForThisInstance(m_uiTeam == ALLIANCE ? SAY_KARGATH_EXECUTE_ALLY : SAY_KARGATH_EXECUTE_HORDE, NPC_KARGATH_BLADEFIST); + + // Set timer for the next execution + DoCastGroupDebuff(SPELL_KARGATH_EXECUTIONER_2); + m_uiExecutionTimer = 10 * MINUTE * IN_MILLISECONDS; + break; + case 1: + if (Creature* pSoldier = GetSingleCreatureFromStorage(m_uiTeam == ALLIANCE ? NPC_SOLDIER_ALLIANCE_2 : NPC_SOLDIER_HORDE_2)) + pSoldier->DealDamage(pSoldier, pSoldier->GetHealth(), NULL, DIRECT_DAMAGE, SPELL_SCHOOL_MASK_NORMAL, NULL, false); + + DoCastGroupDebuff(SPELL_KARGATH_EXECUTIONER_3); + m_uiExecutionTimer = 15 * MINUTE * IN_MILLISECONDS; + break; + case 2: + if (Creature* pSoldier = GetSingleCreatureFromStorage(m_uiTeam == ALLIANCE ? NPC_SOLDIER_ALLIANCE_3 : NPC_SOLDIER_HORDE_3)) + pSoldier->DealDamage(pSoldier, pSoldier->GetHealth(), NULL, DIRECT_DAMAGE, SPELL_SCHOOL_MASK_NORMAL, NULL, false); + + SetData(TYPE_EXECUTION, FAIL); + m_uiExecutionTimer = 0; + break; + } + ++m_uiExecutionStage; + } + else + m_uiExecutionTimer -= uiDiff; +} + +// Add debuff to all players in the instance +void instance_shattered_halls::DoCastGroupDebuff(uint32 uiSpellId) +{ + Map::PlayerList const& lPlayers = instance->GetPlayers(); + + for (Map::PlayerList::const_iterator itr = lPlayers.begin(); itr != lPlayers.end(); ++itr) + { + Player* pPlayer = itr->getSource(); + if (pPlayer && !pPlayer->HasAura(uiSpellId)) + pPlayer->CastSpell(pPlayer, uiSpellId, true); + } +} + +InstanceData* GetInstanceData_instance_shattered_halls(Map* pMap) +{ + return new instance_shattered_halls(pMap); +} + +bool AreaTrigger_at_shattered_halls(Player* pPlayer, AreaTriggerEntry const* /*pAt*/) +{ + if (pPlayer->isGameMaster() || !pPlayer->IsAlive()) + return false; + + instance_shattered_halls* pInstance = (instance_shattered_halls*)pPlayer->GetInstanceData(); + + if (!pInstance) + return false; + + // Only on heroic + if (pInstance->instance->IsRegularDifficulty()) + return false; + + // Don't allow players to cheat + if (pInstance->GetData(TYPE_BLADEFIST) == DONE || pInstance->GetData(TYPE_OMROGG) == DONE) + return false; + + if (pInstance->GetData(TYPE_EXECUTION) == NOT_STARTED) + pInstance->SetData(TYPE_EXECUTION, IN_PROGRESS); + + return true; +} + +void AddSC_instance_shattered_halls() +{ + Script* pNewScript; + + pNewScript = new Script; + pNewScript->Name = "instance_shattered_halls"; + pNewScript->GetInstanceData = &GetInstanceData_instance_shattered_halls; + pNewScript->RegisterSelf(); + + pNewScript = new Script; + pNewScript->Name = "at_shattered_halls"; + pNewScript->pAreaTrigger = &AreaTrigger_at_shattered_halls; + pNewScript->RegisterSelf(); +} diff --git a/src/modules/SD2/scripts/outland/hellfire_citadel/shattered_halls/shattered_halls.h b/src/modules/SD2/scripts/outland/hellfire_citadel/shattered_halls/shattered_halls.h new file mode 100644 index 000000000..80e755257 --- /dev/null +++ b/src/modules/SD2/scripts/outland/hellfire_citadel/shattered_halls/shattered_halls.h @@ -0,0 +1,99 @@ +/* Copyright (C) 2006 - 2013 ScriptDev2 + * This program is free software licensed under GPL version 2 + * Please see the included DOCS/LICENSE.TXT for more information */ + +#ifndef DEF_SHATTERED_H +#define DEF_SHATTERED_H + +enum +{ + MAX_ENCOUNTER = 4, + + TYPE_NETHEKURSE = 0, + TYPE_OMROGG = 1, + TYPE_BLADEFIST = 2, // Note: if players skip Omrogg and go straight to Karagth then Omrogg comes to aid Karagth + TYPE_EXECUTION = 3, + + NPC_NETHEKURSE = 16807, + NPC_KARGATH_BLADEFIST = 16808, + NPC_EXECUTIONER = 17301, // must be killed for the executioner event + + NPC_SOLDIER_ALLIANCE_1 = 17288, // quest giver for 9524 + NPC_SOLDIER_ALLIANCE_2 = 17289, + NPC_SOLDIER_ALLIANCE_3 = 17292, + NPC_OFFICER_ALLIANCE = 17290, // quest objective + + NPC_SOLDIER_HORDE_1 = 17294, // quest giver for 9525 + NPC_SOLDIER_HORDE_2 = 17295, + NPC_SOLDIER_HORDE_3 = 17297, + NPC_OFFICER_HORDE = 17296, // quest objective + + GO_NETHEKURSE_DOOR = 182540, + GO_NETHEKURSE_ENTER_DOOR = 182539, + + SPELL_KARGATH_EXECUTIONER_1 = 39288, // 55 min - first prisoner - officer + SPELL_KARGATH_EXECUTIONER_2 = 39289, // 10 min - second prisoner + SPELL_KARGATH_EXECUTIONER_3 = 39290, // 15 min - last prisoner + + // I'm not sure if these texts are used at the execution but this is most likely they are used to + SAY_KARGATH_EXECUTE_ALLY = -1540049, + SAY_KARGATH_EXECUTE_HORDE = -1540050, + + // AT_NETHEKURSE = 4524, // Area trigger used for the execution event +}; + +struct SpawnLocation +{ + uint32 m_uiAllianceEntry, m_uiHordeEntry; + float m_fX, m_fY, m_fZ, m_fO; +}; + +const float afExecutionerLoc[4] = {151.443f, -84.439f, 1.938f, 6.283f}; + +static SpawnLocation aSoldiersLocs[] = +{ + {0, NPC_SOLDIER_HORDE_1, 119.609f, 256.127f, -45.254f, 5.133f}, + {NPC_SOLDIER_ALLIANCE_1, 0, 131.106f, 254.520f, -45.236f, 3.951f}, + {NPC_SOLDIER_ALLIANCE_3, NPC_SOLDIER_HORDE_3, 151.040f, -91.558f, 1.936f, 1.559f}, + {NPC_SOLDIER_ALLIANCE_2, NPC_SOLDIER_HORDE_2, 150.669f, -77.015f, 1.933f, 4.705f}, + {NPC_OFFICER_ALLIANCE, NPC_OFFICER_HORDE, 138.241f, -84.198f, 1.907f, 0.055f} +}; + +class instance_shattered_halls : public ScriptedInstance +{ + public: + instance_shattered_halls(Map* pMap); + + void Initialize() override; + + void OnPlayerEnter(Player* pPlayer) override; + + void OnObjectCreate(GameObject* pGo) override; + void OnCreatureCreate(Creature* pCreature) override; + + void OnCreatureDeath(Creature* pCreature) override; + void OnCreatureEvade(Creature* pCreature); + void OnCreatureEnterCombat(Creature* pCreature) override; + + void SetData(uint32 uiType, uint32 uiData) override; + uint32 GetData(uint32 uiType) const override; + + const char* Save() const override { return m_strInstData.c_str(); } + void Load(const char* chrIn) override; + + bool CheckConditionCriteriaMeet(Player const* pPlayer, uint32 uiInstanceConditionId, WorldObject const* pConditionSource, uint32 conditionSourceType) const override; + + void Update(uint32 uiDiff) override; + + private: + void DoCastGroupDebuff(uint32 uiSpellId); + + uint32 m_auiEncounter[MAX_ENCOUNTER]; + std::string m_strInstData; + + uint32 m_uiExecutionTimer; + uint32 m_uiTeam; + uint8 m_uiExecutionStage; +}; + +#endif diff --git a/src/modules/SD2/scripts/outland/hellfire_peninsula.cpp b/src/modules/SD2/scripts/outland/hellfire_peninsula.cpp new file mode 100644 index 000000000..1832bf57f --- /dev/null +++ b/src/modules/SD2/scripts/outland/hellfire_peninsula.cpp @@ -0,0 +1,574 @@ +/** + * ScriptDev2 is an extension for mangos providing enhanced features for + * area triggers, creatures, game objects, instances, items, and spells beyond + * the default database scripting in mangos. + * + * Copyright (C) 2006-2013 ScriptDev2 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * World of Warcraft, and all World of Warcraft or Warcraft art, images, + * and lore are copyrighted by Blizzard Entertainment, Inc. + */ + +/* ScriptData +SDName: Hellfire_Peninsula +SD%Complete: 100 +SDComment: Quest support: 9375, 9410, 9418, 10629, 10838 +SDCategory: Hellfire Peninsula +EndScriptData */ + +/* ContentData +npc_aeranas +npc_ancestral_wolf +npc_demoniac_scryer +npc_wounded_blood_elf +npc_fel_guard_hound +EndContentData */ + +#include "precompiled.h" +#include "escort_ai.h" +#include "pet_ai.h" + +/*###### +## npc_aeranas +######*/ + +enum +{ + SAY_SUMMON = -1000138, + SAY_FREE = -1000139, + + FACTION_HOSTILE = 16, + FACTION_FRIENDLY = 35, + + SPELL_ENVELOPING_WINDS = 15535, + SPELL_SHOCK = 12553, +}; + +struct npc_aeranasAI : public ScriptedAI +{ + npc_aeranasAI(Creature* pCreature) : ScriptedAI(pCreature) { Reset(); } + + uint32 m_uiFactionTimer; + uint32 m_uiEnvelopingWindsTimer; + uint32 m_uiShockTimer; + + void Reset() override + { + m_uiFactionTimer = 8000; + m_uiEnvelopingWindsTimer = 9000; + m_uiShockTimer = 5000; + + m_creature->RemoveFlag(UNIT_NPC_FLAGS, UNIT_NPC_FLAG_QUESTGIVER); + + DoScriptText(SAY_SUMMON, m_creature); + } + + void UpdateAI(const uint32 uiDiff) override + { + if (m_uiFactionTimer) + { + if (m_uiFactionTimer <= uiDiff) + { + m_creature->SetFactionTemporary(FACTION_HOSTILE, TEMPFACTION_RESTORE_RESPAWN | TEMPFACTION_RESTORE_COMBAT_STOP); + m_uiFactionTimer = 0; + } + else + { m_uiFactionTimer -= uiDiff; } + } + + if (!m_creature->SelectHostileTarget() || !m_creature->getVictim()) + { return; } + + if (m_creature->GetHealthPercent() < 30.0f) + { + m_creature->SetFlag(UNIT_NPC_FLAGS, UNIT_NPC_FLAG_QUESTGIVER); + m_creature->RemoveAllAuras(); + m_creature->DeleteThreatList(); + m_creature->CombatStop(true); + DoScriptText(SAY_FREE, m_creature); + return; + } + + if (m_uiShockTimer < uiDiff) + { + DoCastSpellIfCan(m_creature->getVictim(), SPELL_SHOCK); + m_uiShockTimer = 10000; + } + else + { m_uiShockTimer -= uiDiff; } + + if (m_uiEnvelopingWindsTimer < uiDiff) + { + DoCastSpellIfCan(m_creature->getVictim(), SPELL_ENVELOPING_WINDS); + m_uiEnvelopingWindsTimer = 25000; + } + else + { m_uiEnvelopingWindsTimer -= uiDiff; } + + DoMeleeAttackIfReady(); + } +}; + +CreatureAI* GetAI_npc_aeranas(Creature* pCreature) +{ + return new npc_aeranasAI(pCreature); +} + +/*###### +## npc_ancestral_wolf +######*/ + +enum +{ + EMOTE_WOLF_LIFT_HEAD = -1000496, + EMOTE_WOLF_HOWL = -1000497, + SAY_WOLF_WELCOME = -1000498, + + SPELL_ANCESTRAL_WOLF_BUFF = 29981, + + NPC_RYGA = 17123 +}; + +struct npc_ancestral_wolfAI : public npc_escortAI +{ + npc_ancestral_wolfAI(Creature* pCreature) : npc_escortAI(pCreature) + { + if (pCreature->GetOwner() && pCreature->GetOwner()->GetTypeId() == TYPEID_PLAYER) + { Start(false, (Player*)pCreature->GetOwner()); } + else + { script_error_log("npc_ancestral_wolf can not obtain owner or owner is not a player."); } + + Reset(); + } + + void Reset() override + { + m_creature->CastSpell(m_creature, SPELL_ANCESTRAL_WOLF_BUFF, true); + } + + void WaypointReached(uint32 uiPointId) override + { + switch (uiPointId) + { + case 0: + DoScriptText(EMOTE_WOLF_LIFT_HEAD, m_creature); + break; + case 2: + DoScriptText(EMOTE_WOLF_HOWL, m_creature); + break; + case 50: + Creature* pRyga = GetClosestCreatureWithEntry(m_creature, NPC_RYGA, 30.0f); + if (pRyga && pRyga->IsAlive() && !pRyga->IsInCombat()) + { DoScriptText(SAY_WOLF_WELCOME, pRyga); } + break; + } + } +}; + +CreatureAI* GetAI_npc_ancestral_wolf(Creature* pCreature) +{ + return new npc_ancestral_wolfAI(pCreature); +} + +/*###### +## npc_demoniac_scryer +######*/ + +#define GOSSIP_ITEM_ATTUNE "Yes, Scryer. You may possess me." + +enum +{ + GOSSIP_TEXTID_PROTECT = 10659, + GOSSIP_TEXTID_ATTUNED = 10643, + + QUEST_DEMONIAC = 10838, + NPC_HELLFIRE_WARDLING = 22259, + NPC_BUTTRESS = 22267, // the 4x nodes + NPC_SPAWNER = 22260, // just a dummy, not used + + MAX_BUTTRESS = 4, + TIME_TOTAL = MINUTE * 10 * IN_MILLISECONDS, + + SPELL_SUMMONED_DEMON = 7741, // visual spawn-in for demon + SPELL_DEMONIAC_VISITATION = 38708, // create item + + SPELL_BUTTRESS_APPERANCE = 38719, // visual on 4x bunnies + the flying ones + SPELL_SUCKER_CHANNEL = 38721, // channel to the 4x nodes + SPELL_SUCKER_DESPAWN_MOB = 38691 +}; + +// script is basic support, details like end event are not implemented +struct npc_demoniac_scryerAI : public ScriptedAI +{ + npc_demoniac_scryerAI(Creature* pCreature) : ScriptedAI(pCreature) + { + m_bIsComplete = false; + m_uiSpawnDemonTimer = 15000; + m_uiSpawnButtressTimer = 45000; + m_uiButtressCount = 0; + Reset(); + } + + bool m_bIsComplete; + + uint32 m_uiSpawnDemonTimer; + uint32 m_uiSpawnButtressTimer; + uint32 m_uiButtressCount; + + void Reset() override {} + + // we don't want anything to happen when attacked + void AttackedBy(Unit* /*pEnemy*/) override {} + void AttackStart(Unit* /*pEnemy*/) override {} + + void DoSpawnButtress() + { + ++m_uiButtressCount; + + float fAngle = 0.0f; + + switch (m_uiButtressCount) + { + case 1: fAngle = 0.0f; break; + case 2: fAngle = M_PI_F + M_PI_F / 2; break; + case 3: fAngle = M_PI_F / 2; break; + case 4: fAngle = M_PI_F; break; + } + + float fX, fY, fZ; + m_creature->GetNearPoint(m_creature, fX, fY, fZ, 0.0f, 5.0f, fAngle); + + uint32 uiTime = TIME_TOTAL - (m_uiSpawnButtressTimer * m_uiButtressCount); + m_creature->SummonCreature(NPC_BUTTRESS, fX, fY, fZ, m_creature->GetAngle(fX, fY), TEMPSUMMON_TIMED_DESPAWN, uiTime); + } + + void DoSpawnDemon() + { + float fX, fY, fZ; + m_creature->GetRandomPoint(m_creature->GetPositionX(), m_creature->GetPositionY(), m_creature->GetPositionZ(), 20.0f, fX, fY, fZ); + + m_creature->SummonCreature(NPC_HELLFIRE_WARDLING, fX, fY, fZ, 0.0f, TEMPSUMMON_TIMED_OOC_DESPAWN, 5000); + } + + void JustSummoned(Creature* pSummoned) override + { + if (pSummoned->GetEntry() == NPC_HELLFIRE_WARDLING) + { + pSummoned->CastSpell(pSummoned, SPELL_SUMMONED_DEMON, false); + pSummoned->AI()->AttackStart(m_creature); + } + else + { + if (pSummoned->GetEntry() == NPC_BUTTRESS) + { + pSummoned->CastSpell(pSummoned, SPELL_BUTTRESS_APPERANCE, false); + pSummoned->CastSpell(m_creature, SPELL_SUCKER_CHANNEL, true); + } + } + } + + void SpellHitTarget(Unit* pTarget, const SpellEntry* pSpell) override + { + if (pTarget->GetEntry() == NPC_HELLFIRE_WARDLING && pSpell->Id == SPELL_SUCKER_DESPAWN_MOB) + { ((Creature*)pTarget)->ForcedDespawn(); } + } + + void UpdateAI(const uint32 uiDiff) override + { + if (m_bIsComplete || !m_creature->IsAlive()) + { return; } + + if (m_uiSpawnButtressTimer <= uiDiff) + { + if (m_uiButtressCount >= MAX_BUTTRESS) + { + m_creature->CastSpell(m_creature, SPELL_SUCKER_DESPAWN_MOB, false); + + if (m_creature->IsInCombat()) + { + m_creature->DeleteThreatList(); + m_creature->CombatStop(); + } + + m_bIsComplete = true; + return; + } + + m_uiSpawnButtressTimer = 45000; + DoSpawnButtress(); + } + else + { m_uiSpawnButtressTimer -= uiDiff; } + + if (m_uiSpawnDemonTimer <= uiDiff) + { + DoSpawnDemon(); + m_uiSpawnDemonTimer = 15000; + } + else + { m_uiSpawnDemonTimer -= uiDiff; } + } +}; + +CreatureAI* GetAI_npc_demoniac_scryer(Creature* pCreature) +{ + return new npc_demoniac_scryerAI(pCreature); +} + +bool GossipHello_npc_demoniac_scryer(Player* pPlayer, Creature* pCreature) +{ + if (npc_demoniac_scryerAI* pScryerAI = dynamic_cast(pCreature->AI())) + { + if (pScryerAI->m_bIsComplete) + { + if (pPlayer->GetQuestStatus(QUEST_DEMONIAC) == QUEST_STATUS_INCOMPLETE) + { pPlayer->ADD_GOSSIP_ITEM(GOSSIP_ICON_CHAT, GOSSIP_ITEM_ATTUNE, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 1); } + + pPlayer->SEND_GOSSIP_MENU(GOSSIP_TEXTID_ATTUNED, pCreature->GetObjectGuid()); + return true; + } + } + + pPlayer->SEND_GOSSIP_MENU(GOSSIP_TEXTID_PROTECT, pCreature->GetObjectGuid()); + return true; +} + +bool GossipSelect_npc_demoniac_scryer(Player* pPlayer, Creature* pCreature, uint32 /*uiSender*/, uint32 uiAction) +{ + if (uiAction == GOSSIP_ACTION_INFO_DEF + 1) + { + pPlayer->CLOSE_GOSSIP_MENU(); + pCreature->CastSpell(pPlayer, SPELL_DEMONIAC_VISITATION, false); + } + + return true; +} + +/*###### +## npc_wounded_blood_elf +######*/ + +enum +{ + SAY_ELF_START = -1000117, + SAY_ELF_SUMMON1 = -1000118, + SAY_ELF_RESTING = -1000119, + SAY_ELF_SUMMON2 = -1000120, + SAY_ELF_COMPLETE = -1000121, + SAY_ELF_AGGRO = -1000122, + + NPC_WINDWALKER = 16966, + NPC_TALONGUARD = 16967, + + QUEST_ROAD_TO_FALCON_WATCH = 9375, +}; + +struct npc_wounded_blood_elfAI : public npc_escortAI +{ + npc_wounded_blood_elfAI(Creature* pCreature) : npc_escortAI(pCreature) {Reset();} + + void WaypointReached(uint32 uiPointId) override + { + Player* pPlayer = GetPlayerForEscort(); + + if (!pPlayer) + { return; } + + switch (uiPointId) + { + case 0: + DoScriptText(SAY_ELF_START, m_creature, pPlayer); + break; + case 9: + DoScriptText(SAY_ELF_SUMMON1, m_creature, pPlayer); + // Spawn two Haal'eshi Talonguard + DoSpawnCreature(NPC_WINDWALKER, -15, -15, 0, 0, TEMPSUMMON_TIMED_OOC_DESPAWN, 5000); + DoSpawnCreature(NPC_WINDWALKER, -17, -17, 0, 0, TEMPSUMMON_TIMED_OOC_DESPAWN, 5000); + break; + case 13: + DoScriptText(SAY_ELF_RESTING, m_creature, pPlayer); + break; + case 14: + DoScriptText(SAY_ELF_SUMMON2, m_creature, pPlayer); + // Spawn two Haal'eshi Windwalker + DoSpawnCreature(NPC_WINDWALKER, -15, -15, 0, 0, TEMPSUMMON_TIMED_OOC_DESPAWN, 5000); + DoSpawnCreature(NPC_WINDWALKER, -17, -17, 0, 0, TEMPSUMMON_TIMED_OOC_DESPAWN, 5000); + break; + case 27: + DoScriptText(SAY_ELF_COMPLETE, m_creature, pPlayer); + // Award quest credit + pPlayer->GroupEventHappens(QUEST_ROAD_TO_FALCON_WATCH, m_creature); + break; + } + } + + void Reset() override { } + + void Aggro(Unit* /*pWho*/) override + { + if (HasEscortState(STATE_ESCORT_ESCORTING)) + { DoScriptText(SAY_ELF_AGGRO, m_creature); } + } + + void JustSummoned(Creature* pSummoned) override + { + pSummoned->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, const Quest* pQuest) +{ + if (pQuest->GetQuestId() == QUEST_ROAD_TO_FALCON_WATCH) + { + // Change faction so mobs attack + pCreature->SetFactionTemporary(FACTION_ESCORT_H_PASSIVE, TEMPFACTION_RESTORE_RESPAWN); + + if (npc_wounded_blood_elfAI* pEscortAI = dynamic_cast(pCreature->AI())) + { pEscortAI->Start(false, pPlayer, pQuest); } + } + + return true; +} + +/*###### +## npc_fel_guard_hound +######*/ + +enum +{ + SPELL_CREATE_POODAD = 37688, + SPELL_FAKE_DOG_SPART = 37692, + SPELL_INFORM_DOG = 37689, + + NPC_DERANGED_HELBOAR = 16863, +}; + +struct npc_fel_guard_houndAI : public ScriptedPetAI +{ + npc_fel_guard_houndAI(Creature* pCreature) : ScriptedPetAI(pCreature) { Reset(); } + + uint32 m_uiPoodadTimer; + + bool m_bIsPooActive; + + void Reset() override + { + m_uiPoodadTimer = 0; + m_bIsPooActive = false; + } + + void MovementInform(uint32 uiMoveType, uint32 uiPointId) override + { + if (uiMoveType != POINT_MOTION_TYPE || !uiPointId) + { return; } + + if (DoCastSpellIfCan(m_creature, SPELL_FAKE_DOG_SPART) == CAST_OK) + { m_uiPoodadTimer = 2000; } + } + + // Function to allow the boar to move to target + void DoMoveToCorpse(Unit* pBoar) + { + if (!pBoar) + { return; } + + m_bIsPooActive = true; + m_creature->GetMotionMaster()->MovePoint(1, pBoar->GetPositionX(), pBoar->GetPositionY(), pBoar->GetPositionZ()); + } + + void UpdateAI(const uint32 uiDiff) override + { + if (m_uiPoodadTimer) + { + if (m_uiPoodadTimer <= uiDiff) + { + if (DoCastSpellIfCan(m_creature, SPELL_CREATE_POODAD) == CAST_OK) + { + m_uiPoodadTimer = 0; + m_bIsPooActive = false; + } + } + else + { m_uiPoodadTimer -= uiDiff; } + } + + if (!m_bIsPooActive) + { ScriptedPetAI::UpdateAI(uiDiff); } + } +}; + +CreatureAI* GetAI_npc_fel_guard_hound(Creature* pCreature) +{ + return new npc_fel_guard_houndAI(pCreature); +} + +bool EffectDummyCreature_npc_fel_guard_hound(Unit* pCaster, uint32 uiSpellId, SpellEffectIndex uiEffIndex, Creature* pCreatureTarget, ObjectGuid /*originalCasterGuid*/) +{ + // always check spellid and effectindex + if (uiSpellId == SPELL_INFORM_DOG && uiEffIndex == EFFECT_INDEX_0) + { + if (pCaster->GetEntry() == NPC_DERANGED_HELBOAR) + { + if (npc_fel_guard_houndAI* pHoundAI = dynamic_cast(pCreatureTarget->AI())) + { pHoundAI->DoMoveToCorpse(pCaster); } + } + + // always return true when we are handling this spell and effect + return true; + } + + return false; +} + +void AddSC_hellfire_peninsula() +{ + Script* pNewScript; + + pNewScript = new Script; + pNewScript->Name = "npc_aeranas"; + pNewScript->GetAI = &GetAI_npc_aeranas; + pNewScript->RegisterSelf(); + + pNewScript = new Script; + pNewScript->Name = "npc_ancestral_wolf"; + pNewScript->GetAI = &GetAI_npc_ancestral_wolf; + pNewScript->RegisterSelf(); + + pNewScript = new Script; + pNewScript->Name = "npc_demoniac_scryer"; + pNewScript->GetAI = &GetAI_npc_demoniac_scryer; + pNewScript->pGossipHello = &GossipHello_npc_demoniac_scryer; + pNewScript->pGossipSelect = &GossipSelect_npc_demoniac_scryer; + pNewScript->RegisterSelf(); + + pNewScript = new Script; + pNewScript->Name = "npc_wounded_blood_elf"; + pNewScript->GetAI = &GetAI_npc_wounded_blood_elf; + pNewScript->pQuestAcceptNPC = &QuestAccept_npc_wounded_blood_elf; + pNewScript->RegisterSelf(); + + pNewScript = new Script; + pNewScript->Name = "npc_fel_guard_hound"; + pNewScript->GetAI = &GetAI_npc_fel_guard_hound; + pNewScript->pEffectDummyNPC = &EffectDummyCreature_npc_fel_guard_hound; + pNewScript->RegisterSelf(); +} diff --git a/src/modules/SD2/scripts/outland/nagrand.cpp b/src/modules/SD2/scripts/outland/nagrand.cpp new file mode 100644 index 000000000..ce4ff0da3 --- /dev/null +++ b/src/modules/SD2/scripts/outland/nagrand.cpp @@ -0,0 +1,376 @@ +/** + * ScriptDev2 is an extension for mangos providing enhanced features for + * area triggers, creatures, game objects, instances, items, and spells beyond + * the default database scripting in mangos. + * + * Copyright (C) 2006-2013 ScriptDev2 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * World of Warcraft, and all World of Warcraft or Warcraft art, images, + * and lore are copyrighted by Blizzard Entertainment, Inc. + */ + +/* ScriptData +SDName: Nagrand +SD%Complete: 90 +SDComment: Quest support: 9868, 9918, 10085, 10646. +SDCategory: Nagrand +EndScriptData */ + +/* ContentData +mob_lump +npc_maghar_captive +npc_creditmarker_visit_with_ancestors +EndContentData */ + +#include "precompiled.h" +#include "escort_ai.h" + +/*###### +## mob_lump +######*/ + +enum +{ + SAY_LUMP_AGGRO_1 = -1000190, + SAY_LUMP_AGGRO_2 = -1000191, + SAY_LUMP_DEFEAT = -1000192, + + SPELL_VISUAL_SLEEP = 16093, + SPELL_SPEAR_THROW = 32248, + + FACTION_FRIENDLY = 35 +}; + +struct mob_lumpAI : public ScriptedAI +{ + mob_lumpAI(Creature* pCreature) : ScriptedAI(pCreature) + { + m_bReset = false; + Reset(); + } + + uint32 m_uiResetTimer; + uint32 m_uiSpearThrowTimer; + bool m_bReset; + + void Reset() override + { + m_uiResetTimer = MINUTE * IN_MILLISECONDS; + m_uiSpearThrowTimer = 2000; + } + + void AttackedBy(Unit* pAttacker) override + { + if (m_creature->getVictim()) + { return; } + + if (m_creature->IsFriendlyTo(pAttacker)) + { return; } + + AttackStart(pAttacker); + } + + void DamageTaken(Unit* /*pDealer*/, uint32& uiDamage) override + { + if (m_creature->GetHealth() < uiDamage || (m_creature->GetHealth() - uiDamage) * 100 / m_creature->GetMaxHealth() < 30) + { + uiDamage = 0; // Take 0 damage + + m_creature->RemoveAllAuras(); + m_creature->DeleteThreatList(); + m_creature->CombatStop(true); + + // should get unit flags UNIT_FLAG_OOC_NOT_ATTACKABLE | UNIT_FLAG_PASSIVE at faction change, but unclear why/for what reason, skipped (no flags expected as default) + m_creature->SetFactionTemporary(FACTION_FRIENDLY, TEMPFACTION_RESTORE_REACH_HOME); + + m_creature->SetStandState(UNIT_STAND_STATE_SIT); + DoScriptText(SAY_LUMP_DEFEAT, m_creature); + + m_bReset = true; + } + } + + void Aggro(Unit* pWho) override + { + if (m_creature->HasAura(SPELL_VISUAL_SLEEP, EFFECT_INDEX_0)) + { m_creature->RemoveAurasDueToSpell(SPELL_VISUAL_SLEEP); } + + if (!m_creature->IsStandState()) + { m_creature->SetStandState(UNIT_STAND_STATE_STAND); } + + DoScriptText(urand(0, 1) ? SAY_LUMP_AGGRO_1 : SAY_LUMP_AGGRO_2, m_creature, pWho); + } + + void UpdateAI(const uint32 uiDiff) override + { + // Check if we waiting for a reset + if (m_bReset) + { + if (m_uiResetTimer < uiDiff) + { + EnterEvadeMode(); + m_bReset = false; + } + else + { m_uiResetTimer -= uiDiff; } + } + + // Return since we have no target + if (!m_creature->SelectHostileTarget() || !m_creature->getVictim()) + { return; } + + // SpearThrow Timer + if (m_uiSpearThrowTimer < uiDiff) + { + DoCastSpellIfCan(m_creature->getVictim(), SPELL_SPEAR_THROW); + m_uiSpearThrowTimer = 20000; + } + else + { m_uiSpearThrowTimer -= uiDiff; } + + DoMeleeAttackIfReady(); + } +}; + +CreatureAI* GetAI_mob_lump(Creature* pCreature) +{ + return new mob_lumpAI(pCreature); +} + +/*###### +## npc_maghar_captive +#####*/ + +enum +{ + SAY_MAG_START = -1000482, + SAY_MAG_NO_ESCAPE = -1000483, + SAY_MAG_MORE = -1000484, + SAY_MAG_MORE_REPLY = -1000485, + 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.805786f, 8533.873047f, 1.958f}; +static float m_afAmbushB[] = { -1491.554321f, 8506.483398f, 1.248f}; + +struct 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() override + { + m_uiChainLightningTimer = 1000; + m_uiHealTimer = 0; + m_uiFrostShockTimer = 6000; + } + + void Aggro(Unit* /*pWho*/) override + { + m_creature->CastSpell(m_creature, SPELL_EARTHBIND_TOTEM, false); + } + + void WaypointReached(uint32 uiPointId) override + { + 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_OOC_DESPAWN, 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_OOC_DESPAWN, 25000); + + m_creature->SummonCreature(NPC_MURK_SCAVENGER, m_afAmbushB[0] + 2.5f, m_afAmbushB[1] + 2.5f, m_afAmbushB[2], 0.0f, TEMPSUMMON_TIMED_OOC_DESPAWN, 25000); + m_creature->SummonCreature(NPC_MURK_SCAVENGER, m_afAmbushB[0] + 2.5f, m_afAmbushB[1] - 2.5f, m_afAmbushB[2], 0.0f, TEMPSUMMON_TIMED_OOC_DESPAWN, 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) override + { + if (pSummoned->GetEntry() == NPC_MURK_BRUTE) + { DoScriptText(SAY_MAG_NO_ESCAPE, pSummoned); } + + if (pSummoned->IsTotem()) + { return; } + + pSummoned->SetWalk(false); + pSummoned->GetMotionMaster()->MovePoint(0, m_creature->GetPositionX(), m_creature->GetPositionY(), m_creature->GetPositionZ()); + } + + void SpellHitTarget(Unit* /*pTarget*/, const SpellEntry* pSpell) override + { + if (pSpell->Id == SPELL_CHAIN_LIGHTNING) + { + if (urand(0, 9)) + { return; } + + DoScriptText(SAY_MAG_LIGHTNING, m_creature); + } + } + + void UpdateEscortAI(const uint32 uiDiff) override + { + if (!m_creature->SelectHostileTarget() || !m_creature->getVictim()) + { return; } + + if (m_uiChainLightningTimer < uiDiff) + { + DoCastSpellIfCan(m_creature->getVictim(), SPELL_CHAIN_LIGHTNING); + m_uiChainLightningTimer = urand(7000, 14000); + } + else + { m_uiChainLightningTimer -= uiDiff; } + + if (m_creature->GetHealthPercent() < 30.0f) + { + if (m_uiHealTimer < uiDiff) + { + DoCastSpellIfCan(m_creature, SPELL_HEALING_WAVE); + m_uiHealTimer = 5000; + } + else + { m_uiHealTimer -= uiDiff; } + } + + if (m_uiFrostShockTimer < uiDiff) + { + DoCastSpellIfCan(m_creature->getVictim(), SPELL_FROST_SHOCK); + m_uiFrostShockTimer = urand(7500, 15000); + } + else + { m_uiFrostShockTimer -= uiDiff; } + + DoMeleeAttackIfReady(); + } +}; + +bool QuestAccept_npc_maghar_captive(Player* pPlayer, Creature* pCreature, const Quest* pQuest) +{ + if (pQuest->GetQuestId() == QUEST_TOTEM_KARDASH_H) + { + if (npc_maghar_captiveAI* pEscortAI = dynamic_cast(pCreature->AI())) + { + pCreature->SetStandState(UNIT_STAND_STATE_STAND); + pCreature->SetFactionTemporary(FACTION_ESCORT_H_NEUTRAL_ACTIVE, TEMPFACTION_RESTORE_RESPAWN); + + pEscortAI->Start(false, pPlayer, 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_OOC_DESPAWN, 25000); + pCreature->SummonCreature(NPC_MURK_PUTRIFIER, m_afAmbushA[0] - 2.5f, m_afAmbushA[1] + 2.5f, m_afAmbushA[2], 0.0f, TEMPSUMMON_TIMED_OOC_DESPAWN, 25000); + pCreature->SummonCreature(NPC_MURK_BRUTE, m_afAmbushA[0], m_afAmbushA[1], m_afAmbushA[2], 0.0f, TEMPSUMMON_TIMED_OOC_DESPAWN, 25000); + } + } + return true; +} + +CreatureAI* GetAI_npc_maghar_captive(Creature* pCreature) +{ + return new npc_maghar_captiveAI(pCreature); +} + +/*###### +## npc_creditmarker_visist_with_ancestors (Quest 10085) +######*/ + +enum +{ + QUEST_VISIT_WITH_ANCESTORS = 10085 +}; + +struct npc_creditmarker_visit_with_ancestorsAI : public ScriptedAI +{ + npc_creditmarker_visit_with_ancestorsAI(Creature* pCreature) : ScriptedAI(pCreature) { Reset(); } + + void Reset() override {} + + void MoveInLineOfSight(Unit* pWho) override + { + if (pWho->GetTypeId() == TYPEID_PLAYER && m_creature->IsWithinDistInMap(pWho, 30.0f)) + { + if (((Player*)pWho)->GetQuestStatus(QUEST_VISIT_WITH_ANCESTORS) == QUEST_STATUS_INCOMPLETE) + { + uint32 creditMarkerId = m_creature->GetEntry(); + if ((creditMarkerId >= 18840) && (creditMarkerId <= 18843)) + { + // 18840: Sunspring, 18841: Laughing, 18842: Garadar, 18843: Bleeding + if (!((Player*)pWho)->GetReqKillOrCastCurrentCount(QUEST_VISIT_WITH_ANCESTORS, creditMarkerId)) + { ((Player*)pWho)->KilledMonsterCredit(creditMarkerId, m_creature->GetObjectGuid()); } + } + } + } + } +}; + +CreatureAI* GetAI_npc_creditmarker_visit_with_ancestors(Creature* pCreature) +{ + return new npc_creditmarker_visit_with_ancestorsAI(pCreature); +} + +/*###### +## AddSC +######*/ + +void AddSC_nagrand() +{ + Script* pNewScript; + + pNewScript = new Script; + pNewScript->Name = "mob_lump"; + pNewScript->GetAI = &GetAI_mob_lump; + pNewScript->RegisterSelf(); + + pNewScript = new Script; + pNewScript->Name = "npc_maghar_captive"; + pNewScript->GetAI = &GetAI_npc_maghar_captive; + pNewScript->pQuestAcceptNPC = &QuestAccept_npc_maghar_captive; + pNewScript->RegisterSelf(); + + pNewScript = new Script; + pNewScript->Name = "npc_creditmarker_visit_with_ancestors"; + pNewScript->GetAI = &GetAI_npc_creditmarker_visit_with_ancestors; + pNewScript->RegisterSelf(); +} diff --git a/src/modules/SD2/scripts/outland/netherstorm.cpp b/src/modules/SD2/scripts/outland/netherstorm.cpp new file mode 100644 index 000000000..a2cfb50ef --- /dev/null +++ b/src/modules/SD2/scripts/outland/netherstorm.cpp @@ -0,0 +1,1195 @@ +/** + * ScriptDev2 is an extension for mangos providing enhanced features for + * area triggers, creatures, game objects, instances, items, and spells beyond + * the default database scripting in mangos. + * + * Copyright (C) 2006-2013 ScriptDev2 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * World of Warcraft, and all World of Warcraft or Warcraft art, images, + * and lore are copyrighted by Blizzard Entertainment, Inc. + */ + +/* ScriptData +SDName: Netherstorm +SD%Complete: 80 +SDComment: Quest support: 10191, 10198, 10299, 10321, 10322, 10323, 10329, 10330, 10337, 10338, 10365(Shutting Down Manaforge), 10406, 10425, 10438, 10924. +SDCategory: Netherstorm +EndScriptData */ + +/* ContentData +npc_manaforge_control_console +go_manaforge_control_console +npc_commander_dawnforge +npc_bessy +npc_maxx_a_million +npc_zeppit +npc_protectorate_demolitionist +npc_captured_vanguard +EndContentData */ + +#include "precompiled.h" +#include "escort_ai.h" +#include "pet_ai.h" + +/*###### +## npc_manaforge_control_console +######*/ +enum +{ + EMOTE_START = -1000211, + EMOTE_60 = -1000212, + EMOTE_30 = -1000213, + EMOTE_10 = -1000214, + EMOTE_COMPLETE = -1000215, + EMOTE_ABORT = -1000216, + + NPC_BNAAR_C_CONSOLE = 20209, + NPC_CORUU_C_CONSOLE = 20417, + NPC_DURO_C_CONSOLE = 20418, + NPC_ARA_C_CONSOLE = 20440, + + NPC_SUNFURY_TECH = 20218, + NPC_SUNFURY_PROT = 20436, + + NPC_ARA_TECH = 20438, + NPC_ARA_ENGI = 20439, + NPC_ARA_GORKLONN = 20460, + + QUEST_SHUTDOWN_BNAAR_ALDOR = 10299, + QUEST_SHUTDOWN_BNAAR_SCRYERS = 10329, + QUEST_SHUTDOWN_CORUU_ALDOR = 10321, + QUEST_SHUTDOWN_CORUU_SCRYERS = 10330, + QUEST_SHUTDOWN_DURO_ALDOR = 10322, + QUEST_SHUTDOWN_DURO_SCRYERS = 10338, + QUEST_SHUTDOWN_ARA_ALDOR = 10323, + QUEST_SHUTDOWN_ARA_SCRYERS = 10365, + + ITEM_BNAAR_ACESS_CRYSTAL = 29366, + ITEM_CORUU_ACESS_CRYSTAL = 29396, + ITEM_DURO_ACESS_CRYSTAL = 29397, + ITEM_ARA_ACESS_CRYSTAL = 29411, + + SPELL_DISABLE_VISUAL = 35031, + SPELL_INTERRUPT_1 = 35016, // ACID mobs should cast this + SPELL_INTERRUPT_2 = 35176, // ACID mobs should cast this (Manaforge Ara-version) +}; + +struct npc_manaforge_control_consoleAI : public ScriptedAI +{ + npc_manaforge_control_consoleAI(Creature* pCreature) : ScriptedAI(pCreature) {Reset();} + + ObjectGuid m_playerGuid; + ObjectGuid m_consoleGuid; + uint32 m_uiEventTimer; + uint32 m_uiWaveTimer; + uint32 m_uiPhase; + bool m_bWave; + + void Reset() override + { + m_playerGuid.Clear(); + m_consoleGuid.Clear(); + m_uiEventTimer = 3000; + m_uiWaveTimer = 0; + m_uiPhase = 1; + m_bWave = false; + } + + /*void SpellHit(Unit *caster, const SpellEntry *spell) override + { + // we have no way of telling the creature was hit by spell -> got aura applied after 10-12 seconds + // then no way for the mobs to actually stop the shutdown as intended. + if (spell->Id == SPELL_INTERRUPT_1) + ... + }*/ + + void JustDied(Unit* /*pKiller*/) override + { + DoScriptText(EMOTE_ABORT, m_creature); + + Player* pPlayer = m_creature->GetMap()->GetPlayer(m_playerGuid); + + if (pPlayer) + { + switch (m_creature->GetEntry()) + { + case NPC_BNAAR_C_CONSOLE: + pPlayer->FailQuest(QUEST_SHUTDOWN_BNAAR_ALDOR); + pPlayer->FailQuest(QUEST_SHUTDOWN_BNAAR_SCRYERS); + break; + case NPC_CORUU_C_CONSOLE: + pPlayer->FailQuest(QUEST_SHUTDOWN_CORUU_ALDOR); + pPlayer->FailQuest(QUEST_SHUTDOWN_CORUU_SCRYERS); + break; + case NPC_DURO_C_CONSOLE: + pPlayer->FailQuest(QUEST_SHUTDOWN_DURO_ALDOR); + pPlayer->FailQuest(QUEST_SHUTDOWN_DURO_SCRYERS); + break; + case NPC_ARA_C_CONSOLE: + pPlayer->FailQuest(QUEST_SHUTDOWN_ARA_ALDOR); + pPlayer->FailQuest(QUEST_SHUTDOWN_ARA_SCRYERS); + break; + } + } + + if (GameObject* pGo = m_creature->GetMap()->GetGameObject(m_consoleGuid)) + { pGo->RemoveFlag(GAMEOBJECT_FLAGS, GO_FLAG_IN_USE); } + } + + void DoWaveSpawnForCreature(Creature* pCreature) + { + switch (pCreature->GetEntry()) + { + case NPC_BNAAR_C_CONSOLE: + if (urand(0, 1)) + { + if (Creature* pAdd = m_creature->SummonCreature(NPC_SUNFURY_TECH, 2933.68f, 4162.55f, 164.00f, 1.60f, TEMPSUMMON_TIMED_OOC_DESPAWN, 120000)) + { pAdd->GetMotionMaster()->MovePoint(0, 2927.36f, 4212.97f, 164.00f); } + } + else + { + if (Creature* pAdd = m_creature->SummonCreature(NPC_SUNFURY_TECH, 2927.36f, 4212.97f, 164.00f, 4.94f, TEMPSUMMON_TIMED_OOC_DESPAWN, 120000)) + { pAdd->GetMotionMaster()->MovePoint(0, 2933.68f, 4162.55f, 164.00f); } + } + m_uiWaveTimer = 30000; + break; + case NPC_CORUU_C_CONSOLE: + if (Creature* pAdd = m_creature->SummonCreature(NPC_SUNFURY_TECH, 2445.21f, 2765.26f, 134.49f, 3.93f, TEMPSUMMON_TIMED_OOC_DESPAWN, 120000)) + { pAdd->GetMotionMaster()->MovePoint(0, 2424.21f, 2740.15f, 133.81f); } + if (Creature* pAdd = m_creature->SummonCreature(NPC_SUNFURY_TECH, 2429.86f, 2731.85f, 134.53f, 1.31f, TEMPSUMMON_TIMED_OOC_DESPAWN, 120000)) + { pAdd->GetMotionMaster()->MovePoint(0, 2435.37f, 2766.04f, 133.81f); } + m_uiWaveTimer = 20000; + break; + case NPC_DURO_C_CONSOLE: + if (Creature* pAdd = m_creature->SummonCreature(NPC_SUNFURY_TECH, 2986.80f, 2205.36f, 165.37f, 3.74f, TEMPSUMMON_TIMED_OOC_DESPAWN, 120000)) + { pAdd->GetMotionMaster()->MovePoint(0, 2985.15f, 2197.32f, 164.79f); } + if (Creature* pAdd = m_creature->SummonCreature(NPC_SUNFURY_TECH, 2952.91f, 2191.20f, 165.32f, 0.22f, TEMPSUMMON_TIMED_OOC_DESPAWN, 120000)) + { pAdd->GetMotionMaster()->MovePoint(0, 2060.01f, 2185.27f, 164.67f); } + m_uiWaveTimer = 15000; + break; + case NPC_ARA_C_CONSOLE: + if (urand(0, 1)) + { + if (Creature* pAdd = m_creature->SummonCreature(NPC_ARA_TECH, 4035.11f, 4038.97f, 194.27f, 2.57f, TEMPSUMMON_TIMED_OOC_DESPAWN, 120000)) + { pAdd->GetMotionMaster()->MovePoint(0, 4003.42f, 4040.19f, 193.49f); } + if (Creature* pAdd = m_creature->SummonCreature(NPC_ARA_TECH, 4033.66f, 4036.79f, 194.28f, 2.57f, TEMPSUMMON_TIMED_OOC_DESPAWN, 120000)) + { pAdd->GetMotionMaster()->MovePoint(0, 4003.42f, 4040.19f, 193.49f); } + if (Creature* pAdd = m_creature->SummonCreature(NPC_ARA_TECH, 4037.13f, 4037.30f, 194.23f, 2.57f, TEMPSUMMON_TIMED_OOC_DESPAWN, 120000)) + { pAdd->GetMotionMaster()->MovePoint(0, 4003.42f, 4040.19f, 193.49f); } + } + else + { + if (Creature* pAdd = m_creature->SummonCreature(NPC_ARA_TECH, 3099.59f, 4049.30f, 194.22f, 0.05f, TEMPSUMMON_TIMED_OOC_DESPAWN, 120000)) + { pAdd->GetMotionMaster()->MovePoint(0, 4028.01f, 4035.17f, 193.59f); } + if (Creature* pAdd = m_creature->SummonCreature(NPC_ARA_TECH, 3999.72f, 4046.75f, 194.22f, 0.05f, TEMPSUMMON_TIMED_OOC_DESPAWN, 120000)) + { pAdd->GetMotionMaster()->MovePoint(0, 4028.01f, 4035.17f, 193.59f); } + if (Creature* pAdd = m_creature->SummonCreature(NPC_ARA_TECH, 3996.81f, 4048.26f, 194.22f, 0.05f, TEMPSUMMON_TIMED_OOC_DESPAWN, 120000)) + { pAdd->GetMotionMaster()->MovePoint(0, 4028.01f, 4035.17f, 193.59f); } + } + m_uiWaveTimer = 15000; + break; + } + } + + void DoFinalSpawnForCreature(Creature* pCreature) + { + switch (pCreature->GetEntry()) + { + case NPC_BNAAR_C_CONSOLE: + if (Creature* pAdd = m_creature->SummonCreature(NPC_SUNFURY_TECH, 2946.52f, 4201.42f, 163.47f, 3.54f, TEMPSUMMON_TIMED_OOC_DESPAWN, 120000)) + { pAdd->GetMotionMaster()->MovePoint(0, 2927.49f, 4192.81f, 163.00f); } + break; + case NPC_CORUU_C_CONSOLE: + if (Creature* pAdd = m_creature->SummonCreature(NPC_SUNFURY_TECH, 2453.88f, 2737.85f, 133.27f, 2.59f, TEMPSUMMON_TIMED_OOC_DESPAWN, 120000)) + { pAdd->GetMotionMaster()->MovePoint(0, 2433.96f, 2751.53f, 133.85f); } + if (Creature* pAdd = m_creature->SummonCreature(NPC_SUNFURY_TECH, 2441.62f, 2735.32f, 134.49f, 1.97f, TEMPSUMMON_TIMED_OOC_DESPAWN, 120000)) + { pAdd->GetMotionMaster()->MovePoint(0, 2433.96f, 2751.53f, 133.85f); } + if (Creature* pAdd = m_creature->SummonCreature(NPC_SUNFURY_TECH, 2450.73f, 2754.50f, 134.49f, 3.29f, TEMPSUMMON_TIMED_OOC_DESPAWN, 120000)) + { pAdd->GetMotionMaster()->MovePoint(0, 2433.96f, 2751.53f, 133.85f); } + break; + case NPC_DURO_C_CONSOLE: + if (Creature* pAdd = m_creature->SummonCreature(NPC_SUNFURY_TECH, 2956.18f, 2202.85f, 165.32f, 5.45f, TEMPSUMMON_TIMED_OOC_DESPAWN, 120000)) + { pAdd->GetMotionMaster()->MovePoint(0, 2972.27f, 2193.22f, 164.48f); } + if (Creature* pAdd = m_creature->SummonCreature(NPC_SUNFURY_TECH, 2975.30f, 2211.50f, 165.32f, 4.55f, TEMPSUMMON_TIMED_OOC_DESPAWN, 120000)) + { pAdd->GetMotionMaster()->MovePoint(0, 2972.27f, 2193.22f, 164.48f); } + if (Creature* pAdd = m_creature->SummonCreature(NPC_SUNFURY_PROT, 2965.02f, 2217.45f, 164.16f, 4.96f, TEMPSUMMON_TIMED_OOC_DESPAWN, 120000)) + { pAdd->GetMotionMaster()->MovePoint(0, 2972.27f, 2193.22f, 164.48f); } + break; + case NPC_ARA_C_CONSOLE: + if (Creature* pAdd = m_creature->SummonCreature(NPC_ARA_ENGI, 3994.51f, 4020.46f, 192.18f, 0.91f, TEMPSUMMON_TIMED_OOC_DESPAWN, 120000)) + { pAdd->GetMotionMaster()->MovePoint(0, 4008.35f, 4035.04f, 192.70f); } + if (Creature* pAdd = m_creature->SummonCreature(NPC_ARA_GORKLONN, 4021.56f, 4059.35f, 193.59f, 4.44f, TEMPSUMMON_TIMED_OOC_DESPAWN, 120000)) + { pAdd->GetMotionMaster()->MovePoint(0, 4016.62f, 4039.89f, 193.46f); } + break; + } + } + + void UpdateAI(const uint32 uiDiff) override + { + if (m_uiEventTimer < uiDiff) + { + Player* pPlayer = m_creature->GetMap()->GetPlayer(m_playerGuid); + + if (!pPlayer) + { + // Reset Event + if (GameObject* pGo = m_creature->GetMap()->GetGameObject(m_consoleGuid)) + { pGo->RemoveFlag(GAMEOBJECT_FLAGS, GO_FLAG_IN_USE); } + + m_creature->ForcedDespawn(); + return; + } + + switch (m_uiPhase) + { + case 1: + DoScriptText(EMOTE_START, m_creature, pPlayer); + m_uiEventTimer = 60000; + m_bWave = true; + ++m_uiPhase; + break; + case 2: + DoScriptText(EMOTE_60, m_creature, pPlayer); + m_uiEventTimer = 30000; + ++m_uiPhase; + break; + case 3: + DoScriptText(EMOTE_30, m_creature, pPlayer); + m_uiEventTimer = 20000; + DoFinalSpawnForCreature(m_creature); + ++m_uiPhase; + break; + case 4: + DoScriptText(EMOTE_10, m_creature, pPlayer); + m_uiEventTimer = 10000; + m_bWave = false; + ++m_uiPhase; + break; + case 5: + DoScriptText(EMOTE_COMPLETE, m_creature, pPlayer); + pPlayer->KilledMonsterCredit(m_creature->GetEntry(), m_creature->GetObjectGuid()); + DoCastSpellIfCan(m_creature, SPELL_DISABLE_VISUAL); + + if (GameObject* pGo = m_creature->GetMap()->GetGameObject(m_consoleGuid)) + { pGo->RemoveFlag(GAMEOBJECT_FLAGS, GO_FLAG_IN_USE); } + + ++m_uiPhase; + break; + } + } + else + { m_uiEventTimer -= uiDiff; } + + if (m_bWave) + { + if (m_uiWaveTimer < uiDiff) + { + DoWaveSpawnForCreature(m_creature); + } + else + { m_uiWaveTimer -= uiDiff; } + } + } +}; +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 mangos adds support to do it properly (with gossip selections instead of instant summon) +bool GOUse_go_manaforge_control_console(Player* pPlayer, GameObject* pGo) +{ + if (pGo->GetGoType() == GAMEOBJECT_TYPE_QUESTGIVER) + { + pPlayer->PrepareQuestMenu(pGo->GetObjectGuid()); + pPlayer->SendPreparedQuest(pGo->GetObjectGuid()); + } + + Creature* pManaforge = NULL; + + switch (pGo->GetAreaId()) + { + case 3726: // b'naar + if ((pPlayer->GetQuestStatus(QUEST_SHUTDOWN_BNAAR_ALDOR) == QUEST_STATUS_INCOMPLETE + || pPlayer->GetQuestStatus(QUEST_SHUTDOWN_BNAAR_SCRYERS) == QUEST_STATUS_INCOMPLETE) + && pPlayer->HasItemCount(ITEM_BNAAR_ACESS_CRYSTAL, 1)) + { pManaforge = pPlayer->SummonCreature(NPC_BNAAR_C_CONSOLE, 2918.95f, 4189.98f, 161.88f, 0.34f, TEMPSUMMON_TIMED_OOC_OR_CORPSE_DESPAWN, 125000); } + break; + case 3730: // coruu + if ((pPlayer->GetQuestStatus(QUEST_SHUTDOWN_CORUU_ALDOR) == QUEST_STATUS_INCOMPLETE + || pPlayer->GetQuestStatus(QUEST_SHUTDOWN_CORUU_SCRYERS) == QUEST_STATUS_INCOMPLETE) + && pPlayer->HasItemCount(ITEM_CORUU_ACESS_CRYSTAL, 1)) + { pManaforge = pPlayer->SummonCreature(NPC_CORUU_C_CONSOLE, 2426.77f, 2750.38f, 133.24f, 2.14f, TEMPSUMMON_TIMED_OOC_OR_CORPSE_DESPAWN, 125000); } + break; + case 3734: // duro + if ((pPlayer->GetQuestStatus(QUEST_SHUTDOWN_DURO_ALDOR) == QUEST_STATUS_INCOMPLETE + || pPlayer->GetQuestStatus(QUEST_SHUTDOWN_DURO_SCRYERS) == QUEST_STATUS_INCOMPLETE) + && pPlayer->HasItemCount(ITEM_DURO_ACESS_CRYSTAL, 1)) + { pManaforge = pPlayer->SummonCreature(NPC_DURO_C_CONSOLE, 2976.48f, 2183.29f, 163.20f, 1.85f, TEMPSUMMON_TIMED_OOC_OR_CORPSE_DESPAWN, 125000); } + break; + case 3722: // ara + if ((pPlayer->GetQuestStatus(QUEST_SHUTDOWN_ARA_ALDOR) == QUEST_STATUS_INCOMPLETE + || pPlayer->GetQuestStatus(QUEST_SHUTDOWN_ARA_SCRYERS) == QUEST_STATUS_INCOMPLETE) + && pPlayer->HasItemCount(ITEM_ARA_ACESS_CRYSTAL, 1)) + { pManaforge = pPlayer->SummonCreature(NPC_ARA_C_CONSOLE, 4013.71f, 4028.76f, 192.10f, 1.25f, TEMPSUMMON_TIMED_OOC_OR_CORPSE_DESPAWN, 125000); } + break; + } + + if (pManaforge) + { + if (npc_manaforge_control_consoleAI* pManaforgeAI = dynamic_cast(pManaforge->AI())) + { + pManaforgeAI->m_playerGuid = pPlayer->GetObjectGuid(); + pManaforgeAI->m_consoleGuid = pGo->GetObjectGuid(); + } + + pGo->SetFlag(GAMEOBJECT_FLAGS, GO_FLAG_IN_USE); + } + return true; +} + +/*###### +## npc_commander_dawnforge +######*/ + +// The Speech of Dawnforge, Ardonis & Pathaleon +enum +{ + SAY_COMMANDER_DAWNFORGE_1 = -1000128, + SAY_ARCANIST_ARDONIS_1 = -1000129, + SAY_COMMANDER_DAWNFORGE_2 = -1000130, + SAY_PATHALEON_THE_CALCULATOR_IMAGE_1 = -1000131, + SAY_COMMANDER_DAWNFORGE_3 = -1000132, + SAY_PATHALEON_THE_CALCULATOR_IMAGE_2 = -1000133, + SAY_PATHALEON_THE_CALCULATOR_IMAGE_2_1 = -1000134, + SAY_PATHALEON_THE_CALCULATOR_IMAGE_2_2 = -1000135, + SAY_COMMANDER_DAWNFORGE_4 = -1000136, + SAY_ARCANIST_ARDONIS_2 = -1000136, + SAY_COMMANDER_DAWNFORGE_5 = -1000137, + + QUEST_INFO_GATHERING = 10198, + SPELL_SUNFURY_DISGUISE = 34603, + + NPC_ARCANIST_ARDONIS = 19830, + NPC_COMMANDER_DAWNFORGE = 19831, + NPC_PATHALEON_THE_CALCULATOR_IMAGE = 21504 +}; + +struct npc_commander_dawnforgeAI : public ScriptedAI +{ + npc_commander_dawnforgeAI(Creature* pCreature) : ScriptedAI(pCreature) { Reset(); } + + ObjectGuid m_playerGuid; + ObjectGuid m_ardonisGuid; + ObjectGuid m_pathaleonGuid; + + uint32 m_uiPhase; + uint32 m_uiPhaseSubphase; + uint32 m_uiPhaseTimer; + bool m_bIsEvent; + + void Reset() override + { + m_playerGuid.Clear(); + m_ardonisGuid.Clear(); + m_pathaleonGuid.Clear(); + + m_uiPhase = 1; + m_uiPhaseSubphase = 0; + m_uiPhaseTimer = 4000; + m_bIsEvent = false; + } + + void JustSummoned(Creature* pSummoned) override + { + if (pSummoned->GetEntry() == NPC_PATHALEON_THE_CALCULATOR_IMAGE) + { m_pathaleonGuid = pSummoned->GetObjectGuid(); } + } + + void TurnToPathaleonsImage() + { + Creature* pArdonis = m_creature->GetMap()->GetCreature(m_ardonisGuid); + Creature* pPathaleon = m_creature->GetMap()->GetCreature(m_pathaleonGuid); + Player* pPlayer = m_creature->GetMap()->GetPlayer(m_playerGuid); + + if (!pArdonis || !pPathaleon || !pPlayer) + { return; } + + m_creature->SetFacingToObject(pPathaleon); + pArdonis->SetFacingToObject(pPathaleon); + + // the boss is there kneel before him + m_creature->SetStandState(UNIT_STAND_STATE_KNEEL); + pArdonis->SetStandState(UNIT_STAND_STATE_KNEEL); + } + + void TurnToEachOther() + { + if (Creature* pArdonis = m_creature->GetMap()->GetCreature(m_ardonisGuid)) + { + Player* pPlayer = m_creature->GetMap()->GetPlayer(m_playerGuid); + + if (!pPlayer) + { return; } + + m_creature->SetFacingToObject(pArdonis); + pArdonis->SetFacingToObject(m_creature); + + // get up + m_creature->SetStandState(UNIT_STAND_STATE_STAND); + pArdonis->SetStandState(UNIT_STAND_STATE_STAND); + } + } + + bool CanStartEvent(Player* pPlayer) + { + if (!m_bIsEvent) + { + Creature* pArdonis = GetClosestCreatureWithEntry(m_creature, NPC_ARCANIST_ARDONIS, 10.0f); + + if (!pArdonis) + { return false; } + + m_ardonisGuid = pArdonis->GetObjectGuid(); + m_playerGuid = pPlayer->GetObjectGuid(); + + m_bIsEvent = true; + + TurnToEachOther(); + return true; + } + + debug_log("SD2: npc_commander_dawnforge event already in progress, need to wait."); + return false; + } + + void UpdateAI(const uint32 uiDiff) override + { + // Is event even running? + if (!m_bIsEvent) + { return; } + + // Phase timing + if (m_uiPhaseTimer >= uiDiff) + { + m_uiPhaseTimer -= uiDiff; + return; + } + + Creature* pArdonis = m_creature->GetMap()->GetCreature(m_ardonisGuid); + Creature* pPathaleon = m_creature->GetMap()->GetCreature(m_pathaleonGuid); + Player* pPlayer = m_creature->GetMap()->GetPlayer(m_playerGuid); + + if (!pArdonis || !pPlayer) + { + Reset(); + return; + } + + if (m_uiPhase > 4 && !pPathaleon) + { + Reset(); + return; + } + + switch (m_uiPhase) + { + case 1: + DoScriptText(SAY_COMMANDER_DAWNFORGE_1, m_creature); + ++m_uiPhase; + m_uiPhaseTimer = 16000; + break; + case 2: + DoScriptText(SAY_ARCANIST_ARDONIS_1, pArdonis); + ++m_uiPhase; + m_uiPhaseTimer = 16000; + break; + case 3: + DoScriptText(SAY_COMMANDER_DAWNFORGE_2, m_creature); + ++m_uiPhase; + m_uiPhaseTimer = 16000; + break; + case 4: + // spawn pathaleon's image + m_creature->SummonCreature(NPC_PATHALEON_THE_CALCULATOR_IMAGE, 2325.851563f, 2799.534668f, 133.084229f, 6.038996f, TEMPSUMMON_TIMED_DESPAWN, 90000); + ++m_uiPhase; + m_uiPhaseTimer = 500; + break; + case 5: + DoScriptText(SAY_PATHALEON_THE_CALCULATOR_IMAGE_1, pPathaleon); + ++m_uiPhase; + m_uiPhaseTimer = 6000; + break; + case 6: + switch (m_uiPhaseSubphase) + { + case 0: + TurnToPathaleonsImage(); + ++m_uiPhaseSubphase; + m_uiPhaseTimer = 8000; + break; + case 1: + DoScriptText(SAY_COMMANDER_DAWNFORGE_3, m_creature); + m_uiPhaseSubphase = 0; + ++m_uiPhase; + m_uiPhaseTimer = 8000; + break; + } + break; + case 7: + switch (m_uiPhaseSubphase) + { + case 0: + DoScriptText(SAY_PATHALEON_THE_CALCULATOR_IMAGE_2, pPathaleon); + ++m_uiPhaseSubphase; + m_uiPhaseTimer = 12000; + break; + case 1: + DoScriptText(SAY_PATHALEON_THE_CALCULATOR_IMAGE_2_1, pPathaleon); + ++m_uiPhaseSubphase; + m_uiPhaseTimer = 16000; + break; + case 2: + DoScriptText(SAY_PATHALEON_THE_CALCULATOR_IMAGE_2_2, pPathaleon); + m_uiPhaseSubphase = 0; + ++m_uiPhase; + m_uiPhaseTimer = 10000; + break; + } + break; + case 8: + DoScriptText(SAY_COMMANDER_DAWNFORGE_4, m_creature); + DoScriptText(SAY_ARCANIST_ARDONIS_2, pArdonis); + ++m_uiPhase; + m_uiPhaseTimer = 4000; + break; + case 9: + TurnToEachOther(); + // hide pathaleon, unit will despawn shortly + pPathaleon->SetVisibility(VISIBILITY_OFF); + m_uiPhaseSubphase = 0; + ++m_uiPhase; + m_uiPhaseTimer = 3000; + break; + case 10: + DoScriptText(SAY_COMMANDER_DAWNFORGE_5, m_creature); + pPlayer->AreaExploredOrEventHappens(QUEST_INFO_GATHERING); + Reset(); + break; + } + } +}; + +CreatureAI* GetAI_npc_commander_dawnforge(Creature* pCreature) +{ + return new npc_commander_dawnforgeAI(pCreature); +} + +bool AreaTrigger_at_commander_dawnforge(Player* pPlayer, AreaTriggerEntry const* /*pAt*/) +{ + // if player lost aura or not have at all, we should not try start event. + if (!pPlayer->HasAura(SPELL_SUNFURY_DISGUISE, EFFECT_INDEX_0)) + { return false; } + + if (pPlayer->IsAlive() && pPlayer->GetQuestStatus(QUEST_INFO_GATHERING) == QUEST_STATUS_INCOMPLETE) + { + Creature* pDawnforge = GetClosestCreatureWithEntry(pPlayer, NPC_COMMANDER_DAWNFORGE, 30.0f); + + if (!pDawnforge) + { return false; } + + if (npc_commander_dawnforgeAI* pDawnforgeAI = dynamic_cast(pDawnforge->AI())) + { + pDawnforgeAI->CanStartEvent(pPlayer); + return true; + } + } + return false; +} + +/*###### +## npc_bessy +######*/ + +enum +{ + QUEST_COWS_COME_HOME = 10337, + + NPC_THADELL = 20464, + NPC_TORMENTED_SOUL = 20512, + NPC_SEVERED_SPIRIT = 19881 +}; + +struct npc_bessyAI : public npc_escortAI +{ + npc_bessyAI(Creature* pCreature) : npc_escortAI(pCreature) { Reset(); } + + void WaypointReached(uint32 uiPointId) override + { + switch (uiPointId) + { + case 3: + m_creature->SummonCreature(NPC_TORMENTED_SOUL, 2449.67f, 2183.11f, 96.85f, 6.20f, TEMPSUMMON_TIMED_OOC_DESPAWN, 25000); + m_creature->SummonCreature(NPC_TORMENTED_SOUL, 2449.53f, 2184.43f, 96.36f, 6.27f, TEMPSUMMON_TIMED_OOC_DESPAWN, 25000); + m_creature->SummonCreature(NPC_TORMENTED_SOUL, 2449.85f, 2186.34f, 97.57f, 6.08f, TEMPSUMMON_TIMED_OOC_DESPAWN, 25000); + break; + case 7: + m_creature->SummonCreature(NPC_SEVERED_SPIRIT, 2309.64f, 2186.24f, 92.25f, 6.06f, TEMPSUMMON_TIMED_OOC_DESPAWN, 25000); + m_creature->SummonCreature(NPC_SEVERED_SPIRIT, 2309.25f, 2183.46f, 91.75f, 6.22f, TEMPSUMMON_TIMED_OOC_DESPAWN, 25000); + break; + case 12: + if (Player* pPlayer = GetPlayerForEscort()) + { pPlayer->GroupEventHappens(QUEST_COWS_COME_HOME, m_creature); } + break; + } + } + + void JustSummoned(Creature* pSummoned) override + { + pSummoned->AI()->AttackStart(m_creature); + } + + void Reset() override {} +}; + +bool QuestAccept_npc_bessy(Player* pPlayer, Creature* pCreature, const Quest* pQuest) +{ + if (pQuest->GetQuestId() == QUEST_COWS_COME_HOME) + { + pCreature->SetFactionTemporary(FACTION_ESCORT_N_NEUTRAL_PASSIVE, TEMPFACTION_RESTORE_RESPAWN | TEMPFACTION_TOGGLE_NON_ATTACKABLE); + + if (npc_bessyAI* pBessyAI = dynamic_cast(pCreature->AI())) + { pBessyAI->Start(true, pPlayer, pQuest); } + } + return true; +} + +CreatureAI* GetAI_npc_bessy(Creature* pCreature) +{ + return new npc_bessyAI(pCreature); +} + +/*###### +## npc_maxx_a_million +######*/ + +enum +{ + QUEST_MARK_V_IS_ALIVE = 10191, + NPC_BOT_SPECIALIST_ALLEY = 19578, + GO_DRAENEI_MACHINE = 183771, + + SAY_START = -1000621, + SAY_ALLEY_FAREWELL = -1000622, + SAY_CONTINUE = -1000623, + SAY_ALLEY_FINISH = -1000624 +}; + +struct npc_maxx_a_million_escortAI : public npc_escortAI +{ + npc_maxx_a_million_escortAI(Creature* pCreature) : npc_escortAI(pCreature) {Reset();} + + uint8 m_uiSubEvent; + uint32 m_uiSubEventTimer; + ObjectGuid m_alleyGuid; + ObjectGuid m_lastDraeneiMachineGuid; + + void Reset() override + { + if (!HasEscortState(STATE_ESCORT_ESCORTING)) + { + m_uiSubEvent = 0; + m_uiSubEventTimer = 0; + m_alleyGuid.Clear(); + m_lastDraeneiMachineGuid.Clear(); + + // Reset fields, that were changed on escort-start + m_creature->HandleEmote(EMOTE_STATE_STUN); + } + } + + void WaypointReached(uint32 uiPoint) override + { + switch (uiPoint) + { + case 1: + // turn 90 degrees , towards doorway. + m_creature->SetFacingTo(m_creature->GetOrientation() + (M_PI_F / 2)); + DoScriptText(SAY_START, m_creature); + m_uiSubEventTimer = 3000; + m_uiSubEvent = 1; + break; + case 7: + case 17: + case 29: + if (GameObject* pMachine = GetClosestGameObjectWithEntry(m_creature, GO_DRAENEI_MACHINE, INTERACTION_DISTANCE)) + { + m_creature->SetFacingToObject(pMachine); + m_lastDraeneiMachineGuid = pMachine->GetObjectGuid(); + m_uiSubEvent = 2; + m_uiSubEventTimer = 1000; + } + else + { m_lastDraeneiMachineGuid.Clear(); } + + break; + case 36: + if (Player* pPlayer = GetPlayerForEscort()) + { pPlayer->GroupEventHappens(QUEST_MARK_V_IS_ALIVE, m_creature); } + + if (Creature* pAlley = m_creature->GetMap()->GetCreature(m_alleyGuid)) + { DoScriptText(SAY_ALLEY_FINISH, pAlley); } + + break; + } + } + + void WaypointStart(uint32 uiPoint) override + { + switch (uiPoint) + { + case 8: + case 18: + case 30: + DoScriptText(SAY_CONTINUE, m_creature); + break; + } + } + + void UpdateEscortAI(const uint32 uiDiff) override + { + if (!m_creature->SelectHostileTarget() || !m_creature->getVictim()) + { + if (m_uiSubEventTimer) + { + if (m_uiSubEventTimer <= uiDiff) + { + switch (m_uiSubEvent) + { + case 1: // Wait time before Say + if (Creature* pAlley = GetClosestCreatureWithEntry(m_creature, NPC_BOT_SPECIALIST_ALLEY, INTERACTION_DISTANCE * 2)) + { + m_alleyGuid = pAlley->GetObjectGuid(); + DoScriptText(SAY_ALLEY_FAREWELL, pAlley); + } + m_uiSubEventTimer = 0; + m_uiSubEvent = 0; + break; + case 2: // Short wait time after reached WP at machine + m_creature->HandleEmote(EMOTE_ONESHOT_ATTACKUNARMED); + m_uiSubEventTimer = 2000; + m_uiSubEvent = 3; + break; + case 3: // Despawn machine after 2s + if (GameObject* pMachine = m_creature->GetMap()->GetGameObject(m_lastDraeneiMachineGuid)) + { pMachine->Use(m_creature); } + + m_lastDraeneiMachineGuid.Clear(); + m_uiSubEventTimer = 0; + m_uiSubEvent = 0; + break; + default: + m_uiSubEventTimer = 0; + break; + } + } + else + { m_uiSubEventTimer -= uiDiff; } + } + } + else + { DoMeleeAttackIfReady(); } + } +}; + +CreatureAI* GetAI_npc_maxx_a_million(Creature* pCreature) +{ + return new npc_maxx_a_million_escortAI(pCreature); +} + +bool QuestAccept_npc_maxx_a_million(Player* pPlayer, Creature* pCreature, const Quest* pQuest) +{ + if (pQuest->GetQuestId() == QUEST_MARK_V_IS_ALIVE) + { + if (npc_maxx_a_million_escortAI* pEscortAI = dynamic_cast(pCreature->AI())) + { + // Set Faction to Escort Faction + pCreature->SetFactionTemporary(FACTION_ESCORT_N_NEUTRAL_PASSIVE, TEMPFACTION_RESTORE_RESPAWN | TEMPFACTION_TOGGLE_OOC_NOT_ATTACK | TEMPFACTION_TOGGLE_PASSIVE); + // Set emote-state to 0 (is EMOTE_STATE_STUN by default) + pCreature->HandleEmote(EMOTE_ONESHOT_NONE); + + pEscortAI->Start(false, pPlayer, pQuest, true); + } + } + return true; +} + +/*###### +## npc_zeppit +######*/ + +enum +{ + EMOTE_GATHER_BLOOD = -1000625, + NPC_WARP_CHASER = 18884, + SPELL_GATHER_WARP_BLOOD = 39244, // for quest 10924 +}; + +struct npc_zeppitAI : public ScriptedPetAI +{ + npc_zeppitAI(Creature* pCreature) : ScriptedPetAI(pCreature) { Reset(); } + + void Reset() override { } + + void OwnerKilledUnit(Unit* pVictim) override + { + if (pVictim->GetTypeId() == TYPEID_UNIT && pVictim->GetEntry() == NPC_WARP_CHASER) + { + // Distance not known, be assumed to be ~10 yards, possibly a bit less. + if (m_creature->IsWithinDistInMap(pVictim, 10.0f)) + { + DoScriptText(EMOTE_GATHER_BLOOD, m_creature); + m_creature->CastSpell(m_creature, SPELL_GATHER_WARP_BLOOD, false); + } + } + } +}; + +CreatureAI* GetAI_npc_zeppit(Creature* pCreature) +{ + return new npc_zeppitAI(pCreature); +} + +/*###### +## npc_protectorate_demolitionist +######*/ + +enum +{ + SAY_INTRO = -1000891, + SAY_ATTACKED_1 = -1000892, + SAY_ATTACKED_2 = -1000893, + SAY_STAGING_GROUNDS = -1000894, + SAY_TOXIC_HORROR = -1000895, + SAY_SALHADAAR = -1000896, + SAY_DISRUPTOR = -1000897, + SAY_NEXUS_PROTECT = -1000898, + SAY_FINISH_1 = -1000899, + SAY_FINISH_2 = -1000900, + + SPELL_ETHEREAL_TELEPORT = 34427, + SPELL_PROTECTORATE = 35679, // dummy aura applied on player + + NPC_NEXUS_STALKER = 20474, + NPC_ARCHON = 20458, + + FACTION_FRIENDLY = 35, + + QUEST_ID_DELIVERING_MESSAGE = 10406, +}; + +struct npc_protectorate_demolitionistAI : public npc_escortAI +{ + npc_protectorate_demolitionistAI(Creature* pCreature) : npc_escortAI(pCreature) { Reset(); } + + uint32 m_uiEventTimer; + uint8 m_uiEventStage; + + void Reset() override + { + if (!HasEscortState(STATE_ESCORT_ESCORTING)) + { + m_uiEventTimer = 0; + m_uiEventStage = 0; + } + } + + void Aggro(Unit* /*pWho*/) override + { + DoScriptText(urand(0, 1) ? SAY_ATTACKED_1 : SAY_ATTACKED_2, m_creature); + } + + // No attack done by this npc + void AttackStart(Unit* /*pWho*/) override { } + + void MoveInLineOfSight(Unit* pWho) override + { + if (HasEscortState(STATE_ESCORT_ESCORTING)) + { return; } + + // Star the escort + if (pWho->GetTypeId() == TYPEID_PLAYER) + { + if (pWho->HasAura(SPELL_PROTECTORATE) && ((Player*)pWho)->GetQuestStatus(QUEST_ID_DELIVERING_MESSAGE) == QUEST_STATUS_INCOMPLETE) + { + if (m_creature->IsWithinDistInMap(pWho, 10.0f)) + { + m_creature->SetFactionTemporary(FACTION_FRIENDLY, TEMPFACTION_RESTORE_RESPAWN); + Start(false, (Player*)pWho); + } + } + } + } + + void JustSummoned(Creature* pSummoned) override + { + if (pSummoned->GetEntry() == NPC_NEXUS_STALKER) + { DoScriptText(SAY_NEXUS_PROTECT, pSummoned); } + else if (pSummoned->GetEntry() == NPC_ARCHON) + { pSummoned->CastSpell(pSummoned, SPELL_ETHEREAL_TELEPORT, true); } + + pSummoned->AI()->AttackStart(m_creature); + } + + void WaypointReached(uint32 uiPointId) override + { + switch (uiPointId) + { + case 0: + DoScriptText(SAY_INTRO, m_creature); + break; + case 3: + DoScriptText(SAY_STAGING_GROUNDS, m_creature); + break; + case 4: + DoScriptText(SAY_TOXIC_HORROR, m_creature); + break; + case 9: + DoScriptText(SAY_SALHADAAR, m_creature); + break; + case 12: + DoScriptText(SAY_DISRUPTOR, m_creature); + SetEscortPaused(true); + m_uiEventTimer = 5000; + break; + case 13: + DoScriptText(SAY_FINISH_2, m_creature); + if (Player* pPlayer = GetPlayerForEscort()) + { + m_creature->SetFacingToObject(pPlayer); + pPlayer->GroupEventHappens(QUEST_ID_DELIVERING_MESSAGE, m_creature); + } + SetEscortPaused(true); + m_uiEventTimer = 6000; + break; + } + } + + void UpdateEscortAI(const uint32 uiDiff) override + { + if (m_uiEventTimer) + { + if (m_uiEventTimer <= uiDiff) + { + switch (m_uiEventStage) + { + case 0: + m_creature->SummonCreature(NPC_ARCHON, 3875.69f, 2308.72f, 115.80f, 1.48f, TEMPSUMMON_TIMED_OOC_OR_DEAD_DESPAWN, 30000); + m_uiEventTimer = 4000; + break; + case 1: + m_creature->SummonCreature(NPC_NEXUS_STALKER, 3884.06f, 2325.22f, 111.37f, 3.45f, TEMPSUMMON_TIMED_OOC_OR_DEAD_DESPAWN, 30000); + m_creature->SummonCreature(NPC_NEXUS_STALKER, 3861.54f, 2320.44f, 111.48f, 0.32f, TEMPSUMMON_TIMED_OOC_OR_DEAD_DESPAWN, 30000); + m_uiEventTimer = 9000; + break; + case 2: + DoScriptText(SAY_FINISH_1, m_creature); + SetRun(); + SetEscortPaused(false); + m_uiEventTimer = 0; + break; + case 3: + if (DoCastSpellIfCan(m_creature, SPELL_ETHEREAL_TELEPORT, CAST_TRIGGERED) == CAST_OK) + { m_creature->ForcedDespawn(1000); } + m_uiEventTimer = 0; + break; + } + ++m_uiEventStage; + } + else + { m_uiEventTimer -= uiDiff; } + } + + // ToDo: research if the npc uses spells or melee for combat + } +}; + +CreatureAI* GetAI_npc_protectorate_demolitionist(Creature* pCreature) +{ + return new npc_protectorate_demolitionistAI(pCreature); +} + +/*###### +## npc_captured_vanguard +######*/ + +enum +{ + SAY_VANGUARD_INTRO = -1000901, + SAY_VANGUARD_START = -1000902, + SAY_VANGUARD_FINISH = -1000903, + EMOTE_VANGUARD_FINISH = -1000904, + + SPELL_GLAIVE = 36500, + SPELL_HAMSTRING = 31553, + + NPC_COMMANDER_AMEER = 20448, + + QUEST_ID_ESCAPE_STAGING_GROUNDS = 10425, +}; + +struct npc_captured_vanguardAI : public npc_escortAI +{ + npc_captured_vanguardAI(Creature* pCreature) : npc_escortAI(pCreature) { Reset(); } + + uint32 m_uiGlaiveTimer; + uint32 m_uiHamstringTimer; + + void Reset() override + { + m_uiGlaiveTimer = urand(4000, 8000); + m_uiHamstringTimer = urand(8000, 13000); + } + + void JustReachedHome() override + { + // Happens only if the player helps the npc in the fight - otherwise he dies + DoScriptText(SAY_VANGUARD_INTRO, m_creature); + } + + void WaypointReached(uint32 uiPointId) override + { + switch (uiPointId) + { + case 15: + if (Player* pPlayer = GetPlayerForEscort()) + { pPlayer->GroupEventHappens(QUEST_ID_ESCAPE_STAGING_GROUNDS, m_creature); } + break; + case 16: + DoScriptText(SAY_VANGUARD_FINISH, m_creature); + SetRun(); + break; + case 17: + if (Creature* pAmeer = GetClosestCreatureWithEntry(m_creature, NPC_COMMANDER_AMEER, 5.0f)) + { DoScriptText(EMOTE_VANGUARD_FINISH, m_creature, pAmeer); } + break; + case 18: + if (DoCastSpellIfCan(m_creature, SPELL_ETHEREAL_TELEPORT, CAST_TRIGGERED) == CAST_OK) + { m_creature->ForcedDespawn(1000); } + break; + } + } + + void UpdateEscortAI(const uint32 uiDiff) override + { + if (!m_creature->SelectHostileTarget() || !m_creature->getVictim()) + { return; } + + if (m_uiGlaiveTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature->getVictim(), SPELL_GLAIVE) == CAST_OK) + { m_uiGlaiveTimer = urand(5000, 9000); } + } + else + { m_uiGlaiveTimer -= uiDiff; } + + if (m_uiHamstringTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature->getVictim(), SPELL_HAMSTRING) == CAST_OK) + { m_uiHamstringTimer = urand(10000, 16000); } + } + else + { m_uiHamstringTimer -= uiDiff; } + + DoMeleeAttackIfReady(); + } +}; + +CreatureAI* GetAI_npc_captured_vanguard(Creature* pCreature) +{ + return new npc_captured_vanguardAI(pCreature); +} + +bool QuestAccept_npc_captured_vanguard(Player* pPlayer, Creature* pCreature, const Quest* pQuest) +{ + if (pQuest->GetQuestId() == QUEST_ID_ESCAPE_STAGING_GROUNDS) + { + if (npc_captured_vanguardAI* pEscortAI = dynamic_cast(pCreature->AI())) + { pEscortAI->Start(false, pPlayer, pQuest); } + + DoScriptText(SAY_VANGUARD_START, pCreature, pPlayer); + } + + return true; +} + +void AddSC_netherstorm() +{ + Script* pNewScript; + + pNewScript = new Script; + pNewScript->Name = "go_manaforge_control_console"; + pNewScript->pGOUse = &GOUse_go_manaforge_control_console; + pNewScript->RegisterSelf(); + + pNewScript = new Script; + pNewScript->Name = "npc_manaforge_control_console"; + pNewScript->GetAI = &GetAI_npc_manaforge_control_console; + pNewScript->RegisterSelf(); + + pNewScript = new Script; + pNewScript->Name = "npc_commander_dawnforge"; + pNewScript->GetAI = &GetAI_npc_commander_dawnforge; + pNewScript->RegisterSelf(); + + pNewScript = new Script; + pNewScript->Name = "at_commander_dawnforge"; + pNewScript->pAreaTrigger = &AreaTrigger_at_commander_dawnforge; + pNewScript->RegisterSelf(); + + pNewScript = new Script; + pNewScript->Name = "npc_bessy"; + pNewScript->GetAI = &GetAI_npc_bessy; + pNewScript->pQuestAcceptNPC = &QuestAccept_npc_bessy; + pNewScript->RegisterSelf(); + + pNewScript = new Script; + pNewScript->Name = "npc_maxx_a_million"; + pNewScript->GetAI = &GetAI_npc_maxx_a_million; + pNewScript->pQuestAcceptNPC = &QuestAccept_npc_maxx_a_million; + pNewScript->RegisterSelf(); + + pNewScript = new Script; + pNewScript->Name = "npc_zeppit"; + pNewScript->GetAI = &GetAI_npc_zeppit; + pNewScript->RegisterSelf(); + + pNewScript = new Script; + pNewScript->Name = "npc_protectorate_demolitionist"; + pNewScript->GetAI = &GetAI_npc_protectorate_demolitionist; + pNewScript->RegisterSelf(); + + pNewScript = new Script; + pNewScript->Name = "npc_captured_vanguard"; + pNewScript->GetAI = &GetAI_npc_captured_vanguard; + pNewScript->pQuestAcceptNPC = &QuestAccept_npc_captured_vanguard; + pNewScript->RegisterSelf(); +} diff --git a/src/modules/SD2/scripts/outland/shadowmoon_valley.cpp b/src/modules/SD2/scripts/outland/shadowmoon_valley.cpp new file mode 100644 index 000000000..caa21ced9 --- /dev/null +++ b/src/modules/SD2/scripts/outland/shadowmoon_valley.cpp @@ -0,0 +1,1919 @@ +/** + * ScriptDev2 is an extension for mangos providing enhanced features for + * area triggers, creatures, game objects, instances, items, and spells beyond + * the default database scripting in mangos. + * + * Copyright (C) 2006-2013 ScriptDev2 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * World of Warcraft, and all World of Warcraft or Warcraft art, images, + * and lore are copyrighted by Blizzard Entertainment, Inc. + */ + +/* ScriptData +SDName: Shadowmoon_Valley +SD%Complete: 100 +SDComment: Quest support: 11020, 10458, 10480, 10481, 10514, 10540, 10588, 10781, 10804, 10854. +SDCategory: Shadowmoon Valley +EndScriptData */ + +/* ContentData +mob_mature_netherwing_drake +mob_enslaved_netherwing_drake +npc_dragonmaw_peon +npc_wilda +mob_torloth +npc_lord_illidan_stormrage +npc_totem_of_spirits +event_spell_soul_captured_credit +go_crystal_prison +npc_spawned_oronok_tornheart +npc_domesticated_felboar +npc_veneratus_spawn_node +EndContentData */ + +#include "precompiled.h" +#include "escort_ai.h" +#include "pet_ai.h" + +/*##### +# mob_mature_netherwing_drake +#####*/ + +enum +{ + SAY_JUST_EATEN = -1000175, + + SPELL_PLACE_CARCASS = 38439, + SPELL_JUST_EATEN = 38502, + SPELL_NETHER_BREATH = 38467, + + QUEST_KINDNESS = 10804, + NPC_EVENT_PINGER = 22131, + + GO_FLAYER_CARCASS = 185155, +}; + +struct mob_mature_netherwing_drakeAI : public ScriptedAI +{ + mob_mature_netherwing_drakeAI(Creature* pCreature) : ScriptedAI(pCreature) { Reset(); } + + ObjectGuid m_playerGuid; + + uint32 m_uiEatTimer; + uint32 m_uiCreditTimer; + uint32 m_uiCastTimer; + + void Reset() override + { + m_playerGuid.Clear(); + + m_uiEatTimer = 0; + m_uiCreditTimer = 0; + m_uiCastTimer = 5000; + } + + void SpellHit(Unit* pCaster, SpellEntry const* pSpell) override + { + if (m_uiEatTimer || m_uiCreditTimer) + { return; } + + if (pCaster->GetTypeId() == TYPEID_PLAYER && pSpell->Id == SPELL_PLACE_CARCASS && !m_creature->HasAura(SPELL_JUST_EATEN)) + { + m_playerGuid = pCaster->GetObjectGuid(); + m_uiEatTimer = 5000; + } + } + + void MovementInform(uint32 uiMoveType, uint32 uiPointId) override + { + if (uiMoveType != POINT_MOTION_TYPE) + { return; } + + if (uiPointId) + { + m_uiCreditTimer = 7000; + m_creature->SetLevitate(false); + m_creature->HandleEmote(EMOTE_ONESHOT_ATTACKUNARMED); + m_creature->RemoveByteFlag(UNIT_FIELD_BYTES_1, 3, UNIT_BYTE1_FLAG_ALWAYS_STAND | UNIT_BYTE1_FLAG_UNK_2); + } + } + + void UpdateAI(const uint32 uiDiff) override + { + if (m_uiEatTimer) + { + if (m_uiEatTimer <= uiDiff) + { + if (GameObject* pGo = GetClosestGameObjectWithEntry(m_creature, GO_FLAYER_CARCASS, 80.0f)) + { + if (m_creature->GetMotionMaster()->GetCurrentMovementGeneratorType() == WAYPOINT_MOTION_TYPE) + { m_creature->GetMotionMaster()->MovementExpired(); } + + m_creature->GetMotionMaster()->MoveIdle(); + + float fX, fY, fZ; + pGo->GetContactPoint(m_creature, fX, fY, fZ, CONTACT_DISTANCE); + + m_creature->GetMotionMaster()->MovePoint(1, fX, fY, fZ); + } + m_uiEatTimer = 0; + } + else + { m_uiEatTimer -= uiDiff; } + + return; + } + + if (m_uiCreditTimer) + { + if (m_uiCreditTimer <= uiDiff) + { + DoCastSpellIfCan(m_creature, SPELL_JUST_EATEN); + DoScriptText(SAY_JUST_EATEN, m_creature); + + if (Player* pPlayer = m_creature->GetMap()->GetPlayer(m_playerGuid)) + { pPlayer->KilledMonsterCredit(NPC_EVENT_PINGER, m_creature->GetObjectGuid()); } + + Reset(); + m_creature->SetLevitate(true); + m_creature->SetByteFlag(UNIT_FIELD_BYTES_1, 3, UNIT_BYTE1_FLAG_ALWAYS_STAND | UNIT_BYTE1_FLAG_UNK_2); + m_creature->GetMotionMaster()->Clear(); + m_uiCreditTimer = 0; + } + else + { m_uiCreditTimer -= uiDiff; } + + return; + } + + if (!m_creature->SelectHostileTarget() || !m_creature->getVictim()) + { return; } + + if (m_uiCastTimer < uiDiff) + { + DoCastSpellIfCan(m_creature->getVictim(), SPELL_NETHER_BREATH); + m_uiCastTimer = 5000; + } + else + { m_uiCastTimer -= uiDiff; } + + DoMeleeAttackIfReady(); + } +}; + +CreatureAI* GetAI_mob_mature_netherwing_drake(Creature* pCreature) +{ + return new mob_mature_netherwing_drakeAI(pCreature); +} + +/*### +# mob_enslaved_netherwing_drake +####*/ + +enum +{ + FACTION_FRIENDLY = 1840, // Not sure if this is correct, it was taken off of Mordenai. + + SPELL_HIT_FORCE_OF_NELTHARAKU = 38762, + SPELL_FORCE_OF_NELTHARAKU = 38775, + + QUEST_FORCE_OF_NELT = 10854, + NPC_DRAGONMAW_SUBJUGATOR = 21718, + NPC_ESCAPE_DUMMY = 21348 +}; + +struct mob_enslaved_netherwing_drakeAI : public ScriptedAI +{ + mob_enslaved_netherwing_drakeAI(Creature* pCreature) : ScriptedAI(pCreature) + { + m_uiFlyTimer = 0; + Reset(); + } + + ObjectGuid m_playerGuid; + uint32 m_uiFlyTimer; + + void Reset() override { } + + void SpellHit(Unit* pCaster, const SpellEntry* pSpell) override + { + if (pSpell->Id == SPELL_HIT_FORCE_OF_NELTHARAKU && !m_uiFlyTimer) + { + if (Player* pPlayer = pCaster->GetCharmerOrOwnerPlayerOrPlayerItself()) + { + m_uiFlyTimer = 2500; + m_playerGuid = pPlayer->GetObjectGuid(); + + m_creature->SetFactionTemporary(FACTION_FRIENDLY, TEMPFACTION_RESTORE_RESPAWN); + + if (Creature* pDragonmaw = GetClosestCreatureWithEntry(m_creature, NPC_DRAGONMAW_SUBJUGATOR, 50.0f)) + { AttackStart(pDragonmaw); } + } + } + } + + void MovementInform(uint32 uiMoveType, uint32 uiPointId) override + { + if (uiMoveType != POINT_MOTION_TYPE) + { return; } + + if (uiPointId) + { m_creature->ForcedDespawn(); } + } + + void UpdateAI(const uint32 uiDiff) override + { + if (!m_creature->SelectHostileTarget() || !m_creature->getVictim()) + { + if (m_uiFlyTimer) + { + if (m_uiFlyTimer <= uiDiff) + { + if (Player* pPlayer = m_creature->GetMap()->GetPlayer(m_playerGuid)) + { + if (pPlayer->GetQuestStatus(QUEST_FORCE_OF_NELT) == QUEST_STATUS_INCOMPLETE) + { + DoCastSpellIfCan(pPlayer, SPELL_FORCE_OF_NELTHARAKU, CAST_TRIGGERED); + m_playerGuid.Clear(); + + float fX, fY, fZ; + + // Get an escape position + if (Creature* pEscapeDummy = GetClosestCreatureWithEntry(m_creature, NPC_ESCAPE_DUMMY, 50.0f)) + { pEscapeDummy->GetPosition(fX, fY, fZ); } + else + { + m_creature->GetRandomPoint(m_creature->GetPositionX(), m_creature->GetPositionY(), m_creature->GetPositionZ(), 20.0f, fX, fY, fZ); + fZ += 25; + } + + m_creature->GetMotionMaster()->MovePoint(1, fX, fY, fZ); + } + } + m_uiFlyTimer = 0; + } + else + { m_uiFlyTimer -= uiDiff; } + } + + return; + } + + DoMeleeAttackIfReady(); + } +}; + +CreatureAI* GetAI_mob_enslaved_netherwing_drake(Creature* pCreature) +{ + return new mob_enslaved_netherwing_drakeAI(pCreature); +} + +/*##### +# npc_dragonmaw_peon +#####*/ + +enum +{ + SAY_PEON_1 = -1000652, + SAY_PEON_2 = -1000653, + SAY_PEON_3 = -1000654, + SAY_PEON_4 = -1000655, + SAY_PEON_5 = -1000656, + + SPELL_SERVING_MUTTON = 40468, + NPC_DRAGONMAW_KILL_CREDIT = 23209, + EQUIP_ID_MUTTON = 2202, + POINT_DEST = 1 +}; + +struct npc_dragonmaw_peonAI : public ScriptedAI +{ + npc_dragonmaw_peonAI(Creature* pCreature) : ScriptedAI(pCreature) { Reset(); } + + ObjectGuid m_playerGuid; + uint32 m_uiPoisonTimer; + uint32 m_uiMoveTimer; + uint32 m_uiEatTimer; + + void Reset() override + { + m_playerGuid.Clear(); + m_uiPoisonTimer = 0; + m_uiMoveTimer = 0; + m_uiEatTimer = 0; + + SetEquipmentSlots(true); + } + + bool SetPlayerTarget(ObjectGuid playerGuid) + { + // Check if event already started + if (m_playerGuid) + { return false; } + + m_playerGuid = playerGuid; + m_uiMoveTimer = 500; + return true; + } + + void MovementInform(uint32 uiType, uint32 uiPointId) override + { + if (uiType != POINT_MOTION_TYPE) + { return; } + + if (uiPointId == POINT_DEST) + { + m_uiEatTimer = 2000; + m_uiPoisonTimer = 3000; + + switch (urand(0, 4)) + { + case 0: DoScriptText(SAY_PEON_1, m_creature); break; + case 1: DoScriptText(SAY_PEON_2, m_creature); break; + case 2: DoScriptText(SAY_PEON_3, m_creature); break; + case 3: DoScriptText(SAY_PEON_4, m_creature); break; + case 4: DoScriptText(SAY_PEON_5, m_creature); break; + } + } + } + + void UpdateAI(const uint32 uiDiff) override + { + if (m_uiMoveTimer) + { + if (m_uiMoveTimer <= uiDiff) + { + if (Player* pPlayer = m_creature->GetMap()->GetPlayer(m_playerGuid)) + { + GameObject* pMutton = pPlayer->GetGameObject(SPELL_SERVING_MUTTON); + + // Workaround for broken function GetGameObject + if (!pMutton) + { + const SpellEntry* pSpell = GetSpellStore()->LookupEntry(SPELL_SERVING_MUTTON); + + uint32 uiGameobjectEntry = pSpell->GetEffectMiscValue(EFFECT_INDEX_0); + + // this can fail, but very low chance + pMutton = GetClosestGameObjectWithEntry(pPlayer, uiGameobjectEntry, 2 * INTERACTION_DISTANCE); + } + + if (pMutton) + { + float fX, fY, fZ; + pMutton->GetContactPoint(m_creature, fX, fY, fZ, CONTACT_DISTANCE); + + m_creature->SetWalk(false); + m_creature->GetMotionMaster()->MovePoint(POINT_DEST, fX, fY, fZ); + } + } + + m_uiMoveTimer = 0; + } + else + { m_uiMoveTimer -= uiDiff; } + } + else if (m_uiEatTimer) + { + if (m_uiEatTimer <= uiDiff) + { + SetEquipmentSlots(false, EQUIP_ID_MUTTON, EQUIP_UNEQUIP); + m_creature->HandleEmote(EMOTE_ONESHOT_EAT_NOSHEATHE); + m_uiEatTimer = 0; + } + else + { m_uiEatTimer -= uiDiff; } + } + else if (m_uiPoisonTimer) + { + if (m_uiPoisonTimer <= uiDiff) + { + if (Player* pPlayer = m_creature->GetMap()->GetPlayer(m_playerGuid)) + { pPlayer->KilledMonsterCredit(NPC_DRAGONMAW_KILL_CREDIT, m_creature->GetObjectGuid()); } + + m_uiPoisonTimer = 0; + + // dies + m_creature->SetDeathState(JUST_DIED); + m_creature->SetHealth(0); + } + else + { m_uiPoisonTimer -= uiDiff; } + } + } +}; + +CreatureAI* GetAI_npc_dragonmaw_peon(Creature* pCreature) +{ + return new npc_dragonmaw_peonAI(pCreature); +} + +bool EffectDummyCreature_npc_dragonmaw_peon(Unit* pCaster, uint32 uiSpellId, SpellEffectIndex uiEffIndex, Creature* pCreatureTarget, ObjectGuid /*originalCasterGuid*/) +{ + if (uiEffIndex != EFFECT_INDEX_1 || uiSpellId != SPELL_SERVING_MUTTON || pCaster->GetTypeId() != TYPEID_PLAYER) + { return false; } + + npc_dragonmaw_peonAI* pPeonAI = dynamic_cast(pCreatureTarget->AI()); + + if (!pPeonAI) + { return false; } + + if (pPeonAI->SetPlayerTarget(pCaster->GetObjectGuid())) + { + pCreatureTarget->HandleEmote(EMOTE_ONESHOT_NONE); + return true; + } + + return false; +} + +/*###### +# npc_wilda +######*/ + +enum +{ + SAY_WIL_START = -1000381, + SAY_WIL_AGGRO1 = -1000382, + SAY_WIL_AGGRO2 = -1000383, + SAY_WIL_PROGRESS1 = -1000384, + SAY_WIL_PROGRESS2 = -1000385, + SAY_WIL_FIND_EXIT = -1000386, + SAY_WIL_PROGRESS4 = -1000387, + 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 npc_wildaAI : public npc_escortAI +{ + npc_wildaAI(Creature* pCreature) : npc_escortAI(pCreature) { Reset(); } + + uint32 m_uiHealingTimer; + + void Reset() override + { + m_uiHealingTimer = 0; + } + + void WaypointReached(uint32 uiPointId) override + { + Player* pPlayer = GetPlayerForEscort(); + + if (!pPlayer) + return; + + switch (uiPointId) + { + case 13: + DoScriptText(SAY_WIL_PROGRESS1, m_creature, pPlayer); + DoSpawnAssassin(); + break; + case 14: + DoSpawnAssassin(); + break; + case 15: + DoScriptText(SAY_WIL_FIND_EXIT, m_creature, pPlayer); + break; + case 19: + DoRandomSay(); + break; + case 20: + DoSpawnAssassin(); + break; + case 26: + DoRandomSay(); + break; + case 27: + DoSpawnAssassin(); + break; + case 33: + DoRandomSay(); + break; + case 34: + DoSpawnAssassin(); + break; + case 37: + DoRandomSay(); + break; + case 38: + DoSpawnAssassin(); + break; + case 39: + DoScriptText(SAY_WIL_JUST_AHEAD, m_creature, pPlayer); + break; + case 43: + DoRandomSay(); + break; + case 44: + DoSpawnAssassin(); + break; + case 50: + DoScriptText(SAY_WIL_END, m_creature, pPlayer); + pPlayer->GroupEventHappens(QUEST_ESCAPE_COILSCAR, m_creature); + break; + } + } + + void JustSummoned(Creature* pSummoned) override + { + 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() + { + switch (urand(0, 2)) + { + case 0: DoScriptText(SAY_WIL_PROGRESS2, m_creature); break; + case 1: DoScriptText(SAY_WIL_PROGRESS4, m_creature); break; + case 2: DoScriptText(SAY_WIL_PROGRESS5, m_creature); break; + } + } + + void DoSpawnAssassin() + { + // unknown where they actually appear + float fX, fY, fZ; + m_creature->GetRandomPoint(m_creature->GetPositionX(), m_creature->GetPositionY(), m_creature->GetPositionZ(), 15.0f, fX, fY, fZ); + + m_creature->SummonCreature(NPC_COILSKAR_ASSASSIN, fX, fY, fZ, 0.0f, TEMPSUMMON_TIMED_OOC_DESPAWN, 5000); + } + + void Aggro(Unit* pWho) override + { + // don't always use + if (urand(0, 4)) + return; + + // only aggro text if not player + if (pWho->GetTypeId() != TYPEID_PLAYER) + { + // appears to be random + switch (urand(0, 3)) + { + case 0: DoScriptText(SAY_WIL_AGGRO1, m_creature, pWho); break; + case 1: DoScriptText(SAY_WIL_AGGRO2, m_creature, pWho); break; + } + } + } + + void UpdateEscortAI(const uint32 uiDiff) override + { + if (!m_creature->SelectHostileTarget() || !m_creature->getVictim()) + { return; } + + // TODO: add more abilities + if (m_creature->GetHealthPercent() <= 30.0f) + { + if (m_uiHealingTimer < uiDiff) + { + DoCastSpellIfCan(m_creature, SPELL_HEALING_WAVE); + m_uiHealingTimer = 15000; + } + else + { m_uiHealingTimer -= uiDiff; } + } + + DoMeleeAttackIfReady(); + } +}; + +CreatureAI* GetAI_npc_wilda(Creature* pCreature) +{ + return new npc_wildaAI(pCreature); +} + +bool QuestAccept_npc_wilda(Player* pPlayer, Creature* pCreature, const Quest* pQuest) +{ + if (pQuest->GetQuestId() == QUEST_ESCAPE_COILSCAR) + { + DoScriptText(SAY_WIL_START, pCreature, pPlayer); + pCreature->SetFactionTemporary(FACTION_EARTHEN, TEMPFACTION_RESTORE_RESPAWN); + + if (npc_wildaAI* pEscortAI = dynamic_cast(pCreature->AI())) + { pEscortAI->Start(false, pPlayer, pQuest); } + } + return true; +} + +/*##### +# Quest: Battle of the Crimson Watch +#####*/ + +enum +{ + QUEST_BATTLE_OF_THE_CRIMSON_WATCH = 10781, + + EVENT_COOLDOWN = 30000, + + SAY_TORLOTH_DIALOGUE1 = -1000532, + SAY_TORLOTH_DIALOGUE2 = -1000533, + SAY_TORLOTH_DIALOGUE3 = -1000534, + SAY_ILLIDAN_DIALOGUE = -1000535, + SAY_ILLIDAN_SUMMON1 = -1000536, + SAY_ILLIDAN_SUMMON2 = -1000537, + SAY_ILLIDAN_SUMMON3 = -1000538, + SAY_ILLIDAN_SUMMON4 = -1000539, + SAY_EVENT_COMPLETED = -1000540, + + MODEL_ID_FELGUARD = 18654, + MODEL_ID_DREADLORD = 19991, + + NPC_ILLIDARI_SOLDIER = 22075, + NPC_ILLIDARI_MIND_BREAKER = 22074, + NPC_ILLIDARI_HIGHLORD = 19797, + NPC_TORLOTH_THE_MAGNIFICENT = 22076, + NPC_LORD_ILLIDAN = 22083 +}; + +enum CinematicCreature +{ + LORD_ILLIDAN = 1, + TORLOTH = 0 +}; + +const float EVENT_AREA_RADIUS = 65.0; + +struct TorlothCinematic +{ + int32 iTextId; + uint32 uiCreature; + uint32 uiTimer; +}; + +static TorlothCinematic TorlothAnim[] = +{ + {SAY_TORLOTH_DIALOGUE1, TORLOTH, 2000}, + {SAY_ILLIDAN_DIALOGUE, LORD_ILLIDAN, 7000}, + {SAY_TORLOTH_DIALOGUE2, TORLOTH, 3000}, + {0, TORLOTH, 2000}, // Torloth stand + {SAY_TORLOTH_DIALOGUE3, TORLOTH, 1000}, + {0, TORLOTH, 3000}, + {0, TORLOTH, 0} +}; + +struct Location +{ + float fLocX; + float fLocY; + float fLocZ; + float fOrient; +}; + +static Location SpawnLocation[] = +{ + { -4615.8556f, 1342.2532f, 139.9f, 1.612f}, // Illidari Soldier + { -4598.9365f, 1377.3182f, 139.9f, 3.917f}, // Illidari Soldier + { -4598.4697f, 1360.8999f, 139.9f, 2.427f}, // Illidari Soldier + { -4589.3599f, 1369.1061f, 139.9f, 3.165f}, // Illidari Soldier + { -4608.3477f, 1386.0076f, 139.9f, 4.108f}, // Illidari Soldier + { -4633.1889f, 1359.8033f, 139.9f, 0.949f}, // Illidari Soldier + { -4623.5791f, 1351.4574f, 139.9f, 0.971f}, // Illidari Soldier + { -4607.2988f, 1351.6099f, 139.9f, 2.416f}, // Illidari Soldier + { -4633.7764f, 1376.0417f, 139.9f, 5.608f}, // Illidari Soldier + { -4600.2461f, 1369.1240f, 139.9f, 3.056f}, // Illidari Mind Breaker + { -4631.7808f, 1367.9459f, 139.9f, 0.020f}, // Illidari Mind Breaker + { -4600.2461f, 1369.1240f, 139.9f, 3.056f}, // Illidari Highlord + { -4631.7808f, 1367.9459f, 139.9f, 0.020f}, // Illidari Highlord + { -4615.5586f, 1353.0031f, 139.9f, 1.540f}, // Illidari Highlord + { -4616.4736f, 1384.2170f, 139.9f, 4.971f}, // Illidari Highlord + { -4627.1240f, 1378.8752f, 139.9f, 2.544f} // Torloth The Magnificent +}; + +struct WaveData +{ + uint8 uiSpawnCount; + uint8 uiUsedSpawnPoint; + uint32 uiCreatureId; + uint32 uiSpawnTimer; + uint32 uiYellTimer; + int32 iTextId; +}; + +static WaveData WavesInfo[] = +{ + // Illidari Soldier + {9, 0, NPC_ILLIDARI_SOLDIER, 10000, 7000, SAY_ILLIDAN_SUMMON1}, + // Illidari Mind Breaker + {2, 9, NPC_ILLIDARI_MIND_BREAKER, 10000, 7000, SAY_ILLIDAN_SUMMON2}, + // Illidari Highlord + {4, 11, NPC_ILLIDARI_HIGHLORD, 10000, 7000, SAY_ILLIDAN_SUMMON3}, + // Torloth The Magnificent + {1, 15, NPC_TORLOTH_THE_MAGNIFICENT, 10000, 7000, SAY_ILLIDAN_SUMMON4} +}; + +/*###### +# mob_torloth +#####*/ + +enum +{ + SPELL_CLEAVE = 15284, + SPELL_SHADOWFURY = 39082, + SPELL_SPELL_REFLECTION = 33961 +}; + +struct mob_torlothAI : public ScriptedAI +{ + mob_torlothAI(Creature* pCreature) : ScriptedAI(pCreature) {Reset();} + + ObjectGuid m_lordIllidanGuid; + ObjectGuid m_playerGuid; + + uint32 m_uiCleaveTimer; + uint32 m_uiShadowfuryTimer; + uint32 m_uiSpellReflectionTimer; + uint8 m_uiAnimationCount; + uint32 m_uiAnimationTimer; + + void Reset() override + { + m_lordIllidanGuid.Clear(); + m_playerGuid.Clear(); + + m_uiAnimationCount = 0; + m_uiAnimationTimer = 4000; + m_uiCleaveTimer = 10000; + m_uiShadowfuryTimer = 18000; + m_uiSpellReflectionTimer = 25000; + + // make him not attackable for the time of animation + m_creature->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE); + SetCombatMovement(false); + } + + void EnterEvadeMode() override + { + m_creature->ForcedDespawn(); + } + + void HandleAnimation() + { + Creature* pCreature = m_creature; + + if (TorlothAnim[m_uiAnimationCount].uiCreature == LORD_ILLIDAN) + { + pCreature = m_creature->GetMap()->GetCreature(m_lordIllidanGuid); + + if (!pCreature) + { + m_creature->ForcedDespawn(); + return; + } + } + + if (TorlothAnim[m_uiAnimationCount].iTextId) + { DoScriptText(TorlothAnim[m_uiAnimationCount].iTextId, pCreature); } + + m_uiAnimationTimer = TorlothAnim[m_uiAnimationCount].uiTimer; + + switch (m_uiAnimationCount) + { + case 0: + m_creature->SetStandState(UNIT_STAND_STATE_KNEEL); + break; + case 3: + m_creature->SetStandState(UNIT_STAND_STATE_STAND); + break; + case 5: + if (Player* pTarget = m_creature->GetMap()->GetPlayer(m_playerGuid)) + { + m_creature->AddThreat(pTarget); + m_creature->SetFacingToObject(pTarget); + m_creature->HandleEmote(EMOTE_ONESHOT_POINT); + } + break; + case 6: + { + if (Player* pTarget = m_creature->GetMap()->GetPlayer(m_playerGuid)) + { + SetCombatMovement(true); + m_creature->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE); + float fLocX, fLocY, fLocZ; + pTarget->GetPosition(fLocX, fLocY, fLocZ); + m_creature->GetMotionMaster()->MovePoint(0, fLocX, fLocY, fLocZ); + } + break; + } + } + + ++m_uiAnimationCount; + } + + void JustDied(Unit* pKiller) override + { + if (Player* pPlayer = pKiller->GetCharmerOrOwnerPlayerOrPlayerItself()) + { + pPlayer->GroupEventHappens(QUEST_BATTLE_OF_THE_CRIMSON_WATCH, m_creature); + + if (Creature* pLordIllidan = m_creature->GetMap()->GetCreature(m_lordIllidanGuid)) + { + DoScriptText(SAY_EVENT_COMPLETED, pLordIllidan, pPlayer); + pLordIllidan->AI()->EnterEvadeMode(); + } + } + } + + void UpdateAI(const uint32 uiDiff) override + { + if (m_uiAnimationCount < 7) + { + if (m_uiAnimationTimer < uiDiff) + { HandleAnimation(); } + else + { m_uiAnimationTimer -= uiDiff; } + } + else + { + if (!m_creature->SelectHostileTarget() || !m_creature->getVictim()) + { return; } + + if (m_uiCleaveTimer < uiDiff) + { + DoCastSpellIfCan(m_creature->getVictim(), SPELL_CLEAVE); + m_uiCleaveTimer = 15000; + } + else + { m_uiCleaveTimer -= uiDiff; } + + if (m_uiShadowfuryTimer < uiDiff) + { + DoCastSpellIfCan(m_creature->getVictim(), SPELL_SHADOWFURY); + m_uiShadowfuryTimer = 20000; + } + else + { m_uiShadowfuryTimer -= uiDiff; } + + if (m_uiSpellReflectionTimer < uiDiff) + { + DoCastSpellIfCan(m_creature, SPELL_SPELL_REFLECTION); + m_uiSpellReflectionTimer = 30000; + } + else + { m_uiSpellReflectionTimer -= uiDiff; } + + DoMeleeAttackIfReady(); + } + } +}; + +CreatureAI* GetAI_mob_torloth(Creature* pCreature) +{ + return new mob_torlothAI(pCreature); +} + +/*##### +# npc_lord_illidan_stormrage +#####*/ + +struct npc_lord_illidan_stormrageAI : public Scripted_NoMovementAI +{ + npc_lord_illidan_stormrageAI(Creature* pCreature) : Scripted_NoMovementAI(pCreature) {Reset();} + + ObjectGuid m_playerGuid; + uint32 m_uiWaveTimer; + uint32 m_uiAnnounceTimer; + uint32 m_uiCheckTimer; + uint8 m_uiMobCount; + uint8 m_uiWaveCount; + + bool m_bEventStarted; + bool m_bEventFailed; + bool m_bWaveAnnounced; + + void Reset() override + { + m_playerGuid.Clear(); + + m_uiWaveTimer = 10000; + m_uiAnnounceTimer = 7000; + m_uiCheckTimer = 2000; + + m_uiMobCount = 0; + m_uiWaveCount = 0; + + m_bEventStarted = false; + m_bEventFailed = false; + m_bWaveAnnounced = false; + } + + void StartEvent(Player* pPlayer) + { + m_bEventStarted = true; + m_playerGuid = pPlayer->GetObjectGuid(); + } + + void SummonWave() + { + uint8 uiCount = WavesInfo[m_uiWaveCount].uiSpawnCount; + uint8 uiLocIndex = WavesInfo[m_uiWaveCount].uiUsedSpawnPoint; + uint8 uiFelguardCount = 0; + uint8 uiDreadlordCount = 0; + + for (uint8 i = 0; i < uiCount; ++i) + { + float fLocX, fLocY, fLocZ, fOrient; + fLocX = SpawnLocation[uiLocIndex + i].fLocX; + fLocY = SpawnLocation[uiLocIndex + i].fLocY; + fLocZ = SpawnLocation[uiLocIndex + i].fLocZ; + fOrient = SpawnLocation[uiLocIndex + i].fOrient; + + if (Creature* pSpawn = m_creature->SummonCreature(WavesInfo[m_uiWaveCount].uiCreatureId, fLocX, fLocY, fLocZ, fOrient, TEMPSUMMON_CORPSE_TIMED_DESPAWN, 15000)) + { + + if (m_uiWaveCount) // only in first wave + { continue; } + + if (!urand(0, 2) && uiFelguardCount < 2) + { + pSpawn->SetDisplayId(MODEL_ID_FELGUARD); + ++uiFelguardCount; + } + else if (uiDreadlordCount < 3) + { + pSpawn->SetDisplayId(MODEL_ID_DREADLORD); + ++uiDreadlordCount; + } + else if (uiFelguardCount < 2) + { + pSpawn->SetDisplayId(MODEL_ID_FELGUARD); + ++uiFelguardCount; + } + } + } + + ++m_uiWaveCount; + m_uiWaveTimer = WavesInfo[m_uiWaveCount].uiSpawnTimer; + m_uiAnnounceTimer = WavesInfo[m_uiWaveCount].uiYellTimer; + } + + void JustSummoned(Creature* pSummoned) override + { + // increment mob count + ++m_uiMobCount; + + if (!m_playerGuid) + { return; } + + if (pSummoned->GetEntry() == NPC_TORLOTH_THE_MAGNIFICENT) + { + if (mob_torlothAI* pTorlothAI = dynamic_cast(pSummoned->AI())) + { + pTorlothAI->m_lordIllidanGuid = m_creature->GetObjectGuid(); + pTorlothAI->m_playerGuid = m_playerGuid; + } + } + else + { + if (Player* pTarget = m_creature->GetMap()->GetPlayer(m_playerGuid)) + { + float fLocX, fLocY, fLocZ; + pTarget->GetPosition(fLocX, fLocY, fLocZ); + pSummoned->GetMotionMaster()->MovePoint(0, fLocX, fLocY, fLocZ); + } + } + } + + void SummonedCreatureDespawn(Creature* /*pCreature*/) override + { + // decrement mob count + --m_uiMobCount; + + if (!m_uiMobCount) + { m_bWaveAnnounced = false; } + } + + void CheckEventFail() + { + Player* pPlayer = m_creature->GetMap()->GetPlayer(m_playerGuid); + + if (!pPlayer) + { return; } + + if (Group* pEventGroup = pPlayer->GetGroup()) + { + uint8 uiDeadMemberCount = 0; + uint8 uiFailedMemberCount = 0; + + for (GroupReference* pRef = pEventGroup->GetFirstMember(); pRef != NULL; pRef = pRef->next()) + { + if (Player* pMember = pRef->getSource()) + { + if (!pMember->IsAlive()) + { ++uiDeadMemberCount; } + + // if we already failed no need to check other things + if (pMember->GetQuestStatus(QUEST_BATTLE_OF_THE_CRIMSON_WATCH) == QUEST_STATUS_FAILED) + { + ++uiFailedMemberCount; + continue; + } + + // we left event area fail quest + if (!pMember->IsWithinDistInMap(m_creature, EVENT_AREA_RADIUS)) + { + pMember->FailQuest(QUEST_BATTLE_OF_THE_CRIMSON_WATCH); + ++uiFailedMemberCount; + } + } + } + + if (pEventGroup->GetMembersCount() == uiFailedMemberCount) + { + m_bEventFailed = true; + return; + } + + if (pEventGroup->GetMembersCount() == uiDeadMemberCount) + { + for (GroupReference* pRef = pEventGroup->GetFirstMember(); pRef != NULL; pRef = pRef->next()) + { + if (Player* pMember = pRef->getSource()) + { + if (pMember->GetQuestStatus(QUEST_BATTLE_OF_THE_CRIMSON_WATCH) == QUEST_STATUS_INCOMPLETE) + { pMember->FailQuest(QUEST_BATTLE_OF_THE_CRIMSON_WATCH); } + } + } + + m_bEventFailed = true; + } + } + else if (pPlayer->IsDead() || !pPlayer->IsWithinDistInMap(m_creature, EVENT_AREA_RADIUS)) + { + pPlayer->FailQuest(QUEST_BATTLE_OF_THE_CRIMSON_WATCH); + m_bEventFailed = true; + } + } + + void UpdateAI(const uint32 uiDiff) override + { + if (!m_playerGuid || !m_bEventStarted) + { return; } + + if (!m_uiMobCount && m_uiWaveCount < 4) + { + if (!m_bWaveAnnounced && m_uiAnnounceTimer < uiDiff) + { + DoScriptText(WavesInfo[m_uiWaveCount].iTextId, m_creature); + m_bWaveAnnounced = true; + } + else + { m_uiAnnounceTimer -= uiDiff; } + + if (m_uiWaveTimer < uiDiff) + { SummonWave(); } + else + { m_uiWaveTimer -= uiDiff; } + } + + if (m_uiCheckTimer < uiDiff) + { + CheckEventFail(); + m_uiCheckTimer = 2000; + } + else + { m_uiCheckTimer -= uiDiff; } + + if (m_bEventFailed) + { Reset(); } + } +}; + +CreatureAI* GetAI_npc_lord_illidan_stormrage(Creature * (pCreature)) +{ + return new npc_lord_illidan_stormrageAI(pCreature); +} + +/*##### +# go_crystal_prison : GameObject that begins the event and hands out quest +######*/ +bool GOQuestAccept_GO_crystal_prison(Player* pPlayer, GameObject* /*pGo*/, Quest const* pQuest) +{ + if (pQuest->GetQuestId() == QUEST_BATTLE_OF_THE_CRIMSON_WATCH) + if (Creature* pLordIllidan = GetClosestCreatureWithEntry(pPlayer, NPC_LORD_ILLIDAN, 50.0)) + if (npc_lord_illidan_stormrageAI* pIllidanAI = dynamic_cast(pLordIllidan->AI())) + if (!pIllidanAI->m_bEventStarted) + { pIllidanAI->StartEvent(pPlayer); } + + return true; +} + +/*###### +## npc_totem_of_spirits +######*/ + +enum +{ + QUEST_SPIRITS_FIRE_AND_EARTH = 10458, + QUEST_SPIRITS_WATER = 10480, + QUEST_SPIRITS_AIR = 10481, + + // quest 10458, 10480, 10481 + SPELL_ELEMENTAL_SIEVE = 36035, + SPELL_CALL_TO_THE_SPIRITS = 36206, + + SPELL_EARTH_CAPTURED = 36025, // dummies (having visual effects) + SPELL_FIERY_CAPTURED = 36115, + SPELL_WATER_CAPTURED = 36170, + SPELL_AIR_CAPTURED = 36181, + + SPELL_EARTH_CAPTURED_CREDIT = 36108, // event 13513 + SPELL_FIERY_CAPTURED_CREDIT = 36117, // event 13514 + SPELL_WATER_CAPTURED_CREDIT = 36171, // event 13515 + SPELL_AIR_CAPTURED_CREDIT = 36182, // event 13516 + + NPC_TOTEM_OF_SPIRITS = 21071, + NPC_EARTH_SPIRIT = 21050, // to be killed + NPC_FIERY_SPIRIT = 21061, + NPC_WATER_SPIRIT = 21059, + NPC_AIR_SPIRIT = 21060, + + NPC_EARTHEN_SOUL = 21073, // invisible souls summoned by the totem + NPC_FIERY_SOUL = 21097, + NPC_WATERY_SOUL = 21109, + NPC_AIRY_SOUL = 21116, + + NPC_CREDIT_MARKER_EARTH = 21092, // quest objective npc's + NPC_CREDIT_MARKER_FIERY = 21094, + NPC_CREDIT_MARKER_WATER = 21095, + NPC_CREDIT_MARKER_AIR = 21096, + + EVENT_EARTH = 13513, // credit events + EVENT_FIERY = 13514, + EVENT_WATER = 13515, + EVENT_AIR = 13516, +}; + +struct npc_totem_of_spiritsAI : public ScriptedPetAI +{ + npc_totem_of_spiritsAI(Creature* pCreature) : ScriptedPetAI(pCreature) { Reset(); } + + void Reset() override {} + + void UpdateAI(const uint32 /*uiDiff*/) override {} + void AttackedBy(Unit* /*pAttacker*/) override {} + + void MoveInLineOfSight(Unit* pWho) override + { + if (pWho->GetTypeId() != TYPEID_UNIT) + { return; } + + // Use the LoS function to check for the souls in range due to the fact that pets do not support SummonedMovementInform() + uint32 uiEntry = pWho->GetEntry(); + if (uiEntry == NPC_EARTHEN_SOUL || uiEntry == NPC_FIERY_SOUL || uiEntry == NPC_WATERY_SOUL || uiEntry == NPC_AIRY_SOUL) + { + // Only when it's close to the totem + if (!pWho->IsWithinDistInMap(m_creature, 1.5f)) + { return; } + + switch (uiEntry) + { + case NPC_EARTHEN_SOUL: + pWho->CastSpell(m_creature, SPELL_EARTH_CAPTURED, true); + break; + case NPC_FIERY_SOUL: + pWho->CastSpell(m_creature, SPELL_FIERY_CAPTURED, true); + break; + case NPC_WATERY_SOUL: + pWho->CastSpell(m_creature, SPELL_WATER_CAPTURED, true); + break; + case NPC_AIRY_SOUL: + pWho->CastSpell(m_creature, SPELL_AIR_CAPTURED, true); + break; + } + + // Despawn the spirit soul after it's captured + ((Creature*)pWho)->ForcedDespawn(); + } + } + + void OwnerKilledUnit(Unit* pVictim) override + { + if (pVictim->GetTypeId() != TYPEID_UNIT) + { return; } + + uint32 uiEntry = pVictim->GetEntry(); + + // make elementals cast the sieve is only way to make it work properly, due to the spell target modes 22/7 + if (uiEntry == NPC_EARTH_SPIRIT || uiEntry == NPC_FIERY_SPIRIT || uiEntry == NPC_WATER_SPIRIT || uiEntry == NPC_AIR_SPIRIT) + { pVictim->CastSpell(pVictim, SPELL_ELEMENTAL_SIEVE, true); } + } + + void JustSummoned(Creature* pSummoned) override + { + // After summoning the spirit soul, make it move towards the totem + float fX, fY, fZ; + m_creature->GetContactPoint(pSummoned, fX, fY, fZ); + pSummoned->GetMotionMaster()->MovePoint(1, fX, fY, fZ); + } +}; + +CreatureAI* GetAI_npc_totem_of_spirits(Creature* pCreature) +{ + return new npc_totem_of_spiritsAI(pCreature); +} + +bool EffectDummyCreature_npc_totem_of_spirits(Unit* /*pCaster*/, uint32 uiSpellId, SpellEffectIndex uiEffIndex, Creature* pCreatureTarget, ObjectGuid /*originalCasterGuid*/) +{ + if (uiEffIndex != EFFECT_INDEX_0) + { return false; } + + switch (uiSpellId) + { + case SPELL_EARTH_CAPTURED: + { + pCreatureTarget->CastSpell(pCreatureTarget, SPELL_EARTH_CAPTURED_CREDIT, true); + return true; + } + case SPELL_FIERY_CAPTURED: + { + pCreatureTarget->CastSpell(pCreatureTarget, SPELL_FIERY_CAPTURED_CREDIT, true); + return true; + } + case SPELL_WATER_CAPTURED: + { + pCreatureTarget->CastSpell(pCreatureTarget, SPELL_WATER_CAPTURED_CREDIT, true); + return true; + } + case SPELL_AIR_CAPTURED: + { + pCreatureTarget->CastSpell(pCreatureTarget, SPELL_AIR_CAPTURED_CREDIT, true); + return true; + } + } + + return false; +} + +bool EffectAuraDummy_npc_totem_of_spirits(const Aura* pAura, bool bApply) +{ + if (pAura->GetId() != SPELL_ELEMENTAL_SIEVE) + { return true; } + + if (pAura->GetEffIndex() != EFFECT_INDEX_0) + { return true; } + + if (bApply) // possible it should be some visual effects, using "enraged soul" npc and "Cosmetic: ... soul" spell + { return true; } + + Creature* pCreature = (Creature*)pAura->GetTarget(); + Unit* pCaster = pAura->GetCaster(); + + // aura only affect the spirit totem, since this is the one that need to be in range. + // It is possible though, that player is the one who should actually have the aura + // and check for presense of spirit totem, but then we can't script the dummy. + if (!pCreature || !pCreature->IsPet() || !pCaster) + { return true; } + + // Summon the soul of the spirit and cast the visual + uint32 uiSoulEntry = 0; + switch (pCaster->GetEntry()) + { + case NPC_EARTH_SPIRIT: uiSoulEntry = NPC_EARTHEN_SOUL; break; + case NPC_FIERY_SPIRIT: uiSoulEntry = NPC_FIERY_SOUL; break; + case NPC_WATER_SPIRIT: uiSoulEntry = NPC_WATERY_SOUL; break; + case NPC_AIR_SPIRIT: uiSoulEntry = NPC_AIRY_SOUL; break; + } + + pCreature->CastSpell(pCreature, SPELL_CALL_TO_THE_SPIRITS, true); + pCreature->SummonCreature(uiSoulEntry, pCaster->GetPositionX(), pCaster->GetPositionY(), pCaster->GetPositionZ(), 0, TEMPSUMMON_TIMED_OOC_OR_CORPSE_DESPAWN, 10000); + + return true; +} + +bool ProcessEventId_event_spell_soul_captured_credit(uint32 uiEventId, Object* pSource, Object* /*pTarget*/, bool bIsStart) +{ + if (bIsStart && pSource->GetTypeId() == TYPEID_UNIT) + { + Player* pOwner = (Player*)((Creature*)pSource)->GetOwner(); + + if (!pOwner) + { return true; } + + switch (uiEventId) + { + case EVENT_EARTH: + pOwner->KilledMonsterCredit(NPC_CREDIT_MARKER_EARTH); + return true; + case EVENT_FIERY: + pOwner->KilledMonsterCredit(NPC_CREDIT_MARKER_FIERY); + return true; + case EVENT_WATER: + pOwner->KilledMonsterCredit(NPC_CREDIT_MARKER_WATER); + return true; + case EVENT_AIR: + pOwner->KilledMonsterCredit(NPC_CREDIT_MARKER_AIR); + return true; + } + } + + return false; +} + +/*##### +#npc_spawned_oronok_tornheart +#####*/ + +enum +{ + // texts + SAY_ORONOK_TOGETHER = -1000803, + SAY_ORONOK_READY = -1000804, + SAY_ORONOK_ELEMENTS = -1000805, + SAY_ORONOK_EPILOGUE_1 = -1000806, + SAY_TORLOK_EPILOGUE_2 = -1000807, + SAY_ORONOK_EPILOGUE_3 = -1000808, + SAY_EARTH_EPILOGUE_4 = -1000809, + SAY_FIRE_EPILOGUE_5 = -1000810, + SAY_EARTH_EPILOGUE_6 = -1000811, + SAY_ORONOK_EPILOGUE_7 = -1000812, + EMOTE_GIVE_WEAPONS = -1000813, + SAY_ORONOK_EPILOGUE_8 = -1000814, + + GOSSIP_ITEM_FIGHT = -3000109, + GOSSIP_TEXT_ID_ORONOK = 10421, + + // spells - some are already defined above + // SPELL_CHAIN_LIGHTNING = 16006, + SPELL_EARTHBIND_TOTEM = 15786, + // SPELL_FROST_SHOCK = 12548, + // SPELL_HEALING_WAVE = 12491, + + // npcs + NPC_ORONOK_TORN_HEART = 21685, + NPC_GROMTOR_SON_OF_ORONOK = 21687, + NPC_BORAK_SON_OF_ORONOK = 21686, + NPC_CYRUKH_THE_FIRELORD = 21181, + // NPC_EARTH_SPIRIT = 21050, + NPC_REDEEMED_SPIRIT_OF_EARTH = 21739, + NPC_REDEEMED_SPIRIT_OF_FIRE = 21740, + NPC_REDEEMED_SPIRIT_OF_AIR = 21738, + NPC_REDEEMED_SPIRIT_OF_WATER = 21741, + NPC_EARTHMENDER_TORLOK = 21024, + + GO_MARK_OF_KAELTHAS = 185170, + + QUEST_CIPHER_OF_DAMNATION = 10588, + + POINT_ID_ATTACK_READY = 1, + POINT_ID_ELEMENTS = 2, + POINT_ID_EPILOGUE = 3, +}; + +static const DialogueEntry aOutroDialogue[] = +{ + {QUEST_CIPHER_OF_DAMNATION, 0, 1000}, + {NPC_CYRUKH_THE_FIRELORD, 0, 0}, + {NPC_EARTHMENDER_TORLOK, 0, 1000}, + {SAY_ORONOK_EPILOGUE_1, NPC_ORONOK_TORN_HEART, 5000}, + {SAY_TORLOK_EPILOGUE_2, NPC_EARTHMENDER_TORLOK, 5000}, + {NPC_REDEEMED_SPIRIT_OF_EARTH, 0, 5000}, + {SAY_ORONOK_EPILOGUE_3, NPC_ORONOK_TORN_HEART, 5000}, + {SAY_EARTH_EPILOGUE_4, NPC_REDEEMED_SPIRIT_OF_EARTH, 5000}, + {SAY_FIRE_EPILOGUE_5, NPC_REDEEMED_SPIRIT_OF_FIRE, 14000}, + {SAY_EARTH_EPILOGUE_6, NPC_REDEEMED_SPIRIT_OF_EARTH, 6000}, + {SAY_ORONOK_EPILOGUE_7, NPC_ORONOK_TORN_HEART, 6000}, + {EMOTE_GIVE_WEAPONS, NPC_ORONOK_TORN_HEART, 6000}, + {SAY_ORONOK_EPILOGUE_8, NPC_ORONOK_TORN_HEART, 10000}, + {NPC_ORONOK_TORN_HEART, 0, 0}, + {0, 0, 0}, +}; + +struct EventLocations +{ + float m_fX, m_fY, m_fZ, m_fO; +}; + +const static EventLocations aDamnationLocations[] = +{ + { -3605.09f, 1885.47f, 47.24f, 1.81f}, // 0 fire spirit summon loc + { -3600.68f, 1886.58f, 47.24f, 1.81f}, // 1 earth spirit summon loc + { -3597.19f, 1887.46f, 47.24f, 1.77f}, // 2 water spirit summon loc + { -3593.18f, 1888.27f, 47.24f, 1.77f}, // 3 air spirit summon loc + { -3595.36f, 1869.78f, 47.24f}, // 4 fight ready move loc + { -3635.90f, 1860.94f, 52.93f}, // 5 elementals move loc + { -3599.71f, 1897.94f, 47.24f} // 6 epilogue move loc +}; + +struct npc_spawned_oronok_tornheartAI : public ScriptedAI, private DialogueHelper +{ + npc_spawned_oronok_tornheartAI(Creature* pCreature) : ScriptedAI(pCreature), + DialogueHelper(aOutroDialogue) + { + Reset(); + StartNextDialogueText(QUEST_CIPHER_OF_DAMNATION); + } + + uint32 m_uiLightningTimer; + uint32 m_uiTotemTimer; + uint32 m_uiFrostTimer; + uint32 m_uiHealTimer; + + ObjectGuid m_torlokGuid; + ObjectGuid m_fireSpiritGuid; + ObjectGuid m_earthSpiritGuid; + ObjectGuid m_borakGuid; + ObjectGuid m_gromtorGuid; + ObjectGuid m_cyrukhGuid; + + bool m_bHasAttackStart; + + void Reset() override + { + m_uiLightningTimer = 15000; + m_uiTotemTimer = 10000; + m_uiFrostTimer = 20000; + m_uiHealTimer = 8000; + + m_bHasAttackStart = false; + } + + void JustDidDialogueStep(int32 iEntry) override + { + switch (iEntry) + { + case NPC_CYRUKH_THE_FIRELORD: + // Set them in motion + m_creature->SetWalk(false); + m_creature->GetMotionMaster()->MovePoint(POINT_ID_ATTACK_READY, aDamnationLocations[4].m_fX, aDamnationLocations[4].m_fY, aDamnationLocations[4].m_fZ); + if (Creature* pBorak = GetClosestCreatureWithEntry(m_creature, NPC_BORAK_SON_OF_ORONOK, 10.0f)) + { + m_borakGuid = pBorak->GetObjectGuid(); + pBorak->GetMotionMaster()->MoveFollow(m_creature, 5.0f, -M_PI_F / 2); + } + if (Creature* pGromtor = GetClosestCreatureWithEntry(m_creature, NPC_GROMTOR_SON_OF_ORONOK, 10.0f)) + { + m_gromtorGuid = pGromtor->GetObjectGuid(); + pGromtor->GetMotionMaster()->MoveFollow(m_creature, 5.0f, M_PI_F / 2); + } + break; + case NPC_EARTHMENDER_TORLOK: + if (Creature* pTorlok = GetClosestCreatureWithEntry(m_creature, NPC_EARTHMENDER_TORLOK, 25.0f)) + { + m_torlokGuid = pTorlok->GetObjectGuid(); + m_creature->SetFacingToObject(pTorlok); + } + break; + case NPC_REDEEMED_SPIRIT_OF_EARTH: + m_creature->SetFacingTo(4.9f); + m_creature->SummonCreature(NPC_REDEEMED_SPIRIT_OF_FIRE, aDamnationLocations[0].m_fX, aDamnationLocations[0].m_fY, aDamnationLocations[0].m_fZ, aDamnationLocations[0].m_fO, TEMPSUMMON_TIMED_DESPAWN, 32000); + m_creature->SummonCreature(NPC_REDEEMED_SPIRIT_OF_EARTH, aDamnationLocations[1].m_fX, aDamnationLocations[1].m_fY, aDamnationLocations[1].m_fZ, aDamnationLocations[1].m_fO, TEMPSUMMON_TIMED_DESPAWN, 32000); + m_creature->SummonCreature(NPC_REDEEMED_SPIRIT_OF_WATER, aDamnationLocations[2].m_fX, aDamnationLocations[2].m_fY, aDamnationLocations[2].m_fZ, aDamnationLocations[2].m_fO, TEMPSUMMON_TIMED_DESPAWN, 32000); + m_creature->SummonCreature(NPC_REDEEMED_SPIRIT_OF_AIR, aDamnationLocations[3].m_fX, aDamnationLocations[3].m_fY, aDamnationLocations[3].m_fZ, aDamnationLocations[3].m_fO, TEMPSUMMON_TIMED_DESPAWN, 32000); + break; + case SAY_ORONOK_EPILOGUE_7: + if (Creature* pTorlok = m_creature->GetMap()->GetCreature(m_torlokGuid)) + { m_creature->SetFacingToObject(pTorlok); } + break; + case NPC_ORONOK_TORN_HEART: + if (GameObject* pMark = GetClosestGameObjectWithEntry(m_creature, GO_MARK_OF_KAELTHAS, 30.0f)) + { + pMark->SetRespawnTime(5 * MINUTE); + pMark->Refresh(); + } + if (Creature* pBorak = m_creature->GetMap()->GetCreature(m_borakGuid)) + { pBorak->ForcedDespawn(); } + if (Creature* pGromtor = m_creature->GetMap()->GetCreature(m_gromtorGuid)) + { pGromtor->ForcedDespawn(); } + m_creature->ForcedDespawn(); + break; + } + } + + Creature* GetSpeakerByEntry(uint32 uiEntry) override + { + switch (uiEntry) + { + case NPC_ORONOK_TORN_HEART: return m_creature; + case NPC_EARTHMENDER_TORLOK: return m_creature->GetMap()->GetCreature(m_torlokGuid); + case NPC_REDEEMED_SPIRIT_OF_EARTH: return m_creature->GetMap()->GetCreature(m_earthSpiritGuid); + case NPC_REDEEMED_SPIRIT_OF_FIRE: return m_creature->GetMap()->GetCreature(m_fireSpiritGuid); + + default: + return NULL; + } + } + + void Aggro(Unit* pWho) override + { + if (!m_bHasAttackStart && pWho->GetEntry() == NPC_EARTH_SPIRIT) + { + // Cyrukh starts to attack + if (Creature* pCyrukh = m_creature->GetMap()->GetCreature(m_cyrukhGuid)) + { + pCyrukh->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_PASSIVE); + pCyrukh->AI()->AttackStart(m_creature); + AttackStart(pCyrukh); + m_bHasAttackStart = true; + } + } + } + + void JustSummoned(Creature* pSummoned) override + { + switch (pSummoned->GetEntry()) + { + case NPC_REDEEMED_SPIRIT_OF_FIRE: + m_fireSpiritGuid = pSummoned->GetObjectGuid(); + break; + case NPC_REDEEMED_SPIRIT_OF_EARTH: + m_earthSpiritGuid = pSummoned->GetObjectGuid(); + break; + } + } + + void EnterEvadeMode() override + { + m_creature->RemoveAllAurasOnEvade(); + m_creature->DeleteThreatList(); + m_creature->CombatStop(true); + m_creature->LoadCreatureAddon(true); + + m_creature->SetLootRecipient(NULL); + + Reset(); + + if (!m_creature->IsAlive()) + { return; } + + if (Creature* pCyrukh = m_creature->GetMap()->GetCreature(m_cyrukhGuid)) + { + if (!pCyrukh->IsAlive()) + { m_creature->GetMotionMaster()->MovePoint(POINT_ID_EPILOGUE, aDamnationLocations[6].m_fX, aDamnationLocations[6].m_fY, aDamnationLocations[6].m_fZ); } + } + else + { + script_error_log("Npc %u couldn't be found or something really bad happened. Epilogue event for quest %u will stop.", NPC_CYRUKH_THE_FIRELORD, QUEST_CIPHER_OF_DAMNATION); + m_creature->GetMotionMaster()->MoveTargetedHome(); + } + } + + void MovementInform(uint32 uiMotionType, uint32 uiPointId) override + { + if (uiMotionType != POINT_MOTION_TYPE) + { return; } + + switch (uiPointId) + { + case POINT_ID_ATTACK_READY: + // Get Cyrukh guid now, because we are at a closer distance + if (Creature* pCyrukh = GetClosestCreatureWithEntry(m_creature, NPC_CYRUKH_THE_FIRELORD, 70.0f)) + { m_cyrukhGuid = pCyrukh->GetObjectGuid(); } + + m_creature->SetFlag(UNIT_NPC_FLAGS, UNIT_NPC_FLAG_GOSSIP); + DoScriptText(SAY_ORONOK_READY, m_creature); + break; + case POINT_ID_ELEMENTS: + // Attack the closest earth element + if (Creature* pElement = GetClosestCreatureWithEntry(m_creature, NPC_EARTH_SPIRIT, 50.0f)) + { AttackStart(pElement); } + break; + case POINT_ID_EPILOGUE: + StartNextDialogueText(NPC_EARTHMENDER_TORLOK); + break; + } + } + + void UpdateAI(const uint32 uiDiff) override + { + DialogueUpdate(uiDiff); + + if (!m_creature->SelectHostileTarget() || !m_creature->getVictim()) + { return; } + + if (m_uiLightningTimer < uiDiff) + { + if (Unit* pTarget = m_creature->SelectAttackingTarget(ATTACKING_TARGET_RANDOM, 0)) + { + if (DoCastSpellIfCan(pTarget, SPELL_CHAIN_LIGHTNING) == CAST_OK) + { m_uiLightningTimer = urand(9000, 15000); } + } + } + else + { m_uiLightningTimer -= uiDiff; } + + if (m_uiTotemTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature, SPELL_EARTHBIND_TOTEM) == CAST_OK) + { m_uiTotemTimer = urand(40000, 60000); } + } + else + { m_uiTotemTimer -= uiDiff; } + + if (m_uiFrostTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature->getVictim(), SPELL_FROST_SHOCK) == CAST_OK) + { m_uiFrostTimer = urand(14000, 18000); } + } + else + { m_uiFrostTimer -= uiDiff; } + + if (m_uiHealTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature, SPELL_HEALING_WAVE) == CAST_OK) + { m_uiHealTimer = urand(6000, 10000); } + } + else + { m_uiHealTimer -= uiDiff; } + + DoMeleeAttackIfReady(); + } +}; + +CreatureAI* GetAI_npc_spawned_oronok_tornheart(Creature* pCreature) +{ + return new npc_spawned_oronok_tornheartAI(pCreature); +} + +bool GossipHello_npc_spawned_oronok_tornheart(Player* pPlayer, Creature* pCreature) +{ + if (pPlayer->GetQuestStatus(QUEST_CIPHER_OF_DAMNATION) == QUEST_STATUS_INCOMPLETE) + { + pPlayer->ADD_GOSSIP_ITEM_ID(GOSSIP_ICON_CHAT, GOSSIP_ITEM_FIGHT, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 1); + pPlayer->SEND_GOSSIP_MENU(GOSSIP_TEXT_ID_ORONOK, pCreature->GetObjectGuid()); + } + else + { pPlayer->SEND_GOSSIP_MENU(pPlayer->GetGossipTextId(pCreature), pCreature->GetObjectGuid()); } + + return true; +} + +bool GossipSelect_npc_spawned_oronok_tornheart(Player* pPlayer, Creature* pCreature, uint32 /*uiSender*/, uint32 uiAction) +{ + if (uiAction == GOSSIP_ACTION_INFO_DEF + 1) + { + // Note: this movement expects MMaps. + DoScriptText(SAY_ORONOK_ELEMENTS, pCreature); + pCreature->GetMotionMaster()->MovePoint(POINT_ID_ELEMENTS, aDamnationLocations[5].m_fX, aDamnationLocations[5].m_fY, aDamnationLocations[5].m_fZ); + pCreature->RemoveFlag(UNIT_NPC_FLAGS, UNIT_NPC_FLAG_GOSSIP); + + pPlayer->CLOSE_GOSSIP_MENU(); + } + + return true; +} + +/*###### +## npc_domesticated_felboar +######*/ + +enum +{ + EMOTE_SNIFF_AIR = -1000907, + EMOTE_START_DIG = -1000908, + EMOTE_SQUEAL = -1000909, + + SPELL_SHADOWMOON_TUBER = 36462, + SPELL_SPECIAL_UNARMED = 33334, + SPELL_TUBER_WHISTLE = 36652, + + NPC_DOMESTICATED_FELBOAR = 21195, + NPC_SHADOWMOON_TUBER_NODE = 21347, + GO_SHADOWMOON_TUBER_MOUND = 184701, +}; + +struct npc_domesticated_felboarAI : public ScriptedAI +{ + npc_domesticated_felboarAI(Creature* pCreature) : ScriptedAI(pCreature) { Reset(); } + + uint32 m_uiTuberTimer; + uint8 m_uiTuberStage; + + void Reset() override + { + m_uiTuberTimer = 0; + m_uiTuberStage = 0; + } + + void MovementInform(uint32 uiMoveType, uint32 uiPointId) override + { + if (uiMoveType != POINT_MOTION_TYPE || !uiPointId) + { return; } + + if (DoCastSpellIfCan(m_creature, SPELL_SPECIAL_UNARMED) == CAST_OK) + { + DoScriptText(EMOTE_START_DIG, m_creature); + m_uiTuberTimer = 2000; + } + } + + void ReceiveAIEvent(AIEventType eventType, Creature* pSender, Unit* pInvoker, uint32 /*uiMiscValue*/) override + { + if (eventType == AI_EVENT_START_EVENT && pInvoker->GetTypeId() == TYPEID_PLAYER) + { + DoScriptText(EMOTE_SNIFF_AIR, m_creature); + + float fX, fY, fZ; + m_creature->SetWalk(false); + m_creature->GetMotionMaster()->MoveIdle(); + pSender->GetContactPoint(m_creature, fX, fY, fZ); + m_creature->GetMotionMaster()->MovePoint(1, fX, fY, fZ); + } + } + + void UpdateAI(const uint32 uiDiff) override + { + if (m_uiTuberTimer) + { + if (m_uiTuberTimer <= uiDiff) + { + switch (m_uiTuberStage) + { + case 0: + if (DoCastSpellIfCan(m_creature, SPELL_SPECIAL_UNARMED) == CAST_OK) + { m_uiTuberTimer = 2000; } + break; + case 1: + if (DoCastSpellIfCan(m_creature, SPELL_SHADOWMOON_TUBER) == CAST_OK) + { + // Despawn current tuber + if (GameObject* pTuber = GetClosestGameObjectWithEntry(m_creature, GO_SHADOWMOON_TUBER_MOUND, 3.0f)) + { pTuber->SetLootState(GO_JUST_DEACTIVATED); } + + DoScriptText(EMOTE_SQUEAL, m_creature); + m_uiTuberTimer = 2000; + } + break; + case 2: + EnterEvadeMode(); + break; + } + ++m_uiTuberStage; + } + else + { m_uiTuberTimer -= uiDiff; } + } + + if (!m_creature->SelectHostileTarget() || !m_creature->getVictim()) + { return; } + + DoMeleeAttackIfReady(); + } +}; + +CreatureAI* GetAI_npc_domesticated_felboar(Creature* pCreature) +{ + return new npc_domesticated_felboarAI(pCreature); +} + +bool EffectDummyCreature_npc_shadowmoon_tuber_node(Unit* pCaster, uint32 uiSpellId, SpellEffectIndex uiEffIndex, Creature* pCreatureTarget, ObjectGuid /*originalCasterGuid*/) +{ + // always check spellid and effectindex + if (uiSpellId == SPELL_TUBER_WHISTLE && uiEffIndex == EFFECT_INDEX_0) + { + if (pCreatureTarget->GetEntry() == NPC_SHADOWMOON_TUBER_NODE) + { + // Check if tuber mound exists or it's spawned + GameObject* pTuber = GetClosestGameObjectWithEntry(pCreatureTarget, GO_SHADOWMOON_TUBER_MOUND, 1.0f); + if (!pTuber || !pTuber->isSpawned()) + { return true; } + + // Call nearby felboar + if (Creature* pBoar = GetClosestCreatureWithEntry(pCreatureTarget, NPC_DOMESTICATED_FELBOAR, 40.0f)) + { pCreatureTarget->AI()->SendAIEvent(AI_EVENT_START_EVENT, pCaster, pBoar); } + } + + // always return true when we are handling this spell and effect + return true; + } + + return false; +} + +/*###### +## npc_veneratus_spawn_node +######*/ + +enum +{ + SAY_VENERATUS_SPAWN = -1000579, + + NPC_VENERATUS = 20427, + NPC_SPIRIT_HUNTER = 21332, +}; + +struct npc_veneratus_spawn_nodeAI : public Scripted_NoMovementAI +{ + npc_veneratus_spawn_nodeAI(Creature* pCreature) : Scripted_NoMovementAI(pCreature) { Reset(); } + + void Reset() override { } + + void MoveInLineOfSight(Unit* pWho) override + { + // Check for the spirit hunter in order to spawn Veneratus; this will replace missing spells 36614 (dummy periodic spell) and 36616 (summon spell) + if (pWho->GetEntry() == NPC_SPIRIT_HUNTER && m_creature->IsWithinDistInMap(pWho, 40.0f) && m_creature->IsWithinLOSInMap(pWho)) + { + DoScriptText(SAY_VENERATUS_SPAWN, pWho); + DoSpawnCreature(NPC_VENERATUS, 0, 0, 0, 0, TEMPSUMMON_TIMED_OOC_OR_DEAD_DESPAWN, 60000); + m_creature->ForcedDespawn(); + } + } + + void UpdateAI(const uint32 uiDiff) override { } +}; + +CreatureAI* GetAI_npc_veneratus_spawn_node(Creature* pCreature) +{ + return new npc_veneratus_spawn_nodeAI(pCreature); +} + +void AddSC_shadowmoon_valley() +{ + Script* pNewScript; + + pNewScript = new Script; + pNewScript->Name = "mob_mature_netherwing_drake"; + pNewScript->GetAI = &GetAI_mob_mature_netherwing_drake; + pNewScript->RegisterSelf(); + + pNewScript = new Script; + pNewScript->Name = "mob_enslaved_netherwing_drake"; + pNewScript->GetAI = &GetAI_mob_enslaved_netherwing_drake; + pNewScript->RegisterSelf(); + + pNewScript = new Script; + pNewScript->Name = "npc_dragonmaw_peon"; + pNewScript->GetAI = &GetAI_npc_dragonmaw_peon; + pNewScript->pEffectDummyNPC = &EffectDummyCreature_npc_dragonmaw_peon; + pNewScript->RegisterSelf(); + + pNewScript = new Script; + pNewScript->Name = "npc_wilda"; + pNewScript->GetAI = &GetAI_npc_wilda; + pNewScript->pQuestAcceptNPC = &QuestAccept_npc_wilda; + pNewScript->RegisterSelf(); + + pNewScript = new Script; + pNewScript->Name = "npc_lord_illidan_stormrage"; + pNewScript->GetAI = &GetAI_npc_lord_illidan_stormrage; + pNewScript->RegisterSelf(); + + pNewScript = new Script; + pNewScript->Name = "mob_torloth"; + pNewScript->GetAI = &GetAI_mob_torloth; + pNewScript->RegisterSelf(); + + pNewScript = new Script; + pNewScript->Name = "npc_totem_of_spirits"; + pNewScript->GetAI = &GetAI_npc_totem_of_spirits; + pNewScript->pEffectDummyNPC = &EffectDummyCreature_npc_totem_of_spirits; + pNewScript->pEffectAuraDummy = &EffectAuraDummy_npc_totem_of_spirits; + pNewScript->RegisterSelf(); + + pNewScript = new Script; + pNewScript->Name = "event_spell_soul_captured_credit"; + pNewScript->pProcessEventId = &ProcessEventId_event_spell_soul_captured_credit; + pNewScript->RegisterSelf(); + + pNewScript = new Script; + pNewScript->Name = "go_crystal_prison"; + pNewScript->pQuestAcceptGO = &GOQuestAccept_GO_crystal_prison; + pNewScript->RegisterSelf(); + + pNewScript = new Script; + pNewScript->Name = "npc_spawned_oronok_tornheart"; + pNewScript->GetAI = &GetAI_npc_spawned_oronok_tornheart; + pNewScript->pGossipHello = &GossipHello_npc_spawned_oronok_tornheart; + pNewScript->pGossipSelect = &GossipSelect_npc_spawned_oronok_tornheart; + pNewScript->RegisterSelf(); + + pNewScript = new Script; + pNewScript->Name = "npc_domesticated_felboar"; + pNewScript->GetAI = &GetAI_npc_domesticated_felboar; + pNewScript->RegisterSelf(); + + pNewScript = new Script; + pNewScript->Name = "npc_shadowmoon_tuber_node"; + pNewScript->pEffectDummyNPC = &EffectDummyCreature_npc_shadowmoon_tuber_node; + pNewScript->RegisterSelf(); + + pNewScript = new Script; + pNewScript->Name = "npc_veneratus_spawn_node"; + pNewScript->GetAI = &GetAI_npc_veneratus_spawn_node; + pNewScript->RegisterSelf(); +} diff --git a/src/modules/SD2/scripts/outland/shattrath_city.cpp b/src/modules/SD2/scripts/outland/shattrath_city.cpp new file mode 100644 index 000000000..ef00e0150 --- /dev/null +++ b/src/modules/SD2/scripts/outland/shattrath_city.cpp @@ -0,0 +1,654 @@ +/** + * ScriptDev2 is an extension for mangos providing enhanced features for + * area triggers, creatures, game objects, instances, items, and spells beyond + * the default database scripting in mangos. + * + * Copyright (C) 2006-2013 ScriptDev2 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * World of Warcraft, and all World of Warcraft or Warcraft art, images, + * and lore are copyrighted by Blizzard Entertainment, Inc. + */ + +/* ScriptData +SDName: Shattrath_City +SD%Complete: 100 +SDComment: Quest support: 10004, 10231. +SDCategory: Shattrath City +EndScriptData */ + +/* ContentData +npc_dirty_larry +npc_ishanah +npc_khadgars_servant +npc_salsalabim +EndContentData */ + +#include "precompiled.h" +#include "escort_ai.h" + +enum +{ + SAY_START = -1000274, + SAY_COUNT = -1000275, + SAY_COUNT_1 = -1000276, + SAY_COUNT_2 = -1000277, + SAY_ATTACK = -1000278, + SAY_GIVEUP = -1000279, + QUEST_WHAT_BOOK = 10231, + ENTRY_CREEPJACK = 19726, + ENTRY_MALONE = 19725, + GOSSIP_ITEM_BOOK = -3000105, +}; + +struct npc_dirty_larryAI : public ScriptedAI +{ + npc_dirty_larryAI(Creature* pCreature) : ScriptedAI(pCreature) + { + m_uiNpcFlags = pCreature->GetUInt32Value(UNIT_NPC_FLAGS); + Reset(); + } + + uint32 m_uiNpcFlags; + + ObjectGuid m_creepjackGuid; + ObjectGuid m_maloneGuid; + ObjectGuid m_playerGuid; + + bool bEvent; + bool bActiveAttack; + + uint32 m_uiSayTimer; + uint32 m_uiStep; + + void Reset() override + { + m_creature->SetUInt32Value(UNIT_NPC_FLAGS, m_uiNpcFlags); + + m_playerGuid.Clear(); + m_creepjackGuid.Clear(); + m_maloneGuid.Clear(); + + bEvent = false; + bActiveAttack = false; + + m_uiSayTimer = 1000; + m_uiStep = 0; + + // expect database to have correct faction (1194) and then only unit flags set/remove needed + m_creature->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE); + m_creature->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_PASSIVE); + } + + void SetRuffies(ObjectGuid guid, bool bAttack, bool bReset) + { + Creature* pCreature = m_creature->GetMap()->GetCreature(guid); + + if (!pCreature) + { return; } + + if (bReset) + { + if (!pCreature->IsInEvadeMode() && pCreature->IsAlive()) + { pCreature->AI()->EnterEvadeMode(); } + + pCreature->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE); + pCreature->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_PASSIVE); + } + else + { + pCreature->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE); + pCreature->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_PASSIVE); + + if (!pCreature->IsAlive()) + { return; } + + pCreature->SetStandState(UNIT_STAND_STATE_STAND); + + if (bAttack) + { + if (Player* pPlayer = m_creature->GetMap()->GetPlayer(m_playerGuid)) + { + if (pPlayer->IsAlive()) + { pCreature->AI()->AttackStart(pPlayer); } + } + } + } + } + + void StartEvent(Player* pPlayer) + { + m_playerGuid = pPlayer->GetObjectGuid(); + + m_creature->SetUInt32Value(UNIT_NPC_FLAGS, UNIT_NPC_FLAG_NONE); + + m_creature->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE); + m_creature->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_PASSIVE); + + if (Creature* pCreepjack = GetClosestCreatureWithEntry(m_creature, ENTRY_CREEPJACK, 20.0f)) + { m_creepjackGuid = pCreepjack->GetObjectGuid(); } + + if (Creature* pMalone = GetClosestCreatureWithEntry(m_creature, ENTRY_MALONE, 20.0f)) + { m_maloneGuid = pMalone->GetObjectGuid(); } + + bEvent = true; + } + + uint32 NextStep(uint32 uiStep) + { + Player* pPlayer = m_creature->GetMap()->GetPlayer(m_playerGuid); + + if (!pPlayer) + { + SetRuffies(m_creepjackGuid, false, true); + SetRuffies(m_maloneGuid, false, true); + EnterEvadeMode(); + return 0; + } + + switch (uiStep) + { + case 1: + DoScriptText(SAY_START, m_creature, pPlayer); + SetRuffies(m_creepjackGuid, false, false); + SetRuffies(m_maloneGuid, false, false); + return 3000; + case 2: DoScriptText(SAY_COUNT, m_creature, pPlayer); return 5000; + case 3: DoScriptText(SAY_COUNT_1, m_creature, pPlayer); return 3000; + case 4: DoScriptText(SAY_COUNT_2, m_creature, pPlayer); return 3000; + case 5: DoScriptText(SAY_ATTACK, m_creature, pPlayer); return 3000; + case 6: + if (!m_creature->IsInCombat() && pPlayer->IsAlive()) + { AttackStart(pPlayer); } + + SetRuffies(m_creepjackGuid, true, false); + SetRuffies(m_maloneGuid, true, false); + bActiveAttack = true; + return 2000; + default: + return 0; + } + } + + void AttackedBy(Unit* pAttacker) override + { + if (m_creature->getVictim()) + { return; } + + if (!bActiveAttack) + { return; } + + AttackStart(pAttacker); + } + + void DamageTaken(Unit* /*pDoneBy*/, uint32& uiDamage) override + { + if (uiDamage < m_creature->GetHealth()) + { return; } + + // damage will kill, this is pretty much the same as 1%HP left + if (bEvent) + { + uiDamage = 0; + + if (Player* pPlayer = m_creature->GetMap()->GetPlayer(m_playerGuid)) + { + DoScriptText(SAY_GIVEUP, m_creature, pPlayer); + pPlayer->GroupEventHappens(QUEST_WHAT_BOOK, m_creature); + } + + SetRuffies(m_creepjackGuid, false, true); + SetRuffies(m_maloneGuid, false, true); + EnterEvadeMode(); + } + } + + void UpdateAI(const uint32 diff) override + { + if (bEvent && !bActiveAttack) + { + if (m_uiSayTimer < diff) + { m_uiSayTimer = NextStep(++m_uiStep); } + else + { m_uiSayTimer -= diff; } + } + + if (!m_creature->SelectHostileTarget() || !m_creature->getVictim()) + { return; } + + DoMeleeAttackIfReady(); + } +}; + +bool GossipHello_npc_dirty_larry(Player* pPlayer, Creature* pCreature) +{ + if (pCreature->IsQuestGiver()) + { pPlayer->PrepareQuestMenu(pCreature->GetObjectGuid()); } + + if (pPlayer->GetQuestStatus(QUEST_WHAT_BOOK) == QUEST_STATUS_INCOMPLETE) + { pPlayer->ADD_GOSSIP_ITEM_ID(GOSSIP_ICON_CHAT, GOSSIP_ITEM_BOOK, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 1); } + + pPlayer->SEND_GOSSIP_MENU(pPlayer->GetGossipTextId(pCreature), pCreature->GetObjectGuid()); + return true; +} + +bool GossipSelect_npc_dirty_larry(Player* pPlayer, Creature* pCreature, uint32 /*uiSender*/, uint32 uiAction) +{ + if (uiAction == GOSSIP_ACTION_INFO_DEF + 1) + { + if (npc_dirty_larryAI* pLarryAI = dynamic_cast(pCreature->AI())) + { pLarryAI->StartEvent(pPlayer); } + + pPlayer->CLOSE_GOSSIP_MENU(); + } + + return true; +} + +CreatureAI* GetAI_npc_dirty_larry(Creature* pCreature) +{ + return new npc_dirty_larryAI(pCreature); +} + +/*###### +## npc_khadgars_servant +######*/ + +enum +{ + SAY_KHAD_START = -1000489, + SAY_KHAD_SERV_0 = -1000234, + + SAY_KHAD_SERV_1 = -1000235, + SAY_KHAD_SERV_2 = -1000236, + SAY_KHAD_SERV_3 = -1000237, + SAY_KHAD_SERV_4 = -1000238, + + SAY_KHAD_SERV_5 = -1000239, + SAY_KHAD_SERV_6 = -1000240, + SAY_KHAD_SERV_7 = -1000241, + + SAY_KHAD_SERV_8 = -1000242, + SAY_KHAD_SERV_9 = -1000243, + SAY_KHAD_SERV_10 = -1000244, + SAY_KHAD_SERV_11 = -1000245, + + SAY_KHAD_SERV_12 = -1000246, + SAY_KHAD_SERV_13 = -1000247, + + SAY_KHAD_SERV_14 = -1000248, + SAY_KHAD_SERV_15 = -1000249, + SAY_KHAD_SERV_16 = -1000250, + SAY_KHAD_SERV_17 = -1000251, + + SAY_KHAD_SERV_18 = -1000252, + SAY_KHAD_SERV_19 = -1000253, + SAY_KHAD_SERV_20 = -1000254, + SAY_KHAD_SERV_21 = -1000255, + + SAY_KHAD_INJURED = -1000490, + SAY_KHAD_MIND_YOU = -1000491, + SAY_KHAD_MIND_ALWAYS = -1000492, + SAY_KHAD_ALDOR_GREET = -1000493, + SAY_KHAD_SCRYER_GREET = -1000494, + SAY_KHAD_HAGGARD = -1000495, + + NPC_KHADGAR = 18166, + NPC_SHANIR = 18597, + NPC_IZZARD = 18622, + NPC_ADYRIA = 18596, + NPC_ANCHORITE = 19142, + NPC_ARCANIST = 18547, + NPC_HAGGARD = 19684, + + QUEST_CITY_LIGHT = 10211 +}; + +struct npc_khadgars_servantAI : public npc_escortAI +{ + npc_khadgars_servantAI(Creature* pCreature) : npc_escortAI(pCreature) + { + if (pCreature->GetOwner() && pCreature->GetOwner()->GetTypeId() == TYPEID_PLAYER) + { Start(false, (Player*)pCreature->GetOwner()); } + else + { script_error_log("npc_khadgars_servant can not obtain owner or owner is not a player."); } + + Reset(); + } + + uint32 m_uiPointId; + uint32 m_uiTalkTimer; + uint32 m_uiTalkCount; + uint32 m_uiRandomTalkCooldown; + + void Reset() override + { + m_uiTalkTimer = 2500; + m_uiTalkCount = 0; + m_uiPointId = 0; + m_uiRandomTalkCooldown = 0; + } + + void MoveInLineOfSight(Unit* pWho) override + { + if (!m_uiRandomTalkCooldown && pWho->GetTypeId() == TYPEID_UNIT && m_creature->IsWithinDistInMap(pWho, 10.0f)) + { + switch (pWho->GetEntry()) + { + case NPC_HAGGARD: + if (Player* pPlayer = GetPlayerForEscort()) + { DoScriptText(SAY_KHAD_HAGGARD, pWho, pPlayer); } + m_uiRandomTalkCooldown = 7500; + break; + case NPC_ANCHORITE: + if (Player* pPlayer = GetPlayerForEscort()) + { DoScriptText(SAY_KHAD_ALDOR_GREET, pWho, pPlayer); } + m_uiRandomTalkCooldown = 7500; + break; + case NPC_ARCANIST: + if (Player* pPlayer = GetPlayerForEscort()) + { DoScriptText(SAY_KHAD_SCRYER_GREET, pWho, pPlayer); } + m_uiRandomTalkCooldown = 7500; + break; + } + } + } + + void WaypointStart(uint32 uiPointId) override + { + if (uiPointId == 2) + { DoScriptText(SAY_KHAD_SERV_0, m_creature); } + } + + void WaypointReached(uint32 uiPointId) override + { + m_uiPointId = uiPointId; + + switch (uiPointId) + { + case 0: + if (Creature* pKhadgar = GetClosestCreatureWithEntry(m_creature, NPC_KHADGAR, 10.0f)) + { DoScriptText(SAY_KHAD_START, pKhadgar); } + break; + case 5: + case 24: + case 50: + case 63: + case 74: + case 75: + SetEscortPaused(true); + break; + case 34: + if (Creature* pIzzard = GetClosestCreatureWithEntry(m_creature, NPC_IZZARD, 10.0f)) + { DoScriptText(SAY_KHAD_MIND_YOU, pIzzard); } + break; + case 35: + if (Creature* pAdyria = GetClosestCreatureWithEntry(m_creature, NPC_ADYRIA, 10.0f)) + { DoScriptText(SAY_KHAD_MIND_ALWAYS, pAdyria); } + break; + } + } + + void UpdateEscortAI(const uint32 uiDiff) override + { + if (m_uiRandomTalkCooldown) + { + if (m_uiRandomTalkCooldown <= uiDiff) + { m_uiRandomTalkCooldown = 0; } + else + { m_uiRandomTalkCooldown -= uiDiff; } + } + + if (HasEscortState(STATE_ESCORT_PAUSED)) + { + if (m_uiTalkTimer <= uiDiff) + { + ++m_uiTalkCount; + m_uiTalkTimer = 7500; + + Player* pPlayer = GetPlayerForEscort(); + + if (!pPlayer) + { return; } + + switch (m_uiPointId) + { + case 5: // to lower city + { + switch (m_uiTalkCount) + { + case 1: + DoScriptText(SAY_KHAD_SERV_1, m_creature, pPlayer); + break; + case 2: + DoScriptText(SAY_KHAD_SERV_2, m_creature, pPlayer); + break; + case 3: + DoScriptText(SAY_KHAD_SERV_3, m_creature, pPlayer); + break; + case 4: + DoScriptText(SAY_KHAD_SERV_4, m_creature, pPlayer); + SetEscortPaused(false); + break; + } + break; + } + case 24: // in lower city + { + switch (m_uiTalkCount) + { + case 5: + if (Creature* pShanir = GetClosestCreatureWithEntry(m_creature, NPC_SHANIR, 15.0f)) + { DoScriptText(SAY_KHAD_INJURED, pShanir, pPlayer); } + + DoScriptText(SAY_KHAD_SERV_5, m_creature, pPlayer); + break; + case 6: + DoScriptText(SAY_KHAD_SERV_6, m_creature, pPlayer); + break; + case 7: + DoScriptText(SAY_KHAD_SERV_7, m_creature, pPlayer); + SetEscortPaused(false); + break; + } + break; + } + case 50: // outside + { + switch (m_uiTalkCount) + { + case 8: + DoScriptText(SAY_KHAD_SERV_8, m_creature, pPlayer); + break; + case 9: + DoScriptText(SAY_KHAD_SERV_9, m_creature, pPlayer); + break; + case 10: + DoScriptText(SAY_KHAD_SERV_10, m_creature, pPlayer); + break; + case 11: + DoScriptText(SAY_KHAD_SERV_11, m_creature, pPlayer); + SetEscortPaused(false); + break; + } + break; + } + case 63: // scryer + { + switch (m_uiTalkCount) + { + case 12: + DoScriptText(SAY_KHAD_SERV_12, m_creature, pPlayer); + break; + case 13: + DoScriptText(SAY_KHAD_SERV_13, m_creature, pPlayer); + SetEscortPaused(false); + break; + } + break; + } + case 74: // aldor + { + switch (m_uiTalkCount) + { + case 14: + DoScriptText(SAY_KHAD_SERV_14, m_creature, pPlayer); + break; + case 15: + DoScriptText(SAY_KHAD_SERV_15, m_creature, pPlayer); + break; + case 16: + DoScriptText(SAY_KHAD_SERV_16, m_creature, pPlayer); + break; + case 17: + DoScriptText(SAY_KHAD_SERV_17, m_creature, pPlayer); + SetEscortPaused(false); + break; + } + break; + } + case 75: // a'dal + { + switch (m_uiTalkCount) + { + case 18: + DoScriptText(SAY_KHAD_SERV_18, m_creature, pPlayer); + break; + case 19: + DoScriptText(SAY_KHAD_SERV_19, m_creature, pPlayer); + break; + case 20: + DoScriptText(SAY_KHAD_SERV_20, m_creature, pPlayer); + break; + case 21: + DoScriptText(SAY_KHAD_SERV_21, m_creature, pPlayer); + pPlayer->AreaExploredOrEventHappens(QUEST_CITY_LIGHT); + SetEscortPaused(false); + break; + } + break; + } + } + } + else + { m_uiTalkTimer -= uiDiff; } + } + } +}; + +CreatureAI* GetAI_npc_khadgars_servant(Creature* pCreature) +{ + return new npc_khadgars_servantAI(pCreature); +} + +/*###### +# npc_salsalabim +######*/ + +enum +{ + FACTION_HOSTILE_SA = 90, + QUEST_10004 = 10004, + + SPELL_MAGNETIC_PULL = 31705, +}; + +struct npc_salsalabimAI : public ScriptedAI +{ + npc_salsalabimAI(Creature* pCreature) : ScriptedAI(pCreature) { Reset(); } + + uint32 m_uiMagneticPullTimer; + + void Reset() override + { + m_uiMagneticPullTimer = 15000; + } + + void DamageTaken(Unit* pDoneBy, uint32& uiDamage) override + { + if (pDoneBy->GetTypeId() == TYPEID_PLAYER) + { + if (m_creature->GetHealthPercent() < 20.0f) + { + ((Player*)pDoneBy)->GroupEventHappens(QUEST_10004, m_creature); + uiDamage = 0; + EnterEvadeMode(); + } + } + } + + void UpdateAI(const uint32 uiDiff) override + { + if (!m_creature->SelectHostileTarget() || !m_creature->getVictim()) + { return; } + + if (m_uiMagneticPullTimer < uiDiff) + { + DoCastSpellIfCan(m_creature->getVictim(), SPELL_MAGNETIC_PULL); + m_uiMagneticPullTimer = 15000; + } + else + { m_uiMagneticPullTimer -= uiDiff; } + + DoMeleeAttackIfReady(); + } +}; + +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) + { + pCreature->SetFactionTemporary(FACTION_HOSTILE_SA, TEMPFACTION_RESTORE_REACH_HOME); + pCreature->AI()->AttackStart(pPlayer); + } + else + { + if (pCreature->IsQuestGiver()) + { pPlayer->PrepareQuestMenu(pCreature->GetObjectGuid()); } + + pPlayer->SEND_GOSSIP_MENU(pPlayer->GetGossipTextId(pCreature), pCreature->GetObjectGuid()); + } + + return true; +} + +void AddSC_shattrath_city() +{ + Script* pNewScript; + + pNewScript = new Script; + pNewScript->Name = "npc_dirty_larry"; + pNewScript->GetAI = &GetAI_npc_dirty_larry; + pNewScript->pGossipHello = &GossipHello_npc_dirty_larry; + pNewScript->pGossipSelect = &GossipSelect_npc_dirty_larry; + pNewScript->RegisterSelf(); + + pNewScript = new Script; + pNewScript->Name = "npc_khadgars_servant"; + pNewScript->GetAI = &GetAI_npc_khadgars_servant; + pNewScript->RegisterSelf(); + + pNewScript = new Script; + pNewScript->Name = "npc_salsalabim"; + pNewScript->GetAI = &GetAI_npc_salsalabim; + pNewScript->pGossipHello = &GossipHello_npc_salsalabim; + pNewScript->RegisterSelf(); +} diff --git a/src/modules/SD2/scripts/outland/tempest_keep/arcatraz/arcatraz.cpp b/src/modules/SD2/scripts/outland/tempest_keep/arcatraz/arcatraz.cpp new file mode 100644 index 000000000..a1ed86def --- /dev/null +++ b/src/modules/SD2/scripts/outland/tempest_keep/arcatraz/arcatraz.cpp @@ -0,0 +1,363 @@ +/* Copyright (C) 2006 - 2013 ScriptDev2 + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along 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 "arcatraz.h" + +/*##### +# npc_millhouse_manastorm +#####*/ + +enum +{ + SAY_INTRO_1 = -1552010, + SAY_INTRO_2 = -1552011, + SAY_WATER = -1552012, + SAY_BUFFS = -1552013, + SAY_DRINK = -1552014, + SAY_READY = -1552015, + SAY_KILL_1 = -1552016, + SAY_KILL_2 = -1552017, + SAY_PYRO = -1552018, + SAY_ICEBLOCK = -1552019, + SAY_LOWHP = -1552020, + SAY_DEATH = -1552021, + + SPELL_CONJURE_WATER = 36879, + SPELL_ARCANE_INTELLECT = 36880, + SPELL_ICE_ARMOR = 36881, + SPELL_DRINK = 30024, + + SPELL_ARCANE_MISSILES = 33833, + SPELL_CONE_OF_COLD = 12611, + SPELL_FIRE_BLAST = 13341, + SPELL_FIREBALL = 14034, + SPELL_FROSTBOLT = 15497, + SPELL_PYROBLAST = 33975, + SPELL_ICE_BLOCK = 36911, + + POINT_ID_CENTER = 1, +}; + +static const DialogueEntry aIntroDialogue[] = +{ + {NPC_MILLHOUSE, 0, 2000}, + {SAY_INTRO_1, NPC_MILLHOUSE, 10000}, + {TYPE_WARDEN_2, 0, 10000}, + {SAY_INTRO_2, NPC_MILLHOUSE, 18000}, + {SAY_WATER, NPC_MILLHOUSE, 7000}, + {SAY_BUFFS, NPC_MILLHOUSE, 6000}, + {SPELL_ICE_ARMOR, 0, 1000}, + {SAY_DRINK, NPC_MILLHOUSE, 7000}, + {SAY_READY, NPC_MILLHOUSE, 6000}, + {POINT_ID_CENTER, 0, 0}, + {0, 0, 0}, +}; + +static const float fRoomCenterCoords[3] = {445.8804f, -158.7055f, 43.06898f}; + +struct npc_millhouse_manastormAI : public ScriptedAI, private DialogueHelper +{ + npc_millhouse_manastormAI(Creature* pCreature) : ScriptedAI(pCreature), + DialogueHelper(aIntroDialogue) + { + m_pInstance = (ScriptedInstance*)pCreature->GetInstanceData(); + InitializeDialogueHelper(m_pInstance); + Reset(); + m_attackDistance = 25.0f; + } + + ScriptedInstance* m_pInstance; + + bool m_bHasLowHp; + uint32 m_uiPyroblastTimer; + uint32 m_uiFireballTimer; + uint32 m_uiFrostBoltTimer; + uint32 m_uiFireBlastTimer; + uint32 m_uiConeColtTimer; + uint32 m_uiArcaneMissileTimer; + + void Reset() override + { + m_bHasLowHp = false; + m_uiPyroblastTimer = urand(6000, 9000); + m_uiFireballTimer = urand(2500, 4000); + m_uiFrostBoltTimer = urand(3000, 5000); + m_uiFireBlastTimer = urand(6000, 14000); + m_uiConeColtTimer = urand(7000, 12000); + m_uiArcaneMissileTimer = urand(5000, 8000); + + StartNextDialogueText(NPC_MILLHOUSE); + } + + void AttackStart(Unit* pWho) override + { + if (m_creature->Attack(pWho, true)) + { + m_creature->AddThreat(pWho); + m_creature->SetInCombatWith(pWho); + pWho->SetInCombatWith(m_creature); + HandleMovementOnAttackStart(pWho); + } + } + + void KilledUnit(Unit* /*pVictim*/) override + { + DoScriptText(urand(0, 1) ? SAY_KILL_1 : SAY_KILL_2, m_creature); + } + + void JustDied(Unit* /*pVictim*/) override + { + DoScriptText(SAY_DEATH, m_creature); + + /*for questId 10886 (heroic mode only) + if (m_pInstance && m_pInstance->GetData(TYPE_HARBINGERSKYRISS) != DONE) + ->FailQuest();*/ + } + + void EnterEvadeMode() override + { + m_creature->RemoveAllAurasOnEvade(); + m_creature->DeleteThreatList(); + m_creature->CombatStop(true); + m_creature->LoadCreatureAddon(true); + + // Boss should evade in the center of the room + if (m_creature->IsAlive()) + m_creature->GetMotionMaster()->MovePoint(1, fRoomCenterCoords[0], fRoomCenterCoords[1], fRoomCenterCoords[2]); + + m_creature->SetLootRecipient(NULL); + + Reset(); + } + + void JustDidDialogueStep(int32 iEntry) override + { + switch (iEntry) + { + case TYPE_WARDEN_2: + if (m_pInstance) + m_pInstance->SetData(TYPE_WARDEN_2, DONE); + break; + case SAY_WATER: + DoCastSpellIfCan(m_creature, SPELL_CONJURE_WATER); + break; + case SAY_BUFFS: + DoCastSpellIfCan(m_creature, SPELL_ARCANE_INTELLECT); + break; + case SPELL_ICE_ARMOR: + DoCastSpellIfCan(m_creature, SPELL_ICE_ARMOR); + break; + case SAY_DRINK: + DoCastSpellIfCan(m_creature, SPELL_DRINK); + break; + case POINT_ID_CENTER: + m_creature->SetWalk(false); + m_creature->GetMotionMaster()->MovePoint(1, fRoomCenterCoords[0], fRoomCenterCoords[1], fRoomCenterCoords[2]); + break; + } + } + + void UpdateAI(const uint32 uiDiff) override + { + DialogueUpdate(uiDiff); + + if (!m_creature->SelectHostileTarget() || !m_creature->getVictim()) + return; + + if (!m_bHasLowHp && m_creature->GetHealthPercent() < 20.0f) + { + DoScriptText(SAY_LOWHP, m_creature); + m_bHasLowHp = true; + } + + if (m_uiPyroblastTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature->getVictim(), SPELL_PYROBLAST) == CAST_OK) + { + m_uiPyroblastTimer = 40000; + DoScriptText(SAY_PYRO, m_creature); + } + } + else + m_uiPyroblastTimer -= uiDiff; + + if (m_uiFireballTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature->getVictim(), SPELL_FIREBALL) == CAST_OK) + m_uiFireballTimer = 4000; + } + else + m_uiFireballTimer -= uiDiff; + + if (m_uiFrostBoltTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature->getVictim(), SPELL_FROSTBOLT) == CAST_OK) + m_uiFrostBoltTimer = urand(4000, 6000); + } + else + m_uiFrostBoltTimer -= uiDiff; + + if (m_uiConeColtTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature, SPELL_CONE_OF_COLD) == CAST_OK) + m_uiConeColtTimer = urand(7000, 12000); + } + else + m_uiConeColtTimer -= uiDiff; + + if (m_uiFireBlastTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature->getVictim(), SPELL_FIRE_BLAST) == CAST_OK) + m_uiFireBlastTimer = urand(5000, 16000); + } + else + m_uiFireBlastTimer -= uiDiff; + + if (m_uiArcaneMissileTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature->getVictim(), SPELL_ARCANE_MISSILES) == CAST_OK) + m_uiArcaneMissileTimer = urand(5000, 8000); + } + else + m_uiArcaneMissileTimer -= uiDiff; + + DoMeleeAttackIfReady(); + } +}; + +CreatureAI* GetAI_npc_millhouse_manastorm(Creature* pCreature) +{ + return new npc_millhouse_manastormAI(pCreature); +} + +/*##### +# npc_warden_mellichar +#####*/ + +enum +{ + SPELL_BUBBLE_VISUAL = 36849, + SPELL_SIMPLE_TELEPORT = 12980, +}; + +struct npc_warden_mellicharAI : public ScriptedAI +{ + npc_warden_mellicharAI(Creature* pCreature) : ScriptedAI(pCreature) + { + m_pInstance = (ScriptedInstance*)pCreature->GetInstanceData(); + Reset(); + } + + ScriptedInstance* m_pInstance; + + uint32 m_uiIntroTimer; + ObjectGuid m_targetPlayerGuid; + + void Reset() override + { + m_uiIntroTimer = 5000; + m_creature->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE); + } + + void AttackStart(Unit* /*pWho*/) override {} + + void Aggro(Unit* pWho) override + { + m_creature->InterruptNonMeleeSpells(false); + m_creature->SetFacingToObject(pWho); + m_targetPlayerGuid = pWho->GetObjectGuid(); + + DoCastSpellIfCan(m_creature, SPELL_BUBBLE_VISUAL); + + // In theory the Seal Sphere should protect the npc from being attacked, but because LoS isn't enabled for Gameobjects we have to use this workaround + m_creature->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE); + + if (m_pInstance) + m_pInstance->SetData(TYPE_HARBINGERSKYRISS, IN_PROGRESS); + } + + void JustSummoned(Creature* pSummoned) override + { + pSummoned->CastSpell(pSummoned, SPELL_SIMPLE_TELEPORT, false); + + if (pSummoned->GetEntry() != NPC_MILLHOUSE && pSummoned->GetEntry() != NPC_SKYRISS) + { + if (Unit* pTarget = m_creature->GetMap()->GetUnit(m_targetPlayerGuid)) + pSummoned->AI()->AttackStart(pTarget); + } + } + + void JustDied(Unit* /*pKiller*/) override + { + if (m_pInstance) + { + if (Creature* pSkyriss = m_pInstance->GetSingleCreatureFromStorage(NPC_SKYRISS)) + { + if (Unit* pTarget = m_creature->GetMap()->GetUnit(m_targetPlayerGuid)) + pSkyriss->AI()->AttackStart(pTarget); + } + } + } + + void UpdateAI(const uint32 uiDiff) override + { + // Set the visual intro on OOC timer + if (m_uiIntroTimer) + { + if (m_uiIntroTimer <= uiDiff) + { + DoCastSpellIfCan(m_creature, SPELL_TARGET_OMEGA); + m_uiIntroTimer = 0; + } + else + m_uiIntroTimer -= uiDiff; + } + } +}; + +CreatureAI* GetAI_npc_warden_mellichar(Creature* pCreature) +{ + return new npc_warden_mellicharAI(pCreature); +} + +void AddSC_arcatraz() +{ + Script* pNewScript; + + pNewScript = new Script; + pNewScript->Name = "npc_millhouse_manastorm"; + pNewScript->GetAI = &GetAI_npc_millhouse_manastorm; + pNewScript->RegisterSelf(); + + pNewScript = new Script; + pNewScript->Name = "npc_warden_mellichar"; + pNewScript->GetAI = &GetAI_npc_warden_mellichar; + pNewScript->RegisterSelf(); +} diff --git a/src/modules/SD2/scripts/outland/tempest_keep/arcatraz/arcatraz.h b/src/modules/SD2/scripts/outland/tempest_keep/arcatraz/arcatraz.h new file mode 100644 index 000000000..39bfc0b3a --- /dev/null +++ b/src/modules/SD2/scripts/outland/tempest_keep/arcatraz/arcatraz.h @@ -0,0 +1,114 @@ +/* Copyright (C) 2006 - 2013 ScriptDev2 + * This program is free software licensed under GPL version 2 + * Please see the included DOCS/LICENSE.TXT for more information */ + +#ifndef DEF_ARCATRAZ_H +#define DEF_ARCATRAZ_H + +enum +{ + MAX_ENCOUNTER = 10, + MAX_WARDENS = 7, + + TYPE_ENTRANCE = 0, + TYPE_ZEREKETH = 1, + TYPE_DALLIAH = 2, + TYPE_SOCCOTHRATES = 3, + TYPE_HARBINGERSKYRISS = 4, // Handled with ACID (FAIL of 20905, 20906, 20908, 20909, 20910, 20911) + TYPE_WARDEN_1 = 5, // Handled with ACID (20905 - Blazing Trickster, 20906 - Phase-Hunter) + TYPE_WARDEN_2 = 6, + TYPE_WARDEN_3 = 7, // Handled with ACID (20908 - Akkiris Lightning-Waker, 20909 - Sulfuron Magma-Thrower) + TYPE_WARDEN_4 = 8, // Handled with ACID (20910 - Twilight Drakonaar, 20911 - Blackwing Drakonaar) + TYPE_WARDEN_5 = 9, + + NPC_DALLIAH = 20885, + NPC_SOCCOTHRATES = 20886, + NPC_MELLICHAR = 20904, // Skyriss will kill this unit + NPC_PRISON_APHPA_POD = 21436, + NPC_PRISON_BETA_POD = 21437, + NPC_PRISON_DELTA_POD = 21438, + NPC_PRISON_GAMMA_POD = 21439, + NPC_PRISON_BOSS_POD = 21440, + + // intro event related + NPC_PROTEAN_NIGHTMARE = 20864, + NPC_PROTEAN_HORROR = 20865, + NPC_ARCATRAZ_WARDEN = 20859, + NPC_ARCATRAZ_DEFENDER = 20857, + + // Harbinger Skyriss event related (trash mobs are scripted in ACID) + NPC_BLAZING_TRICKSTER = 20905, // phase 1 + NPC_PHASE_HUNTER = 20906, + NPC_MILLHOUSE = 20977, // phase 2 + NPC_AKKIRIS = 20908, // phase 3 + NPC_SULFURON = 20909, + NPC_TW_DRAKONAAR = 20910, // phase 4 + NPC_BL_DRAKONAAR = 20911, + NPC_SKYRISS = 20912, // phase 5 + + GO_CORE_SECURITY_FIELD_ALPHA = 184318, // Door opened when Wrath-Scryer Soccothrates dies + GO_CORE_SECURITY_FIELD_BETA = 184319, // Door opened when Dalliah the Doomsayer dies + GO_SEAL_SPHERE = 184802, // Shield 'protecting' mellichar + GO_POD_ALPHA = 183961, // Pod first boss wave + GO_POD_BETA = 183963, // Pod second boss wave + GO_POD_DELTA = 183964, // Pod third boss wave + GO_POD_GAMMA = 183962, // Pod fourth boss wave + GO_POD_OMEGA = 183965, // Pod fifth boss wave + + SPELL_TARGET_OMEGA = 36852, // Visual spell used by Mellichar +}; + +struct SpawnLocation +{ + float m_fX, m_fY, m_fZ, m_fO; +}; + +static const SpawnLocation aSummonPosition[5] = +{ + {478.326f, -148.505f, 42.56f, 3.19f}, // Trickster or Phase Hunter + {413.292f, -148.378f, 42.56f, 6.27f}, // Millhouse + {420.179f, -174.396f, 42.58f, 0.02f}, // Akkiris or Sulfuron + {471.795f, -174.58f, 42.58f, 3.06f}, // Twilight or Blackwing Drakonaar + {445.763f, -191.639f, 44.64f, 1.60f} // Skyriss +}; + +static const float aDalliahStartPos[4] = {118.6038f, 96.84682f, 22.44115f, 1.012f}; +static const float aSoccotharesStartPos[4] = {122.1035f, 192.7203f, 22.44115f, 5.235f}; + +static const float aEntranceMoveLoc[3] = {82.020f, 0.306f, -11.026f}; +static const float aEntranceSpawnLoc[4] = {173.471f, -0.138f, -10.101f, 3.123f}; + +class instance_arcatraz : public ScriptedInstance, private DialogueHelper +{ + public: + instance_arcatraz(Map* pMap); + + void Initialize() override; + + void OnPlayerEnter(Player* pPlayer) override; + void OnObjectCreate(GameObject* pGo) override; + void OnCreatureCreate(Creature* pCreature) override; + void OnCreatureDeath(Creature* pCreature) override; + + void SetData(uint32 uiType, uint32 uiData) override; + uint32 GetData(uint32 uiType) const override; + + const char* Save() const override { return m_strInstData.c_str(); } + void Load(const char* chrIn) override; + + void Update(uint32 uiDiff) override; + + private: + void JustDidDialogueStep(int32 iEntry) override; + + uint32 m_auiEncounter[MAX_ENCOUNTER]; + std::string m_strInstData; + + uint32 m_uiResetDelayTimer; + uint32 m_uiEntranceEventTimer; + uint8 m_uiKilledWardens; + + GuidList m_lSkyrissEventMobsGuidList; +}; + +#endif diff --git a/src/modules/SD2/scripts/outland/tempest_keep/arcatraz/boss_dalliah.cpp b/src/modules/SD2/scripts/outland/tempest_keep/arcatraz/boss_dalliah.cpp new file mode 100644 index 000000000..1caa83a5c --- /dev/null +++ b/src/modules/SD2/scripts/outland/tempest_keep/arcatraz/boss_dalliah.cpp @@ -0,0 +1,217 @@ +/* Copyright (C) 2006 - 2013 ScriptDev2 + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +/* ScriptData +SDName: boss_dalliah +SD%Complete: 100 +SDComment: +SDCategory: Tempest Keep, The Arcatraz +EndScriptData */ + +#include "precompiled.h" +#include "arcatraz.h" + +enum +{ + SAY_AGGRO = -1552031, + SAY_SOCCOTHRATES_TAUNT_1 = -1552040, + SAY_SOCCOTHRATES_TAUNT_2 = -1552041, + SAY_SOCCOTHRATES_TAUNT_3 = -1552042, + SAY_HEAL_1 = -1552032, + SAY_HEAL_2 = -1552033, + SAY_KILL_1 = -1552034, + SAY_KILL_2 = -1552035, + SAY_WHIRLWIND_1 = -1552036, + SAY_WHIRLWIND_2 = -1552037, + SAY_DEATH = -1552038, + + SPELL_GIFT_DOOMSAYER = 36173, + SPELL_GIFT_DOOMSAYER_H = 39009, + SPELL_HEAL = 36144, + SPELL_HEAL_H = 39013, + SPELL_WHIRLWIND = 36142, + SPELL_SHADOW_WAVE = 39016, // heroic spell only +}; + +struct boss_dalliahAI : public ScriptedAI +{ + boss_dalliahAI(Creature* pCreature) : ScriptedAI(pCreature) + { + m_pInstance = (ScriptedInstance*)pCreature->GetInstanceData(); + m_bIsRegularMode = pCreature->GetMap()->IsRegularDifficulty(); + Reset(); + } + + ScriptedInstance* m_pInstance; + bool m_bIsRegularMode; + + uint32 m_uiGiftDoomsayerTimer; + uint32 m_uiHealTimer; + uint32 m_uiWhirlwindTimer; + uint32 m_uiShadowWaveTimer; + + bool m_bHasTaunted; + + void Reset() override + { + m_uiGiftDoomsayerTimer = urand(4000, 7000); + m_uiHealTimer = 0; + m_uiWhirlwindTimer = 15000; + m_uiShadowWaveTimer = urand(9000, 13000); + + m_bHasTaunted = false; + } + + void Aggro(Unit* /*pWho*/) override + { + DoScriptText(SAY_AGGRO, m_creature); + + if (m_pInstance) + m_pInstance->SetData(TYPE_DALLIAH, IN_PROGRESS); + } + + void KilledUnit(Unit* /*pVictim*/) override + { + DoScriptText(urand(0, 1) ? SAY_KILL_1 : SAY_KILL_2, m_creature); + } + + void JustDied(Unit* /*pWho*/) override + { + DoScriptText(SAY_DEATH, m_creature); + + if (m_pInstance) + m_pInstance->SetData(TYPE_DALLIAH, DONE); + } + + void EnterEvadeMode() override + { + m_creature->RemoveAllAurasOnEvade(); + m_creature->DeleteThreatList(); + m_creature->CombatStop(true); + m_creature->LoadCreatureAddon(true); + + // should evade to the attack position + if (m_creature->IsAlive()) + m_creature->GetMotionMaster()->MovePoint(1, aDalliahStartPos[0], aDalliahStartPos[1], aDalliahStartPos[2]); + + if (m_pInstance) + m_pInstance->SetData(TYPE_DALLIAH, FAIL); + + m_creature->SetLootRecipient(NULL); + + Reset(); + } + + void MovementInform(uint32 uiMoveType, uint32 uiPointId) override + { + if (uiMoveType != POINT_MOTION_TYPE) + return; + + // Adjust orientation + if (uiPointId) + m_creature->SetFacingTo(aDalliahStartPos[3]); + } + + void UpdateAI(const uint32 uiDiff) override + { + if (!m_creature->SelectHostileTarget() || !m_creature->getVictim()) + return; + + if (m_uiGiftDoomsayerTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature->getVictim(), m_bIsRegularMode ? SPELL_GIFT_DOOMSAYER : SPELL_GIFT_DOOMSAYER_H) == CAST_OK) + m_uiGiftDoomsayerTimer = urand(14000, 19000); + } + else + m_uiGiftDoomsayerTimer -= uiDiff; + + if (m_uiWhirlwindTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature, SPELL_WHIRLWIND) == CAST_OK) + { + DoScriptText(urand(0, 1) ? SAY_WHIRLWIND_1 : SAY_WHIRLWIND_2, m_creature); + m_uiWhirlwindTimer = urand(25000, 30000); + m_uiHealTimer = urand(6000, 10000); + } + } + else + m_uiWhirlwindTimer -= uiDiff; + + if (m_uiHealTimer) + { + if (m_uiHealTimer <= uiDiff) + { + if (DoCastSpellIfCan(m_creature, m_bIsRegularMode ? SPELL_HEAL : SPELL_HEAL_H) == CAST_OK) + { + DoScriptText(urand(0, 1) ? SAY_HEAL_1 : SAY_HEAL_2, m_creature); + m_uiHealTimer = 0; + } + } + else + m_uiHealTimer -= uiDiff; + } + + if (!m_bIsRegularMode) + { + if (m_uiShadowWaveTimer < uiDiff) + { + if (Unit* pTarget = m_creature->SelectAttackingTarget(ATTACKING_TARGET_RANDOM, 0)) + { + if (DoCastSpellIfCan(pTarget, SPELL_SHADOW_WAVE) == CAST_OK) + m_uiShadowWaveTimer = urand(13000, 17000); + } + } + else + m_uiShadowWaveTimer -= uiDiff; + } + + if (!m_bHasTaunted && m_creature->GetHealthPercent() < 25.0f) + { + // Taunt if Soccothares isn't dead yet + if (m_pInstance && m_pInstance->GetData(TYPE_SOCCOTHRATES) != DONE) + { + if (Creature* pSoccothares = m_pInstance->GetSingleCreatureFromStorage(NPC_SOCCOTHRATES)) + { + switch (urand(0, 2)) + { + case 0: DoScriptText(SAY_SOCCOTHRATES_TAUNT_1, pSoccothares); break; + case 1: DoScriptText(SAY_SOCCOTHRATES_TAUNT_2, pSoccothares); break; + case 2: DoScriptText(SAY_SOCCOTHRATES_TAUNT_3, pSoccothares); break; + } + } + } + + m_bHasTaunted = true; + } + + DoMeleeAttackIfReady(); + } +}; + +CreatureAI* GetAI_boss_dalliah(Creature* pCreature) +{ + return new boss_dalliahAI(pCreature); +} + +void AddSC_boss_dalliah() +{ + Script* pNewScript; + + pNewScript = new Script; + pNewScript->Name = "boss_dalliah"; + pNewScript->GetAI = &GetAI_boss_dalliah; + pNewScript->RegisterSelf(); +} diff --git a/src/modules/SD2/scripts/outland/tempest_keep/arcatraz/boss_harbinger_skyriss.cpp b/src/modules/SD2/scripts/outland/tempest_keep/arcatraz/boss_harbinger_skyriss.cpp new file mode 100644 index 000000000..8b59dfecd --- /dev/null +++ b/src/modules/SD2/scripts/outland/tempest_keep/arcatraz/boss_harbinger_skyriss.cpp @@ -0,0 +1,190 @@ +/* Copyright (C) 2006 - 2013 ScriptDev2 + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with 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: 95 +SDComment: Timers will need adjustments. +SDCategory: Tempest Keep, The Arcatraz +EndScriptData */ + +#include "precompiled.h" +#include "arcatraz.h" + +enum +{ + SAY_KILL_1 = -1552002, + SAY_KILL_2 = -1552003, + SAY_MIND_1 = -1552004, + SAY_MIND_2 = -1552005, + SAY_FEAR_1 = -1552006, + SAY_FEAR_2 = -1552007, + SAY_IMAGE = -1552008, + SAY_DEATH = -1552009, + + SPELL_FEAR = 39415, + SPELL_MIND_REND = 36924, + SPELL_MIND_REND_H = 39017, + SPELL_DOMINATION = 37162, + SPELL_DOMINATION_H = 39019, + SPELL_MANA_BURN_H = 39020, + SPELL_66_ILLUSION = 36931, // Summons 21466 + SPELL_33_ILLUSION = 36932, // Summons 21467 +}; + +struct boss_harbinger_skyrissAI : public ScriptedAI +{ + boss_harbinger_skyrissAI(Creature* pCreature) : ScriptedAI(pCreature) + { + m_pInstance = (ScriptedInstance*)pCreature->GetInstanceData(); + m_bIsRegularMode = pCreature->GetMap()->IsRegularDifficulty(); + Reset(); + } + + ScriptedInstance* m_pInstance; + bool m_bIsRegularMode; + + uint8 m_uiSplitPhase; + uint32 m_uiMindRendTimer; + uint32 m_uiFearTimer; + uint32 m_uiDominationTimer; + uint32 m_uiManaBurnTimer; + + void Reset() override + { + m_uiSplitPhase = 1; + m_uiMindRendTimer = 3000; + m_uiFearTimer = 15000; + m_uiDominationTimer = 30000; + m_uiManaBurnTimer = 25000; + } + + void JustDied(Unit* /*pKiller*/) override + { + DoScriptText(SAY_DEATH, m_creature); + + if (m_pInstance) + m_pInstance->SetData(TYPE_HARBINGERSKYRISS, DONE); + } + + void JustReachedHome() override + { + if (m_pInstance) + m_pInstance->SetData(TYPE_HARBINGERSKYRISS, FAIL); + } + + void KilledUnit(Unit* pVictim) override + { + // won't yell killing pet/other unit + if (pVictim->GetTypeId() != TYPEID_PLAYER) + return; + + DoScriptText(urand(0, 1) ? SAY_KILL_1 : SAY_KILL_2, m_creature); + } + + void JustSummoned(Creature* pSummoned) override + { + if (m_creature->getVictim()) + pSummoned->AI()->AttackStart(m_creature->getVictim()); + } + + void UpdateAI(const uint32 uiDiff) override + { + if (!m_creature->SelectHostileTarget() || !m_creature->getVictim()) + return; + + // Check if creature is below 66% or 33%; Also don't allow it to split the third time + if (m_creature->GetHealthPercent() < 100 - 33 * m_uiSplitPhase && m_creature->GetHealthPercent() > 5.0f) + { + DoCastSpellIfCan(m_creature, m_uiSplitPhase == 1 ? SPELL_66_ILLUSION : SPELL_33_ILLUSION, CAST_INTERRUPT_PREVIOUS); + DoScriptText(SAY_IMAGE, m_creature); + ++m_uiSplitPhase; + } + + if (m_uiMindRendTimer < uiDiff) + { + Unit* pTarget = m_creature->SelectAttackingTarget(ATTACKING_TARGET_RANDOM, 1); + if (!pTarget) + pTarget = m_creature->getVictim(); + + if (DoCastSpellIfCan(pTarget, m_bIsRegularMode ? SPELL_MIND_REND : SPELL_MIND_REND_H) == CAST_OK) + m_uiMindRendTimer = 8000; + } + else + m_uiMindRendTimer -= uiDiff; + + if (m_uiFearTimer < uiDiff) + { + Unit* pTarget = m_creature->SelectAttackingTarget(ATTACKING_TARGET_RANDOM, 1); + if (!pTarget) + pTarget = m_creature->getVictim(); + + if (DoCastSpellIfCan(pTarget, SPELL_FEAR) == CAST_OK) + { + DoScriptText(urand(0, 1) ? SAY_FEAR_1 : SAY_FEAR_2, m_creature); + m_uiFearTimer = 25000; + } + } + else + m_uiFearTimer -= uiDiff; + + if (m_uiDominationTimer < uiDiff) + { + if (Unit* pTarget = m_creature->SelectAttackingTarget(ATTACKING_TARGET_RANDOM, 1, uint32(0), SELECT_FLAG_PLAYER)) + { + if (DoCastSpellIfCan(pTarget, m_bIsRegularMode ? SPELL_DOMINATION : SPELL_DOMINATION_H) == CAST_OK) + { + DoScriptText(urand(0, 1) ? SAY_MIND_1 : SAY_MIND_2, m_creature); + m_uiDominationTimer = urand(16000, 32000); + } + } + } + else + m_uiDominationTimer -= uiDiff; + + if (!m_bIsRegularMode) + { + if (m_uiManaBurnTimer < uiDiff) + { + Unit* pTarget = m_creature->SelectAttackingTarget(ATTACKING_TARGET_RANDOM, 1); + if (!pTarget) + pTarget = m_creature->getVictim(); + + if (DoCastSpellIfCan(pTarget, SPELL_MANA_BURN_H) == CAST_OK) + m_uiManaBurnTimer = urand(16000, 32000); + } + else + m_uiManaBurnTimer -= uiDiff; + } + + DoMeleeAttackIfReady(); + } +}; + +CreatureAI* GetAI_boss_harbinger_skyriss(Creature* pCreature) +{ + return new boss_harbinger_skyrissAI(pCreature); +} + +void AddSC_boss_harbinger_skyriss() +{ + Script* pNewScript; + + pNewScript = new Script; + pNewScript->Name = "boss_harbinger_skyriss"; + pNewScript->GetAI = &GetAI_boss_harbinger_skyriss; + pNewScript->RegisterSelf(); +} diff --git a/src/modules/SD2/scripts/outland/tempest_keep/arcatraz/boss_soccothrates.cpp b/src/modules/SD2/scripts/outland/tempest_keep/arcatraz/boss_soccothrates.cpp new file mode 100644 index 000000000..e1e702198 --- /dev/null +++ b/src/modules/SD2/scripts/outland/tempest_keep/arcatraz/boss_soccothrates.cpp @@ -0,0 +1,252 @@ +/* Copyright (C) 2006 - 2013 ScriptDev2 + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +/* ScriptData +SDName: boss_soccothrates +SD%Complete: 80 +SDComment: Spell Felfire Line Up and Wrath-Scryer's Felfire npc are summoning are NYI and they need additional research. +SDCategory: Tempest Keep, The Arcatraz +EndScriptData */ + +#include "precompiled.h" +#include "arcatraz.h" + +enum +{ + // Intro yells + SAY_SOCCOTHRATES_INTRO_1 = -1552049, + SAY_DALLIAH_INTRO_2 = -1552050, + SAY_SOCCOTHRATES_INTRO_3 = -1552051, + SAY_DALLIAH_INTRO_4 = -1552052, + SAY_SOCCOTHRATES_INTRO_5 = -1552053, + SAY_DALLIAH_INTRO_6 = -1552054, + SAY_SOCCOTHRATES_INTRO_7 = -1552055, + + SAY_AGGRO = -1552048, + SAY_KILL = -1552047, + SAY_DEATH = -1552046, + SAY_CHARGE_1 = -1552044, + SAY_CHARGE_2 = -1552045, + + SPELL_IMMOLATION = 36051, + SPELL_IMMOLATION_H = 39007, + SPELL_KNOCK_AWAY = 36512, + SPELL_FELFIRE_LINE_UP = 35770, // dummy spell - should summon a line of npcs - 20978 to the target + SPELL_CHARGE_TARGETING = 36038, // summons 21030 on target + SPELL_CHARGE = 35754, // script target on 21030; also dummy effect area effect target on 20978 - makes the target cast 35769 + SPELL_FELFIRE_SHOCK = 35759, + SPELL_FELFIRE_SHOCK_H = 39006, +}; + +static const DialogueEntry aIntroDialogue[] = +{ + {SAY_SOCCOTHRATES_INTRO_1, NPC_SOCCOTHRATES, 3000}, + {SAY_DALLIAH_INTRO_2, NPC_DALLIAH, 2000}, + {SAY_SOCCOTHRATES_INTRO_3, NPC_SOCCOTHRATES, 4000}, + {SAY_DALLIAH_INTRO_4, NPC_DALLIAH, 5000}, + {SAY_SOCCOTHRATES_INTRO_5, NPC_SOCCOTHRATES, 3000}, + {SAY_DALLIAH_INTRO_6, NPC_DALLIAH, 3000}, + {SAY_SOCCOTHRATES_INTRO_7, NPC_SOCCOTHRATES, 0}, + {0, 0, 0}, +}; + +struct boss_soccothratesAI : public ScriptedAI, private DialogueHelper +{ + boss_soccothratesAI(Creature* pCreature) : ScriptedAI(pCreature), + DialogueHelper(aIntroDialogue) + { + m_pInstance = (ScriptedInstance*)pCreature->GetInstanceData(); + m_bIsRegularMode = pCreature->GetMap()->IsRegularDifficulty(); + InitializeDialogueHelper(m_pInstance); + m_bHasYelledIntro = false; + Reset(); + } + + ScriptedInstance* m_pInstance; + bool m_bIsRegularMode; + + uint32 m_uiKnockAwayTimer; + uint32 m_uiFelfireShockTimer; + uint32 m_uiFelfireLineupTimer; + uint32 m_uiChargeTimer; + + bool m_bHasYelledIntro; + + void Reset() override + { + m_uiFelfireShockTimer = urand(10000, 13000); + m_uiKnockAwayTimer = urand(22000, 25000); + m_uiFelfireLineupTimer = 0; + m_uiChargeTimer = 0; + + DoCastSpellIfCan(m_creature, m_bIsRegularMode ? SPELL_IMMOLATION : SPELL_IMMOLATION_H); + } + + void Aggro(Unit* /*pWho*/) override + { + DoScriptText(SAY_AGGRO, m_creature); + + if (m_pInstance) + m_pInstance->SetData(TYPE_SOCCOTHRATES, IN_PROGRESS); + } + + void MoveInLineOfSight(Unit* pWho) override + { + if (!m_bHasYelledIntro && pWho->GetTypeId() == TYPEID_PLAYER && m_creature->IsWithinDistInMap(pWho, 75.0f) && m_creature->IsWithinLOSInMap(pWho)) + { + StartNextDialogueText(SAY_SOCCOTHRATES_INTRO_1); + m_bHasYelledIntro = true; + } + + ScriptedAI::MoveInLineOfSight(pWho); + } + + void KilledUnit(Unit* /*pVictim*/) override + { + DoScriptText(SAY_KILL, m_creature); + } + + void JustDied(Unit* /*pWho*/) override + { + DoScriptText(SAY_DEATH, m_creature); + + if (m_pInstance) + m_pInstance->SetData(TYPE_SOCCOTHRATES, DONE); + } + + void EnterEvadeMode() override + { + m_creature->RemoveAllAurasOnEvade(); + m_creature->DeleteThreatList(); + m_creature->CombatStop(true); + m_creature->LoadCreatureAddon(true); + + // should evade to the attack position + if (m_creature->IsAlive()) + m_creature->GetMotionMaster()->MovePoint(1, aSoccotharesStartPos[0], aSoccotharesStartPos[1], aSoccotharesStartPos[2]); + + if (m_pInstance) + m_pInstance->SetData(TYPE_SOCCOTHRATES, FAIL); + + m_creature->SetLootRecipient(NULL); + + Reset(); + } + + void MovementInform(uint32 uiMoveType, uint32 uiPointId) override + { + if (uiMoveType != POINT_MOTION_TYPE) + return; + + // Adjust orientation + if (uiPointId) + m_creature->SetFacingTo(aSoccotharesStartPos[3]); + } + + void JustDidDialogueStep(int32 iEntry) override + { + // Move each of them to their places + if (iEntry == SAY_SOCCOTHRATES_INTRO_7) + { + m_creature->GetMotionMaster()->MovePoint(1, aSoccotharesStartPos[0], aSoccotharesStartPos[1], aSoccotharesStartPos[2]); + + if (m_pInstance) + { + if (Creature* pDalliah = m_pInstance->GetSingleCreatureFromStorage(NPC_DALLIAH)) + pDalliah->GetMotionMaster()->MovePoint(1, aDalliahStartPos[0], aDalliahStartPos[1], aDalliahStartPos[2]); + } + } + } + + void UpdateAI(const uint32 uiDiff) override + { + DialogueUpdate(uiDiff); + + if (!m_creature->SelectHostileTarget() || !m_creature->getVictim()) + return; + + if (m_uiFelfireShockTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature->getVictim(), m_bIsRegularMode ? SPELL_FELFIRE_SHOCK : SPELL_FELFIRE_SHOCK_H) == CAST_OK) + m_uiFelfireShockTimer = urand(35000, 45000); + } + else + m_uiFelfireShockTimer -= uiDiff; + + if (m_uiKnockAwayTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature, SPELL_KNOCK_AWAY) == CAST_OK) + { + m_uiKnockAwayTimer = urand(30000, 35000); + m_uiFelfireLineupTimer = 3000; + } + } + else + m_uiKnockAwayTimer -= uiDiff; + + // Prepare the boss for charging + if (m_uiFelfireLineupTimer) + { + if (m_uiFelfireLineupTimer <= uiDiff) + { + if (Unit* pTarget = m_creature->SelectAttackingTarget(ATTACKING_TARGET_RANDOM, 0)) + { + if (DoCastSpellIfCan(pTarget, SPELL_CHARGE_TARGETING) == CAST_OK) + { + // ToDo: the Wrath-Scryer's Felfire npcs should be summoned at this point and aligned to the chosen target! + DoCastSpellIfCan(m_creature, SPELL_FELFIRE_LINE_UP, CAST_TRIGGERED); + DoScriptText(urand(0, 1) ? SAY_CHARGE_1 : SAY_CHARGE_2, m_creature); + + m_uiChargeTimer = 1500; + m_uiFelfireLineupTimer = 0; + } + } + } + else + m_uiFelfireLineupTimer -= uiDiff; + } + + // Charge the target + if (m_uiChargeTimer) + { + if (m_uiChargeTimer <= uiDiff) + { + // Note: this spell will also light up the Wrath-Scryer's Felfire npcs + if (DoCastSpellIfCan(m_creature, SPELL_CHARGE) == CAST_OK) + m_uiChargeTimer = 0; + } + else + m_uiChargeTimer -= uiDiff; + } + + DoMeleeAttackIfReady(); + } +}; + +CreatureAI* GetAI_boss_soccothrates(Creature* pCreature) +{ + return new boss_soccothratesAI(pCreature); +} + +void AddSC_boss_soccothrates() +{ + Script* pNewScript; + + pNewScript = new Script; + pNewScript->Name = "boss_soccothrates"; + pNewScript->GetAI = &GetAI_boss_soccothrates; + pNewScript->RegisterSelf(); +} diff --git a/src/modules/SD2/scripts/outland/tempest_keep/arcatraz/instance_arcatraz.cpp b/src/modules/SD2/scripts/outland/tempest_keep/arcatraz/instance_arcatraz.cpp new file mode 100644 index 000000000..e42a2cb73 --- /dev/null +++ b/src/modules/SD2/scripts/outland/tempest_keep/arcatraz/instance_arcatraz.cpp @@ -0,0 +1,476 @@ +/* Copyright (C) 2006 - 2013 ScriptDev2 + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along 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 "arcatraz.h" + +/* Arcatraz encounters: +1 - Zereketh the Unbound event +2 - Dalliah the Doomsayer event +3 - Wrath-Scryer Soccothrates event +4 - Harbinger Skyriss event, 5 sub-events +*/ + +enum +{ + SAY_SOCCOTHRATES_AGGRO = -1552039, + SAY_SOCCOTHRATES_DEATH = -1552043, + + YELL_MELLICHAR_INTRO1 = -1552023, + YELL_MELLICHAR_INTRO2 = -1552024, + YELL_MELLICHAR_RELEASE1 = -1552025, + YELL_MELLICHAR_RELEASE2 = -1552026, + YELL_MELLICHAR_RELEASE3 = -1552027, + YELL_MELLICHAR_RELEASE4 = -1552028, + YELL_MELLICHAR_RELEASE5 = -1552029, + YELL_MELLICAR_WELCOME = -1552030, + SAY_SKYRISS_INTRO = -1552000, + SAY_SKYRISS_AGGRO = -1552001, + SAY_MILLHOUSE_COMPLETE = -1552022, + + // Spells used by Mellichar during the dialogue + SPELL_TARGET_BETA = 36854, + SPELL_TARGET_ALPHA = 36856, + SPELL_TARGET_DELTA = 36857, + SPELL_TARGET_GAMMA = 36858, + SPELL_SIMPLE_TELEPORT = 12980, + SPELL_MIND_REND = 36859, +}; + +static const DialogueEntry aArcatrazDialogue[] = +{ + // Soccothares taunts + {TYPE_DALLIAH, 0, 5000}, + {SAY_SOCCOTHRATES_AGGRO, NPC_SOCCOTHRATES, 0}, + {TYPE_SOCCOTHRATES, 0, 5000}, + {SAY_SOCCOTHRATES_DEATH, NPC_SOCCOTHRATES, 0}, + // Skyriss event + {YELL_MELLICHAR_INTRO1, NPC_MELLICHAR, 22000}, + {YELL_MELLICHAR_INTRO2, NPC_MELLICHAR, 7000}, + {SPELL_TARGET_ALPHA, 0, 7000}, + {YELL_MELLICHAR_RELEASE1, NPC_MELLICHAR, 0}, + {YELL_MELLICHAR_RELEASE2, NPC_MELLICHAR, 7000}, + {SPELL_TARGET_BETA, 0, 7000}, + {TYPE_WARDEN_2, 0, 0}, + {YELL_MELLICHAR_RELEASE3, NPC_MELLICHAR, 7000}, + {SPELL_TARGET_DELTA, 0, 7000}, + {TYPE_WARDEN_3, 0, 0}, + {YELL_MELLICHAR_RELEASE4, NPC_MELLICHAR, 7000}, + {SPELL_TARGET_GAMMA, 0, 7000}, + {TYPE_WARDEN_4, 0, 0}, + {YELL_MELLICHAR_RELEASE5, NPC_MELLICHAR, 8000}, + {TYPE_WARDEN_5, 0, 5000}, + {SAY_SKYRISS_INTRO, NPC_SKYRISS, 25000}, + {YELL_MELLICAR_WELCOME, NPC_MELLICHAR, 3000}, + {SAY_SKYRISS_AGGRO, NPC_SKYRISS, 0}, + {0, 0, 0}, +}; + +instance_arcatraz::instance_arcatraz(Map* pMap) : ScriptedInstance(pMap), DialogueHelper(aArcatrazDialogue), + m_uiResetDelayTimer(0), + m_uiEntranceEventTimer(0), + m_uiKilledWardens(0) +{ + Initialize(); +} + +void instance_arcatraz::Initialize() +{ + memset(&m_auiEncounter, 0, sizeof(m_auiEncounter)); + InitializeDialogueHelper(this); +} + +void instance_arcatraz::OnPlayerEnter(Player* /*pPlayer*/) +{ + // Check encounter states + if (GetData(TYPE_ENTRANCE) == DONE || GetData(TYPE_ENTRANCE) == IN_PROGRESS) + return; + + SetData(TYPE_ENTRANCE, IN_PROGRESS); + m_uiEntranceEventTimer = 1000; +} + +void instance_arcatraz::OnObjectCreate(GameObject* pGo) +{ + switch (pGo->GetEntry()) + { + case GO_CORE_SECURITY_FIELD_ALPHA: + if (m_auiEncounter[2] == DONE) + pGo->SetGoState(GO_STATE_ACTIVE); + break; + case GO_CORE_SECURITY_FIELD_BETA: + if (m_auiEncounter[1] == DONE) + pGo->SetGoState(GO_STATE_ACTIVE); + break; + case GO_SEAL_SPHERE: + case GO_POD_ALPHA: + case GO_POD_BETA: + case GO_POD_DELTA: + case GO_POD_GAMMA: + case GO_POD_OMEGA: + break; + default: + return; + } + m_mGoEntryGuidStore[pGo->GetEntry()] = pGo->GetObjectGuid(); +} + +void instance_arcatraz::OnCreatureCreate(Creature* pCreature) +{ + switch (pCreature->GetEntry()) + { + case NPC_SKYRISS: + case NPC_MILLHOUSE: + m_lSkyrissEventMobsGuidList.push_back(pCreature->GetObjectGuid()); + // no break here because we want them in both lists + case NPC_PRISON_APHPA_POD: + case NPC_PRISON_BETA_POD: + case NPC_PRISON_DELTA_POD: + case NPC_PRISON_GAMMA_POD: + case NPC_PRISON_BOSS_POD: + case NPC_MELLICHAR: + case NPC_DALLIAH: + case NPC_SOCCOTHRATES: + m_mNpcEntryGuidStore[pCreature->GetEntry()] = pCreature->GetObjectGuid(); + break; + case NPC_BLAZING_TRICKSTER: + case NPC_PHASE_HUNTER: + case NPC_AKKIRIS: + case NPC_SULFURON: + case NPC_TW_DRAKONAAR: + case NPC_BL_DRAKONAAR: + m_lSkyrissEventMobsGuidList.push_back(pCreature->GetObjectGuid()); + break; + } +} + +void instance_arcatraz::SetData(uint32 uiType, uint32 uiData) +{ + switch (uiType) + { + case TYPE_ENTRANCE: + case TYPE_ZEREKETH: + m_auiEncounter[uiType] = uiData; + break; + + case TYPE_DALLIAH: + if (uiData == IN_PROGRESS) + { + // Soccothares taunts after Dalliah gets aggro + if (GetData(TYPE_SOCCOTHRATES) != DONE) + StartNextDialogueText(TYPE_DALLIAH); + } + if (uiData == DONE) + { + DoUseDoorOrButton(GO_CORE_SECURITY_FIELD_BETA); + + // Soccothares taunts after Dalliah dies + if (GetData(TYPE_SOCCOTHRATES) != DONE) + StartNextDialogueText(TYPE_SOCCOTHRATES); + } + m_auiEncounter[uiType] = uiData; + break; + + case TYPE_SOCCOTHRATES: + if (uiData == DONE) + DoUseDoorOrButton(GO_CORE_SECURITY_FIELD_ALPHA); + m_auiEncounter[uiType] = uiData; + break; + + case TYPE_HARBINGERSKYRISS: + if (uiData == FAIL) + { + SetData(TYPE_WARDEN_1, NOT_STARTED); + SetData(TYPE_WARDEN_2, NOT_STARTED); + SetData(TYPE_WARDEN_3, NOT_STARTED); + SetData(TYPE_WARDEN_4, NOT_STARTED); + SetData(TYPE_WARDEN_5, NOT_STARTED); + + // Reset event in 1 min + if (Creature* pMellichar = GetSingleCreatureFromStorage(NPC_MELLICHAR)) + pMellichar->ForcedDespawn(); + m_uiResetDelayTimer = 60000; + + // Despawn all the summons manually + for (GuidList::const_iterator itr = m_lSkyrissEventMobsGuidList.begin(); itr != m_lSkyrissEventMobsGuidList.end(); ++itr) + { + if (Creature* pTemp = instance->GetCreature(*itr)) + pTemp->ForcedDespawn(); + } + + // Reset these objects, because they doesn't reset automatically + if (GameObject* pGo = GetSingleGameObjectFromStorage(GO_POD_BETA)) + pGo->ResetDoorOrButton(); + if (GameObject* pGo = GetSingleGameObjectFromStorage(GO_POD_OMEGA)) + pGo->ResetDoorOrButton(); + if (GameObject* pGo = GetSingleGameObjectFromStorage(GO_SEAL_SPHERE)) + pGo->ResetDoorOrButton(); + } + if (uiData == IN_PROGRESS) + { + StartNextDialogueText(YELL_MELLICHAR_INTRO1); + DoUseDoorOrButton(GO_SEAL_SPHERE); + } + if (uiData == DONE) + { + if (Creature* pMillhouse = GetSingleCreatureFromStorage(NPC_MILLHOUSE)) + DoScriptText(SAY_MILLHOUSE_COMPLETE, pMillhouse); + } + m_auiEncounter[3] = uiData; + break; + + case TYPE_WARDEN_1: + if (uiData == IN_PROGRESS) + DoUseDoorOrButton(GO_POD_ALPHA); + if (uiData == DONE) + StartNextDialogueText(YELL_MELLICHAR_RELEASE2); + m_auiEncounter[uiType] = uiData; + break; + + case TYPE_WARDEN_2: + if (uiData == IN_PROGRESS) + DoUseDoorOrButton(GO_POD_BETA); + if (uiData == DONE) + StartNextDialogueText(YELL_MELLICHAR_RELEASE3); + m_auiEncounter[uiType] = uiData; + break; + + case TYPE_WARDEN_3: + if (uiData == IN_PROGRESS) + DoUseDoorOrButton(GO_POD_DELTA); + if (uiData == DONE) + StartNextDialogueText(YELL_MELLICHAR_RELEASE4); + m_auiEncounter[uiType] = uiData; + break; + + case TYPE_WARDEN_4: + if (uiData == IN_PROGRESS) + DoUseDoorOrButton(GO_POD_GAMMA); + if (uiData == DONE) + StartNextDialogueText(YELL_MELLICHAR_RELEASE5); + m_auiEncounter[uiType] = uiData; + break; + + case TYPE_WARDEN_5: + if (uiData == IN_PROGRESS) + DoUseDoorOrButton(GO_POD_OMEGA); + m_auiEncounter[uiType] = uiData; + 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_strInstData = saveStream.str(); + + SaveToDB(); + OUT_SAVE_INST_DATA_COMPLETE; + } +} + +uint32 instance_arcatraz::GetData(uint32 uiType) const +{ + if (uiType < MAX_ENCOUNTER) + return m_auiEncounter[uiType]; + + return 0; +} + +void instance_arcatraz::Load(const char* chrIn) +{ + if (!chrIn) + { + 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]; + + for (uint8 i = 0; i < MAX_ENCOUNTER; ++i) + { + if (m_auiEncounter[i] == IN_PROGRESS) + m_auiEncounter[i] = NOT_STARTED; + } + + OUT_LOAD_INST_DATA_COMPLETE; +} + +void instance_arcatraz::JustDidDialogueStep(int32 iEntry) +{ + Creature* pMellichar = GetSingleCreatureFromStorage(NPC_MELLICHAR); + if (!pMellichar) + return; + + switch (iEntry) + { + case SPELL_TARGET_ALPHA: + pMellichar->CastSpell(pMellichar, SPELL_TARGET_ALPHA, false); + if (Creature* pTarget = GetSingleCreatureFromStorage(NPC_PRISON_APHPA_POD)) + pMellichar->SetFacingToObject(pTarget); + SetData(TYPE_WARDEN_1, IN_PROGRESS); + break; + case YELL_MELLICHAR_RELEASE1: + pMellichar->SummonCreature(urand(0, 1) ? NPC_BLAZING_TRICKSTER : NPC_PHASE_HUNTER, aSummonPosition[0].m_fX, aSummonPosition[0].m_fY, aSummonPosition[0].m_fZ, aSummonPosition[0].m_fO, TEMPSUMMON_DEAD_DESPAWN, 0); + break; + case YELL_MELLICHAR_RELEASE2: + if (Creature* pTarget = GetSingleCreatureFromStorage(NPC_PRISON_BETA_POD)) + pMellichar->SetFacingToObject(pTarget); + break; + case SPELL_TARGET_BETA: + pMellichar->CastSpell(pMellichar, SPELL_TARGET_BETA, false); + SetData(TYPE_WARDEN_2, IN_PROGRESS); + break; + case TYPE_WARDEN_2: + pMellichar->SummonCreature(NPC_MILLHOUSE, aSummonPosition[1].m_fX, aSummonPosition[1].m_fY, aSummonPosition[1].m_fZ, aSummonPosition[1].m_fO, TEMPSUMMON_DEAD_DESPAWN, 0); + break; + case SPELL_TARGET_DELTA: + pMellichar->CastSpell(pMellichar, SPELL_TARGET_DELTA, false); + if (Creature* pTarget = GetSingleCreatureFromStorage(NPC_PRISON_DELTA_POD)) + pMellichar->SetFacingToObject(pTarget); + SetData(TYPE_WARDEN_3, IN_PROGRESS); + break; + case TYPE_WARDEN_3: + pMellichar->SummonCreature(urand(0, 1) ? NPC_AKKIRIS : NPC_SULFURON, aSummonPosition[2].m_fX, aSummonPosition[2].m_fY, aSummonPosition[2].m_fZ, aSummonPosition[2].m_fO, TEMPSUMMON_DEAD_DESPAWN, 0); + pMellichar->CastSpell(pMellichar, SPELL_TARGET_OMEGA, false); + if (Creature* pTarget = GetSingleCreatureFromStorage(NPC_PRISON_BOSS_POD)) + pMellichar->SetFacingToObject(pTarget); + break; + case YELL_MELLICHAR_RELEASE4: + pMellichar->InterruptNonMeleeSpells(false); + if (Creature* pTarget = GetSingleCreatureFromStorage(NPC_PRISON_GAMMA_POD)) + pMellichar->SetFacingToObject(pTarget); + break; + case SPELL_TARGET_GAMMA: + pMellichar->CastSpell(pMellichar, SPELL_TARGET_GAMMA, false); + if (Creature* pTarget = GetSingleCreatureFromStorage(NPC_PRISON_GAMMA_POD)) + pMellichar->SetFacingToObject(pTarget); + SetData(TYPE_WARDEN_4, IN_PROGRESS); + break; + case TYPE_WARDEN_4: + pMellichar->SummonCreature(urand(0, 1) ? NPC_TW_DRAKONAAR : NPC_BL_DRAKONAAR, aSummonPosition[3].m_fX, aSummonPosition[3].m_fY, aSummonPosition[3].m_fZ, aSummonPosition[3].m_fO, TEMPSUMMON_DEAD_DESPAWN, 0); + pMellichar->CastSpell(pMellichar, SPELL_TARGET_OMEGA, false); + if (Creature* pTarget = GetSingleCreatureFromStorage(NPC_PRISON_BOSS_POD)) + pMellichar->SetFacingToObject(pTarget); + break; + case YELL_MELLICHAR_RELEASE5: + pMellichar->InterruptNonMeleeSpells(false); + SetData(TYPE_WARDEN_5, IN_PROGRESS); + break; + case TYPE_WARDEN_5: + if (Creature* pSkyriss = pMellichar->SummonCreature(NPC_SKYRISS, aSummonPosition[4].m_fX, aSummonPosition[4].m_fY, aSummonPosition[4].m_fZ, aSummonPosition[4].m_fO, TEMPSUMMON_DEAD_DESPAWN, 0)) + pSkyriss->CastSpell(pSkyriss, SPELL_SIMPLE_TELEPORT, false); + break; + case YELL_MELLICAR_WELCOME: + if (Creature* pSkyriss = GetSingleCreatureFromStorage(NPC_SKYRISS)) + pSkyriss->CastSpell(pSkyriss, SPELL_MIND_REND, false); + break; + case SAY_SKYRISS_AGGRO: + // Kill Mellichar and start combat + if (Creature* pSkyriss = GetSingleCreatureFromStorage(NPC_SKYRISS)) + { + pSkyriss->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_PASSIVE); + pMellichar->DealDamage(pMellichar, pMellichar->GetHealth(), NULL, DIRECT_DAMAGE, SPELL_SCHOOL_MASK_NORMAL, NULL, false); + } + DoUseDoorOrButton(GO_SEAL_SPHERE); + break; + } +} + +void instance_arcatraz::OnCreatureDeath(Creature* pCreature) +{ + if (pCreature->GetEntry() == NPC_ARCATRAZ_WARDEN || pCreature->GetEntry() == NPC_ARCATRAZ_DEFENDER) + { + ++m_uiKilledWardens; + + // Stop the intro spawns when the wardens are killed + if (m_uiKilledWardens == MAX_WARDENS) + { + SetData(TYPE_ENTRANCE, DONE); + m_uiEntranceEventTimer = 0; + } + } +} + +void instance_arcatraz::Update(uint32 uiDiff) +{ + DialogueUpdate(uiDiff); + + if (m_uiResetDelayTimer) + { + if (m_uiResetDelayTimer <= uiDiff) + { + if (Creature* pMellichar = GetSingleCreatureFromStorage(NPC_MELLICHAR)) + pMellichar->Respawn(); + m_uiResetDelayTimer = 0; + } + else + m_uiResetDelayTimer -= uiDiff; + } + + if (m_uiEntranceEventTimer) + { + if (m_uiEntranceEventTimer <= uiDiff) + { + Player* pPlayer = GetPlayerInMap(); + if (!pPlayer) + return; + + uint32 uiEntry = urand(0, 10) ? NPC_PROTEAN_HORROR : NPC_PROTEAN_NIGHTMARE; + + // Summon and move the intro creatures into combat positions + if (Creature* pTemp = pPlayer->SummonCreature(uiEntry, aEntranceSpawnLoc[0], aEntranceSpawnLoc[1], aEntranceSpawnLoc[2], aEntranceSpawnLoc[3], TEMPSUMMON_TIMED_OOC_OR_DEAD_DESPAWN, 30000)) + { + pTemp->SetWalk(false); + pTemp->GetMotionMaster()->MovePoint(0, aEntranceMoveLoc[0], aEntranceMoveLoc[1], aEntranceMoveLoc[2]); + } + m_uiEntranceEventTimer = urand(0, 10) ? urand(2000, 3500) : urand(5000, 7000); + } + else + m_uiEntranceEventTimer -= uiDiff; + } +} + +InstanceData* GetInstanceData_instance_arcatraz(Map* pMap) +{ + return new instance_arcatraz(pMap); +} + +void AddSC_instance_arcatraz() +{ + Script* pNewScript; + + pNewScript = new Script; + pNewScript->Name = "instance_arcatraz"; + pNewScript->GetInstanceData = &GetInstanceData_instance_arcatraz; + pNewScript->RegisterSelf(); +} diff --git a/src/modules/SD2/scripts/outland/tempest_keep/botanica/boss_high_botanist_freywinn.cpp b/src/modules/SD2/scripts/outland/tempest_keep/botanica/boss_high_botanist_freywinn.cpp new file mode 100644 index 000000000..78ab1faf3 --- /dev/null +++ b/src/modules/SD2/scripts/outland/tempest_keep/botanica/boss_high_botanist_freywinn.cpp @@ -0,0 +1,207 @@ +/* Copyright (C) 2006 - 2013 ScriptDev2 + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with 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: Timers may need some fine adjustments +SDCategory: Tempest Keep, The Botanica +EndScriptData */ + +#include "precompiled.h" + +enum +{ + SAY_AGGRO = -1553000, + SAY_KILL_1 = -1553001, + SAY_KILL_2 = -1553002, + SAY_TREE_1 = -1553003, + SAY_TREE_2 = -1553004, + SAY_DEATH = -1553005, + + SPELL_TRANQUILITY = 34550, + SPELL_TREE_FORM = 34551, + SPELL_SUMMON_FRAYER = 34557, + SPELL_PLANT_WHITE = 34759, + SPELL_PLANT_GREEN = 34761, + SPELL_PLANT_BLUE = 34762, + SPELL_PLANT_RED = 34763, + + NPC_FRAYER_PROTECTOR = 19953, +}; + +struct boss_high_botanist_freywinnAI : public ScriptedAI +{ + boss_high_botanist_freywinnAI(Creature* pCreature) : ScriptedAI(pCreature) { Reset(); } + + uint32 m_uiSummonSeedlingTimer; + uint32 m_uiTreeFormTimer; + uint32 m_uiFrayerTimer; + uint32 m_uiTreeFormEndTimer; + uint8 m_uiFrayerAddsCount; + bool m_bCanMoveFree; + + void Reset() override + { + m_uiSummonSeedlingTimer = 6000; + m_uiTreeFormTimer = 30000; + m_uiTreeFormEndTimer = 0; + m_uiFrayerAddsCount = 0; + m_uiFrayerTimer = 0; + m_bCanMoveFree = true; + } + + void Aggro(Unit* /*pWho*/) override + { + DoScriptText(SAY_AGGRO, m_creature); + } + + void JustSummoned(Creature* pSummoned) override + { + if (pSummoned->GetEntry() == NPC_FRAYER_PROTECTOR) + ++m_uiFrayerAddsCount; + + // Attack players + if (m_creature->getVictim()) + pSummoned->AI()->AttackStart(m_creature->getVictim()); + } + + void SummonedCreatureJustDied(Creature* pSummoned) override + { + if (pSummoned->GetEntry() == NPC_FRAYER_PROTECTOR) + { + --m_uiFrayerAddsCount; + + // When all 3 Frayers are killed stop the tree form action (if not done this already) + if (!m_uiFrayerAddsCount && !m_bCanMoveFree) + { + m_uiTreeFormEndTimer = 0; + + if (m_creature->getVictim()) + m_creature->GetMotionMaster()->MoveChase(m_creature->getVictim()); + + // Interrupt all spells and remove auras + m_creature->InterruptNonMeleeSpells(true); + m_creature->RemoveAllAuras(); + m_bCanMoveFree = true; + } + } + } + + // Wrapper to summon one seedling + void DoSummonSeedling() + { + switch (urand(0, 3)) + { + case 0: DoCastSpellIfCan(m_creature, SPELL_PLANT_WHITE); break; + case 1: DoCastSpellIfCan(m_creature, SPELL_PLANT_GREEN); break; + case 2: DoCastSpellIfCan(m_creature, SPELL_PLANT_BLUE); break; + case 3: DoCastSpellIfCan(m_creature, SPELL_PLANT_RED); break; + } + } + + void KilledUnit(Unit* /*pVictim*/) override + { + DoScriptText(urand(0, 1) ? SAY_KILL_1 : SAY_KILL_2, m_creature); + } + + void JustDied(Unit* /*pKiller*/) override + { + DoScriptText(SAY_DEATH, m_creature); + } + + void UpdateAI(const uint32 uiDiff) override + { + if (!m_creature->SelectHostileTarget() || !m_creature->getVictim()) + return; + + if (m_uiTreeFormTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature, SPELL_TRANQUILITY) == CAST_OK) + { + // Note: This should remove only negative auras + m_creature->RemoveAllAuras(); + + DoCastSpellIfCan(m_creature, SPELL_TREE_FORM, CAST_TRIGGERED); + DoScriptText(urand(0, 1) ? SAY_TREE_1 : SAY_TREE_2, m_creature); + + m_creature->GetMotionMaster()->MoveIdle(); + m_bCanMoveFree = false; + m_uiFrayerTimer = 1000; + m_uiTreeFormEndTimer = 45000; + m_uiTreeFormTimer = 75000; + } + } + else + m_uiTreeFormTimer -= uiDiff; + + // The Frayer is summoned after one second in the tree phase + if (m_uiFrayerTimer) + { + if (m_uiFrayerTimer <= uiDiff) + { + if (DoCastSpellIfCan(m_creature, SPELL_SUMMON_FRAYER, CAST_TRIGGERED) == CAST_OK) + m_uiFrayerTimer = 0; + } + else + m_uiFrayerTimer -= uiDiff; + } + + // Tree phase will be removed when the timer expires; + if (m_uiTreeFormEndTimer) + { + if (m_uiTreeFormEndTimer <= uiDiff) + { + if (m_creature->getVictim()) + m_creature->GetMotionMaster()->MoveChase(m_creature->getVictim()); + m_bCanMoveFree = true; + m_uiTreeFormEndTimer = 0; + } + else + m_uiTreeFormEndTimer -= uiDiff; + } + + // Don't do any other actions during tree form + if (!m_bCanMoveFree) + return; + + // one random seedling every 5 secs, but not in tree form + if (m_uiSummonSeedlingTimer < uiDiff) + { + DoSummonSeedling(); + m_uiSummonSeedlingTimer = 6000; + } + else + m_uiSummonSeedlingTimer -= uiDiff; + + DoMeleeAttackIfReady(); + } +}; + +CreatureAI* GetAI_boss_high_botanist_freywinn(Creature* pCreature) +{ + return new boss_high_botanist_freywinnAI(pCreature); +} + +void AddSC_boss_high_botanist_freywinn() +{ + Script* pNewScript; + + pNewScript = new Script; + pNewScript->Name = "boss_high_botanist_freywinn"; + pNewScript->GetAI = &GetAI_boss_high_botanist_freywinn; + pNewScript->RegisterSelf(); +} diff --git a/src/modules/SD2/scripts/outland/tempest_keep/botanica/boss_laj.cpp b/src/modules/SD2/scripts/outland/tempest_keep/botanica/boss_laj.cpp new file mode 100644 index 000000000..f4f28ec86 --- /dev/null +++ b/src/modules/SD2/scripts/outland/tempest_keep/botanica/boss_laj.cpp @@ -0,0 +1,227 @@ +/* Copyright (C) 2006 - 2013 ScriptDev2 + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with 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" + +enum +{ + EMOTE_SUMMON = -1553006, + + SPELL_ALLERGIC_REACTION = 34697, + SPELL_TELEPORT_SELF = 34673, + SPELL_TRASH = 3391, + + SPELL_SUMMON_LASHER_1 = 34681, + SPELL_SUMMON_FLAYER_1 = 34682, + SPELL_SUMMON_LASHER_2 = 34684, + SPELL_SUMMON_FLAYER_2 = 34685, + SPELL_SUMMON_LASHER_3 = 34686, + SPELL_SUMMON_FLAYER_4 = 34687, + SPELL_SUMMON_LASHER_4 = 34688, + SPELL_SUMMON_FLAYER_3 = 34690, + + MODEL_ID_DEFAULT = 13109, + MODEL_ID_ARCANE = 14213, + MODEL_ID_FIRE = 13110, + MODEL_ID_FROST = 14112, + MODEL_ID_NATURE = 14214, +}; + +struct boss_lajAI : public ScriptedAI +{ + boss_lajAI(Creature* pCreature) : ScriptedAI(pCreature) { Reset(); } + + uint32 m_uiTeleportTimer; + uint32 m_uiSummonTimer; + uint32 m_uiTransformTimer; + uint32 m_uiAllergicTimer; + uint32 m_uiTrashTimer; + + void Reset() override + { + m_creature->SetDisplayId(MODEL_ID_DEFAULT); + m_creature->ApplySpellImmune(0, IMMUNITY_SCHOOL, SPELL_SCHOOL_MASK_SHADOW, true); + m_creature->ApplySpellImmune(0, IMMUNITY_SCHOOL, SPELL_SCHOOL_MASK_ARCANE, false); + m_creature->ApplySpellImmune(0, IMMUNITY_SCHOOL, SPELL_SCHOOL_MASK_FIRE, false); + m_creature->ApplySpellImmune(0, IMMUNITY_SCHOOL, SPELL_SCHOOL_MASK_FROST, false); + m_creature->ApplySpellImmune(0, IMMUNITY_SCHOOL, SPELL_SCHOOL_MASK_NATURE, false); + + m_uiTeleportTimer = urand(17000, 26000); + m_uiSummonTimer = 0; + m_uiTransformTimer = 30000; + m_uiAllergicTimer = urand(8500, 30000); + m_uiTrashTimer = urand(3600, 5000); + } + + void DoTransform() + { + // Random transform into a different form + switch (urand(0, 4)) + { + case 0: + m_creature->SetDisplayId(MODEL_ID_DEFAULT); + m_creature->ApplySpellImmune(0, IMMUNITY_SCHOOL, SPELL_SCHOOL_MASK_SHADOW, true); + m_creature->ApplySpellImmune(0, IMMUNITY_SCHOOL, SPELL_SCHOOL_MASK_ARCANE, false); + m_creature->ApplySpellImmune(0, IMMUNITY_SCHOOL, SPELL_SCHOOL_MASK_FIRE, false); + m_creature->ApplySpellImmune(0, IMMUNITY_SCHOOL, SPELL_SCHOOL_MASK_FROST, false); + m_creature->ApplySpellImmune(0, IMMUNITY_SCHOOL, SPELL_SCHOOL_MASK_NATURE, false); + break; + case 1: + m_creature->SetDisplayId(MODEL_ID_ARCANE); + m_creature->ApplySpellImmune(0, IMMUNITY_SCHOOL, SPELL_SCHOOL_MASK_SHADOW, false); + m_creature->ApplySpellImmune(0, IMMUNITY_SCHOOL, SPELL_SCHOOL_MASK_ARCANE, true); + m_creature->ApplySpellImmune(0, IMMUNITY_SCHOOL, SPELL_SCHOOL_MASK_FIRE, false); + m_creature->ApplySpellImmune(0, IMMUNITY_SCHOOL, SPELL_SCHOOL_MASK_FROST, false); + m_creature->ApplySpellImmune(0, IMMUNITY_SCHOOL, SPELL_SCHOOL_MASK_NATURE, false); + break; + case 2: + m_creature->SetDisplayId(MODEL_ID_FIRE); + m_creature->ApplySpellImmune(0, IMMUNITY_SCHOOL, SPELL_SCHOOL_MASK_SHADOW, false); + m_creature->ApplySpellImmune(0, IMMUNITY_SCHOOL, SPELL_SCHOOL_MASK_ARCANE, false); + m_creature->ApplySpellImmune(0, IMMUNITY_SCHOOL, SPELL_SCHOOL_MASK_FIRE, true); + m_creature->ApplySpellImmune(0, IMMUNITY_SCHOOL, SPELL_SCHOOL_MASK_FROST, false); + m_creature->ApplySpellImmune(0, IMMUNITY_SCHOOL, SPELL_SCHOOL_MASK_NATURE, false); + break; + case 3: + m_creature->SetDisplayId(MODEL_ID_FROST); + m_creature->ApplySpellImmune(0, IMMUNITY_SCHOOL, SPELL_SCHOOL_MASK_SHADOW, false); + m_creature->ApplySpellImmune(0, IMMUNITY_SCHOOL, SPELL_SCHOOL_MASK_ARCANE, false); + m_creature->ApplySpellImmune(0, IMMUNITY_SCHOOL, SPELL_SCHOOL_MASK_FIRE, false); + m_creature->ApplySpellImmune(0, IMMUNITY_SCHOOL, SPELL_SCHOOL_MASK_FROST, true); + m_creature->ApplySpellImmune(0, IMMUNITY_SCHOOL, SPELL_SCHOOL_MASK_NATURE, false); + break; + case 4: + m_creature->SetDisplayId(MODEL_ID_NATURE); + m_creature->ApplySpellImmune(0, IMMUNITY_SCHOOL, SPELL_SCHOOL_MASK_SHADOW, false); + m_creature->ApplySpellImmune(0, IMMUNITY_SCHOOL, SPELL_SCHOOL_MASK_ARCANE, false); + m_creature->ApplySpellImmune(0, IMMUNITY_SCHOOL, SPELL_SCHOOL_MASK_FIRE, false); + m_creature->ApplySpellImmune(0, IMMUNITY_SCHOOL, SPELL_SCHOOL_MASK_FROST, false); + m_creature->ApplySpellImmune(0, IMMUNITY_SCHOOL, SPELL_SCHOOL_MASK_NATURE, true); + break; + } + } + + void DoSummons() + { + switch (urand(0, 3)) + { + case 0: + DoCastSpellIfCan(m_creature, SPELL_SUMMON_LASHER_1, CAST_TRIGGERED); + DoCastSpellIfCan(m_creature, SPELL_SUMMON_FLAYER_1, CAST_TRIGGERED); + break; + case 1: + DoCastSpellIfCan(m_creature, SPELL_SUMMON_LASHER_2, CAST_TRIGGERED); + DoCastSpellIfCan(m_creature, SPELL_SUMMON_FLAYER_2, CAST_TRIGGERED); + break; + case 2: + DoCastSpellIfCan(m_creature, SPELL_SUMMON_LASHER_3, CAST_TRIGGERED); + DoCastSpellIfCan(m_creature, SPELL_SUMMON_FLAYER_3, CAST_TRIGGERED); + break; + case 3: + DoCastSpellIfCan(m_creature, SPELL_SUMMON_LASHER_4, CAST_TRIGGERED); + DoCastSpellIfCan(m_creature, SPELL_SUMMON_FLAYER_4, CAST_TRIGGERED); + break; + } + } + + void JustSummoned(Creature* pSummoned) override + { + if (Unit* pTarget = m_creature->SelectAttackingTarget(ATTACKING_TARGET_RANDOM, 0)) + pSummoned->AI()->AttackStart(pTarget); + } + + void UpdateAI(const uint32 uiDiff) override + { + if (!m_creature->SelectHostileTarget() || !m_creature->getVictim()) + return; + + if (m_uiSummonTimer) + { + if (m_uiSummonTimer <= uiDiff) + { + // Summon adds and restart chasing the victim + DoSummons(); + DoScriptText(EMOTE_SUMMON, m_creature); + + if (m_creature->getVictim()) + m_creature->GetMotionMaster()->MoveChase(m_creature->getVictim()); + m_uiSummonTimer = 0; + } + else + m_uiSummonTimer -= uiDiff; + } + + if (m_uiAllergicTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature->getVictim(), SPELL_ALLERGIC_REACTION) == CAST_OK) + m_uiAllergicTimer = urand(21000, 32000); + } + else + m_uiAllergicTimer -= uiDiff; + + if (m_uiTeleportTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature, SPELL_TELEPORT_SELF) == CAST_OK) + { + m_creature->GetMotionMaster()->MoveIdle(); + m_uiTeleportTimer = urand(25000, 33000); + m_uiSummonTimer = 4000; + } + } + else + m_uiTeleportTimer -= uiDiff; + + if (m_uiTransformTimer < uiDiff) + { + DoTransform(); + m_uiTransformTimer = urand(25000, 40000); + } + else + m_uiTransformTimer -= uiDiff; + + if (m_uiTrashTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature->getVictim(), SPELL_TRASH) == CAST_OK) + m_uiTrashTimer = urand(10000, 24000); + } + else + m_uiTrashTimer -= uiDiff; + + DoMeleeAttackIfReady(); + } +}; + +CreatureAI* GetAI_boss_laj(Creature* pCreature) +{ + return new boss_lajAI(pCreature); +} + +void AddSC_boss_laj() +{ + Script* pNewScript; + + pNewScript = new Script; + pNewScript->Name = "boss_laj"; + pNewScript->GetAI = &GetAI_boss_laj; + pNewScript->RegisterSelf(); +} diff --git a/src/modules/SD2/scripts/outland/tempest_keep/botanica/boss_warp_splinter.cpp b/src/modules/SD2/scripts/outland/tempest_keep/botanica/boss_warp_splinter.cpp new file mode 100644 index 000000000..b07e83612 --- /dev/null +++ b/src/modules/SD2/scripts/outland/tempest_keep/botanica/boss_warp_splinter.cpp @@ -0,0 +1,192 @@ +/* Copyright (C) 2006 - 2013 ScriptDev2 + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with 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: 90 +SDComment: Timers may need adjustments +SDCategory: Tempest Keep, The Botanica +EndScriptData */ + +#include "precompiled.h" + +/*##### +# boss_warp_splinter +#####*/ + +enum +{ + SAY_AGGRO = -1553007, + SAY_SLAY_1 = -1553008, + SAY_SLAY_2 = -1553009, + SAY_SUMMON_1 = -1553010, + SAY_SUMMON_2 = -1553011, + SAY_DEATH = -1553012, + + SPELL_WAR_STOMP = 34716, + SPELL_SUMMON_SAPLINGS = 34741, // this will leech the health from all saplings + SPELL_ARCANE_VOLLEY = 36705, + SPELL_ARCANE_VOLLEY_H = 39133, + + NPC_SAPLING = 19949, +}; + +// Summon Saplings spells (too many to declare them above) +static const uint32 aSaplingsSummonSpells[10] = {34727, 34730, 34731, 34732, 34733, 34734, 34735, 34736, 34737, 34739}; + +struct boss_warp_splinterAI : public ScriptedAI +{ + boss_warp_splinterAI(Creature* pCreature) : ScriptedAI(pCreature) + { + // Add the summon spells to a vector for better handling + for (uint8 i = 0; i < 10; ++i) + m_vSummonSpells.push_back(aSaplingsSummonSpells[i]); + + m_bIsRegularMode = pCreature->GetMap()->IsRegularDifficulty(); + Reset(); + } + + bool m_bIsRegularMode; + + uint32 m_uiWarStompTimer; + uint32 m_uiSummonTreantsTimer; + uint32 m_uiArcaneVolleyTimer; + + std::vector m_vSummonSpells; + + void Reset() override + { + m_uiWarStompTimer = urand(6000, 7000); + m_uiSummonTreantsTimer = urand(25000, 35000); + m_uiArcaneVolleyTimer = urand(12000, 14500); + } + + void Aggro(Unit* /*pWho*/) override + { + DoScriptText(SAY_AGGRO, m_creature); + } + + void KilledUnit(Unit* /*pVictim*/) override + { + DoScriptText(urand(0, 1) ? SAY_SLAY_1 : SAY_SLAY_2, m_creature); + } + + void JustDied(Unit* /*pKiller*/) override + { + DoScriptText(SAY_DEATH, m_creature); + } + + void JustSummoned(Creature* pSummoned) override + { + if (pSummoned->GetEntry() == NPC_SAPLING) + pSummoned->GetMotionMaster()->MoveFollow(m_creature, 0, 0); + } + + // Wrapper to summon all Saplings + void SummonTreants() + { + // Choose 6 random spells out of 10 + std::random_shuffle(m_vSummonSpells.begin(), m_vSummonSpells.end()); + for (uint8 i = 0; i < 6; ++i) + DoCastSpellIfCan(m_creature, m_vSummonSpells[i], CAST_TRIGGERED); + + DoCastSpellIfCan(m_creature, SPELL_SUMMON_SAPLINGS, CAST_TRIGGERED); + DoScriptText(urand(0, 1) ? SAY_SUMMON_1 : SAY_SUMMON_2, m_creature); + } + + void UpdateAI(const uint32 uiDiff) override + { + if (!m_creature->SelectHostileTarget() || !m_creature->getVictim()) + return; + + // War Stomp + if (m_uiWarStompTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature, SPELL_WAR_STOMP) == CAST_OK) + m_uiWarStompTimer = urand(17000, 38000); + } + else + m_uiWarStompTimer -= uiDiff; + + // Arcane Volley + if (m_uiArcaneVolleyTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature, m_bIsRegularMode ? SPELL_ARCANE_VOLLEY : SPELL_ARCANE_VOLLEY_H) == CAST_OK) + m_uiArcaneVolleyTimer = urand(16000, 38000); + } + else + m_uiArcaneVolleyTimer -= uiDiff; + + // Summon Treants + if (m_uiSummonTreantsTimer < uiDiff) + { + SummonTreants(); + m_uiSummonTreantsTimer = urand(37000, 55000); + } + else + m_uiSummonTreantsTimer -= uiDiff; + + DoMeleeAttackIfReady(); + } +}; + +/*##### +# mob_treant (Sapling) +#####*/ +struct npc_saplingAI : public ScriptedAI +{ + npc_saplingAI(Creature* pCreature) : ScriptedAI(pCreature) { Reset(); } + + void Reset() override + { + // ToDo: This one may need further reserch + // m_creature->SetSpeedRate(MOVE_RUN, 0.5f); + } + + void MoveInLineOfSight(Unit* /*pWho*/) override { } + void UpdateAI(const uint32 /*uiDiff*/) override + { + if (!m_creature->SelectHostileTarget() || !m_creature->getVictim()) + return; + + DoMeleeAttackIfReady(); + } +}; + +CreatureAI* GetAI_boss_warp_splinter(Creature* pCreature) +{ + return new boss_warp_splinterAI(pCreature); +} + +CreatureAI* GetAI_npc_sapling(Creature* pCreature) +{ + return new npc_saplingAI(pCreature); +} + +void AddSC_boss_warp_splinter() +{ + Script* pNewScript; + + pNewScript = new Script; + pNewScript->Name = "boss_warp_splinter"; + pNewScript->GetAI = &GetAI_boss_warp_splinter; + pNewScript->RegisterSelf(); + + pNewScript = new Script; + pNewScript->Name = "mob_warp_splinter_treant"; + pNewScript->GetAI = &GetAI_npc_sapling; + pNewScript->RegisterSelf(); +} diff --git a/src/modules/SD2/scripts/outland/tempest_keep/the_eye/boss_alar.cpp b/src/modules/SD2/scripts/outland/tempest_keep/the_eye/boss_alar.cpp new file mode 100644 index 000000000..4b904a190 --- /dev/null +++ b/src/modules/SD2/scripts/outland/tempest_keep/the_eye/boss_alar.cpp @@ -0,0 +1,444 @@ +/* Copyright (C) 2006 - 2013 ScriptDev2 + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with 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: 90 +SDComment: Boss movement should be improved. +SDCategory: Tempest Keep, The Eye +EndScriptData */ + +#include "precompiled.h" +#include "the_eye.h" + +enum +{ + // spells + // phase 1 + SPELL_FLAME_BUFFET = 34121, // if nobody is in range + SPELL_FLAME_QUILLS = 34229, + SPELL_EMBER_BLAST = 34341, // usee when the boss dies first time + SPELL_REBIRTH = 34342, + + // phase 2 + SPELL_MELT_ARMOR = 35410, + SPELL_DIVE_BOMB_VISUAL = 35367, // visual transform to fire ball + SPELL_DIVE_BOMB = 35181, // dive bomb damage spell + SPELL_BOMB_REBIRTH = 35369, // used after the dive bomb - to transform back to phoenis + SPELL_CHARGE = 35412, // charge a random target + // SPELL_SUMMON_ADDS = 18814, // summons 3*19551 - Not sure if the spell is the right id + SPELL_BERSERK = 27680, // this spell is used only during phase II + + NPC_EMBER_OF_ALAR = 19551, // scripted in Acid + NPC_FLAME_PATCH = 20602, + SPELL_FLAME_PATCH = 35380, + + MAX_PLATFORMS = 4, + + POINT_ID_RESSURRECT = 0, // center of the hall + POINT_ID_PLATFORM = 1, // platform points + POINT_ID_QUILLS = 2, // center of the hall - in air + + PHASE_ONE = 1, + PHASE_REBIRTH = 2, + PHASE_TWO = 3, + PHASE_DIVE_BOMB = 4, +}; + +struct EventLocation +{ + float m_fX, m_fY, m_fZ; +}; + +// Platform locations from left to right (as standing at the entrance) +static const EventLocation aPlatformLocation[MAX_PLATFORMS] = +{ + {340.15f, 58.65f, 17.71f}, + {388.09f, 31.54f, 20.18f}, + {388.18f, -32.85f, 20.18f}, + {340.29f, -60.19f, 17.72f} +}; + +static const EventLocation aCenterLocation[] = +{ + {331.0f, 0.01f, 39.0f}, + {331.0, 0.01f, -2.39f}, +}; + +struct boss_alarAI : public ScriptedAI +{ + boss_alarAI(Creature* pCreature) : ScriptedAI(pCreature) + { + m_pInstance = ((ScriptedInstance*)pCreature->GetInstanceData()); + Reset(); + } + + ScriptedInstance* m_pInstance; + + uint8 m_uiPhase; + uint8 m_uiCurrentPlatformId; + uint32 m_uiRangeCheckTimer; + uint32 m_uiBerserkTimer; + + uint32 m_uiPlatformMoveTimer; + uint32 m_uiFlameQuillsTimer; + uint32 m_uiFlamePatchTimer; + uint32 m_uiDiveBombTimer; + uint32 m_uiChargeTimer; + uint32 m_uiRebirthTimer; + uint32 m_uiMeltArmorTimer; + + bool m_bCanSummonEmber; + + void Reset() override + { + // Start phase one and move to the closest platform + m_uiPhase = PHASE_ONE; + SetCombatMovement(false); + + m_uiRangeCheckTimer = 0; + m_uiCurrentPlatformId = 0; + m_uiPlatformMoveTimer = 35000; + m_uiFlameQuillsTimer = 170000; // at the 5th platform + + m_uiBerserkTimer = 10 * MINUTE * IN_MILLISECONDS; // only after phase 2 starts + m_uiFlamePatchTimer = 20000; + m_uiDiveBombTimer = 30000; + m_uiMeltArmorTimer = 10000; + m_uiChargeTimer = 20000; + m_uiRebirthTimer = 0; + + m_bCanSummonEmber = true; + } + + void Aggro(Unit* /*pWho*/) override + { + if (m_pInstance) + m_pInstance->SetData(TYPE_ALAR, IN_PROGRESS); + + // The boss will always move to the first platform from the left side; also set the movement to idle to stop the DB movement + m_creature->GetMotionMaster()->MoveIdle(); + m_creature->GetMotionMaster()->MovePoint(POINT_ID_PLATFORM, aPlatformLocation[m_uiCurrentPlatformId].m_fX, aPlatformLocation[m_uiCurrentPlatformId].m_fY, aPlatformLocation[m_uiCurrentPlatformId].m_fZ); + } + + void JustReachedHome() override + { + if (m_pInstance) + m_pInstance->SetData(TYPE_ALAR, FAIL); + } + + void JustDied(Unit* /*pKiller*/) override + { + if (m_pInstance) + m_pInstance->SetData(TYPE_ALAR, DONE); + } + + void JustSummoned(Creature* pSummoned) override + { + if (pSummoned->GetEntry() == NPC_FLAME_PATCH) + pSummoned->CastSpell(pSummoned, SPELL_FLAME_PATCH, true); + else + { + if (Unit* pTarget = m_creature->SelectAttackingTarget(ATTACKING_TARGET_RANDOM, 0)) + pSummoned->AI()->AttackStart(pTarget); + } + } + + void SummonedCreatureJustDied(Creature* pSummoned) override + { + // drain 3% of boss health when the ember dies + if (pSummoned->GetEntry() == NPC_EMBER_OF_ALAR) + { + // Check first if we have enough health to drain + if (m_creature->GetMaxHealth()*.03f > m_creature->GetHealth()) + m_creature->DealDamage(m_creature, m_creature->GetMaxHealth()*.03f, NULL, DIRECT_DAMAGE, SPELL_SCHOOL_MASK_NORMAL, NULL, false); + } + } + + void EnterEvadeMode() override + { + // Don't evade if the boss has the ember blast invisibility aura + if (m_creature->HasAura(SPELL_EMBER_BLAST)) + return; + + ScriptedAI::EnterEvadeMode(); + } + + void MovementInform(uint32 uiMotionType, uint32 uiPointId) override + { + if (uiMotionType != POINT_MOTION_TYPE) + return; + + switch (uiPointId) + { + case POINT_ID_QUILLS: + if (m_uiPhase == PHASE_ONE) + { + if (DoCastSpellIfCan(m_creature, SPELL_FLAME_QUILLS) == CAST_OK) + { + // Set the platform id so the boss will move to the last or the first platform + m_uiCurrentPlatformId = urand(0, 1) ? 2 : 3; + m_uiPlatformMoveTimer = 10000; + } + } + else if (m_uiPhase == PHASE_DIVE_BOMB) + { + if (DoCastSpellIfCan(m_creature, SPELL_DIVE_BOMB_VISUAL) == CAST_OK) + m_uiDiveBombTimer = 5000; + } + break; + case POINT_ID_PLATFORM: + // When we reach the platform we start the range check and we can summon the embers + m_bCanSummonEmber = true; + m_uiRangeCheckTimer = 2000; + break; + case POINT_ID_RESSURRECT: + // remove the invisibility aura + if (m_creature->HasAura(SPELL_EMBER_BLAST)) + m_creature->RemoveAurasDueToSpell(SPELL_EMBER_BLAST); + + m_creature->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE); + m_creature->SetStandState(UNIT_STAND_STATE_STAND); + + // cast rebirth and remove fake death + if (DoCastSpellIfCan(m_creature, SPELL_REBIRTH) == CAST_OK) + { + DoResetThreat(); + + // start following target + SetCombatMovement(true); + if (m_creature->getVictim()) + m_creature->GetMotionMaster()->MoveChase(m_creature->getVictim()); + + m_uiPhase = PHASE_TWO; + } + break; + } + } + + void DamageTaken(Unit* /*pKiller*/, uint32& uiDamage) override + { + // Only init fake in phase one + if (m_uiPhase != PHASE_ONE) + return; + + if (uiDamage < m_creature->GetHealth()) + return; + + m_creature->InterruptNonMeleeSpells(true); + // We set the health to 1 in order to avoid the forced death stand flag - this way we can have the ressurrect animation + m_creature->SetHealth(1); + m_creature->StopMoving(); + m_creature->ClearComboPointHolders(); + m_creature->RemoveAllAurasOnDeath(); + m_creature->ModifyAuraState(AURA_STATE_HEALTHLESS_20_PERCENT, false); + m_creature->ModifyAuraState(AURA_STATE_HEALTHLESS_35_PERCENT, false); + m_creature->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE); + m_creature->ClearAllReactives(); + m_creature->GetMotionMaster()->Clear(); + m_creature->GetMotionMaster()->MoveIdle(); + m_creature->SetStandState(UNIT_STAND_STATE_DEAD); + + // Stop damage and stop checking for flame buffet. + uiDamage = 0; + m_uiRangeCheckTimer = 0; + + if (DoCastSpellIfCan(m_creature, SPELL_EMBER_BLAST, CAST_TRIGGERED) == CAST_OK) + { + // Move to the center of the hall and ressurrect + m_uiPhase = PHASE_REBIRTH; + m_creature->GetMotionMaster()->MovePoint(POINT_ID_RESSURRECT, aCenterLocation[1].m_fX, aCenterLocation[1].m_fY, aCenterLocation[1].m_fZ); + } + } + + void UpdateAI(const uint32 uiDiff) override + { + if (!m_creature->SelectHostileTarget() || !m_creature->getVictim()) + return; + + // Platform phase + if (m_uiPhase == PHASE_ONE) + { + if (m_uiFlameQuillsTimer < uiDiff) + { + // Move to Flame Quills position; stop range check, platform moving and ember summoning + m_creature->GetMotionMaster()->MovePoint(POINT_ID_QUILLS, aCenterLocation[0].m_fX, aCenterLocation[0].m_fY, aCenterLocation[0].m_fZ); + m_uiRangeCheckTimer = 0; + m_bCanSummonEmber = false; + m_uiPlatformMoveTimer = 0; + m_uiFlameQuillsTimer = 180000; + } + else + m_uiFlameQuillsTimer -= uiDiff; + + if (m_uiPlatformMoveTimer) + { + if (m_uiPlatformMoveTimer <= uiDiff) + { + // go to next platform + ++m_uiCurrentPlatformId; + + if (m_uiCurrentPlatformId == MAX_PLATFORMS) + m_uiCurrentPlatformId = 0; + + // move to next platform and summon one ember only if moving on platforms (we avoid the summoning during the Flame Quills move) + if (m_bCanSummonEmber) + m_creature->SummonCreature(NPC_EMBER_OF_ALAR, 0, 0, 0, 0, TEMPSUMMON_DEAD_DESPAWN, 0); + + m_creature->GetMotionMaster()->MovePoint(POINT_ID_PLATFORM, aPlatformLocation[m_uiCurrentPlatformId].m_fX, aPlatformLocation[m_uiCurrentPlatformId].m_fY, aPlatformLocation[m_uiCurrentPlatformId].m_fZ); + + m_uiRangeCheckTimer = 0; + m_uiPlatformMoveTimer = 35000; + } + else + m_uiPlatformMoveTimer -= uiDiff; + } + } + // Combat phase + else if (m_uiPhase == PHASE_TWO) + { + if (m_uiBerserkTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature, SPELL_BERSERK) == CAST_OK) + m_uiBerserkTimer = 10 * MINUTE * IN_MILLISECONDS; + } + else + m_uiBerserkTimer -= uiDiff; + + if (m_uiFlamePatchTimer < uiDiff) + { + if (Unit* pTarget = m_creature->SelectAttackingTarget(ATTACKING_TARGET_RANDOM, 0)) + { + m_creature->SummonCreature(NPC_FLAME_PATCH, pTarget->GetPositionX(), pTarget->GetPositionY(), pTarget->GetPositionZ(), 0, TEMPSUMMON_TIMED_DESPAWN, 30000); + m_uiFlamePatchTimer = 30000; + } + } + else + m_uiFlamePatchTimer -= uiDiff; + + if (m_uiMeltArmorTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature->getVictim(), SPELL_MELT_ARMOR) == CAST_OK) + m_uiMeltArmorTimer = 60000; + } + else + m_uiMeltArmorTimer -= uiDiff; + + if (m_uiChargeTimer < uiDiff) + { + if (Unit* pTarget = m_creature->SelectAttackingTarget(ATTACKING_TARGET_RANDOM, 1)) + { + if (DoCastSpellIfCan(pTarget, SPELL_CHARGE) == CAST_OK) + m_uiChargeTimer = 20000; + } + } + else + m_uiChargeTimer -= uiDiff; + + if (m_uiDiveBombTimer) + { + if (m_uiDiveBombTimer <= uiDiff) + { + SetCombatMovement(false); + m_creature->GetMotionMaster()->MovePoint(POINT_ID_QUILLS, aCenterLocation[0].m_fX, aCenterLocation[0].m_fY, aCenterLocation[0].m_fZ); + m_uiPhase = PHASE_DIVE_BOMB; + m_uiRangeCheckTimer = 0; + m_uiDiveBombTimer = 0; + } + else + m_uiDiveBombTimer -= uiDiff; + } + } + // Dive Bomb event + else if (m_uiPhase == PHASE_DIVE_BOMB) + { + if (m_uiDiveBombTimer) + { + if (m_uiDiveBombTimer <= uiDiff) + { + if (Unit* pTarget = m_creature->SelectAttackingTarget(ATTACKING_TARGET_RANDOM, 0)) + { + if (DoCastSpellIfCan(pTarget, SPELL_DIVE_BOMB) == CAST_OK) + { + m_creature->Relocate(pTarget->GetPositionX(), pTarget->GetPositionY(), pTarget->GetPositionZ()); + m_uiRebirthTimer = 2000; + m_uiDiveBombTimer = 0; + } + } + } + else + m_uiDiveBombTimer -= uiDiff; + } + + if (m_uiRebirthTimer) + { + if (m_uiRebirthTimer <= uiDiff) + { + if (DoCastSpellIfCan(m_creature, SPELL_BOMB_REBIRTH) == CAST_OK) + { + m_creature->RemoveAurasDueToSpell(SPELL_DIVE_BOMB_VISUAL); + SetCombatMovement(true, true); + + // Spawn 2 Embers of Alar + float fX, fY, fZ; + for (uint8 i = 0; i < 2; ++i) + { + m_creature->GetRandomPoint(m_creature->GetPositionX(), m_creature->GetPositionY(), m_creature->GetPositionZ(), 5.0f, fX, fY, fZ); + m_creature->SummonCreature(NPC_EMBER_OF_ALAR, fX, fY, fZ, 0, TEMPSUMMON_DEAD_DESPAWN, 0); + } + + m_uiPhase = PHASE_TWO; + m_uiRangeCheckTimer = 2000; + m_uiDiveBombTimer = 30000; + m_uiRebirthTimer = 0; + } + } + else + m_uiRebirthTimer -= uiDiff; + } + } + + // only cast flame buffet when not in motion + if (m_uiRangeCheckTimer) + { + if (m_uiRangeCheckTimer <= uiDiff) + { + if (!m_creature->IsWithinDistInMap(m_creature->getVictim(), ATTACK_DISTANCE)) + DoCastSpellIfCan(m_creature, SPELL_FLAME_BUFFET); + m_uiRangeCheckTimer = 2000; + } + else + m_uiRangeCheckTimer -= uiDiff; + } + + DoMeleeAttackIfReady(); + } +}; + +CreatureAI* GetAI_boss_alar(Creature* pCreature) +{ + return new boss_alarAI(pCreature); +} + +void AddSC_boss_alar() +{ + Script* pNewScript; + + pNewScript = new Script; + pNewScript->Name = "boss_alar"; + pNewScript->GetAI = &GetAI_boss_alar; + pNewScript->RegisterSelf(); +} diff --git a/src/modules/SD2/scripts/outland/tempest_keep/the_eye/boss_astromancer.cpp b/src/modules/SD2/scripts/outland/tempest_keep/the_eye/boss_astromancer.cpp new file mode 100644 index 000000000..8af67d0c8 --- /dev/null +++ b/src/modules/SD2/scripts/outland/tempest_keep/the_eye/boss_astromancer.cpp @@ -0,0 +1,495 @@ +/* Copyright (C) 2006 - 2013 ScriptDev2 + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with 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: 90 +SDComment: Check if the split phase has some spells involved +SDCategory: Tempest Keep, The Eye +EndScriptData */ + +#include "precompiled.h" +#include "the_eye.h" + +enum +{ + SAY_AGGRO = -1550007, + SAY_SUMMON1 = -1550008, + SAY_SUMMON2 = -1550009, + SAY_KILL1 = -1550010, + SAY_KILL2 = -1550011, + SAY_KILL3 = -1550012, + SAY_DEATH = -1550013, + SAY_VOIDA = -1550014, + SAY_VOIDB = -1550015, + + SPELL_ARCANE_MISSILES = 33031, + SPELL_WRATH_OF_THE_ASTROMANCER = 42783, + SPELL_BLINDING_LIGHT = 33009, + SPELL_PSYHIC_SCREAM = 34322, + SPELL_SOLARIAN_TRANSFORM = 39117, + SPELL_VOID_BOLT = 39329, + SPELL_MARK_OF_SOLARIAN = 33023, // acts as an enrage spell + // SPELL_ROTATE_ASTROMANCER = 33283, // purpose unk + + // summoned creatures + NPC_SOLARIUM_AGENT = 18925, + NPC_SOLARIUM_PRIEST = 18806, + NPC_ASTROMANCER_SOLARIAN_SPOTLIGHT = 18928, + // NPC_ASTROMANCER_TRIGGER = 18932, // purpose unk + + // summoned spells + SPELL_SPOTLIGHT = 25824, // visual aura on the spotlights + + SPELL_SOLARIUM_GREAT_HEAL = 33387, + SPELL_SOLARIUM_HOLY_SMITE = 25054, + SPELL_SOLARIUM_ARCANE_TORRENT = 33390, + + WV_ARMOR = 31000, // ToDo: this value need to be checked + + MAX_SPOTLIGHTS = 3, + MAX_AGENTS = 4, +}; + +// Spells used to summon the Spotlights on 2.4.3 - Astromancer Split +// The boss had to choose 2 large radius split spells and 1 small radius split +// Large radius spotlight: 33189,33281,33282,33347,33348,33349,33350,33351 +// Small radius spotlight: 33352,33353,33354,33355 + +static const float fRoomCenter[4] = {432.909f, -373.424f, 17.9608f, 1.06421f}; +static const float fSpotlightRadius[2] = {13.0f, 25.0f}; + +enum Phases +{ + PHASE_NORMAL = 1, + PHASE_SPLIT = 2, + PHASE_VOID = 3, +}; + +struct boss_high_astromancer_solarianAI : public ScriptedAI +{ + boss_high_astromancer_solarianAI(Creature* pCreature) : ScriptedAI(pCreature) + { + m_pInstance = (ScriptedInstance*)pCreature->GetInstanceData(); + m_uiDefaultArmor = m_creature->GetArmor(); + Reset(); + } + + ScriptedInstance* m_pInstance; + + uint32 m_uiArcaneMissilesTimer; + uint32 m_uiWrathOfTheAstromancerTimer; + uint32 m_uiBlindingLightTimer; + uint32 m_uiFearTimer; + uint32 m_uiVoidBoltTimer; + uint32 m_uiSplitTimer; + uint32 m_uiSummonAgentsTimer; + uint32 m_uiSummonPriestsTimer; + uint32 m_uiDelayTimer; + uint32 m_uiDefaultArmor; + + Phases m_Phase; + + GuidVector m_vSpotLightsGuidVector; + + void Reset() override + { + m_uiArcaneMissilesTimer = 0; + m_uiWrathOfTheAstromancerTimer = urand(15000, 25000); + m_uiBlindingLightTimer = 35000; + m_uiFearTimer = 20000; + m_uiVoidBoltTimer = 10000; + m_uiSplitTimer = 50000; + m_uiSummonAgentsTimer = 0; + m_uiSummonPriestsTimer = 0; + m_uiDelayTimer = 0; + m_Phase = PHASE_NORMAL; + + // The vector will store the summoned spotlights + m_vSpotLightsGuidVector.reserve(MAX_SPOTLIGHTS); + + m_creature->SetArmor(m_uiDefaultArmor); + if (m_creature->GetVisibility() != VISIBILITY_ON) + m_creature->SetVisibility(VISIBILITY_ON); + + SetCombatMovement(true); + } + + void KilledUnit(Unit* pVictim) override + { + if (pVictim->GetTypeId() != TYPEID_PLAYER) + return; + + switch (urand(0, 2)) + { + case 0: DoScriptText(SAY_KILL1, m_creature); break; + case 1: DoScriptText(SAY_KILL2, m_creature); break; + case 2: DoScriptText(SAY_KILL3, m_creature); break; + } + } + + void JustDied(Unit* /*pKiller*/) override + { + DoScriptText(SAY_DEATH, m_creature); + + if (m_pInstance) + m_pInstance->SetData(TYPE_SOLARIAN, DONE); + } + + void Aggro(Unit* /*pWho*/) override + { + DoScriptText(SAY_AGGRO, m_creature); + + if (m_pInstance) + m_pInstance->SetData(TYPE_SOLARIAN, IN_PROGRESS); + } + + void JustReachedHome() override + { + if (m_pInstance) + m_pInstance->SetData(TYPE_SOLARIAN, FAIL); + } + + void JustSummoned(Creature* pSummoned) override + { + switch (pSummoned->GetEntry()) + { + case NPC_ASTROMANCER_SOLARIAN_SPOTLIGHT: + // Note: this should be moved to database + pSummoned->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE); + pSummoned->CastSpell(pSummoned, SPELL_SPOTLIGHT, false); + m_vSpotLightsGuidVector.push_back(pSummoned->GetObjectGuid()); + break; + case NPC_SOLARIUM_AGENT: + case NPC_SOLARIUM_PRIEST: + if (Unit* pTarget = m_creature->SelectAttackingTarget(ATTACKING_TARGET_RANDOM, 0)) + pSummoned->AI()->AttackStart(pTarget); + break; + } + } + + void DoSummonSpotlight(float fRadius, float fAngle, uint8 uiRandPoint) + { + float fX, fY, fZ; + m_creature->GetNearPoint(m_creature, fX, fY, fZ, 0, fRadius, fAngle * uiRandPoint); + m_creature->SummonCreature(NPC_ASTROMANCER_SOLARIAN_SPOTLIGHT, fX, fY, fZ, 0, TEMPSUMMON_TIMED_DESPAWN, 30000); + } + + void UpdateAI(const uint32 uiDiff) override + { + if (!m_creature->SelectHostileTarget() || !m_creature->getVictim()) + return; + + // When Solarian reaches 20% she will transform into a huge void walker. + if (m_Phase != PHASE_VOID && m_creature->GetHealthPercent() < 20.0f) + { + if (DoCastSpellIfCan(m_creature, SPELL_SOLARIAN_TRANSFORM) == CAST_OK) + { + DoScriptText(SAY_VOIDA, m_creature); + m_uiDelayTimer = 2000; + + m_creature->SetArmor(WV_ARMOR); + m_Phase = PHASE_VOID; + + if (m_creature->GetVisibility() != VISIBILITY_ON) + m_creature->SetVisibility(VISIBILITY_ON); + + // Stop the combat for a small delay + SetCombatMovement(false); + m_creature->GetMotionMaster()->MoveIdle(); + } + } + + // Handle delays between combat phases + if (m_uiDelayTimer) + { + if (m_uiDelayTimer <= uiDiff) + { + if (m_Phase == PHASE_SPLIT) + { + // select two different numbers between 0 and 7 so we will get different spawn points for the spotlights + uint8 uiPos1 = urand(0, 7); + uint8 uiPos2 = (uiPos1 + urand(1, 7)) % 8; + + // summon 3 spotlights + m_vSpotLightsGuidVector.clear(); + DoSummonSpotlight(fSpotlightRadius[0], M_PI_F / 2, urand(0, 3)); + DoSummonSpotlight(fSpotlightRadius[1], M_PI_F / 4, uiPos1); + DoSummonSpotlight(fSpotlightRadius[1], M_PI_F / 4, uiPos2); + + m_creature->SetVisibility(VISIBILITY_OFF); + + DoScriptText(urand(0, 1) ? SAY_SUMMON1 : SAY_SUMMON2, m_creature); + m_uiSummonAgentsTimer = 6000; + } + else if (m_Phase == PHASE_VOID) + { + DoScriptText(SAY_VOIDB, m_creature); + + SetCombatMovement(true); + m_creature->GetMotionMaster()->Clear(); + m_creature->GetMotionMaster()->MoveChase(m_creature->getVictim()); + } + + m_uiDelayTimer = 0; + } + else + m_uiDelayTimer -= uiDiff; + + // Combat is still on hold + return; + } + + switch (m_Phase) + { + case PHASE_NORMAL: + // Wrath of the Astromancer targets a random player which will explode after 6 secondes + if (m_uiWrathOfTheAstromancerTimer < uiDiff) + { + // Target the tank ? + if (Unit* pTarget = m_creature->SelectAttackingTarget(ATTACKING_TARGET_RANDOM, 1, SPELL_WRATH_OF_THE_ASTROMANCER, SELECT_FLAG_PLAYER)) + { + if (DoCastSpellIfCan(pTarget, SPELL_WRATH_OF_THE_ASTROMANCER) == CAST_OK) + m_uiWrathOfTheAstromancerTimer = urand(15000, 25000); + } + else + m_uiWrathOfTheAstromancerTimer = 10000; + } + else + m_uiWrathOfTheAstromancerTimer -= uiDiff; + + // Blinding Light Timer + if (m_uiBlindingLightTimer < uiDiff) + { + // She casts this spell every 45 seconds. It is a kind of Moonfire spell, which she strikes down on the whole raid simultaneously. It hits everyone in the raid for 2280 to 2520 arcane damage. + if (DoCastSpellIfCan(m_creature, SPELL_BLINDING_LIGHT) == CAST_OK) + m_uiBlindingLightTimer = 45000; + } + else + m_uiBlindingLightTimer -= uiDiff; + + // Arcane Missiles Timer + if (m_uiArcaneMissilesTimer < uiDiff) + { + // Solarian casts Arcane Missiles on on random targets in the raid. + if (Unit* pTarget = m_creature->SelectAttackingTarget(ATTACKING_TARGET_RANDOM, 0)) + { + if (!m_creature->HasInArc(2.5f, pTarget)) + pTarget = m_creature->getVictim(); + + if (pTarget) + DoCastSpellIfCan(pTarget, SPELL_ARCANE_MISSILES); + } + + m_uiArcaneMissilesTimer = urand(3000, 4000); + } + else + m_uiArcaneMissilesTimer -= uiDiff; + + // Phase 1 Timer + if (m_uiSplitTimer < uiDiff) + { + // ToDo: the timer of this ability is around 45-50 seconds. Please check if this is correct! + DoCastSpellIfCan(m_creature, SPELL_MARK_OF_SOLARIAN, CAST_INTERRUPT_PREVIOUS); + m_Phase = PHASE_SPLIT; + + // After these 50 seconds she portals to the middle of the room and disappears, leaving 3 light portals behind. + // ToDo: check if there are some spells involved in this event! + m_creature->GetMotionMaster()->MoveIdle(); + SetCombatMovement(false); + m_creature->NearTeleportTo(fRoomCenter[0], fRoomCenter[1], fRoomCenter[2], fRoomCenter[3], true); + + m_uiDelayTimer = 1000; + m_uiSplitTimer = 50000; + // Do nothing more, if phase switched + return; + } + else + m_uiSplitTimer -= uiDiff; + + DoMeleeAttackIfReady(); + break; + + case PHASE_SPLIT: + + // Summon 4 Agents on each portal + if (m_uiSummonAgentsTimer) + { + if (m_uiSummonAgentsTimer <= uiDiff) + { + for (uint8 i = 0; i < MAX_SPOTLIGHTS; ++i) + { + if (Creature* pSpotlight = m_creature->GetMap()->GetCreature(m_vSpotLightsGuidVector[i])) + { + for (uint8 j = 0; j < MAX_AGENTS; ++j) + m_creature->SummonCreature(NPC_SOLARIUM_AGENT, pSpotlight->GetPositionX(), pSpotlight->GetPositionY(), pSpotlight->GetPositionZ(), 0, TEMPSUMMON_DEAD_DESPAWN, 0); + } + } + m_uiSummonAgentsTimer = 0; + m_uiSummonPriestsTimer = 15000; + } + else + m_uiSummonAgentsTimer -= uiDiff; + } + + if (m_uiSummonPriestsTimer) + { + if (m_uiSummonPriestsTimer < uiDiff) + { + m_Phase = PHASE_NORMAL; + // Randomize the portals + std::random_shuffle(m_vSpotLightsGuidVector.begin(), m_vSpotLightsGuidVector.end()); + // Summon 2 priests + for (uint8 i = 0; i < 2; ++i) + { + if (Creature* pSpotlight = m_creature->GetMap()->GetCreature(m_vSpotLightsGuidVector[i])) + m_creature->SummonCreature(NPC_SOLARIUM_PRIEST, pSpotlight->GetPositionX(), pSpotlight->GetPositionY(), pSpotlight->GetPositionZ(), 0, TEMPSUMMON_DEAD_DESPAWN, 0); + } + // Teleport the boss at the last portal + if (Creature* pSpotlight = m_creature->GetMap()->GetCreature(m_vSpotLightsGuidVector[2])) + m_creature->NearTeleportTo(pSpotlight->GetPositionX(), pSpotlight->GetPositionY(), pSpotlight->GetPositionZ(), pSpotlight->GetOrientation(), true); + + SetCombatMovement(true); + m_creature->GetMotionMaster()->Clear(); + m_creature->GetMotionMaster()->MoveChase(m_creature->getVictim()); + + // Set as visible and reset spells timers + m_creature->SetVisibility(VISIBILITY_ON); + m_uiArcaneMissilesTimer = 0; + m_uiSummonPriestsTimer = 0; + m_uiBlindingLightTimer = 35000; + m_uiWrathOfTheAstromancerTimer = urand(15000, 25000); + } + else + m_uiSummonPriestsTimer -= uiDiff; + } + + break; + + case PHASE_VOID: + // Fear Timer + if (m_uiFearTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature, SPELL_PSYHIC_SCREAM) == CAST_OK) + m_uiFearTimer = 20000; + } + else + m_uiFearTimer -= uiDiff; + + // Void Bolt Timer + if (m_uiVoidBoltTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature->getVictim(), SPELL_VOID_BOLT) == CAST_OK) + m_uiVoidBoltTimer = 10000; + } + else + m_uiVoidBoltTimer -= uiDiff; + + DoMeleeAttackIfReady(); + break; + } + } +}; + +struct mob_solarium_priestAI : public ScriptedAI +{ + mob_solarium_priestAI(Creature* pCreature) : ScriptedAI(pCreature) { Reset(); } + + uint32 m_uiHealTimer; + uint32 m_uiHolySmiteTimer; + uint32 m_uiAoESilenceTimer; + + void Reset() override + { + m_uiHealTimer = 9000; + m_uiHolySmiteTimer = 1; + m_uiAoESilenceTimer = 15000; + } + + void AttackStart(Unit* pWho) override + { + if (m_creature->Attack(pWho, true)) + { + m_creature->AddThreat(pWho); + m_creature->SetInCombatWith(pWho); + pWho->SetInCombatWith(m_creature); + DoStartMovement(pWho, 25.0f); + } + } + + void UpdateAI(const uint32 uiDiff) override + { + if (!m_creature->SelectHostileTarget() || !m_creature->getVictim()) + return; + + if (m_uiHealTimer < uiDiff) + { + if (Unit* pTarget = DoSelectLowestHpFriendly(50.0f)) + { + if (DoCastSpellIfCan(pTarget, SPELL_SOLARIUM_GREAT_HEAL) == CAST_OK) + m_uiHealTimer = 9000; + } + } + else + m_uiHealTimer -= uiDiff; + + if (m_uiHolySmiteTimer < uiDiff) + { + if (Unit* pTarget = m_creature->SelectAttackingTarget(ATTACKING_TARGET_RANDOM, 0)) + { + if (DoCastSpellIfCan(pTarget, SPELL_SOLARIUM_HOLY_SMITE) == CAST_OK) + m_uiHolySmiteTimer = 4000; + } + } + else + m_uiHolySmiteTimer -= uiDiff; + + if (m_uiAoESilenceTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature, SPELL_SOLARIUM_ARCANE_TORRENT) == CAST_OK) + m_uiAoESilenceTimer = 13000; + } + else + m_uiAoESilenceTimer -= uiDiff; + + 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* pNewScript; + + pNewScript = new Script; + pNewScript->Name = "boss_high_astromancer_solarian"; + pNewScript->GetAI = &GetAI_boss_high_astromancer_solarian; + pNewScript->RegisterSelf(); + + pNewScript = new Script; + pNewScript->Name = "mob_solarium_priest"; + pNewScript->GetAI = &GetAI_mob_solarium_priest; + pNewScript->RegisterSelf(); +} diff --git a/src/modules/SD2/scripts/outland/tempest_keep/the_eye/boss_kaelthas.cpp b/src/modules/SD2/scripts/outland/tempest_keep/the_eye/boss_kaelthas.cpp new file mode 100644 index 000000000..3751325c1 --- /dev/null +++ b/src/modules/SD2/scripts/outland/tempest_keep/the_eye/boss_kaelthas.cpp @@ -0,0 +1,1306 @@ +/* Copyright (C) 2006 - 2013 ScriptDev2 + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with 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: 80 +SDComment: Timers; Transition phase is incomplete, some spells are unk. +SDCategory: Tempest Keep, The Eye +EndScriptData */ + +#include "precompiled.h" +#include "the_eye.h" + +enum +{ + // ***** Event yells ******** + // kael'thas Speech + SAY_INTRO = -1550016, + SAY_INTRO_CAPERNIAN = -1550017, + SAY_INTRO_TELONICUS = -1550018, + SAY_INTRO_THALADRED = -1550019, + SAY_INTRO_SANGUINAR = -1550020, + SAY_PHASE2_WEAPON = -1550021, + SAY_PHASE3_ADVANCE = -1550022, + SAY_PHASE4_INTRO2 = -1550023, + SAY_PHASE5_NUTS = -1550024, + SAY_SLAY1 = -1550025, + SAY_SLAY2 = -1550026, + SAY_SLAY3 = -1550027, + SAY_MINDCONTROL1 = -1550028, + SAY_MINDCONTROL2 = -1550029, + SAY_GRAVITYLAPSE1 = -1550030, + SAY_GRAVITYLAPSE2 = -1550031, + SAY_SUMMON_PHOENIX1 = -1550032, + SAY_SUMMON_PHOENIX2 = -1550033, + SAY_DEATH = -1550034, + EMOTE_PYROBLAST = -1550044, + + // 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, + + // ***** Kaelthas spells ******** + // Phase 2 spells + SPELL_KAEL_PHASE_2 = 36709, // not sure if this is used in the right way + SPELL_SUMMON_WEAPONS = 36976, + SPELL_SUMMON_WEAPONA = 36958, + SPELL_SUMMON_WEAPONB = 36959, + SPELL_SUMMON_WEAPONC = 36960, + SPELL_SUMMON_WEAPOND = 36961, + SPELL_SUMMON_WEAPONE = 36962, + SPELL_SUMMON_WEAPONF = 36963, + SPELL_SUMMON_WEAPONG = 36964, + SPELL_RESURRECTION = 36450, + + // Phase 4 spells + SPELL_FIREBALL = 36805, + SPELL_PYROBLAST = 36819, + SPELL_FLAME_STRIKE = 36735, // summons 21369 + SPELL_FLAME_STRIKE_DUMMY = 36730, + SPELL_ARCANE_DISRUPTION = 36834, + SPELL_SHOCK_BARRIER = 36815, + SPELL_PHOENIX_ANIMATION = 36723, // summons 21362 + SPELL_MIND_CONTROL = 32830, + + // Phase 5 spells + SPELL_GAIN_POWER = 36091, + SPELL_EXPLODE = 36092, + // SPELL_EXPLODE_1 = 36354, // it's not very clear what all these spells should do + SPELL_EXPLODE_2 = 36373, + // SPELL_EXPLODE_3 = 36375, + // SPELL_EXPLODE_4 = 36376, + // SPELL_KAEL_STUN = 36185, // purpose unk + SPELL_FULLPOWER = 36187, + SPELL_GRAVITY_LAPSE = 35941, + SPELL_GRAVITY_LAPSE_KNOCKBACK = 34480, // cast by players - damage effect + SPELL_GRAVITY_LAPSE_AURA = 39432, // cast by players - fly effect + SPELL_NETHER_BEAM = 35869, // triggers 35873 on target + SPELL_NETHER_VAPOR_SUMMON = 35865, // script effect - probably related to 35879 + + // ***** Advisors spells ******** + // Thaladred the Darkener spells + SPELL_PSYCHIC_BLOW = 36966, + SPELL_SILENCE = 30225, + SPELL_REND = 36965, + + // Lord Sanguinar spells + SPELL_BELLOWING_ROAR = 44863, + + // Grand Astromancer Capernian spells + SPELL_CAPERNIAN_FIREBALL = 36971, + SPELL_CONFLAGRATION = 37018, + SPELL_ARCANE_BURST = 36970, + + // Master Engineer Telonicus spells + SPELL_BOMB = 37036, + SPELL_REMOTE_TOY = 37027, + + // ***** Other summons spells ******** + // Nether Vapor spell + SPELL_NETHER_VAPOR = 35858, + // Phoenix spell + SPELL_BURN = 36720, + SPELL_EMBER_BLAST = 34341, + SPELL_REBIRTH = 35369, + + // ***** Creature Entries ******** + NPC_FLAME_STRIKE_TRIGGER = 21369, + NPC_PHOENIX = 21362, + NPC_PHOENIX_EGG = 21364, + NPC_NETHER_VAPOR = 21002, + + // ***** Other ******** + PHASE_0_NOT_BEGUN = 0, + PHASE_1_ADVISOR = 1, + PHASE_2_WEAPON = 2, + PHASE_3_ADVISOR_ALL = 3, + PHASE_4_SOLO = 4, + PHASE_5_WAITING = 5, + PHASE_6_FLYING = 6, + PHASE_7_GRAVITY = 7, + + POINT_ID_CENTER = 1, + POINT_ID_AIR = 2, + + MAX_WEAPONS = 7, + MAX_MIND_CONTROL = 3, +}; + +static const uint32 m_auiSpellSummonWeapon[MAX_WEAPONS] = +{ + SPELL_SUMMON_WEAPONA, SPELL_SUMMON_WEAPONB, SPELL_SUMMON_WEAPONC, SPELL_SUMMON_WEAPOND, + SPELL_SUMMON_WEAPONE, SPELL_SUMMON_WEAPONF, SPELL_SUMMON_WEAPONG +}; + +// teleport spells for gravity lapse event +static const uint32 m_auiSpellGravityLapseTeleport[] = +{ + 35966, 35967, 35968, 35969, 35970, 35971, 35972, 35973, 35974, 35975, 35976, 35977, 35978, 35979, 35980, + 35981, 35982, 35983, 35984, 35985, 35986, 35987, 35988, 35989, 35990 +}; + +static const float aCenterPos[3] = {795.00f, -0.46f, 48.72f}; + +/*###### +## boss_kaelthas +######*/ + +struct boss_kaelthasAI : public ScriptedAI +{ + boss_kaelthasAI(Creature* pCreature) : ScriptedAI(pCreature) + { + m_pInstance = (ScriptedInstance*)pCreature->GetInstanceData(); + Reset(); + } + + ScriptedInstance* m_pInstance; + + uint32 m_uiFireballTimer; + uint32 m_uiArcaneDisruptionTimer; + uint32 m_uiPhoenixTimer; + uint32 m_uiFlameStrikeTimer; + + uint32 m_uiPyroblastTimer; + uint32 m_uiShockBarrierTimer; + uint32 m_uiMindControlTimer; + uint32 m_uiExplodeTimer; + + uint32 m_uiGravityLapseTimer; + uint32 m_uiGravityExpireTimer; + uint32 m_uiNetherBeamTimer; + uint32 m_uiNetherVaporTimer; + uint8 m_uiGravityIndex; + + uint32 m_uiPhaseTimer; + uint8 m_uiPhase; + uint8 m_uiPhaseSubphase; + + void Reset() override + { + // Phases + m_uiPhase = PHASE_0_NOT_BEGUN; + m_uiPhaseTimer = 23000; + m_uiPhaseSubphase = 0; + + // Spells + m_uiFireballTimer = urand(1000, 3000); + m_uiArcaneDisruptionTimer = 45000; + m_uiPhoenixTimer = 50000; + m_uiFlameStrikeTimer = 30000; + + m_uiShockBarrierTimer = 60000; + m_uiMindControlTimer = 40000; + m_uiPyroblastTimer = 0; + m_uiExplodeTimer = 0; + + m_uiGravityLapseTimer = 12000; + m_uiGravityExpireTimer = 0; + m_uiNetherBeamTimer = 8000; + m_uiNetherVaporTimer = 10000; + m_uiGravityIndex = 0; + + m_creature->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE); + + SetCombatMovement(true); + } + + void GetAIInformation(ChatHandler& reader) override + { + reader.PSendSysMessage("Kael'thas is currently in phase %u", m_uiPhase); + } + + // Custom Move in LoS function + void MoveInLineOfSight(Unit* pWho) override + { + if (m_uiPhase == PHASE_0_NOT_BEGUN && pWho->GetTypeId() == TYPEID_PLAYER && !((Player*)pWho)->isGameMaster() && + m_creature->IsWithinDistInMap(pWho, m_creature->GetAttackDistance(pWho)) && m_creature->IsWithinLOSInMap(pWho)) + { + DoScriptText(SAY_INTRO, m_creature); + m_uiPhase = PHASE_1_ADVISOR; + + // Set the player in combat with the boss + pWho->SetInCombatWith(m_creature); + m_creature->AddThreat(pWho); + + if (m_pInstance) + m_pInstance->SetData(TYPE_KAELTHAS, IN_PROGRESS); + } + } + + void AttackStart(Unit* pWho) override + { + if (m_creature->Attack(pWho, true)) + { + m_creature->AddThreat(pWho); + m_creature->SetInCombatWith(pWho); + pWho->SetInCombatWith(m_creature); + DoStartMovement(pWho, 25.0f); + } + } + + void KilledUnit(Unit* /*pUnit*/) override + { + switch (urand(0, 2)) + { + case 0: DoScriptText(SAY_SLAY1, m_creature); break; + case 1: DoScriptText(SAY_SLAY2, m_creature); break; + case 2: DoScriptText(SAY_SLAY3, m_creature); break; + } + } + + void JustDied(Unit* /*pKiller*/) override + { + DoScriptText(SAY_DEATH, m_creature); + + if (m_pInstance) + m_pInstance->SetData(TYPE_KAELTHAS, DONE); + } + + void JustReachedHome() override + { + if (m_pInstance) + m_pInstance->SetData(TYPE_KAELTHAS, FAIL); + } + + void JustSummoned(Creature* pSummoned) override + { + if (pSummoned->GetEntry() == NPC_FLAME_STRIKE_TRIGGER) + pSummoned->CastSpell(pSummoned, SPELL_FLAME_STRIKE_DUMMY, false, NULL, NULL, m_creature->GetObjectGuid()); + else if (pSummoned->GetEntry() == NPC_NETHER_VAPOR) + pSummoned->CastSpell(pSummoned, SPELL_NETHER_VAPOR, false, NULL, NULL, m_creature->GetObjectGuid()); + // Start combat for Weapons of Phoenix + else + pSummoned->SetInCombatWithZone(); + } + + void SpellHit(Unit* /*pCaster*/, const SpellEntry* pSpell) override + { + // Handle summon weapons event + if (pSpell->Id == SPELL_SUMMON_WEAPONS) + { + for (uint8 i = 0; i < MAX_WEAPONS; ++i) + DoCastSpellIfCan(m_creature, m_auiSpellSummonWeapon[i], CAST_TRIGGERED); + + m_uiPhase = PHASE_2_WEAPON; + m_uiPhaseTimer = 120000; + } + } + + void SpellHitTarget(Unit* pTarget, const SpellEntry* pSpell) override + { + // Handle gravity lapse teleport - each player hit has his own teleport spell + if (pSpell->Id == SPELL_GRAVITY_LAPSE && pTarget->GetTypeId() == TYPEID_PLAYER) + { + DoCastSpellIfCan(pTarget, m_auiSpellGravityLapseTeleport[m_uiGravityIndex], CAST_TRIGGERED); + pTarget->CastSpell(pTarget, SPELL_GRAVITY_LAPSE_KNOCKBACK, true, NULL, NULL, m_creature->GetObjectGuid()); + pTarget->CastSpell(pTarget, SPELL_GRAVITY_LAPSE_AURA, true, NULL, NULL, m_creature->GetObjectGuid()); + ++m_uiGravityIndex; + } + } + + void MovementInform(uint32 uiMotionType, uint32 uiPointId) override + { + if (uiMotionType != POINT_MOTION_TYPE || !uiPointId) + return; + + if (uiPointId == POINT_ID_CENTER) + { + if (m_uiPhase == PHASE_5_WAITING) + { + // ToDo: also start channeling to the giant crystals nearby + m_creature->SetLevitate(true); + m_creature->GetMotionMaster()->MovePoint(POINT_ID_AIR, m_creature->GetPositionX(), m_creature->GetPositionY(), m_creature->GetPositionZ() + 30.0f, false); + m_uiPhaseTimer = 0; + m_uiPhase = PHASE_6_FLYING; + } + else if (m_uiPhase == PHASE_6_FLYING) + { + SetCombatMovement(true); + m_creature->SetLevitate(false); + m_creature->InterruptNonMeleeSpells(false); + m_creature->GetMotionMaster()->Clear(); + DoStartMovement(m_creature->getVictim(), 25.0f); + m_uiShockBarrierTimer = 10000; + m_uiPhase = PHASE_7_GRAVITY; + } + } + if (uiPointId == POINT_ID_AIR) + { + if (DoCastSpellIfCan(m_creature, SPELL_EXPLODE_2) == CAST_OK) + { + // ToDo: start channeling some additional crystals + // Also it's not very clear which other spells should be used here (which modifies his scale) + m_uiExplodeTimer = 8000; + } + } + } + + void AdvisorDefeated(uint32 uiEntry) + { + if (m_uiPhase != PHASE_1_ADVISOR) + return; + + // Handle phase 1 end + if (uiEntry == NPC_TELONICUS) + { + if (DoCastSpellIfCan(m_creature, SPELL_SUMMON_WEAPONS) == CAST_OK) + DoScriptText(SAY_PHASE2_WEAPON, m_creature); + } + else + m_uiPhaseTimer = 1000; + } + + void UpdateAI(const uint32 uiDiff) override + { + switch (m_uiPhase) + { + // ***** Advisors phase ******** + case PHASE_1_ADVISOR: + { + if (!m_uiPhaseTimer) + return; + + if (m_uiPhaseTimer <= uiDiff) + { + if (!m_pInstance) + return; + + switch (m_uiPhaseSubphase) + { + case 0: + DoScriptText(SAY_INTRO_THALADRED, m_creature); + m_uiPhaseTimer = 7000; + break; + + case 1: + if (Creature* pAdvisor = m_pInstance->GetSingleCreatureFromStorage(NPC_THALADRED)) + { + pAdvisor->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE); + pAdvisor->SetInCombatWithZone(); + } + m_uiPhaseTimer = 0; + break; + + case 2: + DoScriptText(SAY_INTRO_SANGUINAR, m_creature); + m_uiPhaseTimer = 12500; + break; + + case 3: + if (Creature* pAdvisor = m_pInstance->GetSingleCreatureFromStorage(NPC_SANGUINAR)) + { + pAdvisor->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE); + pAdvisor->SetInCombatWithZone(); + } + m_uiPhaseTimer = 0; + break; + + case 4: + DoScriptText(SAY_INTRO_CAPERNIAN, m_creature); + m_uiPhaseTimer = 7000; + break; + + case 5: + if (Creature* pAdvisor = m_pInstance->GetSingleCreatureFromStorage(NPC_CAPERNIAN)) + { + pAdvisor->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE); + pAdvisor->SetInCombatWithZone(); + } + m_uiPhaseTimer = 0; + break; + + case 6: + DoScriptText(SAY_INTRO_TELONICUS, m_creature); + m_uiPhaseTimer = 8400; + break; + + case 7: + if (Creature* pAdvisor = m_pInstance->GetSingleCreatureFromStorage(NPC_TELONICUS)) + { + pAdvisor->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE); + pAdvisor->SetInCombatWithZone(); + } + m_uiPhaseTimer = 0; + break; + } + + ++m_uiPhaseSubphase; + } + else + m_uiPhaseTimer -= uiDiff; + + break; + } + + // ***** Weapons phase ******** + case PHASE_2_WEAPON: + { + if (m_uiPhaseTimer < uiDiff) + { + // Switch to next phase, no matter if the weapons are killed or not + if (DoCastSpellIfCan(m_creature, SPELL_RESURRECTION) == CAST_OK) + { + DoScriptText(SAY_PHASE3_ADVANCE, m_creature); + m_uiPhaseSubphase = 0; + m_uiPhaseTimer = 180000; + m_uiPhase = PHASE_3_ADVISOR_ALL; + } + } + else + m_uiPhaseTimer -= uiDiff; + + break; + } + + // ***** All advisors phase ******** + case PHASE_3_ADVISOR_ALL: + { + if (m_uiPhaseTimer < uiDiff) + { + DoScriptText(SAY_PHASE4_INTRO2, m_creature); + m_creature->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE); + DoResetThreat(); + m_creature->SetInCombatWithZone(); + m_uiPhase = PHASE_4_SOLO; + m_uiPhaseTimer = 30000; + } + else + m_uiPhaseTimer -= uiDiff; + + break; + } + + // ***** Solo phases ******** + case PHASE_4_SOLO: + case PHASE_7_GRAVITY: + { + if (!m_creature->SelectHostileTarget() || !m_creature->getVictim()) + return; + + if (m_uiGravityExpireTimer) + { + if (m_uiNetherBeamTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature, SPELL_NETHER_BEAM) == CAST_OK) + m_uiNetherBeamTimer = urand(2000, 4000); + } + else + m_uiNetherBeamTimer -= uiDiff; + + // Switch to the other spells after gravity lapse expired + if (m_uiGravityExpireTimer <= uiDiff) + m_uiGravityExpireTimer = 0; + else + m_uiGravityExpireTimer -= uiDiff; + } + else + { + if (m_uiFireballTimer < uiDiff) + { + if (Unit* pTarget = m_creature->SelectAttackingTarget(ATTACKING_TARGET_RANDOM, 0)) + { + if (DoCastSpellIfCan(pTarget, SPELL_FIREBALL) == CAST_OK) + m_uiFireballTimer = urand(3000, 5000); + } + } + else + m_uiFireballTimer -= uiDiff; + + if (m_uiArcaneDisruptionTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature, SPELL_ARCANE_DISRUPTION) == CAST_OK) + m_uiArcaneDisruptionTimer = 60000; + } + else + m_uiArcaneDisruptionTimer -= uiDiff; + + if (m_uiFlameStrikeTimer < uiDiff) + { + if (Unit* pTarget = m_creature->SelectAttackingTarget(ATTACKING_TARGET_RANDOM, 0)) + { + if (DoCastSpellIfCan(pTarget, SPELL_FLAME_STRIKE) == CAST_OK) + m_uiFlameStrikeTimer = 30000; + } + } + else + m_uiFlameStrikeTimer -= uiDiff; + + if (m_uiPhoenixTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature, SPELL_PHOENIX_ANIMATION) == CAST_OK) + { + DoScriptText(urand(0, 1) ? SAY_SUMMON_PHOENIX1 : SAY_SUMMON_PHOENIX2, m_creature); + m_uiPhoenixTimer = 60000; + } + } + else + m_uiPhoenixTimer -= uiDiff; + } + + DoMeleeAttackIfReady(); + + // ***** Phase 4 specific actions ******** + if (m_uiPhase == PHASE_4_SOLO) + { + if (m_creature->GetHealthPercent() < 50.0f) + { + // ToDo: should he cast something here? + m_creature->InterruptNonMeleeSpells(false); + DoScriptText(SAY_PHASE5_NUTS, m_creature); + + SetCombatMovement(false); + m_creature->GetMotionMaster()->Clear(); + m_creature->GetMotionMaster()->MovePoint(POINT_ID_CENTER, aCenterPos[0], aCenterPos[1], aCenterPos[2]); + + m_uiPhase = PHASE_5_WAITING; + } + + if (m_uiShockBarrierTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature, SPELL_SHOCK_BARRIER) == CAST_OK) + { + m_uiPyroblastTimer = 1000; + m_uiShockBarrierTimer = 60000; + } + } + else + m_uiShockBarrierTimer -= uiDiff; + + if (m_uiPyroblastTimer) + { + if (m_uiPyroblastTimer <= uiDiff) + { + if (DoCastSpellIfCan(m_creature, SPELL_PYROBLAST) == CAST_OK) + { + DoScriptText(EMOTE_PYROBLAST, m_creature); + m_uiPyroblastTimer = 0; + } + } + else + m_uiPyroblastTimer -= uiDiff; + } + + if (m_uiMindControlTimer < uiDiff) + { + for (uint8 i = 0; i < MAX_MIND_CONTROL; ++i) + { + if (Unit* pTarget = m_creature->SelectAttackingTarget(ATTACKING_TARGET_RANDOM, 1, SPELL_MIND_CONTROL, SELECT_FLAG_PLAYER)) + DoCastSpellIfCan(pTarget, SPELL_MIND_CONTROL); + } + m_uiMindControlTimer = 60000; + } + else + m_uiMindControlTimer -= uiDiff; + } + + // ***** Phase 7 specific actions ******** + if (m_uiPhase == PHASE_7_GRAVITY) + { + if (m_uiGravityLapseTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature, SPELL_GRAVITY_LAPSE) == CAST_OK) + { + DoScriptText(urand(0, 1) ? SAY_GRAVITYLAPSE1 : SAY_GRAVITYLAPSE2, m_creature);; + m_uiGravityIndex = 0; + m_uiNetherBeamTimer = 8000; + m_uiNetherVaporTimer = 4000; + m_uiGravityExpireTimer = 30000; + m_uiGravityLapseTimer = 90000; + } + } + else + m_uiGravityLapseTimer -= uiDiff; + + if (m_uiShockBarrierTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature, SPELL_SHOCK_BARRIER) == CAST_OK) + m_uiShockBarrierTimer = 20000; + } + else + m_uiShockBarrierTimer -= uiDiff; + + if (m_uiNetherVaporTimer) + { + if (m_uiNetherVaporTimer <= uiDiff) + { + if (DoCastSpellIfCan(m_creature, SPELL_NETHER_VAPOR_SUMMON) == CAST_OK) + m_uiNetherVaporTimer = 0; + } + else + m_uiNetherVaporTimer -= uiDiff; + } + } + } + // ***** Phase 5 - transition ******** + case PHASE_5_WAITING: + // Nothing here; wait for boss to arive at point + break; + // ***** Phase 6 - explode the bridge ******** + case PHASE_6_FLYING: + if (m_uiExplodeTimer) + { + if (m_uiExplodeTimer <= uiDiff) + { + if (DoCastSpellIfCan(m_creature, SPELL_EXPLODE) == CAST_OK) + { + if (m_pInstance) + { + m_pInstance->DoUseDoorOrButton(GO_KAEL_STATUE_LEFT); + m_pInstance->DoUseDoorOrButton(GO_KAEL_STATUE_RIGHT); + m_pInstance->DoUseDoorOrButton(GO_BRIDGE_WINDOW); + } + // Note: also Kael casts some other unk spells here + m_uiPhaseTimer = 5000; + m_uiExplodeTimer = 0; + } + } + else + m_uiExplodeTimer -= uiDiff; + } + + if (m_uiPhaseTimer) + { + if (m_uiPhaseTimer <= uiDiff) + { + m_creature->GetMotionMaster()->Clear(); + m_creature->GetMotionMaster()->MovePoint(POINT_ID_CENTER, aCenterPos[0], aCenterPos[1], aCenterPos[2]); + m_uiPhaseTimer = 0; + } + else + m_uiPhaseTimer -= uiDiff; + } + break; + } + } +}; + +bool EffectDummyCreature_kael_phase_2(Unit* pCaster, uint32 uiSpellId, SpellEffectIndex uiEffIndex, Creature* pCreatureTarget, ObjectGuid /*originalCasterGuid*/) +{ + // always check spellid and effectindex + if (uiSpellId == SPELL_KAEL_PHASE_2 && uiEffIndex == EFFECT_INDEX_0) + { + if (boss_kaelthasAI* pKaelAI = dynamic_cast(pCreatureTarget->AI())) + pKaelAI->AdvisorDefeated(pCaster->GetEntry()); + + // always return true when we are handling this spell and effect + return true; + } + + return false; +} + +/*###### +## advisor_base_ai +######*/ + +struct advisor_base_ai : public ScriptedAI +{ + advisor_base_ai(Creature* pCreature) : ScriptedAI(pCreature) + { + m_pInstance = (ScriptedInstance*)pCreature->GetInstanceData(); + Reset(); + } + + ScriptedInstance* m_pInstance; + + bool m_bFakeDeath; + bool m_bCanFakeDeath; + + void Reset() override + { + m_bCanFakeDeath = true; + m_bFakeDeath = false; + + 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); + } + + void JustReachedHome() override + { + // Reset Kael if needed + if (m_pInstance) + { + if (Creature* pKael = m_pInstance->GetSingleCreatureFromStorage(NPC_KAELTHAS)) + pKael->AI()->EnterEvadeMode(); + + m_pInstance->SetData(TYPE_KAELTHAS, FAIL); + } + } + + void DamageTaken(Unit* /*pDoneby*/, uint32& uiDamage) override + { + // Allow fake death only in the first phase + if (!m_bCanFakeDeath) + return; + + if (uiDamage < m_creature->GetHealth()) + return; + + // Make sure it won't die by accident + if (m_bFakeDeath) + { + uiDamage = 0; + return; + } + + uiDamage = 0; + m_bFakeDeath = true; + + m_creature->InterruptNonMeleeSpells(true); + m_creature->SetHealth(0); + m_creature->StopMoving(); + m_creature->ClearComboPointHolders(); + m_creature->RemoveAllAurasOnDeath(); + m_creature->ModifyAuraState(AURA_STATE_HEALTHLESS_20_PERCENT, false); + m_creature->ModifyAuraState(AURA_STATE_HEALTHLESS_35_PERCENT, false); + m_creature->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE); + m_creature->ClearAllReactives(); + m_creature->GetMotionMaster()->Clear(); + m_creature->GetMotionMaster()->MoveIdle(); + m_creature->SetStandState(UNIT_STAND_STATE_DEAD); + + DoCastSpellIfCan(m_creature, SPELL_KAEL_PHASE_2, CAST_TRIGGERED); + } + + void SpellHit(Unit* pCaster, const SpellEntry* pSpell) override + { + // Remove fake death + if (pSpell->Id == SPELL_RESURRECTION && pCaster->GetEntry() == NPC_KAELTHAS) + { + m_creature->SetStandState(UNIT_STAND_STATE_STAND); + m_creature->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE | UNIT_FLAG_NON_ATTACKABLE); + m_creature->GetMotionMaster()->Clear(); + if (m_creature->GetEntry() == NPC_CAPERNIAN) + DoStartMovement(m_creature->getVictim(), 20.0f); + else + DoStartMovement(m_creature->getVictim()); + m_bCanFakeDeath = false; + m_bFakeDeath = false; + } + } +}; + +/*###### +## boss_thaladred_the_darkener +######*/ + +struct boss_thaladred_the_darkenerAI : public advisor_base_ai +{ + boss_thaladred_the_darkenerAI(Creature* pCreature) : advisor_base_ai(pCreature) { Reset(); } + + uint32 m_uiGazeTimer; + uint32 m_uiRendTimer; + uint32 m_uiSilenceTimer; + uint32 m_uiPsychicBlowTimer; + + void Reset() override + { + m_uiGazeTimer = 0; + m_uiRendTimer = urand(4000, 8000); + m_uiSilenceTimer = 5000; + m_uiPsychicBlowTimer = 25000; + + advisor_base_ai::Reset(); + } + + void Aggro(Unit* pWho) override + { + DoScriptText(SAY_THALADRED_AGGRO, m_creature); + m_creature->TauntApply(pWho); + } + + void JustDied(Unit* /*pKiller*/) override + { + DoScriptText(SAY_THALADRED_DEATH, m_creature); + } + + void UpdateAI(const uint32 uiDiff) override + { + if (!m_creature->SelectHostileTarget() || !m_creature->getVictim()) + return; + + // Don't use abilities during fake death + if (m_bFakeDeath) + return; + + if (m_uiGazeTimer < uiDiff) + { + if (Unit* pTarget = m_creature->SelectAttackingTarget(ATTACKING_TARGET_RANDOM, 0)) + { + DoResetThreat(); + m_creature->TauntApply(pTarget); + DoScriptText(EMOTE_THALADRED_GAZE, m_creature, pTarget); + } + m_uiGazeTimer = 10000; + } + else + m_uiGazeTimer -= uiDiff; + + if (m_uiRendTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature->getVictim(), SPELL_REND) == CAST_OK) + m_uiRendTimer = urand(7000, 12000); + } + else + m_uiRendTimer -= uiDiff; + + if (m_uiSilenceTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature, SPELL_SILENCE) == CAST_OK) + m_uiSilenceTimer = urand(7000, 13000); + } + else + m_uiSilenceTimer -= uiDiff; + + if (m_uiPsychicBlowTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature->getVictim(), SPELL_PSYCHIC_BLOW) == CAST_OK) + m_uiPsychicBlowTimer = urand(20000, 25000); + } + else + m_uiPsychicBlowTimer -= uiDiff; + + DoMeleeAttackIfReady(); + } +}; + +/*###### +## boss_lord_sanguinar +######*/ + +struct boss_lord_sanguinarAI : public advisor_base_ai +{ + boss_lord_sanguinarAI(Creature* pCreature) : advisor_base_ai(pCreature) { Reset(); } + + uint32 m_uiFearTimer; + + void Reset() override + { + m_uiFearTimer = 10000; + + advisor_base_ai::Reset(); + } + + void Aggro(Unit* /*pWho*/) override + { + DoScriptText(SAY_SANGUINAR_AGGRO, m_creature); + } + + void JustDied(Unit* /*pKiller*/) override + { + DoScriptText(SAY_SANGUINAR_DEATH, m_creature); + } + + void UpdateAI(const uint32 uiDiff) override + { + if (!m_creature->SelectHostileTarget() || !m_creature->getVictim()) + return; + + // Don't use abilities during fake death + if (m_bFakeDeath) + return; + + if (m_uiFearTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature, SPELL_BELLOWING_ROAR) == CAST_OK) + m_uiFearTimer = 30000; + } + else + m_uiFearTimer -= uiDiff; + + DoMeleeAttackIfReady(); + } +}; + +/*###### +## boss_grand_astromancer_capernian +######*/ + +struct boss_grand_astromancer_capernianAI : public advisor_base_ai +{ + boss_grand_astromancer_capernianAI(Creature* pCreature) : advisor_base_ai(pCreature) { Reset(); } + + uint32 m_uiFireballTimer; + uint32 m_uiConflagrationTimer; + uint32 m_uiArcaneExplosionTimer; + + void Reset() override + { + m_uiFireballTimer = 2000; + m_uiConflagrationTimer = 20000; + m_uiArcaneExplosionTimer = 5000; + + advisor_base_ai::Reset(); + } + + void AttackStart(Unit* pWho) override + { + if (m_creature->Attack(pWho, true)) + { + m_creature->AddThreat(pWho); + m_creature->SetInCombatWith(pWho); + pWho->SetInCombatWith(m_creature); + + m_creature->GetMotionMaster()->MoveChase(pWho, 20.0f); + } + } + + void Aggro(Unit* /*pWho*/) override + { + DoScriptText(SAY_CAPERNIAN_AGGRO, m_creature); + } + + void JustDied(Unit* /*pKiller*/) override + { + DoScriptText(SAY_CAPERNIAN_DEATH, m_creature); + } + + void UpdateAI(const uint32 uiDiff) override + { + if (!m_creature->SelectHostileTarget() || !m_creature->getVictim()) + return; + + // Don't use abilities during fake death + if (m_bFakeDeath) + return; + + if (m_uiFireballTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature->getVictim(), SPELL_CAPERNIAN_FIREBALL) == CAST_OK) + m_uiFireballTimer = 4000; + } + else + m_uiFireballTimer -= uiDiff; + + if (m_uiConflagrationTimer < uiDiff) + { + Unit* pTarget = m_creature->SelectAttackingTarget(ATTACKING_TARGET_RANDOM, 0); + + if (pTarget && m_creature->IsWithinDistInMap(pTarget, 30.0f)) + DoCastSpellIfCan(pTarget, SPELL_CONFLAGRATION); + else + DoCastSpellIfCan(m_creature->getVictim(), SPELL_CONFLAGRATION); + + m_uiConflagrationTimer = urand(10000, 15000); + } + else + m_uiConflagrationTimer -= uiDiff; + + if (m_uiArcaneExplosionTimer < uiDiff) + { + if (m_creature->SelectAttackingTarget(ATTACKING_TARGET_TOPAGGRO, 0, SPELL_ARCANE_BURST, SELECT_FLAG_IN_MELEE_RANGE)) + { + if (DoCastSpellIfCan(m_creature, SPELL_ARCANE_BURST) == CAST_OK) + m_uiArcaneExplosionTimer = urand(4000, 6000); + } + } + else + m_uiArcaneExplosionTimer -= uiDiff; + + // Do NOT deal any melee damage. + } +}; + +/*###### +## boss_master_engineer_telonicus +######*/ + +struct boss_master_engineer_telonicusAI : public advisor_base_ai +{ + boss_master_engineer_telonicusAI(Creature* pCreature) : advisor_base_ai(pCreature) { Reset(); } + + uint32 m_uiBombTimer; + uint32 m_uiRemoteToyTimer; + + void Reset() override + { + m_uiBombTimer = 10000; + m_uiRemoteToyTimer = 5000; + + advisor_base_ai::Reset(); + } + + void Aggro(Unit* /*pWho*/) override + { + DoScriptText(SAY_TELONICUS_AGGRO, m_creature); + } + + void JustDied(Unit* /*pKiller*/) override + { + DoScriptText(SAY_TELONICUS_DEATH, m_creature); + } + + void UpdateAI(const uint32 uiDiff) override + { + if (!m_creature->SelectHostileTarget() || !m_creature->getVictim()) + return; + + // Don't use abilities during fake death + if (m_bFakeDeath) + return; + + if (m_uiBombTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature->getVictim(), SPELL_BOMB) == CAST_OK) + m_uiBombTimer = 25000; + } + else + m_uiBombTimer -= uiDiff; + + if (m_uiRemoteToyTimer < uiDiff) + { + if (Unit* pTarget = m_creature->SelectAttackingTarget(ATTACKING_TARGET_RANDOM, 0)) + { + if (DoCastSpellIfCan(pTarget, SPELL_REMOTE_TOY) == CAST_OK) + m_uiRemoteToyTimer = urand(10000, 15000); + } + } + else + m_uiRemoteToyTimer -= uiDiff; + + DoMeleeAttackIfReady(); + } +}; + +/*###### +## mob_phoenix_tk +######*/ + +struct mob_phoenix_tkAI : public ScriptedAI +{ + mob_phoenix_tkAI(Creature* pCreature) : ScriptedAI(pCreature) {Reset();} + + uint32 m_uiCycleTimer; + + bool m_bFakeDeath; + + void Reset() override + { + m_uiCycleTimer = 2000; + m_bFakeDeath = false; + } + + void Aggro(Unit* /*pWho*/) override + { + DoCastSpellIfCan(m_creature, SPELL_BURN); + } + + void EnterEvadeMode() override + { + // Don't evade during ember blast + if (m_bFakeDeath) + return; + + ScriptedAI::EnterEvadeMode(); + } + + void DamageTaken(Unit* /*pKiller*/, uint32& uiDamage) override + { + if (uiDamage < m_creature->GetHealth()) + return; + + // Prevent glitch if in fake death + if (m_bFakeDeath) + { + uiDamage = 0; + return; + } + + // prevent death + uiDamage = 0; + DoSetFakeDeath(); + } + + void DoSetFakeDeath() + { + m_bFakeDeath = true; + + m_creature->InterruptNonMeleeSpells(false); + m_creature->SetHealth(1); + m_creature->StopMoving(); + m_creature->ClearComboPointHolders(); + m_creature->RemoveAllAurasOnDeath(); + m_creature->ModifyAuraState(AURA_STATE_HEALTHLESS_20_PERCENT, false); + m_creature->ModifyAuraState(AURA_STATE_HEALTHLESS_35_PERCENT, false); + m_creature->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE); + m_creature->ClearAllReactives(); + m_creature->SetTargetGuid(ObjectGuid()); + m_creature->GetMotionMaster()->Clear(); + m_creature->GetMotionMaster()->MoveIdle(); + m_creature->SetStandState(UNIT_STAND_STATE_DEAD); + + // Spawn egg and make invisible + DoCastSpellIfCan(m_creature, SPELL_EMBER_BLAST, CAST_TRIGGERED); + m_creature->SummonCreature(NPC_PHOENIX_EGG, 0, 0, 0, 0, TEMPSUMMON_TIMED_OOC_OR_DEAD_DESPAWN, 15000); + } + + void SummonedCreatureDespawn(Creature* /*pSummoned*/) override + { + // Remove fake death if the egg despawns after 15 secs + m_creature->RemoveAurasDueToSpell(SPELL_EMBER_BLAST); + m_creature->SetStandState(UNIT_STAND_STATE_STAND); + m_creature->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE); + + if (DoCastSpellIfCan(m_creature, SPELL_REBIRTH) == CAST_OK) + { + m_creature->SetHealth(m_creature->GetMaxHealth()); + m_creature->GetMotionMaster()->Clear(); + DoStartMovement(m_creature->getVictim()); + m_bFakeDeath = false; + + DoCastSpellIfCan(m_creature, SPELL_BURN, CAST_TRIGGERED); + } + } + + void SummonedCreatureJustDied(Creature* /*pSummoned*/) override + { + // Self kill if the egg is killed + if (m_bFakeDeath) + m_creature->DealDamage(m_creature, m_creature->GetHealth(), NULL, DIRECT_DAMAGE, SPELL_SCHOOL_MASK_NORMAL, NULL, false); + } + + void UpdateAI(const uint32 uiDiff) override + { + if (!m_creature->SelectHostileTarget() || !m_creature->getVictim()) + return; + + if (m_bFakeDeath) + return; + + // ToDo: research if this is correct and how can this be done by spell + if (m_uiCycleTimer < uiDiff) + { + // spell Burn should possible do this, but it doesn't, so do this for now. + uint32 uiDmg = urand(4500, 5500); + if (uiDmg > m_creature->GetHealth()) + DoSetFakeDeath(); + else + m_creature->DealDamage(m_creature, uiDmg, 0, DOT, SPELL_SCHOOL_MASK_FIRE, NULL, false); + + m_uiCycleTimer = 2000; + } + else + m_uiCycleTimer -= uiDiff; + + DoMeleeAttackIfReady(); + } +}; + +/*###### +## mob_phoenix_egg_tk +######*/ + +// TODO Remove this 'script' when combat movement can be proper prevented from core-side +struct mob_phoenix_egg_tkAI : public Scripted_NoMovementAI +{ + mob_phoenix_egg_tkAI(Creature* pCreature) : Scripted_NoMovementAI(pCreature) { Reset(); } + + void Reset() override { } + void MoveInLineOfSight(Unit* /*pWho*/) override { } + void AttackStart(Unit* /*pWho*/) override { } + void UpdateAI(const uint32 /*uiDiff*/) override { } +}; + +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_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* pNewScript; + + pNewScript = new Script; + pNewScript->Name = "boss_kaelthas"; + pNewScript->GetAI = &GetAI_boss_kaelthas; + pNewScript->pEffectDummyNPC = &EffectDummyCreature_kael_phase_2; + pNewScript->RegisterSelf(); + + pNewScript = new Script; + pNewScript->Name = "boss_thaladred_the_darkener"; + pNewScript->GetAI = &GetAI_boss_thaladred_the_darkener; + pNewScript->RegisterSelf(); + + pNewScript = new Script; + pNewScript->Name = "boss_lord_sanguinar"; + pNewScript->GetAI = &GetAI_boss_lord_sanguinar; + pNewScript->RegisterSelf(); + + pNewScript = new Script; + pNewScript->Name = "boss_grand_astromancer_capernian"; + pNewScript->GetAI = &GetAI_boss_grand_astromancer_capernian; + pNewScript->RegisterSelf(); + + pNewScript = new Script; + pNewScript->Name = "boss_master_engineer_telonicus"; + pNewScript->GetAI = &GetAI_boss_master_engineer_telonicus; + pNewScript->RegisterSelf(); + + pNewScript = new Script; + pNewScript->Name = "mob_phoenix_tk"; + pNewScript->GetAI = &GetAI_mob_phoenix_tk; + pNewScript->RegisterSelf(); + + pNewScript = new Script; + pNewScript->Name = "mob_phoenix_egg_tk"; + pNewScript->GetAI = &GetAI_mob_phoenix_egg_tk; + pNewScript->RegisterSelf(); +} diff --git a/src/modules/SD2/scripts/outland/tempest_keep/the_eye/boss_void_reaver.cpp b/src/modules/SD2/scripts/outland/tempest_keep/the_eye/boss_void_reaver.cpp new file mode 100644 index 000000000..3589bdfd1 --- /dev/null +++ b/src/modules/SD2/scripts/outland/tempest_keep/the_eye/boss_void_reaver.cpp @@ -0,0 +1,197 @@ +/* Copyright (C) 2006 - 2013 ScriptDev2 + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with 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: 95 +SDComment: Small adjustments may be required +SDCategory: Tempest Keep, The Eye +EndScriptData */ + +#include "precompiled.h" +#include "the_eye.h" + +enum +{ + SAY_AGGRO = -1550000, + SAY_SLAY1 = -1550001, + SAY_SLAY2 = -1550002, + SAY_SLAY3 = -1550003, + SAY_DEATH = -1550004, + SAY_POUNDING1 = -1550005, + SAY_POUNDING2 = -1550006, + + SPELL_POUNDING = 34162, + SPELL_ARCANE_ORB_MISSILE = 34172, + SPELL_KNOCK_AWAY = 25778, + SPELL_BERSERK = 26662, + + NPC_ARCANE_ORB_TARGET = 19577, +}; + +struct boss_void_reaverAI : public ScriptedAI +{ + boss_void_reaverAI(Creature* pCreature) : ScriptedAI(pCreature) + { + m_pInstance = (ScriptedInstance*)pCreature->GetInstanceData(); + Reset(); + } + + ScriptedInstance* m_pInstance; + + uint32 m_uiPoundingTimer; + uint32 m_uiArcaneOrbTimer; + uint32 m_uiKnockAwayTimer; + uint32 m_uiBerserkTimer; + + void Reset() override + { + m_uiPoundingTimer = 13000; + m_uiArcaneOrbTimer = 3000; + m_uiKnockAwayTimer = 30000; + m_uiBerserkTimer = 10 * MINUTE * IN_MILLISECONDS; + } + + void KilledUnit(Unit* pVictim) override + { + if (pVictim->GetTypeId() != TYPEID_PLAYER) + return; + + switch (urand(0, 2)) + { + case 0: DoScriptText(SAY_SLAY1, m_creature); break; + case 1: DoScriptText(SAY_SLAY2, m_creature); break; + case 2: DoScriptText(SAY_SLAY3, m_creature); break; + } + } + + void JustDied(Unit* /*pKiller*/) override + { + DoScriptText(SAY_DEATH, m_creature); + + if (m_pInstance) + m_pInstance->SetData(TYPE_VOIDREAVER, DONE); + } + + void Aggro(Unit* /*pWho*/) override + { + DoScriptText(SAY_AGGRO, m_creature); + + if (m_pInstance) + m_pInstance->SetData(TYPE_VOIDREAVER, IN_PROGRESS); + } + + void JustReachedHome() override + { + if (m_pInstance) + m_pInstance->SetData(TYPE_VOIDREAVER, NOT_STARTED); + } + + void JustSummoned(Creature* pSummoned) override + { + // Cast the Arcane Orb missile on the npc, not on player + DoCastSpellIfCan(pSummoned, SPELL_ARCANE_ORB_MISSILE, CAST_TRIGGERED); + } + + void UpdateAI(const uint32 uiDiff) override + { + if (!m_creature->SelectHostileTarget() || !m_creature->getVictim()) + return; + + // Pounding + if (m_uiPoundingTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature, SPELL_POUNDING) == CAST_OK) + { + DoScriptText(urand(0, 1) ? SAY_POUNDING1 : SAY_POUNDING2, m_creature); + m_uiPoundingTimer = 14000; + } + } + else + m_uiPoundingTimer -= uiDiff; + + // Arcane Orb + if (m_uiArcaneOrbTimer < uiDiff) + { + // Search only for players which are not within 18 yards of the boss + std::vector suitableTargets; + ThreatList const& threatList = m_creature->GetThreatManager().getThreatList(); + + for (ThreatList::const_iterator itr = threatList.begin(); itr != threatList.end(); ++itr) + { + if (Unit* pTarget = m_creature->GetMap()->GetUnit((*itr)->getUnitGuid())) + { + if (pTarget->GetTypeId() == TYPEID_PLAYER && !pTarget->IsWithinDist(m_creature, 18.0f)) + suitableTargets.push_back(pTarget); + } + } + + if (suitableTargets.empty()) + m_uiArcaneOrbTimer = 3000; + else + { + Unit* pTarget = suitableTargets[urand(0, suitableTargets.size() - 1)]; + + if (pTarget) + m_creature->SummonCreature(NPC_ARCANE_ORB_TARGET, pTarget->GetPositionX(), pTarget->GetPositionY(), pTarget->GetPositionZ(), 0, TEMPSUMMON_CORPSE_DESPAWN, 0); + + m_uiArcaneOrbTimer = 3000; + } + } + else + m_uiArcaneOrbTimer -= uiDiff; + + // Single Target knock back, reduces aggro + if (m_uiKnockAwayTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature->getVictim(), SPELL_KNOCK_AWAY) == CAST_OK) + m_uiKnockAwayTimer = 30000; + } + else + m_uiKnockAwayTimer -= uiDiff; + + // Berserk + if (m_uiBerserkTimer) + { + if (m_uiBerserkTimer <= uiDiff) + { + if (DoCastSpellIfCan(m_creature, SPELL_BERSERK) == CAST_OK) + m_uiBerserkTimer = 0; + } + else + m_uiBerserkTimer -= uiDiff; + } + + DoMeleeAttackIfReady(); + + EnterEvadeIfOutOfCombatArea(uiDiff); + } +}; + +CreatureAI* GetAI_boss_void_reaver(Creature* pCreature) +{ + return new boss_void_reaverAI(pCreature); +} + +void AddSC_boss_void_reaver() +{ + Script* pNewScript; + + pNewScript = new Script; + pNewScript->Name = "boss_void_reaver"; + pNewScript->GetAI = &GetAI_boss_void_reaver; + pNewScript->RegisterSelf(); +} diff --git a/src/modules/SD2/scripts/outland/tempest_keep/the_eye/instance_the_eye.cpp b/src/modules/SD2/scripts/outland/tempest_keep/the_eye/instance_the_eye.cpp new file mode 100644 index 000000000..c8b58e8d3 --- /dev/null +++ b/src/modules/SD2/scripts/outland/tempest_keep/the_eye/instance_the_eye.cpp @@ -0,0 +1,139 @@ +/* Copyright (C) 2006 - 2013 ScriptDev2 + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along 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 "the_eye.h" + +instance_the_eye::instance_the_eye(Map* pMap) : ScriptedInstance(pMap), + m_uiKaelthasEventPhase(0) +{ + Initialize(); +} + +void instance_the_eye::Initialize() +{ + memset(&m_auiEncounter, 0, sizeof(m_auiEncounter)); +} + +bool instance_the_eye::IsEncounterInProgress() const +{ + for (uint8 i = 0; i < MAX_ENCOUNTER; ++i) + { + if (m_auiEncounter[i] == IN_PROGRESS) + return true; + } + + return false; +} + +void instance_the_eye::OnCreatureCreate(Creature* pCreature) +{ + switch (pCreature->GetEntry()) + { + case NPC_THALADRED: + case NPC_TELONICUS: + case NPC_CAPERNIAN: + case NPC_SANGUINAR: + case NPC_KAELTHAS: + m_mNpcEntryGuidStore[pCreature->GetEntry()] = pCreature->GetObjectGuid(); + break; + } +} + +void instance_the_eye::OnObjectCreate(GameObject* pGo) +{ + switch (pGo->GetEntry()) + { + case GO_ARCANE_DOOR_HORIZ_3: + case GO_ARCANE_DOOR_HORIZ_4: + case GO_KAEL_STATUE_LEFT: + case GO_KAEL_STATUE_RIGHT: + case GO_BRIDGE_WINDOW: + m_mGoEntryGuidStore[pGo->GetEntry()] = pGo->GetObjectGuid(); + break; + } +} + +void instance_the_eye::SetData(uint32 uiType, uint32 uiData) +{ + switch (uiType) + { + case TYPE_ALAR: + case TYPE_SOLARIAN: + case TYPE_VOIDREAVER: + m_auiEncounter[uiType] = uiData; + break; + case TYPE_KAELTHAS: + // Don't set the same data twice + if (m_auiEncounter[uiType] == uiData) + break; + DoUseDoorOrButton(GO_ARCANE_DOOR_HORIZ_3); + DoUseDoorOrButton(GO_ARCANE_DOOR_HORIZ_4); + if (uiData == FAIL) + { + if (GameObject* pGo = GetSingleGameObjectFromStorage(GO_KAEL_STATUE_LEFT)) + pGo->ResetDoorOrButton(); + if (GameObject* pGo = GetSingleGameObjectFromStorage(GO_KAEL_STATUE_RIGHT)) + pGo->ResetDoorOrButton(); + if (GameObject* pGo = GetSingleGameObjectFromStorage(GO_BRIDGE_WINDOW)) + pGo->ResetDoorOrButton(); + + // Respawn or reset the advisors + for (uint8 i = 0; i < MAX_ADVISORS; ++i) + { + if (Creature* pTemp = GetSingleCreatureFromStorage(aAdvisors[i])) + { + if (!pTemp->IsAlive()) + pTemp->Respawn(); + else + pTemp->AI()->EnterEvadeMode(); + } + } + } + m_auiEncounter[uiType] = uiData; + break; + } +} + +uint32 instance_the_eye::GetData(uint32 uiType) const +{ + if (uiType < MAX_ENCOUNTER) + return m_auiEncounter[uiType]; + + return 0; +} + +InstanceData* GetInstanceData_instance_the_eye(Map* pMap) +{ + return new instance_the_eye(pMap); +} + +void AddSC_instance_the_eye() +{ + Script* pNewScript; + + pNewScript = new Script; + pNewScript->Name = "instance_the_eye"; + pNewScript->GetInstanceData = &GetInstanceData_instance_the_eye; + pNewScript->RegisterSelf(); +} diff --git a/src/modules/SD2/scripts/outland/tempest_keep/the_eye/the_eye.h b/src/modules/SD2/scripts/outland/tempest_keep/the_eye/the_eye.h new file mode 100644 index 000000000..348e0bf4d --- /dev/null +++ b/src/modules/SD2/scripts/outland/tempest_keep/the_eye/the_eye.h @@ -0,0 +1,61 @@ +/* Copyright (C) 2006 - 2013 ScriptDev2 + * This program is free software licensed under GPL version 2 + * Please see the included DOCS/LICENSE.TXT for more information */ + +#ifndef DEF_THE_EYE_H +#define DEF_THE_EYE_H + +enum +{ + MAX_ENCOUNTER = 4, + MAX_ADVISORS = 4, + + TYPE_ALAR = 0, + TYPE_SOLARIAN = 1, + TYPE_VOIDREAVER = 2, + TYPE_KAELTHAS = 3, + + // NPC_ASTROMANCER = 18805, + NPC_KAELTHAS = 19622, + + NPC_CAPERNIAN = 20062, + NPC_SANGUINAR = 20060, + NPC_TELONICUS = 20063, + NPC_THALADRED = 20064, + + GO_ARCANE_DOOR_HORIZ_3 = 184325, // combat doors for Kael + GO_ARCANE_DOOR_HORIZ_4 = 184324, + // GO_RAID_DOOR_4 = 184329, // encounter doors - no longer used since 2.4.0 + // GO_RAID_DOOR_3 = 184327, + // GO_ARCANE_DOOR_VERT_3 = 184326, + // GO_ARCANE_DOOR_VERT_4 = 184328, + GO_KAEL_STATUE_LEFT = 184597, // cosmetic objects for Kael encounter + GO_KAEL_STATUE_RIGHT = 184596, + GO_BRIDGE_WINDOW = 184069, +}; + +static const uint32 aAdvisors[MAX_ADVISORS] = {NPC_CAPERNIAN, NPC_SANGUINAR, NPC_TELONICUS, NPC_THALADRED}; + +class instance_the_eye : public ScriptedInstance +{ + public: + instance_the_eye(Map* pMap); + + void Initialize() override; + bool IsEncounterInProgress() const override; + + void OnCreatureCreate(Creature* pCreature) override; + void OnObjectCreate(GameObject* pGo) override; + + void SetData(uint32 uiType, uint32 uiData) override; + uint32 GetData(uint32 uiType) const override; + + // No Save or Load needed to current knowledge + + private: + uint32 m_auiEncounter[MAX_ENCOUNTER]; + + uint32 m_uiKaelthasEventPhase; +}; + +#endif diff --git a/src/modules/SD2/scripts/outland/tempest_keep/the_mechanar/boss_nethermancer_sepethrea.cpp b/src/modules/SD2/scripts/outland/tempest_keep/the_mechanar/boss_nethermancer_sepethrea.cpp new file mode 100644 index 000000000..7e32b1e38 --- /dev/null +++ b/src/modules/SD2/scripts/outland/tempest_keep/the_mechanar/boss_nethermancer_sepethrea.cpp @@ -0,0 +1,156 @@ +/* Copyright (C) 2006 - 2013 ScriptDev2 + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with 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: 95 +SDComment: May need some small adjustments +SDCategory: Tempest Keep, The Mechanar +EndScriptData */ + +#include "precompiled.h" +#include "mechanar.h" + +enum +{ + SAY_AGGRO = -1554013, + SAY_SUMMON = -1554014, + SAY_DRAGONS_BREATH_1 = -1554015, + SAY_DRAGONS_BREATH_2 = -1554016, + SAY_SLAY1 = -1554017, + SAY_SLAY2 = -1554018, + SAY_DEATH = -1554019, + + SPELL_SUMMON_RAGING_FLAMES = 35275, + SPELL_SUMMON_RAGING_FLAMES_H = 39084, + SPELL_FROST_ATTACK = 45195, + SPELL_ARCANE_BLAST = 35314, + SPELL_DRAGONS_BREATH = 35250, + + NPC_RAGING_FLAMES = 20481, +}; + +struct boss_nethermancer_sepethreaAI : public ScriptedAI +{ + boss_nethermancer_sepethreaAI(Creature* pCreature) : ScriptedAI(pCreature) + { + m_pInstance = (ScriptedInstance*)pCreature->GetInstanceData(); + m_bIsRegularMode = pCreature->GetMap()->IsRegularDifficulty(); + Reset(); + } + + ScriptedInstance* m_pInstance; + bool m_bIsRegularMode; + + uint32 m_uiFrostAttackTimer; + uint32 m_uiArcaneBlastTimer; + uint32 m_uiDragonsBreathTimer; + + void Reset() override + { + m_uiFrostAttackTimer = urand(8000, 17000); + m_uiArcaneBlastTimer = urand(14000, 25000); + m_uiDragonsBreathTimer = urand(20000, 26000); + } + + void Aggro(Unit* /*pWho*/) override + { + DoScriptText(SAY_AGGRO, m_creature); + DoCastSpellIfCan(m_creature, m_bIsRegularMode ? SPELL_SUMMON_RAGING_FLAMES : SPELL_SUMMON_RAGING_FLAMES_H); + } + + void KilledUnit(Unit* /*pVictim*/) override + { + DoScriptText(urand(0, 1) ? SAY_SLAY1 : SAY_SLAY2, m_creature); + } + + void JustDied(Unit* /*pKiller*/) override + { + DoScriptText(SAY_DEATH, m_creature); + + if (m_pInstance) + m_pInstance->SetData(TYPE_SEPETHREA, DONE); + } + + void JustSummoned(Creature* pSummoned) override + { + if (pSummoned->GetEntry() == NPC_RAGING_FLAMES) + { + pSummoned->ApplySpellImmune(0, IMMUNITY_DAMAGE, SPELL_SCHOOL_MASK_MAGIC, true); + pSummoned->ApplySpellImmune(0, IMMUNITY_DAMAGE, SPELL_SCHOOL_MASK_NORMAL, true); + + // ToDo: need to fixate target and make them walk! + if (Unit* pTarget = m_creature->SelectAttackingTarget(ATTACKING_TARGET_RANDOM, 0)) + pSummoned->GetMotionMaster()->MoveChase(pTarget); + } + } + + void UpdateAI(const uint32 uiDiff) override + { + // Return since we have no target + if (!m_creature->SelectHostileTarget() || !m_creature->getVictim()) + return; + + // Frost Attack + if (m_uiFrostAttackTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature->getVictim(), SPELL_FROST_ATTACK) == CAST_OK) + m_uiFrostAttackTimer = urand(5000, 17000); + } + else + m_uiFrostAttackTimer -= uiDiff; + + // Arcane Blast + if (m_uiArcaneBlastTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature->getVictim(), SPELL_ARCANE_BLAST) == CAST_OK) + m_uiArcaneBlastTimer = urand(15000, 30000); + } + else + m_uiArcaneBlastTimer -= uiDiff; + + // Dragons Breath + if (m_uiDragonsBreathTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature, SPELL_DRAGONS_BREATH) == CAST_OK) + { + if (urand(0, 1)) + DoScriptText(urand(0, 1) ? SAY_DRAGONS_BREATH_1 : SAY_DRAGONS_BREATH_2, m_creature); + + m_uiDragonsBreathTimer = urand(20000, 35000); + } + } + else + m_uiDragonsBreathTimer -= uiDiff; + + DoMeleeAttackIfReady(); + } +}; + +CreatureAI* GetAI_boss_nethermancer_sepethrea(Creature* pCreature) +{ + return new boss_nethermancer_sepethreaAI(pCreature); +} + +void AddSC_boss_nethermancer_sepethrea() +{ + Script* pNewScript; + + pNewScript = new Script; + pNewScript->Name = "boss_nethermancer_sepethrea"; + pNewScript->GetAI = &GetAI_boss_nethermancer_sepethrea; + pNewScript->RegisterSelf(); +} diff --git a/src/modules/SD2/scripts/outland/tempest_keep/the_mechanar/boss_pathaleon_the_calculator.cpp b/src/modules/SD2/scripts/outland/tempest_keep/the_mechanar/boss_pathaleon_the_calculator.cpp new file mode 100644 index 000000000..fe9f98c1f --- /dev/null +++ b/src/modules/SD2/scripts/outland/tempest_keep/the_mechanar/boss_pathaleon_the_calculator.cpp @@ -0,0 +1,273 @@ +/* Copyright (C) 2006 - 2013 ScriptDev2 + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with 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: 95 +SDComment: Timers may need update. +SDCategory: Tempest Keep, The Mechanar +EndScriptData */ + +#include "precompiled.h" +#include "mechanar.h" + +enum +{ + SAY_AGGRO = -1554020, + SAY_DOMINATION_1 = -1554021, + SAY_DOMINATION_2 = -1554022, + SAY_SUMMON = -1554023, + SAY_ENRAGE = -1554024, + SAY_SLAY_1 = -1554025, + SAY_SLAY_2 = -1554026, + SAY_DEATH = -1554027, + + // Spells to be casted + SPELL_MANA_TAP = 36021, + SPELL_ARCANE_TORRENT = 36022, + SPELL_DOMINATION = 35280, + SPELL_ARCANE_EXPLOSION_H = 15453, + SPELL_FRENZY = 36992, + SPELL_SUICIDE = 35301, // kill the Nether Wraiths + SPELL_DISGRUNTLED_ANGER = 35289, // empower a Nether Wraith + + SPELL_SUMMON_NETHER_WRAITH_1 = 35285, + SPELL_SUMMON_NETHER_WRAITH_2 = 35286, + SPELL_SUMMON_NETHER_WRAITH_3 = 35287, + SPELL_SUMMON_NETHER_WRAITH_4 = 35288, + + // Add Spells + SPELL_DETONATION = 35058, + SPELL_ARCANE_BOLT = 20720, +}; + +static const uint32 aWraithSummonSpells[4] = {SPELL_SUMMON_NETHER_WRAITH_1, SPELL_SUMMON_NETHER_WRAITH_2, SPELL_SUMMON_NETHER_WRAITH_3, SPELL_SUMMON_NETHER_WRAITH_4}; + +struct boss_pathaleon_the_calculatorAI : public ScriptedAI +{ + boss_pathaleon_the_calculatorAI(Creature* pCreature) : ScriptedAI(pCreature) + { + m_pInstance = (ScriptedInstance*)pCreature->GetInstanceData(); + m_bIsRegularMode = pCreature->GetMap()->IsRegularDifficulty(); + Reset(); + } + + ScriptedInstance* m_pInstance; + bool m_bIsRegularMode; + + uint32 m_uiSummonTimer; + uint32 m_uiAngerTimer; + uint32 m_uiManaTapTimer; + uint32 m_uiArcaneTorrentTimer; + uint32 m_uiDominationTimer; + uint32 m_uiArcaneExplosionTimer; + bool m_bIsEnraged; + + void Reset() override + { + m_uiSummonTimer = urand(12000, 23000); + m_uiAngerTimer = urand(31000, 42000); + m_uiManaTapTimer = urand(2000, 9000); + m_uiArcaneTorrentTimer = urand(11000, 24000); + m_uiDominationTimer = urand(25000, 40000); + m_uiArcaneExplosionTimer = urand(18000, 45000); + m_bIsEnraged = false; + } + + void Aggro(Unit* /*pWho*/) override + { + DoScriptText(SAY_AGGRO, m_creature); + } + + void KilledUnit(Unit* /*pVictim*/) override + { + DoScriptText(urand(0, 1) ? SAY_SLAY_1 : SAY_SLAY_2, m_creature); + } + + void JustDied(Unit* /*pKiller*/) override + { + DoScriptText(SAY_DEATH, m_creature); + + if (m_pInstance) + m_pInstance->SetData(TYPE_PATHALEON, DONE); + } + + void JustSummoned(Creature* pSummoned) override + { + if (m_creature->getVictim()) + pSummoned->AI()->AttackStart(m_creature->getVictim()); + } + + void UpdateAI(const uint32 uiDiff) override + { + // Return since we have no target + if (!m_creature->SelectHostileTarget() || !m_creature->getVictim()) + return; + + if (m_uiManaTapTimer < uiDiff) + { + if (Unit* pTarget = m_creature->SelectAttackingTarget(ATTACKING_TARGET_RANDOM, 0, SPELL_MANA_TAP, SELECT_FLAG_POWER_MANA)) + { + if (DoCastSpellIfCan(pTarget, SPELL_MANA_TAP) == CAST_OK) + m_uiManaTapTimer = urand(16000, 34000); + } + } + else + m_uiManaTapTimer -= uiDiff; + + if (m_uiArcaneTorrentTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature, SPELL_ARCANE_TORRENT) == CAST_OK) + m_uiArcaneTorrentTimer = urand(40000, 52000); + } + else + m_uiArcaneTorrentTimer -= uiDiff; + + if (m_uiDominationTimer < uiDiff) + { + if (Unit* pTarget = m_creature->SelectAttackingTarget(ATTACKING_TARGET_RANDOM, 1)) + { + if (DoCastSpellIfCan(pTarget, SPELL_DOMINATION) == CAST_OK) + { + DoScriptText(urand(0, 1) ? SAY_DOMINATION_1 : SAY_DOMINATION_2, m_creature); + m_uiDominationTimer = urand(25000, 30000); + } + } + } + else + m_uiDominationTimer -= uiDiff; + + // Only casting if Heroic Mode is used + if (!m_bIsRegularMode) + { + if (m_uiArcaneExplosionTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature, SPELL_ARCANE_EXPLOSION_H) == CAST_OK) + m_uiArcaneExplosionTimer = urand(13000, 25000); + } + else + m_uiArcaneExplosionTimer -= uiDiff; + } + + if (!m_bIsEnraged && m_creature->GetHealthPercent() < 21.0f) + { + if (DoCastSpellIfCan(m_creature, SPELL_FRENZY) == CAST_OK) + { + DoCastSpellIfCan(m_creature, SPELL_SUICIDE, CAST_TRIGGERED); + DoScriptText(SAY_ENRAGE, m_creature); + m_bIsEnraged = true; + } + } + // Summon and empower Nether Wraiths only when not enraged + else + { + if (m_uiSummonTimer < uiDiff) + { + uint8 uiMaxWraith = urand(3, 4); + for (uint8 i = 0; i < uiMaxWraith; ++i) + DoCastSpellIfCan(m_creature, aWraithSummonSpells[i], CAST_TRIGGERED); + + DoScriptText(SAY_SUMMON, m_creature); + m_uiSummonTimer = urand(45000, 50000); + } + else + m_uiSummonTimer -= uiDiff; + + if (m_uiAngerTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature, SPELL_DISGRUNTLED_ANGER) == CAST_OK) + m_uiAngerTimer = urand(55000, 84000); + } + else + m_uiAngerTimer -= uiDiff; + } + + DoMeleeAttackIfReady(); + } +}; + +struct mob_nether_wraithAI : public ScriptedAI +{ + mob_nether_wraithAI(Creature* pCreature) : ScriptedAI(pCreature) {Reset();} + + uint32 m_uiArcaneMissilesTimer; + bool m_bHasDetonated; + + void Reset() override + { + m_uiArcaneMissilesTimer = urand(1000, 4000); + m_bHasDetonated = false; + } + + void UpdateAI(const uint32 uiDiff) override + { + if (!m_creature->SelectHostileTarget() || !m_creature->getVictim()) + return; + + if (m_uiArcaneMissilesTimer < uiDiff) + { + Unit* pTarget = m_creature->SelectAttackingTarget(ATTACKING_TARGET_RANDOM, 1); + if (!pTarget) + pTarget = m_creature->getVictim(); + + if (pTarget) + { + if (DoCastSpellIfCan(pTarget, SPELL_ARCANE_BOLT) == CAST_OK) + m_uiArcaneMissilesTimer = urand(5000, 10000); + } + } + else + m_uiArcaneMissilesTimer -= uiDiff; + + if (!m_bHasDetonated && m_creature->GetHealthPercent() < 10.0f) + { + if (DoCastSpellIfCan(m_creature, SPELL_DETONATION, CAST_TRIGGERED) == CAST_OK) + { + // Selfkill after the detonation + m_creature->DealDamage(m_creature, m_creature->GetHealth(), NULL, DIRECT_DAMAGE, SPELL_SCHOOL_MASK_NONE, NULL, false); + m_bHasDetonated = true; + return; + } + } + + DoMeleeAttackIfReady(); + } +}; + +CreatureAI* GetAI_boss_pathaleon_the_calculator(Creature* pCreature) +{ + return new boss_pathaleon_the_calculatorAI(pCreature); +} + +CreatureAI* GetAI_mob_nether_wraith(Creature* pCreature) +{ + return new mob_nether_wraithAI(pCreature); +} + +void AddSC_boss_pathaleon_the_calculator() +{ + Script* pNewScript; + + pNewScript = new Script; + pNewScript->Name = "boss_pathaleon_the_calculator"; + pNewScript->GetAI = &GetAI_boss_pathaleon_the_calculator; + pNewScript->RegisterSelf(); + + pNewScript = new Script; + pNewScript->Name = "mob_nether_wraith"; + pNewScript->GetAI = &GetAI_mob_nether_wraith; + pNewScript->RegisterSelf(); +} diff --git a/src/modules/SD2/scripts/outland/tempest_keep/the_mechanar/instance_mechanar.cpp b/src/modules/SD2/scripts/outland/tempest_keep/the_mechanar/instance_mechanar.cpp new file mode 100644 index 000000000..1bac266f3 --- /dev/null +++ b/src/modules/SD2/scripts/outland/tempest_keep/the_mechanar/instance_mechanar.cpp @@ -0,0 +1,237 @@ +/* Copyright (C) 2006 - 2013 ScriptDev2 + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along 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: 70 +SDComment: Elevator needs core support +SDCategory: Mechanar +EndScriptData */ + +#include "precompiled.h" +#include "mechanar.h" + +instance_mechanar::instance_mechanar(Map* pMap) : ScriptedInstance(pMap), + m_uiBridgeEventTimer(0), + m_uiBridgeEventPhase(0) +{ + Initialize(); +} + +void instance_mechanar::Initialize() +{ + memset(&m_auiEncounter, 0, sizeof(m_auiEncounter)); +} + +void instance_mechanar::OnPlayerEnter(Player* pPlayer) +{ + // Check encounter states + if (GetData(TYPE_SEPETHREA) != DONE || GetData(TYPE_PATHALEON) == DONE) + return; + + // Check if already summoned + if (GetSingleCreatureFromStorage(NPC_PATHALEON, true)) + return; + + pPlayer->SummonCreature(aBridgeEventLocs[6][0].m_uiSpawnEntry, aBridgeEventLocs[6][0].m_fX, aBridgeEventLocs[6][0].m_fY, aBridgeEventLocs[6][0].m_fZ, aBridgeEventLocs[6][0].m_fO, TEMPSUMMON_DEAD_DESPAWN, 0); +} + +void instance_mechanar::OnCreatureCreate(Creature* pCreature) +{ + switch (pCreature->GetEntry()) + { + case NPC_PATHALEON: + m_mNpcEntryGuidStore[pCreature->GetEntry()] = pCreature->GetObjectGuid(); + break; + case NPC_ASTROMAGE: + case NPC_PHYSICIAN: + case NPC_CENTURION: + case NPC_ENGINEER: + case NPC_NETHERBINDER: + case NPC_FORGE_DESTROYER: + if (pCreature->IsTemporarySummon()) + m_sBridgeTrashGuidSet.insert(pCreature->GetObjectGuid()); + break; + } +} + +void instance_mechanar::OnObjectCreate(GameObject* pGo) +{ + if (pGo->GetEntry() == GO_FACTORY_ELEVATOR) + { + // ToDo: activate elevator if TYPE_GYRO_KILL && TYPE_IRON_HAND && TYPE_CAPACITUS are DONE + m_mGoEntryGuidStore[GO_FACTORY_ELEVATOR] = pGo->GetObjectGuid(); + } +} + +void instance_mechanar::SetData(uint32 uiType, uint32 uiData) +{ + switch (uiType) + { + case TYPE_GYRO_KILL: + case TYPE_IRON_HAND: + case TYPE_CAPACITUS: + m_auiEncounter[uiType] = uiData; + // ToDo: Activate the Elevator when all these 3 are done + break; + case TYPE_SEPETHREA: + m_auiEncounter[uiType] = uiData; + if (uiData == DONE) + m_uiBridgeEventTimer = 10000; + break; + case TYPE_PATHALEON: + m_auiEncounter[uiType] = uiData; + 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_strInstData = saveStream.str(); + + SaveToDB(); + OUT_SAVE_INST_DATA_COMPLETE; + } +} + +uint32 instance_mechanar::GetData(uint32 uiType) const +{ + if (uiType < MAX_ENCOUNTER) + return m_auiEncounter[uiType]; + + return 0; +} + +void instance_mechanar::Load(const char* chrIn) +{ + if (!chrIn) + { + 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]; + + for (uint8 i = 0; i < MAX_ENCOUNTER; ++i) + { + if (m_auiEncounter[i] == IN_PROGRESS) + m_auiEncounter[i] = NOT_STARTED; + } + + OUT_LOAD_INST_DATA_COMPLETE; +} + +void instance_mechanar::OnCreatureDeath(Creature* pCreature) +{ + switch (pCreature->GetEntry()) + { + case NPC_GYRO_KILL: SetData(TYPE_GYRO_KILL, DONE); break; + case NPC_IRON_HAND: SetData(TYPE_IRON_HAND, DONE); break; + case NPC_LORD_CAPACITUS: SetData(TYPE_CAPACITUS, DONE); break; + + case NPC_ASTROMAGE: + case NPC_PHYSICIAN: + case NPC_CENTURION: + case NPC_ENGINEER: + case NPC_NETHERBINDER: + case NPC_FORGE_DESTROYER: + if (m_sBridgeTrashGuidSet.find(pCreature->GetObjectGuid()) != m_sBridgeTrashGuidSet.end()) + { + m_sBridgeTrashGuidSet.erase(pCreature->GetObjectGuid()); + + if (m_sBridgeTrashGuidSet.empty()) + { + // After the 3rd wave wait 10 seconds + if (m_uiBridgeEventPhase == 3) + m_uiBridgeEventTimer = 10000; + else + DoSpawnBridgeWave(); + } + } + break; + } +} + +void instance_mechanar::DoSpawnBridgeWave() +{ + if (Player* pPlayer = GetPlayerInMap(true, false)) + { + for (uint8 i = 0; i < MAX_BRIDGE_TRASH; ++i) + { + // Skip the blank entries + if (aBridgeEventLocs[m_uiBridgeEventPhase][i].m_uiSpawnEntry == 0) + break; + + if (Creature* pTemp = pPlayer->SummonCreature(aBridgeEventLocs[m_uiBridgeEventPhase][i].m_uiSpawnEntry, aBridgeEventLocs[m_uiBridgeEventPhase][i].m_fX, aBridgeEventLocs[m_uiBridgeEventPhase][i].m_fY, aBridgeEventLocs[m_uiBridgeEventPhase][i].m_fZ, aBridgeEventLocs[m_uiBridgeEventPhase][i].m_fO, TEMPSUMMON_DEAD_DESPAWN, 0)) + { + pTemp->CastSpell(pTemp, SPELL_ETHEREAL_TELEPORT, false); + + switch (m_uiBridgeEventPhase) + { + case 1: // These waves should attack the player directly + case 2: + case 4: + case 5: + pTemp->AI()->AttackStart(pPlayer); + break; + case 6: // Pathaleon + DoScriptText(SAY_PATHALEON_INTRO, pTemp); + break; + } + } + } + } + ++m_uiBridgeEventPhase; +} + +void instance_mechanar::Update(uint32 uiDiff) +{ + if (m_uiBridgeEventTimer) + { + if (m_uiBridgeEventTimer <= uiDiff) + { + DoSpawnBridgeWave(); + m_uiBridgeEventTimer = 0; + } + else + m_uiBridgeEventTimer -= uiDiff; + } +} + +InstanceData* GetInstanceData_instance_mechanar(Map* pMap) +{ + return new instance_mechanar(pMap); +} + +void AddSC_instance_mechanar() +{ + Script* pNewScript; + + pNewScript = new Script; + pNewScript->Name = "instance_mechanar"; + pNewScript->GetInstanceData = &GetInstanceData_instance_mechanar; + pNewScript->RegisterSelf(); +} diff --git a/src/modules/SD2/scripts/outland/tempest_keep/the_mechanar/mechanar.h b/src/modules/SD2/scripts/outland/tempest_keep/the_mechanar/mechanar.h new file mode 100644 index 000000000..e40512d2d --- /dev/null +++ b/src/modules/SD2/scripts/outland/tempest_keep/the_mechanar/mechanar.h @@ -0,0 +1,120 @@ +/* Copyright (C) 2006 - 2013 ScriptDev2 + * This program is free software licensed under GPL version 2 + * Please see the included DOCS/LICENSE.TXT for more information */ + +#ifndef DEF_MECHANAR_H +#define DEF_MECHANAR_H + +enum +{ + MAX_ENCOUNTER = 5, + MAX_BRIDGE_LOCATIONS = 7, + MAX_BRIDGE_TRASH = 4, + + TYPE_GYRO_KILL = 0, + TYPE_IRON_HAND = 1, + TYPE_CAPACITUS = 2, + TYPE_SEPETHREA = 3, + TYPE_PATHALEON = 4, + + NPC_GYRO_KILL = 19218, + NPC_IRON_HAND = 19710, + NPC_LORD_CAPACITUS = 19219, + // NPC_SEPETHREA = 19221, + NPC_PATHALEON = 19220, + + // bridge event related + NPC_ASTROMAGE = 19168, + NPC_PHYSICIAN = 20990, + NPC_CENTURION = 19510, + NPC_ENGINEER = 20988, + NPC_NETHERBINDER = 20059, + NPC_FORGE_DESTROYER = 19735, + + GO_FACTORY_ELEVATOR = 183788, + + SPELL_ETHEREAL_TELEPORT = 34427, + + SAY_PATHALEON_INTRO = -1554028, +}; + +struct SpawnLocation +{ + uint32 m_uiSpawnEntry; + float m_fX, m_fY, m_fZ, m_fO; +}; + +static const SpawnLocation aBridgeEventLocs[MAX_BRIDGE_LOCATIONS][4] = +{ + { + {NPC_ASTROMAGE, 243.9323f, -24.53621f, 26.3284f, 0}, + {NPC_ASTROMAGE, 240.5847f, -21.25438f, 26.3284f, 0}, + {NPC_PHYSICIAN, 238.4178f, -25.92982f, 26.3284f, 0}, + {NPC_CENTURION, 237.1122f, -19.14261f, 26.3284f, 0}, + }, + { + {NPC_FORGE_DESTROYER, 199.945f, -22.85885f, 24.95783f, 0}, + {0, 0, 0, 0, 0}, + }, + { + {NPC_ENGINEER, 179.8642f, -25.84609f, 24.8745f, 0}, + {NPC_ENGINEER, 181.9983f, -17.56084f, 24.8745f, 0}, + {NPC_PHYSICIAN, 183.4078f, -22.46612f, 24.8745f, 0}, + {0, 0, 0, 0, 0}, + }, + { + {NPC_ENGINEER, 141.0496f, 37.86048f, 24.87399f, 4.65f}, + {NPC_ASTROMAGE, 137.6626f, 34.89631f, 24.8742f, 4.65f}, + {NPC_PHYSICIAN, 135.3587f, 38.03816f, 24.87417f, 4.65f}, + {0, 0, 0, 0, 0}, + }, + { + {NPC_FORGE_DESTROYER, 137.8275f, 53.18128f, 24.95783f, 4.65f}, + {0, 0, 0, 0, 0}, + }, + { + {NPC_PHYSICIAN, 134.3062f, 109.1506f, 26.45663f, 4.65f}, + {NPC_ASTROMAGE, 135.3307f, 99.96439f, 26.45663f, 4.65f}, + {NPC_NETHERBINDER, 141.3976f, 102.7863f, 26.45663f, 4.65f}, + {NPC_ENGINEER, 140.8281f, 112.0363f, 26.45663f, 4.65f}, + }, + { + {NPC_PATHALEON, 139.5425f, 149.3192f, 25.65904f, 4.63f}, + {0, 0, 0, 0, 0}, + }, +}; + +class instance_mechanar : public ScriptedInstance +{ + public: + instance_mechanar(Map* pMap); + + void Initialize() override; + + void OnPlayerEnter(Player* pPlayer) override; + void OnObjectCreate(GameObject* pGo) override; + void OnCreatureCreate(Creature* pCreature) override; + + void OnCreatureDeath(Creature* pCreature) override; + + void SetData(uint32 uiType, uint32 uiData) override; + uint32 GetData(uint32 uiType) const override; + + const char* Save() const override { return m_strInstData.c_str(); } + void Load(const char* chrIn) override; + + void Update(uint32 uiDiff) override; + + private: + void DoSpawnBridgeWave(); + + uint32 m_auiEncounter[MAX_ENCOUNTER]; + std::string m_strInstData; + + uint32 m_uiBridgeEventTimer; + uint8 m_uiBridgeEventPhase; + + GuidSet m_sBridgeTrashGuidSet; +}; + +#endif diff --git a/src/modules/SD2/scripts/outland/terokkar_forest.cpp b/src/modules/SD2/scripts/outland/terokkar_forest.cpp new file mode 100644 index 000000000..0ffaa044a --- /dev/null +++ b/src/modules/SD2/scripts/outland/terokkar_forest.cpp @@ -0,0 +1,1100 @@ +/** + * ScriptDev2 is an extension for mangos providing enhanced features for + * area triggers, creatures, game objects, instances, items, and spells beyond + * the default database scripting in mangos. + * + * Copyright (C) 2006-2013 ScriptDev2 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * World of Warcraft, and all World of Warcraft or Warcraft art, images, + * and lore are copyrighted by Blizzard Entertainment, Inc. + */ + +/* ScriptData +SDName: Terokkar_Forest +SD%Complete: 80 +SDComment: Quest support: 9889, 10009, 10873, 10896, 10446/10447, 10852, 10887, 10922, 11096, 11093, 10051, 10052, 10898. +SDCategory: Terokkar Forest +EndScriptData */ + +/* ContentData +mob_unkor_the_ruthless +mob_netherweb_victim +npc_akuno +npc_hungry_nether_ray +npc_letoll +npc_mana_bomb_exp_trigger +go_mana_bomb +go_veil_skith_cage +npc_captive_child +npc_isla_starmane +npc_skywing +EndContentData */ + +#include "precompiled.h" +#include "escort_ai.h" +#include "pet_ai.h" + +/*###### +## mob_unkor_the_ruthless +######*/ + +enum +{ + SAY_SUBMIT = -1000194, + + FACTION_FRIENDLY = 35, + + SPELL_PULVERIZE = 2676, + SPELL_QUID9889 = 32174, +}; + +struct mob_unkor_the_ruthlessAI : public ScriptedAI +{ + mob_unkor_the_ruthlessAI(Creature* pCreature) : ScriptedAI(pCreature) { Reset(); } + + bool m_bCanDoQuest; + uint32 m_uiUnfriendlyTimer; + uint32 m_uiPulverizeTimer; + uint32 m_uiFriendlyTimer; + + void Reset() override + { + m_bCanDoQuest = false; + m_uiUnfriendlyTimer = 0; + m_uiFriendlyTimer = 0; + m_uiPulverizeTimer = 3000; + m_creature->SetStandState(UNIT_STAND_STATE_STAND); + } + + void DoNice() + { + DoScriptText(SAY_SUBMIT, m_creature); + m_creature->SetFactionTemporary(FACTION_FRIENDLY, TEMPFACTION_RESTORE_REACH_HOME); + m_creature->SetStandState(UNIT_STAND_STATE_SIT); + m_creature->RemoveAllAuras(); + m_creature->DeleteThreatList(); + m_creature->CombatStop(true); + m_uiUnfriendlyTimer = 60000; + } + + void UpdateAI(const uint32 uiDiff) override + { + // Reset npc on timer + if (m_uiUnfriendlyTimer) + { + if (m_uiUnfriendlyTimer <= uiDiff) + { EnterEvadeMode(); } + else + { m_uiUnfriendlyTimer -= uiDiff; } + } + + if (!m_creature->SelectHostileTarget() || !m_creature->getVictim()) + { return; } + + // Do quest kill credit at 30% + if (!m_bCanDoQuest && m_creature->GetHealthPercent() < 30.0f) + { + DoCastSpellIfCan(m_creature, SPELL_QUID9889, CAST_TRIGGERED); + m_uiFriendlyTimer = 1000; + m_bCanDoQuest = true; + } + + // Set faction right after the spell is casted, in order to avoid any issues + if (m_uiFriendlyTimer) + { + if (m_uiFriendlyTimer <= uiDiff) + { + DoNice(); + m_uiFriendlyTimer = 0; + } + else + { m_uiFriendlyTimer -= uiDiff; } + } + + if (m_uiPulverizeTimer < uiDiff) + { + DoCastSpellIfCan(m_creature, SPELL_PULVERIZE); + m_uiPulverizeTimer = 9000; + } + else + { m_uiPulverizeTimer -= uiDiff; } + + DoMeleeAttackIfReady(); + } +}; + +CreatureAI* GetAI_mob_unkor_the_ruthless(Creature* pCreature) +{ + return new mob_unkor_the_ruthlessAI(pCreature); +} + +/*###### +## mob_netherweb_victim +######*/ + +enum +{ + NPC_FREED_WARRIOR = 22459, + QUEST_TAKEN_IN_NIGHT = 10873 + // SPELL_FREE_WEBBED = 38950 +}; + +const uint32 netherwebVictims[6] = +{ + 18470, 16805, 21242, 18452, 22482, 21285 +}; + +struct mob_netherweb_victimAI : public ScriptedAI +{ + mob_netherweb_victimAI(Creature* pCreature) : ScriptedAI(pCreature) + { + SetCombatMovement(false); + Reset(); + } + + void Reset() override { } + void MoveInLineOfSight(Unit* /*pWho*/) override { } + + void JustDied(Unit* pKiller) override + { + if (Player* pPlayer = pKiller->GetCharmerOrOwnerPlayerOrPlayerItself()) + { + if (pPlayer->GetQuestStatus(QUEST_TAKEN_IN_NIGHT) == QUEST_STATUS_INCOMPLETE) + { + if (!urand(0, 3)) + { + m_creature->SummonCreature(NPC_FREED_WARRIOR, 0.0f, 0.0f, 0.0f, 0.0f, TEMPSUMMON_TIMED_OOC_DESPAWN, 60000); + pPlayer->KilledMonsterCredit(NPC_FREED_WARRIOR, m_creature->GetObjectGuid()); + } + else + { m_creature->SummonCreature(netherwebVictims[urand(0, 5)], 0.0f, 0.0f, 0.0f, 0.0f, TEMPSUMMON_TIMED_OOC_DESPAWN, 60000); } + } + } + } +}; + +CreatureAI* GetAI_mob_netherweb_victim(Creature* pCreature) +{ + return new mob_netherweb_victimAI(pCreature); +} + +/*##### +## npc_akuno +#####*/ + +enum +{ + SAY_AKU_START = -1000477, + SAY_AKU_AMBUSH_A = -1000478, + SAY_AKU_AMBUSH_B = -1000479, + SAY_AKU_AMBUSH_B_REPLY = -1000480, + SAY_AKU_COMPLETE = -1000481, + + SPELL_CHAIN_LIGHTNING = 39945, + + QUEST_ESCAPING_TOMB = 10887, + NPC_CABAL_SKIRMISHER = 21661 +}; + +static float m_afAmbushB1[] = { -2895.525879f, 5336.431641f, -11.800f}; +static float m_afAmbushB2[] = { -2890.604980f, 5331.938965f, -11.282f}; + +struct npc_akunoAI : public npc_escortAI +{ + npc_akunoAI(Creature* pCreature) : npc_escortAI(pCreature) { Reset(); } + + uint32 m_uiChainLightningTimer; + + void Reset() override + { + m_uiChainLightningTimer = 1000; + } + + void WaypointReached(uint32 uiPointId) override + { + switch (uiPointId) + { + case 5: + DoScriptText(SAY_AKU_AMBUSH_A, m_creature); + m_creature->SummonCreature(NPC_CABAL_SKIRMISHER, 0.0f, 0.0f, 0.0f, 0.0f, TEMPSUMMON_TIMED_OOC_DESPAWN, 25000); + break; + case 14: + DoScriptText(SAY_AKU_AMBUSH_B, m_creature); + + if (Creature* pTemp = m_creature->SummonCreature(NPC_CABAL_SKIRMISHER, m_afAmbushB1[0], m_afAmbushB1[1], m_afAmbushB1[2], 0.0f, TEMPSUMMON_TIMED_OOC_DESPAWN, 25000)) + { DoScriptText(SAY_AKU_AMBUSH_B_REPLY, pTemp); } + + m_creature->SummonCreature(NPC_CABAL_SKIRMISHER, m_afAmbushB2[0], m_afAmbushB2[1], m_afAmbushB2[2], 0.0f, TEMPSUMMON_TIMED_OOC_DESPAWN, 25000); + break; + case 15: + SetRun(); + break; + case 18: + DoScriptText(SAY_AKU_COMPLETE, m_creature); + + if (Player* pPlayer = GetPlayerForEscort()) + { pPlayer->GroupEventHappens(QUEST_ESCAPING_TOMB, m_creature); } + + break; + } + } + + void JustSummoned(Creature* pSummoned) override + { + pSummoned->AI()->AttackStart(m_creature); + } + + void UpdateEscortAI(const uint32 uiDiff) override + { + if (!m_creature->SelectHostileTarget() || !m_creature->getVictim()) + { return; } + + if (m_uiChainLightningTimer < uiDiff) + { + DoCastSpellIfCan(m_creature->getVictim(), SPELL_CHAIN_LIGHTNING); + m_uiChainLightningTimer = urand(7000, 14000); + } + else + { m_uiChainLightningTimer -= uiDiff; } + + DoMeleeAttackIfReady(); + } +}; + +bool QuestAccept_npc_akuno(Player* pPlayer, Creature* pCreature, const Quest* pQuest) +{ + if (pQuest->GetQuestId() == QUEST_ESCAPING_TOMB) + { + if (npc_akunoAI* pEscortAI = dynamic_cast(pCreature->AI())) + { + pCreature->SetStandState(UNIT_STAND_STATE_STAND); + pCreature->SetFactionTemporary(FACTION_ESCORT_N_NEUTRAL_ACTIVE, TEMPFACTION_RESTORE_RESPAWN); + + DoScriptText(SAY_AKU_START, pCreature); + pEscortAI->Start(false, pPlayer, pQuest); + } + } + return true; +} + +CreatureAI* GetAI_npc_akuno(Creature* pCreature) +{ + return new npc_akunoAI(pCreature); +} + +/*###### +## npc_hungry_nether_ray +######*/ + +enum +{ + EMOTE_FEED = -1000628, + NPC_BLACK_WARP_CHASER = 23219, + SPELL_FEED_CREDIT = 41427, // credit for quest 11093 +}; + +struct npc_hungry_nether_rayAI : public ScriptedPetAI +{ + npc_hungry_nether_rayAI(Creature* pCreature) : ScriptedPetAI(pCreature) { Reset(); } + + void Reset() override { } + + void OwnerKilledUnit(Unit* pVictim) override + { + if (pVictim->GetTypeId() == TYPEID_UNIT && pVictim->GetEntry() == NPC_BLACK_WARP_CHASER) + { + // Distance expected? + if (m_creature->IsWithinDistInMap(pVictim, 10.0f)) + { + DoScriptText(EMOTE_FEED, m_creature); + m_creature->CastSpell(m_creature, SPELL_FEED_CREDIT, true); + } + } + } +}; + +CreatureAI* GetAI_npc_hungry_nether_ray(Creature* pCreature) +{ + return new npc_hungry_nether_rayAI(pCreature); +} + +/*###### +## npc_letoll +######*/ + +enum +{ + SAY_LE_START = -1000511, + SAY_LE_KEEP_SAFE = -1000512, + SAY_LE_NORTH = -1000513, + SAY_LE_ARRIVE = -1000514, + SAY_LE_BURIED = -1000515, + SAY_LE_ALMOST = -1000516, + SAY_LE_DRUM = -1000517, + SAY_LE_DRUM_REPLY = -1000518, + SAY_LE_DISCOVERY = -1000519, + SAY_LE_DISCOVERY_REPLY = -1000520, + SAY_LE_NO_LEAVE = -1000521, + SAY_LE_NO_LEAVE_REPLY1 = -1000522, + SAY_LE_NO_LEAVE_REPLY2 = -1000523, + SAY_LE_NO_LEAVE_REPLY3 = -1000524, + SAY_LE_NO_LEAVE_REPLY4 = -1000525, + SAY_LE_SHUT = -1000526, + SAY_LE_REPLY_HEAR = -1000527, + SAY_LE_IN_YOUR_FACE = -1000528, + SAY_LE_HELP_HIM = -1000529, + EMOTE_LE_PICK_UP = -1000530, + SAY_LE_THANKS = -1000531, + + QUEST_DIGGING_BONES = 10922, + + NPC_RESEARCHER = 22464, + NPC_BONE_SIFTER = 22466, + + MAX_RESEARCHER = 4 +}; + +// Some details still missing from here, and will also have issues if followers evade for any reason. +struct npc_letollAI : public npc_escortAI +{ + npc_letollAI(Creature* pCreature) : npc_escortAI(pCreature) + { + m_uiEventTimer = 5000; + m_uiEventCount = 0; + Reset(); + } + + std::list m_lResearchersList; + + uint32 m_uiEventTimer; + uint32 m_uiEventCount; + + void Reset() override {} + + // will make them follow, but will only work until they enter combat with any unit + void SetFormation() + { + uint32 uiCount = 0; + + for (std::list::iterator itr = m_lResearchersList.begin(); itr != m_lResearchersList.end(); ++itr) + { + float fAngle = uiCount < MAX_RESEARCHER ? M_PI / MAX_RESEARCHER - (uiCount * 2 * M_PI / MAX_RESEARCHER) : 0.0f; + + if ((*itr)->IsAlive() && !(*itr)->IsInCombat()) + { (*itr)->GetMotionMaster()->MoveFollow(m_creature, 2.5f, fAngle); } + + ++uiCount; + } + } + + Creature* GetAvailableResearcher(uint8 uiListNum) + { + if (!m_lResearchersList.empty()) + { + uint8 uiNum = 1; + + for (std::list::iterator itr = m_lResearchersList.begin(); itr != m_lResearchersList.end(); ++itr) + { + if (uiListNum && uiListNum != uiNum) + { + ++uiNum; + continue; + } + + if ((*itr)->IsAlive() && (*itr)->IsWithinDistInMap(m_creature, 20.0f)) + { return (*itr); } + } + } + + return NULL; + } + + void JustStartedEscort() override + { + m_uiEventTimer = 5000; + m_uiEventCount = 0; + + m_lResearchersList.clear(); + + GetCreatureListWithEntryInGrid(m_lResearchersList, m_creature, NPC_RESEARCHER, 25.0f); + + if (!m_lResearchersList.empty()) + { SetFormation(); } + } + + void WaypointReached(uint32 uiPointId) override + { + switch (uiPointId) + { + case 0: + if (Player* pPlayer = GetPlayerForEscort()) + { DoScriptText(SAY_LE_KEEP_SAFE, m_creature, pPlayer); } + break; + case 1: + DoScriptText(SAY_LE_NORTH, m_creature); + break; + case 10: + DoScriptText(SAY_LE_ARRIVE, m_creature); + break; + case 12: + DoScriptText(SAY_LE_BURIED, m_creature); + SetEscortPaused(true); + break; + case 13: + SetRun(); + break; + } + } + + void Aggro(Unit* pWho) override + { + if (pWho->IsInCombat() && pWho->GetTypeId() == TYPEID_UNIT && pWho->GetEntry() == NPC_BONE_SIFTER) + { DoScriptText(SAY_LE_HELP_HIM, m_creature); } + } + + void JustSummoned(Creature* pSummoned) override + { + Player* pPlayer = GetPlayerForEscort(); + + if (pPlayer && pPlayer->IsAlive()) + { pSummoned->AI()->AttackStart(pPlayer); } + else + { pSummoned->AI()->AttackStart(m_creature); } + } + + void UpdateEscortAI(const uint32 uiDiff) override + { + if (!m_creature->SelectHostileTarget() || !m_creature->getVictim()) + { + if (HasEscortState(STATE_ESCORT_PAUSED)) + { + if (m_uiEventTimer < uiDiff) + { + m_uiEventTimer = 7000; + + switch (m_uiEventCount) + { + case 0: + DoScriptText(SAY_LE_ALMOST, m_creature); + break; + case 1: + DoScriptText(SAY_LE_DRUM, m_creature); + break; + case 2: + if (Creature* pResearcher = GetAvailableResearcher(0)) + { DoScriptText(SAY_LE_DRUM_REPLY, pResearcher); } + break; + case 3: + DoScriptText(SAY_LE_DISCOVERY, m_creature); + break; + case 4: + if (Creature* pResearcher = GetAvailableResearcher(0)) + { DoScriptText(SAY_LE_DISCOVERY_REPLY, pResearcher); } + break; + case 5: + DoScriptText(SAY_LE_NO_LEAVE, m_creature); + break; + case 6: + if (Creature* pResearcher = GetAvailableResearcher(1)) + { DoScriptText(SAY_LE_NO_LEAVE_REPLY1, pResearcher); } + break; + case 7: + if (Creature* pResearcher = GetAvailableResearcher(2)) + { DoScriptText(SAY_LE_NO_LEAVE_REPLY2, pResearcher); } + break; + case 8: + if (Creature* pResearcher = GetAvailableResearcher(3)) + { DoScriptText(SAY_LE_NO_LEAVE_REPLY3, pResearcher); } + break; + case 9: + if (Creature* pResearcher = GetAvailableResearcher(4)) + { DoScriptText(SAY_LE_NO_LEAVE_REPLY4, pResearcher); } + break; + case 10: + DoScriptText(SAY_LE_SHUT, m_creature); + break; + case 11: + if (Creature* pResearcher = GetAvailableResearcher(0)) + { DoScriptText(SAY_LE_REPLY_HEAR, pResearcher); } + break; + case 12: + DoScriptText(SAY_LE_IN_YOUR_FACE, m_creature); + m_creature->SummonCreature(NPC_BONE_SIFTER, 0.0f, 0.0f, 0.0f, 0.0f, TEMPSUMMON_TIMED_OOC_DESPAWN, 30000); + break; + case 13: + DoScriptText(EMOTE_LE_PICK_UP, m_creature); + + if (Player* pPlayer = GetPlayerForEscort()) + { + DoScriptText(SAY_LE_THANKS, m_creature, pPlayer); + pPlayer->GroupEventHappens(QUEST_DIGGING_BONES, m_creature); + } + + SetEscortPaused(false); + break; + } + + ++m_uiEventCount; + } + else + { m_uiEventTimer -= uiDiff; } + } + + return; + } + + DoMeleeAttackIfReady(); + } +}; + +CreatureAI* GetAI_npc_letoll(Creature* pCreature) +{ + return new npc_letollAI(pCreature); +} + +bool QuestAccept_npc_letoll(Player* pPlayer, Creature* pCreature, const Quest* pQuest) +{ + if (pQuest->GetQuestId() == QUEST_DIGGING_BONES) + { + if (npc_letollAI* pEscortAI = dynamic_cast(pCreature->AI())) + { + DoScriptText(SAY_LE_START, pCreature); + pCreature->SetFactionTemporary(FACTION_ESCORT_N_NEUTRAL_PASSIVE, TEMPFACTION_RESTORE_RESPAWN); + + pEscortAI->Start(false, pPlayer, pQuest, true); + } + } + + return true; +} + +/*###### +## npc_mana_bomb_exp_trigger +######*/ + +enum +{ + SAY_COUNT_1 = -1000472, + SAY_COUNT_2 = -1000473, + SAY_COUNT_3 = -1000474, + SAY_COUNT_4 = -1000475, + SAY_COUNT_5 = -1000476, + + SPELL_MANA_BOMB_LIGHTNING = 37843, + SPELL_MANA_BOMB_EXPL = 35513, + + NPC_MANA_BOMB_EXPL_TRIGGER = 20767, + NPC_MANA_BOMB_KILL_TRIGGER = 21039 +}; + +struct npc_mana_bomb_exp_triggerAI : public ScriptedAI +{ + npc_mana_bomb_exp_triggerAI(Creature* pCreature) : ScriptedAI(pCreature) { Reset(); } + + GameObject* pManaBomb; + + bool m_bIsActivated; + uint32 m_uiEventTimer; + uint32 m_uiEventCounter; + + void Reset() override + { + pManaBomb = NULL; + m_bIsActivated = false; + m_uiEventTimer = 1000; + m_uiEventCounter = 0; + } + + void DoTrigger(Player* pPlayer, GameObject* pGo) + { + if (m_bIsActivated) + { return; } + + m_bIsActivated = true; + + pPlayer->KilledMonsterCredit(NPC_MANA_BOMB_KILL_TRIGGER); + + pManaBomb = pGo; + } + + void UpdateAI(const uint32 uiDiff) override + { + if (!m_bIsActivated) + { return; } + + if (m_uiEventTimer < uiDiff) + { + m_uiEventTimer = 1000; + + if (m_uiEventCounter < 10) + { m_creature->CastSpell(m_creature, SPELL_MANA_BOMB_LIGHTNING, false); } + + switch (m_uiEventCounter) + { + case 5: + if (pManaBomb) + { pManaBomb->SetGoState(GO_STATE_ACTIVE); } + + DoScriptText(SAY_COUNT_1, m_creature); + break; + case 6: + DoScriptText(SAY_COUNT_2, m_creature); + break; + case 7: + DoScriptText(SAY_COUNT_3, m_creature); + break; + case 8: + DoScriptText(SAY_COUNT_4, m_creature); + break; + case 9: + DoScriptText(SAY_COUNT_5, m_creature); + break; + case 10: + m_creature->CastSpell(m_creature, SPELL_MANA_BOMB_EXPL, false); + break; + case 30: + if (pManaBomb) + { pManaBomb->SetGoState(GO_STATE_READY); } + + Reset(); + break; + } + + ++m_uiEventCounter; + } + else + { m_uiEventTimer -= uiDiff; } + } +}; + +CreatureAI* GetAI_npc_mana_bomb_exp_trigger(Creature* pCreature) +{ + return new npc_mana_bomb_exp_triggerAI(pCreature); +} + +/*###### +## go_mana_bomb +######*/ + +bool GOUse_go_mana_bomb(Player* pPlayer, GameObject* pGo) +{ + if (Creature* pCreature = GetClosestCreatureWithEntry(pGo, NPC_MANA_BOMB_EXPL_TRIGGER, INTERACTION_DISTANCE)) + { + if (npc_mana_bomb_exp_triggerAI* pBombAI = dynamic_cast(pCreature->AI())) + { pBombAI->DoTrigger(pPlayer, pGo); } + } + + return true; +} + +/*###### +## go_veil_skith_cage & npc_captive_child +#####*/ + +enum +{ + QUEST_MISSING_FRIENDS = 10852, + NPC_CAPTIVE_CHILD = 22314, + SAY_THANKS_1 = -1000590, + SAY_THANKS_2 = -1000591, + SAY_THANKS_3 = -1000592, + SAY_THANKS_4 = -1000593 +}; + +bool GOUse_go_veil_skith_cage(Player* pPlayer, GameObject* pGo) +{ + if (pPlayer->GetQuestStatus(QUEST_MISSING_FRIENDS) == QUEST_STATUS_INCOMPLETE) + { + std::list lChildrenList; + GetCreatureListWithEntryInGrid(lChildrenList, pGo, NPC_CAPTIVE_CHILD, INTERACTION_DISTANCE); + for (std::list::const_iterator itr = lChildrenList.begin(); itr != lChildrenList.end(); ++itr) + { + pPlayer->KilledMonsterCredit(NPC_CAPTIVE_CHILD, (*itr)->GetObjectGuid()); + switch (urand(0, 3)) + { + case 0: DoScriptText(SAY_THANKS_1, *itr); break; + case 1: DoScriptText(SAY_THANKS_2, *itr); break; + case 2: DoScriptText(SAY_THANKS_3, *itr); break; + case 3: DoScriptText(SAY_THANKS_4, *itr); break; + } + + (*itr)->GetMotionMaster()->Clear(); + (*itr)->GetMotionMaster()->MovePoint(0, -2648.049f, 5274.573f, 1.691529f); + } + } + return false; +}; + +struct npc_captive_child : public ScriptedAI +{ + npc_captive_child(Creature* pCreature) : ScriptedAI(pCreature) { Reset(); } + + void Reset() override {} + + void MovementInform(uint32 uiMotionType, uint32 /*uiPointId*/) override + { + if (uiMotionType == POINT_MOTION_TYPE) + { m_creature->ForcedDespawn(); } // we only have one waypoint + } +}; + +CreatureAI* GetAI_npc_captive_child(Creature* pCreature) +{ + return new npc_captive_child(pCreature); +} + +/*##### +## npc_isla_starmane +## +## TODO: Verify SpellIDs, Research Timers, Finish Text? +#####*/ + +enum +{ + QUEST_ESCAPE_FROM_FIREWING_POINT_A = 10051, + QUEST_ESCAPE_FROM_FIREWING_POINT_H = 10052, + + SAY_ISLA_PERIODIC_1 = -1000629, + SAY_ISLA_PERIODIC_2 = -1000630, + SAY_ISLA_PERIODIC_3 = -1000631, + SAY_ISLA_START = -1000632, + SAY_ISLA_WAITING = -1000633, + SAY_ISLA_LEAVE_BUILDING = -1000634, + + GO_CAGE = 182794, + + SPELL_ENTANGLING_ROOTS = 33844, // these spell IDs seem to deal not enough dmg, but are linked + SPELL_MOONFIRE = 15798, + SPELL_WRATH = 9739, + SPELL_TRAVELFORM = 32447 // guesswork +}; + +struct npc_isla_starmaneAI : public npc_escortAI +{ + npc_isla_starmaneAI(Creature* pCreature) : npc_escortAI(pCreature) + { + Reset(); + } + + uint32 m_uiPeriodicTalkTimer; + uint32 m_uiEntanglingRootsTimer; + uint32 m_uiMoonfireTimer; + uint32 m_uiWrathTimer; + + void Reset() override + { + m_uiPeriodicTalkTimer = urand(20000, 40000); + m_uiEntanglingRootsTimer = 100; + m_uiMoonfireTimer = 1600; + m_uiWrathTimer = 2000; + + if (!HasEscortState(STATE_ESCORT_ESCORTING)) + { m_creature->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_PASSIVE); } + } + + void JustStartedEscort() override + { + m_creature->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_PASSIVE); + DoScriptText(SAY_ISLA_START, m_creature); + if (GameObject* pCage = GetClosestGameObjectWithEntry(m_creature, GO_CAGE, 2 * INTERACTION_DISTANCE)) + { pCage->Use(m_creature); } + } + + void WaypointStart(uint32 uiPointId) override + { + switch (uiPointId) + { + case 7: DoScriptText(SAY_ISLA_LEAVE_BUILDING, m_creature); break; + case 68: DoCastSpellIfCan(m_creature, SPELL_TRAVELFORM); break; + } + } + + void WaypointReached(uint32 uiPointId) override + { + switch (uiPointId) + { + case 6: + DoScriptText(SAY_ISLA_WAITING, m_creature); + break; + case 61: + if (Player* pPlayer = GetPlayerForEscort()) + { pPlayer->GroupEventHappens(pPlayer->GetTeam() == ALLIANCE ? QUEST_ESCAPE_FROM_FIREWING_POINT_A : QUEST_ESCAPE_FROM_FIREWING_POINT_H, m_creature); } + break; + case 67: + if (Player* pPlayer = GetPlayerForEscort()) + { m_creature->SetFacingToObject(pPlayer); } + m_creature->HandleEmote(EMOTE_ONESHOT_WAVE); + break; + } + } + + void UpdateEscortAI(const uint32 uiDiff) override + { + if (!HasEscortState(STATE_ESCORT_ESCORTING)) + { + if (m_uiPeriodicTalkTimer < uiDiff) + { + m_uiPeriodicTalkTimer = urand(30000, 60000); + switch (urand(0, 2)) + { + case 0: DoScriptText(SAY_ISLA_PERIODIC_1, m_creature); break; + case 1: DoScriptText(SAY_ISLA_PERIODIC_2, m_creature); break; + case 2: DoScriptText(SAY_ISLA_PERIODIC_3, m_creature); break; + } + } + else + { m_uiPeriodicTalkTimer -= uiDiff; } + } + + if (!m_creature->SelectHostileTarget() || !m_creature->getVictim()) + { return; } + + if (m_uiEntanglingRootsTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature->getVictim(), SPELL_ENTANGLING_ROOTS) == CAST_OK) + { m_uiEntanglingRootsTimer = urand(8000, 16000); } + } + else + { m_uiEntanglingRootsTimer -= uiDiff; } + + if (m_uiMoonfireTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature->getVictim(), SPELL_MOONFIRE) == CAST_OK) + { m_uiMoonfireTimer = urand(6000, 12000); } + } + else + { m_uiMoonfireTimer -= uiDiff; } + + if (m_uiWrathTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature->getVictim(), SPELL_WRATH) == CAST_OK) + { m_uiWrathTimer = 2000; } + } + else + { m_uiWrathTimer -= uiDiff; } + + DoMeleeAttackIfReady(); + } +}; + +bool QuestAccept_npc_isla_starmane(Player* pPlayer, Creature* pCreature, const Quest* pQuest) +{ + if (pQuest->GetQuestId() == QUEST_ESCAPE_FROM_FIREWING_POINT_A || pQuest->GetQuestId() == QUEST_ESCAPE_FROM_FIREWING_POINT_H) + { + if (npc_isla_starmaneAI* pEscortAI = dynamic_cast(pCreature->AI())) + { + pCreature->SetFactionTemporary(pPlayer->GetTeam() == ALLIANCE ? FACTION_ESCORT_A_NEUTRAL_ACTIVE : FACTION_ESCORT_H_NEUTRAL_ACTIVE, TEMPFACTION_RESTORE_RESPAWN); + pEscortAI->Start(false, pPlayer, pQuest); + } + } + return true; +} + +CreatureAI* GetAI_npc_isla_starmane(Creature* pCreature) +{ + return new npc_isla_starmaneAI(pCreature); +} + +/*###### +## npc_skywing +######*/ + +enum +{ + SAY_SKYWING_START = -1000797, + SAY_SKYWING_TREE_DOWN = -1000798, + SAY_SKYWING_TREE_UP = -1000799, + SAY_SKYWING_JUMP = -1000800, + SAY_SKYWING_SUMMON = -1000801, + SAY_SKYWING_END = -1000802, + + SPELL_FEATHERY_CYCLONE_BURST = 39166, // triggered many times by server side spell - 39167 (channeled for 5 sec) + SPELL_RILAK_THE_REDEEMED = 39179, + + NPC_LUANGA_THE_IMPRISONER = 18533, + + QUEST_SKYWING = 10898 +}; + +static const float aLuangaSpawnCoords[3] = { -3507.203f, 4084.619f, 92.947f}; + +struct npc_skywingAI : public npc_escortAI +{ + npc_skywingAI(Creature* pCreature) : npc_escortAI(pCreature) { Reset(); } + + uint32 m_uiCycloneTimer; + uint8 m_uiCycloneCounter; + + void Reset() override + { + if (!HasEscortState(STATE_ESCORT_ESCORTING)) + { + m_uiCycloneTimer = 0; + m_uiCycloneCounter = 0; + } + } + + void WaypointReached(uint32 uiPointId) override + { + switch (uiPointId) + { + case 6: + DoScriptText(SAY_SKYWING_TREE_DOWN , m_creature); + break; + case 36: + DoScriptText(SAY_SKYWING_TREE_UP, m_creature); + break; + case 60: + DoScriptText(SAY_SKYWING_JUMP, m_creature); + m_creature->SetLevitate(true); + break; + case 61: + m_creature->SetLevitate(false); + break; + case 80: + DoScriptText(SAY_SKYWING_SUMMON, m_creature); + m_creature->SummonCreature(NPC_LUANGA_THE_IMPRISONER, aLuangaSpawnCoords[0], aLuangaSpawnCoords[1], aLuangaSpawnCoords[2], 0, TEMPSUMMON_TIMED_OOC_DESPAWN, 30000); + break; + case 81: + // Start transformation + m_uiCycloneTimer = 100; + break; + case 82: + DoScriptText(SAY_SKYWING_END, m_creature); + + if (Player* pPlayer = GetPlayerForEscort()) + { pPlayer->GroupEventHappens(QUEST_SKYWING, m_creature); } + } + } + + void JustSummoned(Creature* pSummoned) override + { + pSummoned->AI()->AttackStart(m_creature); + } + + void UpdateEscortAI(const uint32 uiDiff) override + { + if (m_uiCycloneTimer) + { + if (m_uiCycloneTimer <= uiDiff) + { + if (DoCastSpellIfCan(m_creature, SPELL_FEATHERY_CYCLONE_BURST) == CAST_OK) + { + ++m_uiCycloneCounter; + + if (m_uiCycloneCounter == 30) + { DoCastSpellIfCan(m_creature, SPELL_RILAK_THE_REDEEMED, CAST_TRIGGERED); } + + // Only cast this spell 50 times + if (m_uiCycloneCounter == 50) + { m_uiCycloneTimer = 0; } + else + { m_uiCycloneTimer = 100; } + } + } + else + { m_uiCycloneTimer -= uiDiff; } + } + + if (!m_creature->SelectHostileTarget() || !m_creature->getVictim()) + { return; } + } +}; + +bool QuestAccept_npc_skywing(Player* pPlayer, Creature* pCreature, const Quest* pQuest) +{ + if (pQuest->GetQuestId() == QUEST_SKYWING) + { + if (npc_skywingAI* pEscortAI = dynamic_cast(pCreature->AI())) + { + pCreature->SetFactionTemporary(FACTION_ESCORT_N_NEUTRAL_PASSIVE, TEMPFACTION_RESTORE_RESPAWN); + DoScriptText(SAY_SKYWING_START, pCreature); + + pEscortAI->Start(false, pPlayer, pQuest); + } + } + return true; +} + +CreatureAI* GetAI_npc_skywing(Creature* pCreature) +{ + return new npc_skywingAI(pCreature); +} + +void AddSC_terokkar_forest() +{ + Script* pNewScript; + + pNewScript = new Script; + pNewScript->Name = "mob_unkor_the_ruthless"; + pNewScript->GetAI = &GetAI_mob_unkor_the_ruthless; + pNewScript->RegisterSelf(); + + pNewScript = new Script; + pNewScript->Name = "mob_netherweb_victim"; + pNewScript->GetAI = &GetAI_mob_netherweb_victim; + pNewScript->RegisterSelf(); + + pNewScript = new Script; + pNewScript->Name = "npc_akuno"; + pNewScript->GetAI = &GetAI_npc_akuno; + pNewScript->pQuestAcceptNPC = &QuestAccept_npc_akuno; + pNewScript->RegisterSelf(); + + pNewScript = new Script; + pNewScript->Name = "npc_hungry_nether_ray"; + pNewScript->GetAI = &GetAI_npc_hungry_nether_ray; + pNewScript->RegisterSelf(); + + pNewScript = new Script; + pNewScript->Name = "npc_letoll"; + pNewScript->GetAI = &GetAI_npc_letoll; + pNewScript->pQuestAcceptNPC = &QuestAccept_npc_letoll; + pNewScript->RegisterSelf(); + + pNewScript = new Script; + pNewScript->Name = "npc_mana_bomb_exp_trigger"; + pNewScript->GetAI = &GetAI_npc_mana_bomb_exp_trigger; + pNewScript->RegisterSelf(); + + pNewScript = new Script; + pNewScript->Name = "go_mana_bomb"; + pNewScript->pGOUse = &GOUse_go_mana_bomb; + pNewScript->RegisterSelf(); + + pNewScript = new Script; + pNewScript->Name = "go_veil_skith_cage"; + pNewScript->pGOUse = &GOUse_go_veil_skith_cage; + pNewScript->RegisterSelf(); + + pNewScript = new Script; + pNewScript->Name = "npc_captive_child"; + pNewScript->GetAI = &GetAI_npc_captive_child; + pNewScript->RegisterSelf(); + + pNewScript = new Script; + pNewScript->Name = "npc_isla_starmane"; + pNewScript->GetAI = &GetAI_npc_isla_starmane; + pNewScript->pQuestAcceptNPC = &QuestAccept_npc_isla_starmane; + pNewScript->RegisterSelf(); + + pNewScript = new Script; + pNewScript->Name = "npc_skywing"; + pNewScript->GetAI = &GetAI_npc_skywing; + pNewScript->pQuestAcceptNPC = &QuestAccept_npc_skywing; + pNewScript->RegisterSelf(); +} diff --git a/src/modules/SD2/scripts/outland/zangarmarsh.cpp b/src/modules/SD2/scripts/outland/zangarmarsh.cpp new file mode 100644 index 000000000..b78e98fb1 --- /dev/null +++ b/src/modules/SD2/scripts/outland/zangarmarsh.cpp @@ -0,0 +1,203 @@ +/** + * ScriptDev2 is an extension for mangos providing enhanced features for + * area triggers, creatures, game objects, instances, items, and spells beyond + * the default database scripting in mangos. + * + * Copyright (C) 2006-2013 ScriptDev2 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * World of Warcraft, and all World of Warcraft or Warcraft art, images, + * and lore are copyrighted by Blizzard Entertainment, Inc. + */ + +/* ScriptData +SDName: Zangarmarsh +SD%Complete: 100 +SDComment: Quest support: 9752, 9785, 10009. +SDCategory: Zangarmarsh +EndScriptData */ + +/* ContentData +npc_cooshcoosh +npc_kayra_longmane +event_stormcrow +EndContentData */ + +#include "precompiled.h" +#include "escort_ai.h" + +/*###### +## npc_cooshcoosh +######*/ + +enum +{ + SPELL_LIGHTNING_BOLT = 9532, +}; + +struct npc_cooshcooshAI : public ScriptedAI +{ + npc_cooshcooshAI(Creature* pCreature) : ScriptedAI(pCreature) + { + m_uiNormFaction = pCreature->getFaction(); + Reset(); + } + + uint32 m_uiNormFaction; + uint32 m_uiLightningBolt_Timer; + + void Reset() override + { + m_uiLightningBolt_Timer = 2000; + + if (m_creature->getFaction() != m_uiNormFaction) + { m_creature->setFaction(m_uiNormFaction); } + } + + void UpdateAI(const uint32 uiDiff) override + { + if (!m_creature->SelectHostileTarget() || !m_creature->getVictim()) + { return; } + + if (m_uiLightningBolt_Timer < uiDiff) + { + DoCastSpellIfCan(m_creature->getVictim(), SPELL_LIGHTNING_BOLT); + m_uiLightningBolt_Timer = 5000; + } + else { m_uiLightningBolt_Timer -= uiDiff; } + + DoMeleeAttackIfReady(); + } +}; + +CreatureAI* GetAI_npc_cooshcoosh(Creature* pCreature) +{ + return new npc_cooshcooshAI(pCreature); +} + +/*##### +## npc_kayra_longmane +#####*/ + +enum +{ + SAY_START = -1000343, + SAY_AMBUSH1 = -1000344, + SAY_PROGRESS = -1000345, + SAY_AMBUSH2 = -1000346, + SAY_END = -1000347, + + QUEST_ESCAPE_FROM = 9752, + NPC_SLAVEBINDER = 18042 +}; + +struct npc_kayra_longmaneAI : public npc_escortAI +{ + npc_kayra_longmaneAI(Creature* pCreature) : npc_escortAI(pCreature) { Reset(); } + + void WaypointReached(uint32 i) override + { + Player* pPlayer = GetPlayerForEscort(); + + if (!pPlayer) + { return; } + + switch (i) + { + case 4: + DoScriptText(SAY_AMBUSH1, m_creature, pPlayer); + DoSpawnCreature(NPC_SLAVEBINDER, -10.0f, -5.0f, 0.0f, 0.0f, TEMPSUMMON_TIMED_OOC_DESPAWN, 30000); + DoSpawnCreature(NPC_SLAVEBINDER, -8.0f, 5.0f, 0.0f, 0.0f, TEMPSUMMON_TIMED_OOC_DESPAWN, 30000); + break; + case 5: + DoScriptText(SAY_PROGRESS, m_creature, pPlayer); + SetRun(); + break; + case 16: + DoScriptText(SAY_AMBUSH2, m_creature, pPlayer); + DoSpawnCreature(NPC_SLAVEBINDER, -10.0f, -5.0f, 0.0f, 0.0f, TEMPSUMMON_TIMED_OOC_DESPAWN, 30000); + DoSpawnCreature(NPC_SLAVEBINDER, -8.0f, 5.0f, 0.0f, 0.0f, TEMPSUMMON_TIMED_OOC_DESPAWN, 30000); + break; + case 17: + DoScriptText(SAY_END, m_creature, pPlayer); + break; + case 25: + pPlayer->GroupEventHappens(QUEST_ESCAPE_FROM, m_creature); + break; + } + } + + void Reset() override { } +}; + +bool QuestAccept_npc_kayra_longmane(Player* pPlayer, Creature* pCreature, const Quest* pQuest) +{ + if (pQuest->GetQuestId() == QUEST_ESCAPE_FROM) + { + DoScriptText(SAY_START, pCreature, pPlayer); + + if (npc_kayra_longmaneAI* pEscortAI = dynamic_cast(pCreature->AI())) + { pEscortAI->Start(false, pPlayer, pQuest); } + } + return true; +} + +CreatureAI* GetAI_npc_kayra_longmane(Creature* pCreature) +{ + return new npc_kayra_longmaneAI(pCreature); +} + +/*###### +## event_stormcrow +######*/ + +enum +{ + QUEST_AS_THE_CROW_FLIES = 9718, + EVENT_ID_STORMCROW = 11225, +}; + +bool ProcessEventId_event_taxi_stormcrow(uint32 uiEventId, Object* pSource, Object* /*pTarget*/, bool bIsStart) +{ + if (uiEventId == EVENT_ID_STORMCROW && !bIsStart && pSource->GetTypeId() == TYPEID_PLAYER) + { + ((Player*)pSource)->SetDisplayId(((Player*)pSource)->GetNativeDisplayId()); + ((Player*)pSource)->AreaExploredOrEventHappens(QUEST_AS_THE_CROW_FLIES); + return true; + } + return false; +} + +void AddSC_zangarmarsh() +{ + Script* pNewScript; + + pNewScript = new Script; + pNewScript->Name = "npc_cooshcoosh"; + pNewScript->GetAI = &GetAI_npc_cooshcoosh; + pNewScript->RegisterSelf(); + + pNewScript = new Script; + pNewScript->Name = "npc_kayra_longmane"; + pNewScript->GetAI = &GetAI_npc_kayra_longmane; + pNewScript->pQuestAcceptNPC = &QuestAccept_npc_kayra_longmane; + pNewScript->RegisterSelf(); + + pNewScript = new Script; + pNewScript->Name = "event_taxi_stormcrow"; + pNewScript->pProcessEventId = &ProcessEventId_event_taxi_stormcrow; + pNewScript->RegisterSelf(); +} diff --git a/src/modules/SD2/scripts/world/areatrigger_scripts.cpp b/src/modules/SD2/scripts/world/areatrigger_scripts.cpp new file mode 100644 index 000000000..706b201cf --- /dev/null +++ b/src/modules/SD2/scripts/world/areatrigger_scripts.cpp @@ -0,0 +1,403 @@ +/** + * ScriptDev2 is an extension for mangos providing enhanced features for + * area triggers, creatures, game objects, instances, items, and spells beyond + * the default database scripting in mangos. + * + * Copyright (C) 2006-2013 ScriptDev2 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * World of Warcraft, and all World of Warcraft or Warcraft art, images, + * and lore are copyrighted by Blizzard Entertainment, Inc. + */ + +/** + * ScriptData + * SDName: Areatrigger_Scripts + * SD%Complete: 100 + * SDComment: Quest support: 4291, 6681, 11686, 10589/10604, 12741, 12548, 13315/13351, 12575 + * SDCategory: Areatrigger + * EndScriptData + */ + +/* ContentData +at_aldurthar_gate 5284,5285,5286,5287 +at_coilfang_waterfall 4591 +at_legion_teleporter 4560 Teleporter TO Invasion Point: Cataclysm +at_ravenholdt +at_spearborn_encampment 5030 +at_warsong_farms +at_stormwright_shelf 5108 +at_childrens_week_spot 3546,3547,3548,3552,3549,3550 +at_scent_larkorwi 1726,1727,1728,1729,1730,1731,1732,1733,1734,1735,1736,1737,1738,1739,1740 +at_murkdeep 1966 +EndContentData */ + +#include "precompiled.h" +#include "world_map_scripts.h" + +static uint32 TriggerOrphanSpell[6][3] = +{ + {3546, 14305, 65056}, // The Bough of the Eternals + {3547, 14444, 65059}, // Lordaeron Throne Room + {3548, 14305, 65055}, // The Stonewrought Dam + {3549, 14444, 65058}, // Gateway to the Frontier + {3550, 14444, 65057}, // Down at the Docks + {3552, 14305, 65054} // Spooky Lighthouse +}; + +bool AreaTrigger_at_childrens_week_spot(Player* pPlayer, AreaTriggerEntry const* pAt) +{ + for (uint8 i = 0; i < 6; ++i) + { + if (pAt->id == TriggerOrphanSpell[i][0] && + pPlayer->GetMiniPet() && pPlayer->GetMiniPet()->GetEntry() == TriggerOrphanSpell[i][1]) + { + pPlayer->CastSpell(pPlayer, TriggerOrphanSpell[i][2], true); + return true; + } + } + return false; +} + +/*###### +## Quest 13315/13351 +######*/ + +enum +{ + TRIGGER_SOUTH = 5284, + TRIGGER_CENTRAL = 5285, + TRIGGER_NORTH = 5286, + TRIGGER_NORTHWEST = 5287, + + NPC_SOUTH_GATE = 32195, + NPC_CENTRAL_GATE = 32196, + NPC_NORTH_GATE = 32197, + NPC_NORTHWEST_GATE = 32199 +}; + +bool AreaTrigger_at_aldurthar_gate(Player* pPlayer, AreaTriggerEntry const* pAt) +{ + switch (pAt->id) + { + case TRIGGER_SOUTH: pPlayer->KilledMonsterCredit(NPC_SOUTH_GATE); break; + case TRIGGER_CENTRAL: pPlayer->KilledMonsterCredit(NPC_CENTRAL_GATE); break; + case TRIGGER_NORTH: pPlayer->KilledMonsterCredit(NPC_NORTH_GATE); break; + case TRIGGER_NORTHWEST: pPlayer->KilledMonsterCredit(NPC_NORTHWEST_GATE); break; + } + return true; +} + +/*###### +## at_coilfang_waterfall +######*/ + +enum +{ + GO_COILFANG_WATERFALL = 184212 +}; + +bool AreaTrigger_at_coilfang_waterfall(Player* pPlayer, AreaTriggerEntry const* /*pAt*/) +{ + if (GameObject* pGo = GetClosestGameObjectWithEntry(pPlayer, GO_COILFANG_WATERFALL, 35.0f)) + { + if (pGo->getLootState() == GO_READY) + { pGo->UseDoorOrButton(); } + } + return false; +} + +/*###### +## at_legion_teleporter +######*/ + +enum +{ + 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 const* /*pAt*/) +{ + if (pPlayer->IsAlive() && !pPlayer->IsInCombat()) + { + if (pPlayer->GetTeam() == ALLIANCE && pPlayer->GetQuestRewardStatus(QUEST_GAINING_ACCESS_A)) + { + 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; +} + +/*###### +## at_ravenholdt +######*/ + +enum +{ + QUEST_MANOR_RAVENHOLDT = 6681, + NPC_RAVENHOLDT = 13936 +}; + +bool AreaTrigger_at_ravenholdt(Player* pPlayer, AreaTriggerEntry const* /*pAt*/) +{ + if (pPlayer->GetQuestStatus(QUEST_MANOR_RAVENHOLDT) == QUEST_STATUS_INCOMPLETE) + pPlayer->KilledMonsterCredit(NPC_RAVENHOLDT); + + return false; +} + +/*###### +## at_spearborn_encampment +######*/ + +enum +{ + QUEST_MISTWHISPER_TREASURE = 12575, + NPC_TARTEK = 28105, +}; + +// 5030 +bool AreaTrigger_at_spearborn_encampment(Player* pPlayer, AreaTriggerEntry const* pAt) +{ + if (pPlayer->GetQuestStatus(QUEST_MISTWHISPER_TREASURE) == QUEST_STATUS_INCOMPLETE && + pPlayer->GetReqKillOrCastCurrentCount(QUEST_MISTWHISPER_TREASURE, NPC_TARTEK) == 0) + { + // can only spawn one at a time, it's not a too good solution + if (GetClosestCreatureWithEntry(pPlayer, NPC_TARTEK, 50.0f)) + return false; + + pPlayer->SummonCreature(NPC_TARTEK, pAt->x, pAt->y, pAt->z, 0.0f, TEMPSUMMON_TIMED_OOC_OR_DEAD_DESPAWN, MINUTE * IN_MILLISECONDS); + } + + return false; +} + +/*###### +## at_warsong_farms +######*/ + +enum +{ + QUEST_THE_WARSONG_FARMS = 11686, + NPC_CREDIT_SLAUGHTERHOUSE = 25672, + NPC_CREDIT_GRAINERY = 25669, + NPC_CREDIT_TORP_FARM = 25671, + + AT_SLAUGHTERHOUSE = 4873, + AT_GRAINERY = 4871, + AT_TORP_FARM = 4872 +}; + +bool AreaTrigger_at_warsong_farms(Player* pPlayer, AreaTriggerEntry const* pAt) +{ + if (!pPlayer->IsDead() && pPlayer->GetQuestStatus(QUEST_THE_WARSONG_FARMS) == QUEST_STATUS_INCOMPLETE) + { + switch (pAt->id) + { + case AT_SLAUGHTERHOUSE: pPlayer->KilledMonsterCredit(NPC_CREDIT_SLAUGHTERHOUSE); break; + case AT_GRAINERY: pPlayer->KilledMonsterCredit(NPC_CREDIT_GRAINERY); break; + case AT_TORP_FARM: pPlayer->KilledMonsterCredit(NPC_CREDIT_TORP_FARM); break; + } + } + return true; +} + +/*###### +## Quest 12548 +######*/ + +enum +{ + SPELL_SHOLOZAR_TO_UNGORO_TELEPORT = 52056, + SPELL_UNGORO_TO_SHOLOZAR_TELEPORT = 52057, + AT_WAYGATE_SHOLOZAR = 5046, + AT_WAYGATE_UNGORO = 5047, + QUEST_THE_MARKERS_OVERLOOK = 12613, + QUEST_THE_MARKERS_PERCH = 12559 +}; + +bool AreaTrigger_at_waygate(Player* pPlayer, AreaTriggerEntry const* pAt) +{ + if (!pPlayer->IsDead() && pPlayer->GetQuestStatus(QUEST_THE_MARKERS_OVERLOOK) == QUEST_STATUS_COMPLETE && pPlayer->GetQuestStatus(QUEST_THE_MARKERS_PERCH) == QUEST_STATUS_COMPLETE) + { + switch (pAt->id) + { + case AT_WAYGATE_SHOLOZAR: pPlayer->CastSpell(pPlayer, SPELL_SHOLOZAR_TO_UNGORO_TELEPORT, false); break; + case AT_WAYGATE_UNGORO: pPlayer->CastSpell(pPlayer, SPELL_UNGORO_TO_SHOLOZAR_TELEPORT, false); break; + } + } + + return false; +} + +/*###### +## Quest 12741 +######*/ + +enum +{ + QUEST_STRENGTH_OF_THE_TEMPEST = 12741, + SPELL_CREATE_TRUE_POWER_OF_THE_TEMPEST = 53067 +}; + +bool AreaTrigger_at_stormwright_shelf(Player* pPlayer, AreaTriggerEntry const* /*pAt*/) +{ + if (!pPlayer->IsDead() && pPlayer->GetQuestStatus(QUEST_STRENGTH_OF_THE_TEMPEST) == QUEST_STATUS_INCOMPLETE) + pPlayer->CastSpell(pPlayer, SPELL_CREATE_TRUE_POWER_OF_THE_TEMPEST, false); + + return true; +} + +/*###### +## at_scent_larkorwi +######*/ + +enum +{ + QUEST_SCENT_OF_LARKORWI = 4291, + NPC_LARKORWI_MATE = 9683 +}; + +bool AreaTrigger_at_scent_larkorwi(Player* pPlayer, AreaTriggerEntry const* pAt) +{ + if (pPlayer->IsAlive() && !pPlayer->isGameMaster() && pPlayer->GetQuestStatus(QUEST_SCENT_OF_LARKORWI) == QUEST_STATUS_INCOMPLETE) + { + if (!GetClosestCreatureWithEntry(pPlayer, NPC_LARKORWI_MATE, 25.0f, false, false)) + { + pPlayer->SummonCreature(NPC_LARKORWI_MATE, pAt->x, pAt->y, pAt->z, 3.3f, TEMPSUMMON_TIMED_OOC_DESPAWN, 2 * MINUTE * IN_MILLISECONDS); + } + } + + return false; +} + +/*###### +## at_murkdeep +######*/ + +bool AreaTrigger_at_murkdeep(Player* pPlayer, AreaTriggerEntry const* /*pAt*/) +{ + // Handle Murkdeep event start + // The area trigger summons 3 Greymist Coastrunners; The rest of the event is handled by world map scripts + if (pPlayer->IsAlive() && !pPlayer->isGameMaster() && pPlayer->GetQuestStatus(QUEST_WANTED_MURKDEEP) == QUEST_STATUS_INCOMPLETE) + { + ScriptedMap* pScriptedMap = (ScriptedMap*)pPlayer->GetInstanceData(); + if (!pScriptedMap) + { + return false; + } + + // If Murkdeep is already spawned, skip the rest + if (pScriptedMap->GetSingleCreatureFromStorage(NPC_MURKDEEP, true)) + { + return true; + } + + // Check if there are already coastrunners (dead or alive) around the area + if (GetClosestCreatureWithEntry(pPlayer, NPC_GREYMIST_COASTRUNNNER, 60.0f, false, false)) + { + return true; + } + + float fX, fY, fZ; + for (uint8 i = 0; i < 3; ++i) + { + // Spawn locations are defined in World Maps Scripts.h + pPlayer->GetRandomPoint(aSpawnLocations[POS_IDX_MURKDEEP_SPAWN][0], aSpawnLocations[POS_IDX_MURKDEEP_SPAWN][1], aSpawnLocations[POS_IDX_MURKDEEP_SPAWN][2], 5.0f, fX, fY, fZ); + + if (Creature* pTemp = pPlayer->SummonCreature(NPC_GREYMIST_COASTRUNNNER, fX, fY, fZ, aSpawnLocations[POS_IDX_MURKDEEP_SPAWN][3], TEMPSUMMON_DEAD_DESPAWN, 0)) + { + pTemp->SetWalk(false); + pTemp->GetRandomPoint(aSpawnLocations[POS_IDX_MURKDEEP_MOVE][0], aSpawnLocations[POS_IDX_MURKDEEP_MOVE][1], aSpawnLocations[POS_IDX_MURKDEEP_MOVE][2], 5.0f, fX, fY, fZ); + pTemp->GetMotionMaster()->MovePoint(0, fX, fY, fZ); + } + } + } + + return false; +} + +void AddSC_areatrigger_scripts() +{ + Script* pNewScript; + + pNewScript = new Script; + pNewScript->Name = "at_childrens_week_spot"; + pNewScript->pAreaTrigger = &AreaTrigger_at_childrens_week_spot; + pNewScript->RegisterSelf(); + + pNewScript = new Script; + pNewScript->Name = "at_aldurthar_gate"; + pNewScript->pAreaTrigger = &AreaTrigger_at_aldurthar_gate; + pNewScript->RegisterSelf(); + + pNewScript = new Script; + pNewScript->Name = "at_coilfang_waterfall"; + pNewScript->pAreaTrigger = &AreaTrigger_at_coilfang_waterfall; + pNewScript->RegisterSelf(); + + pNewScript = new Script; + pNewScript->Name = "at_legion_teleporter"; + pNewScript->pAreaTrigger = &AreaTrigger_at_legion_teleporter; + pNewScript->RegisterSelf(); + + pNewScript = new Script; + pNewScript->Name = "at_ravenholdt"; + pNewScript->pAreaTrigger = &AreaTrigger_at_ravenholdt; + pNewScript->RegisterSelf(); + + pNewScript = new Script; + pNewScript->Name = "at_spearborn_encampment"; + pNewScript->pAreaTrigger = &AreaTrigger_at_spearborn_encampment; + pNewScript->RegisterSelf(); + + pNewScript = new Script; + pNewScript->Name = "at_warsong_farms"; + pNewScript->pAreaTrigger = &AreaTrigger_at_warsong_farms; + pNewScript->RegisterSelf(); + + pNewScript = new Script; + pNewScript->Name = "at_waygate"; + pNewScript->pAreaTrigger = &AreaTrigger_at_waygate; + pNewScript->RegisterSelf(); + + pNewScript = new Script; + pNewScript->Name = "at_stormwright_shelf"; + pNewScript->pAreaTrigger = &AreaTrigger_at_stormwright_shelf; + pNewScript->RegisterSelf(); + + pNewScript = new Script; + pNewScript->Name = "at_scent_larkorwi"; + pNewScript->pAreaTrigger = &AreaTrigger_at_scent_larkorwi; + pNewScript->RegisterSelf(); + + pNewScript = new Script; + pNewScript->Name = "at_murkdeep"; + pNewScript->pAreaTrigger = &AreaTrigger_at_murkdeep; + pNewScript->RegisterSelf(); +} diff --git a/src/modules/SD2/scripts/world/bosses_emerald_dragons.cpp b/src/modules/SD2/scripts/world/bosses_emerald_dragons.cpp new file mode 100644 index 000000000..064586847 --- /dev/null +++ b/src/modules/SD2/scripts/world/bosses_emerald_dragons.cpp @@ -0,0 +1,615 @@ +/** + * ScriptDev2 is an extension for mangos providing enhanced features for + * area triggers, creatures, game objects, instances, items, and spells beyond + * the default database scripting in mangos. + * + * Copyright (C) 2006-2013 ScriptDev2 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * World of Warcraft, and all World of Warcraft or Warcraft art, images, + * and lore are copyrighted by Blizzard Entertainment, Inc. + */ + +/** + * ScriptData + * SDName: bosses_emerald_dragons + * SD%Complete: 95 + * SDComment: Missing correct behaviour of used trigger NPCs, some spell issues, summon player NYI + * SDCategory: Emerald Dragon Bosses + * EndScriptData + */ + +/** + * ContentData + * boss_emerald_dragon -- Superclass for the four dragons + * boss_emeriss + * boss_lethon + * npc_spirit_shade + * boss_taerar + * boss_ysondre + * EndContentData + */ + +#include "precompiled.h" + +/*###### +## boss_emerald_dragon -- Superclass for the four dragons +######*/ + +enum +{ + SPELL_MARK_OF_NATURE_PLAYER = 25040, + SPELL_MARK_OF_NATURE_AURA = 25041, + SPELL_SEEPING_FOG_R = 24813, // Summons 15224 'Dream Fog' + SPELL_SEEPING_FOG_L = 24814, + SPELL_DREAM_FOG = 24777, // Used by summoned Adds + SPELL_NOXIOUS_BREATH = 24818, + SPELL_TAILSWEEP = 15847, + SPELL_SUMMON_PLAYER = 24776, // NYI + + NPC_DREAM_FOG = 15224, +}; + +struct boss_emerald_dragonAI : public ScriptedAI +{ + boss_emerald_dragonAI(Creature* pCreature) : ScriptedAI(pCreature) { Reset(); } + + uint32 m_uiEventCounter; + + uint32 m_uiSeepingFogTimer; + uint32 m_uiNoxiousBreathTimer; + uint32 m_uiTailsweepTimer; + + void Reset() override + { + m_uiEventCounter = 1; + + m_uiSeepingFogTimer = urand(15000, 20000); + m_uiNoxiousBreathTimer = 8000; + m_uiTailsweepTimer = 4000; + } + + void EnterCombat(Unit* pEnemy) override + { + DoCastSpellIfCan(m_creature, SPELL_MARK_OF_NATURE_AURA, CAST_TRIGGERED); + + ScriptedAI::EnterCombat(pEnemy); + } + + void KilledUnit(Unit* pVictim) override + { + // Mark killed players with Mark of Nature + if (pVictim->GetTypeId() == TYPEID_PLAYER) + { + pVictim->CastSpell(pVictim, SPELL_MARK_OF_NATURE_PLAYER, true, NULL, NULL, m_creature->GetObjectGuid()); + } + } + + void JustSummoned(Creature* pSummoned) override + { + if (Unit* pTarget = m_creature->SelectAttackingTarget(ATTACKING_TARGET_RANDOM, 0)) + { + pSummoned->AI()->AttackStart(pTarget); + } + + if (pSummoned->GetEntry() == NPC_DREAM_FOG) + { + pSummoned->CastSpell(pSummoned, SPELL_DREAM_FOG, true, NULL, NULL, m_creature->GetObjectGuid()); + } + } + + // Return true, if succeeded + virtual bool DoSpecialDragonAbility() = 0; + + // Return true to handle shared timers and MeleeAttack + virtual bool UpdateDragonAI(const uint32 /*uiDiff*/) { return true; } + + void UpdateAI(const uint32 uiDiff) override + { + // Return since we have no target + if (!m_creature->SelectHostileTarget() || !m_creature->getVictim()) + { + return; + } + + // Trigger special ability function at 75, 50 and 25% health + if (m_creature->GetHealthPercent() < 100.0f - m_uiEventCounter * 25.0f && DoSpecialDragonAbility()) + { + ++m_uiEventCounter; + } + + // Call dragon specific virtual function + if (!UpdateDragonAI(uiDiff)) + { + return; + } + + if (m_uiSeepingFogTimer < uiDiff) + { + DoCastSpellIfCan(m_creature, SPELL_SEEPING_FOG_R, CAST_TRIGGERED); + DoCastSpellIfCan(m_creature, SPELL_SEEPING_FOG_L, CAST_TRIGGERED); + m_uiSeepingFogTimer = urand(120000, 150000); // Rather Guesswork, but one Fog has 2min duration, hence a bit longer + } + else + { m_uiSeepingFogTimer -= uiDiff; } + + if (m_uiNoxiousBreathTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature, SPELL_NOXIOUS_BREATH) == CAST_OK) + { + m_uiNoxiousBreathTimer = urand(14000, 20000); + } + } + else + { m_uiNoxiousBreathTimer -= uiDiff; } + + if (m_uiTailsweepTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature, SPELL_TAILSWEEP) == CAST_OK) + { + m_uiTailsweepTimer = 2000; + } + } + else + { m_uiTailsweepTimer -= uiDiff; } + + DoMeleeAttackIfReady(); + } +}; + +/*###### +## boss_emeriss +######*/ + +enum +{ + SAY_EMERISS_AGGRO = -1000401, + SAY_CAST_CORRUPTION = -1000402, + + SPELL_VOLATILE_INFECTION = 24928, + SPELL_CORRUPTION_OF_EARTH = 24910, + SPELL_PUTRID_MUSHROOM = 24904, // Summons a mushroom on killing a player +}; + +struct boss_emerissAI : public boss_emerald_dragonAI +{ + boss_emerissAI(Creature* pCreature) : boss_emerald_dragonAI(pCreature) { Reset(); } + + uint32 m_uiVolatileInfectionTimer; + + void Reset() override + { + boss_emerald_dragonAI::Reset(); + + m_uiVolatileInfectionTimer = 12000; + } + + void Aggro(Unit* /*pWho*/) override + { + DoScriptText(SAY_EMERISS_AGGRO, m_creature); + } + + void KilledUnit(Unit* pVictim) override + { + // summon a mushroom on the spot the player dies + if (pVictim->GetTypeId() == TYPEID_PLAYER) + { + pVictim->CastSpell(pVictim, SPELL_PUTRID_MUSHROOM, true, NULL, NULL, m_creature->GetObjectGuid()); + } + + boss_emerald_dragonAI::KilledUnit(pVictim); + } + + // Corruption of Earth at 75%, 50% and 25% + bool DoSpecialDragonAbility() + { + if (DoCastSpellIfCan(m_creature, SPELL_CORRUPTION_OF_EARTH) == CAST_OK) + { + DoScriptText(SAY_CAST_CORRUPTION, m_creature); + + // Successfull cast + return true; + } + + return false; + } + + bool UpdateDragonAI(const uint32 uiDiff) + { + // Volatile Infection Timer + if (m_uiVolatileInfectionTimer < uiDiff) + { + Unit* pTarget = m_creature->SelectAttackingTarget(ATTACKING_TARGET_RANDOM, 0); + if (pTarget && DoCastSpellIfCan(pTarget, SPELL_VOLATILE_INFECTION) == CAST_OK) + { + m_uiVolatileInfectionTimer = urand(7000, 12000); + } + } + else + { + m_uiVolatileInfectionTimer -= uiDiff; + } + + return true; + } +}; + +CreatureAI* GetAI_boss_emeriss(Creature* pCreature) +{ + return new boss_emerissAI(pCreature); +} + +/*###### +## boss_lethon +######*/ + +enum +{ + SAY_LETHON_AGGRO = -1000666, + SAY_DRAW_SPIRIT = -1000667, + + SPELL_SHADOW_BOLT_WIRL = 24834, // Periodic aura + SPELL_DRAW_SPIRIT = 24811, + SPELL_SUMMON_SPIRIT_SHADE = 24810, // Summon spell was removed, was SPELL_EFFECT_SUMMON_DEMON + + NPC_LETHON = 14888, + NPC_SPIRIT_SHADE = 15261, // Add summoned by Lethon + SPELL_DARK_OFFERING = 24804, + SPELL_SPIRIT_SHAPE_VISUAL = 24809, +}; + +struct boss_lethonAI : public boss_emerald_dragonAI +{ + boss_lethonAI(Creature* pCreature) : boss_emerald_dragonAI(pCreature) {} + + void Aggro(Unit* /*pWho*/) override + { + DoScriptText(SAY_LETHON_AGGRO, m_creature); + // Shadow bolt wirl is a periodic aura which triggers a set of shadowbolts every 2 secs; may need some core tunning + DoCastSpellIfCan(m_creature, SPELL_SHADOW_BOLT_WIRL, CAST_TRIGGERED); + } + + // Summon a spirit which moves toward the boss and heals him for each player hit by the spell; used at 75%, 50% and 25% + bool DoSpecialDragonAbility() + { + if (DoCastSpellIfCan(m_creature, SPELL_DRAW_SPIRIT) == CAST_OK) + { + DoScriptText(SAY_DRAW_SPIRIT, m_creature); + return true; + } + + return false; + } + + // Need this code here, as SPELL_DRAW_SPIRIT has no Script- or Dummyeffect + void SpellHitTarget(Unit* pTarget, const SpellEntry* pSpell) override + { + // Summon a shade for each player hit + if (pTarget->GetTypeId() == TYPEID_PLAYER && pSpell->Id == SPELL_DRAW_SPIRIT) + { + // Summon this way, to be able to cast the shade visual spell with player as original caster + // This might not be supported currently by core, but this spell's visual should be dependend on the player + // Also possible that this was no problem due to the special way these NPCs had been summoned in classic times + if (Creature* pSummoned = pTarget->SummonCreature(NPC_SPIRIT_SHADE, 0.0f, 0.0f, 0.0f, pTarget->GetOrientation(), TEMPSUMMON_DEAD_DESPAWN, 0)) + { + pSummoned->CastSpell(pSummoned, SPELL_SPIRIT_SHAPE_VISUAL, true, NULL, NULL, pTarget->GetObjectGuid()); + } + } + } + + void JustSummoned(Creature* pSummoned) override + { + // Move the shade to lethon + if (pSummoned->GetEntry() == NPC_SPIRIT_SHADE) + { + pSummoned->GetMotionMaster()->MoveFollow(m_creature, 0.0f, 0.0f); + } + else + { boss_emerald_dragonAI::JustSummoned(pSummoned); } + } +}; + +struct npc_spirit_shadeAI : public ScriptedAI +{ + npc_spirit_shadeAI(Creature* pCreature) : ScriptedAI(pCreature) { Reset(); } + + bool m_bHasHealed; + + void Reset() override + { + m_bHasHealed = false; + } + + void MoveInLineOfSight(Unit* pWho) override + { + if (!m_bHasHealed && pWho->GetEntry() == NPC_LETHON && pWho->IsWithinDistInMap(m_creature, 3.0f)) + { + if (DoCastSpellIfCan(pWho, SPELL_DARK_OFFERING) == CAST_OK) + { + m_bHasHealed = true; + m_creature->ForcedDespawn(1000); + } + } + } + + void AttackStart(Unit* /*pWho*/) override { } + + void UpdateAI(const uint32 /*uiDiff*/) override { } +}; + +CreatureAI* GetAI_boss_lethon(Creature* pCreature) +{ + return new boss_lethonAI(pCreature); +} + +CreatureAI* GetAI_npc_spirit_shade(Creature* pCreature) +{ + return new npc_spirit_shadeAI(pCreature); +} + +/*###### +## boss_taerar +######*/ + +enum +{ + SAY_TAERAR_AGGRO = -1000399, + SAY_SUMMONSHADE = -1000400, + + SPELL_ARCANE_BLAST = 24857, + SPELL_BELLOWING_ROAR = 22686, + + SPELL_SUMMON_SHADE_1 = 24841, + SPELL_SUMMON_SHADE_2 = 24842, + SPELL_SUMMON_SHADE_3 = 24843, + SPELL_SELF_STUN = 24883, // Stunns the main boss until the shades are dead or timer expires + + NPC_SHADE_OF_TAERAR = 15302, + SPELL_POSIONCLOUD = 24840, + SPELL_POSIONBREATH = 20667 +}; + +struct boss_taerarAI : public boss_emerald_dragonAI +{ + boss_taerarAI(Creature* pCreature) : boss_emerald_dragonAI(pCreature) { Reset(); } + + uint32 m_uiArcaneBlastTimer; + uint32 m_uiBellowingRoarTimer; + uint32 m_uiShadesTimeoutTimer; + uint8 m_uiShadesDead; + + void Reset() override + { + boss_emerald_dragonAI::Reset(); + + m_uiArcaneBlastTimer = 12000; + m_uiBellowingRoarTimer = 30000; + m_uiShadesTimeoutTimer = 0; // The time that Taerar is banished + m_uiShadesDead = 0; + + // Remove Unselectable if needed + if (m_creature->HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE)) + { + m_creature->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE); + } + } + + void Aggro(Unit* /*pWho*/) override + { + DoScriptText(SAY_TAERAR_AGGRO, m_creature); + } + + // Summon 3 Shades at 75%, 50% and 25% and Banish Self + bool DoSpecialDragonAbility() + { + if (DoCastSpellIfCan(m_creature, SPELL_SELF_STUN) == CAST_OK) + { + // Summon the shades at boss position + DoCastSpellIfCan(m_creature, SPELL_SUMMON_SHADE_1, CAST_TRIGGERED); + DoCastSpellIfCan(m_creature, SPELL_SUMMON_SHADE_2, CAST_TRIGGERED); + DoCastSpellIfCan(m_creature, SPELL_SUMMON_SHADE_3, CAST_TRIGGERED); + + // Make boss not selectable when banished + m_creature->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE); + + DoScriptText(SAY_SUMMONSHADE, m_creature); + m_uiShadesTimeoutTimer = 60000; + + return true; + } + + return false; + } + + void SummonedCreatureJustDied(Creature* pSummoned) override + { + if (pSummoned->GetEntry() == NPC_SHADE_OF_TAERAR) + { + ++m_uiShadesDead; + + // If all shades are dead then unbanish the boss + if (m_uiShadesDead == 3) + { + DoUnbanishBoss(); + } + } + } + + void DoUnbanishBoss() + { + m_creature->RemoveAurasDueToSpell(SPELL_SELF_STUN); + m_creature->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE); + + m_uiShadesTimeoutTimer = 0; + m_uiShadesDead = 0; + } + + bool UpdateDragonAI(const uint32 uiDiff) + { + // Timer to unbanish the boss + if (m_uiShadesTimeoutTimer) + { + if (m_uiShadesTimeoutTimer <= uiDiff) + { + DoUnbanishBoss(); + } + else + { + m_uiShadesTimeoutTimer -= uiDiff; + } + + // Prevent further spells or timer handling while banished + return false; + } + + // Arcane Blast Timer + if (m_uiArcaneBlastTimer < uiDiff) + { + Unit* pTarget = m_creature->SelectAttackingTarget(ATTACKING_TARGET_RANDOM, 0); + if (pTarget && DoCastSpellIfCan(pTarget, SPELL_ARCANE_BLAST) == CAST_OK) + { + m_uiArcaneBlastTimer = urand(7000, 12000); + } + } + else + { + m_uiArcaneBlastTimer -= uiDiff; + } + + // Bellowing Roar Timer + if (m_uiBellowingRoarTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature, SPELL_BELLOWING_ROAR) == CAST_OK) + { + m_uiBellowingRoarTimer = urand(20000, 30000); + } + } + else + { + m_uiBellowingRoarTimer -= uiDiff; + } + + return true; + } +}; + +CreatureAI* GetAI_boss_taerar(Creature* pCreature) +{ + return new boss_taerarAI(pCreature); +} + +/*###### +## boss_ysondre +######*/ + +enum +{ + SAY_YSONDRE_AGGRO = -1000360, + SAY_SUMMON_DRUIDS = -1000361, + + SPELL_LIGHTNING_WAVE = 24819, + SPELL_SUMMON_DRUIDS = 24795, + + // druid spells + SPELL_MOONFIRE = 21669 +}; + +// Ysondre script +struct boss_ysondreAI : public boss_emerald_dragonAI +{ + boss_ysondreAI(Creature* pCreature) : boss_emerald_dragonAI(pCreature) { Reset(); } + + uint32 m_uiLightningWaveTimer; + + void Reset() override + { + boss_emerald_dragonAI::Reset(); + + m_uiLightningWaveTimer = 12000; + } + + void Aggro(Unit* /*pWho*/) override + { + DoScriptText(SAY_YSONDRE_AGGRO, m_creature); + } + + // Summon Druids - TODO FIXME (spell not understood) + bool DoSpecialDragonAbility() + { + DoScriptText(SAY_SUMMON_DRUIDS, m_creature); + + for (int i = 0; i < 10; ++i) + { + DoCastSpellIfCan(m_creature, SPELL_SUMMON_DRUIDS, CAST_TRIGGERED); + } + + return true; + } + + bool UpdateDragonAI(const uint32 uiDiff) + { + // Lightning Wave Timer + if (m_uiLightningWaveTimer < uiDiff) + { + Unit* pTarget = m_creature->SelectAttackingTarget(ATTACKING_TARGET_RANDOM, 0); + if (pTarget && DoCastSpellIfCan(pTarget, SPELL_LIGHTNING_WAVE) == CAST_OK) + { + m_uiLightningWaveTimer = urand(7000, 12000); + } + } + else + { + m_uiLightningWaveTimer -= uiDiff; + } + + return true; + } +}; + +CreatureAI* GetAI_boss_ysondre(Creature* pCreature) +{ + return new boss_ysondreAI(pCreature); +} + +void AddSC_bosses_emerald_dragons() +{ + Script* pNewScript; + + pNewScript = new Script; + pNewScript->Name = "boss_emeriss"; + pNewScript->GetAI = &GetAI_boss_emeriss; + pNewScript->RegisterSelf(); + + pNewScript = new Script; + pNewScript->Name = "boss_lethon"; + pNewScript->GetAI = &GetAI_boss_lethon; + pNewScript->RegisterSelf(); + + pNewScript = new Script; + pNewScript->Name = "npc_spirit_shade"; + pNewScript->GetAI = &GetAI_npc_spirit_shade; + pNewScript->RegisterSelf(); + + pNewScript = new Script; + pNewScript->Name = "boss_taerar"; + pNewScript->GetAI = &GetAI_boss_taerar; + pNewScript->RegisterSelf(); + + pNewScript = new Script; + pNewScript->Name = "boss_ysondre"; + pNewScript->GetAI = &GetAI_boss_ysondre; + pNewScript->RegisterSelf(); +} diff --git a/src/modules/SD2/scripts/world/go_scripts.cpp b/src/modules/SD2/scripts/world/go_scripts.cpp new file mode 100644 index 000000000..bf434876d --- /dev/null +++ b/src/modules/SD2/scripts/world/go_scripts.cpp @@ -0,0 +1,390 @@ +/** + * ScriptDev2 is an extension for mangos providing enhanced features for + * area triggers, creatures, game objects, instances, items, and spells beyond + * the default database scripting in mangos. + * + * Copyright (C) 2006-2013 ScriptDev2 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * World of Warcraft, and all World of Warcraft or Warcraft art, images, + * and lore are copyrighted by Blizzard Entertainment, Inc. + */ + +/** + * ScriptData + * SDName: GO_Scripts + * SD%Complete: 100 + * SDComment: Quest support: 5097, 5098, 12557, 14092/14076. Barov_journal->Teaches spell 26089 + * SDCategory: Game Objects + * EndScriptData + */ + +/* ContentData +go_barov_journal +go_ethereum_prison +go_ethereum_stasis +go_mysterious_snow_mound +go_tele_to_dalaran_crystal +go_tele_to_violet_stand +go_andorhal_tower +go_scourge_enclosure +go_lab_work_reagents +EndContentData */ + +#include "precompiled.h" + +/*###### +## go_barov_journal +######*/ + +enum +{ + SPELL_TAILOR_FELCLOTH_BAG = 26086, + SPELL_LEARN_FELCLOTH_BAG = 26095 +}; + +bool GOUse_go_barov_journal(Player* pPlayer, GameObject* /*pGo*/) +{ + if (pPlayer->HasSkill(SKILL_TAILORING) && pPlayer->GetBaseSkillValue(SKILL_TAILORING) >= 280 && !pPlayer->HasSpell(SPELL_TAILOR_FELCLOTH_BAG)) + { + pPlayer->CastSpell(pPlayer, SPELL_LEARN_FELCLOTH_BAG, false); + } + return true; +} + +/*###### +## go_ethereum_prison +######*/ + +enum +{ + FACTION_LC = 1011, + FACTION_SHAT = 935, + FACTION_CE = 942, + FACTION_CON = 933, + FACTION_KT = 989, + FACTION_SPOR = 970, + + SPELL_REP_LC = 39456, + SPELL_REP_SHAT = 39457, + SPELL_REP_CE = 39460, + SPELL_REP_CON = 39474, + SPELL_REP_KT = 39475, + SPELL_REP_SPOR = 39476 +}; + +const uint32 uiNpcPrisonEntry[] = +{ + 22810, 22811, 22812, 22813, 22814, 22815, // good guys + 20783, 20784, 20785, 20786, 20788, 20789, 20790 // bad guys +}; + +bool GOUse_go_ethereum_prison(Player* pPlayer, GameObject* pGo) +{ + uint8 uiRandom = urand(0, countof(uiNpcPrisonEntry) - 1); + + if (Creature* pCreature = pPlayer->SummonCreature(uiNpcPrisonEntry[uiRandom], + pGo->GetPositionX(), pGo->GetPositionY(), pGo->GetPositionZ(), pGo->GetAngle(pPlayer), + TEMPSUMMON_TIMED_OOC_DESPAWN, 30000)) + { + if (!pCreature->IsHostileTo(pPlayer)) + { + uint32 uiSpell = 0; + + if (FactionTemplateEntry const* pFaction = pCreature->getFactionTemplateEntry()) + { + switch (pFaction->faction) + { + case FACTION_LC: uiSpell = SPELL_REP_LC; break; + case FACTION_SHAT: uiSpell = SPELL_REP_SHAT; break; + case FACTION_CE: uiSpell = SPELL_REP_CE; break; + case FACTION_CON: uiSpell = SPELL_REP_CON; break; + case FACTION_KT: uiSpell = SPELL_REP_KT; break; + case FACTION_SPOR: uiSpell = SPELL_REP_SPOR; break; + } + + if (uiSpell) + { pCreature->CastSpell(pPlayer, uiSpell, false); } + else + { script_error_log("go_ethereum_prison summoned creature (entry %u) but faction (%u) are not expected by script.", pCreature->GetEntry(), pCreature->getFaction()); } + } + } + } + + return false; +} + +/*###### +## go_ethereum_stasis +######*/ + +const uint32 uiNpcStasisEntry[] = +{ + 22825, 20888, 22827, 22826, 22828 +}; + +bool GOUse_go_ethereum_stasis(Player* pPlayer, GameObject* pGo) +{ + uint8 uiRandom = urand(0, countof(uiNpcStasisEntry) - 1); + + pPlayer->SummonCreature(uiNpcStasisEntry[uiRandom], + pGo->GetPositionX(), pGo->GetPositionY(), pGo->GetPositionZ(), pGo->GetAngle(pPlayer), + TEMPSUMMON_TIMED_OOC_DESPAWN, 30000); + + return false; +} + +/*###### +## go_jump_a_tron +######*/ + +enum +{ + SPELL_JUMP_A_TRON = 33382, + NPC_JUMP_A_TRON = 19041 +}; + +bool GOUse_go_jump_a_tron(Player* pPlayer, GameObject* pGo) +{ + if (Creature* pCreature = GetClosestCreatureWithEntry(pGo, NPC_JUMP_A_TRON, INTERACTION_DISTANCE)) + { pCreature->CastSpell(pPlayer, SPELL_JUMP_A_TRON, false); } + + return false; +} + +/*###### +## go_mysterious_snow_mound +######*/ + +enum +{ + SPELL_SUMMON_DEEP_JORMUNGAR = 66510, + SPELL_SUMMON_MOLE_MACHINE = 66492, + SPELL_SUMMON_MARAUDER = 66491, +}; + +bool GOUse_go_mysterious_snow_mound(Player* pPlayer, GameObject* pGo) +{ + if (urand(0, 1)) + { + pPlayer->CastSpell(pPlayer, SPELL_SUMMON_DEEP_JORMUNGAR, true); + } + else + { + // This is basically wrong, but added for support. + // Mole machine would summon, along with unkonwn GO (a GO trap?) and then + // the npc would summon with base of that location. + pPlayer->CastSpell(pPlayer, SPELL_SUMMON_MOLE_MACHINE, true); + pPlayer->CastSpell(pPlayer, SPELL_SUMMON_MARAUDER, true); + } + + pGo->SetLootState(GO_JUST_DEACTIVATED); + return true; +} + +/*###### +## go_tele_to_dalaran_crystal +######*/ + +enum +{ + QUEST_LEARN_LEAVE_RETURN = 12790, + QUEST_TELE_CRYSTAL_FLAG = 12845 +}; + +bool GOUse_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 GOUse_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_andorhal_tower +######*/ + +enum +{ + QUEST_ALL_ALONG_THE_WATCHTOWERS_ALLIANCE = 5097, + QUEST_ALL_ALONG_THE_WATCHTOWERS_HORDE = 5098, + NPC_ANDORHAL_TOWER_1 = 10902, + NPC_ANDORHAL_TOWER_2 = 10903, + NPC_ANDORHAL_TOWER_3 = 10904, + NPC_ANDORHAL_TOWER_4 = 10905, + GO_ANDORHAL_TOWER_1 = 176094, + GO_ANDORHAL_TOWER_2 = 176095, + GO_ANDORHAL_TOWER_3 = 176096, + GO_ANDORHAL_TOWER_4 = 176097 +}; + +bool GOUse_go_andorhal_tower(Player* pPlayer, GameObject* pGo) +{ + if (pPlayer->GetQuestStatus(QUEST_ALL_ALONG_THE_WATCHTOWERS_ALLIANCE) == QUEST_STATUS_INCOMPLETE || pPlayer->GetQuestStatus(QUEST_ALL_ALONG_THE_WATCHTOWERS_HORDE) == QUEST_STATUS_INCOMPLETE) + { + uint32 uiKillCredit = 0; + switch (pGo->GetEntry()) + { + case GO_ANDORHAL_TOWER_1: + uiKillCredit = NPC_ANDORHAL_TOWER_1; + break; + case GO_ANDORHAL_TOWER_2: + uiKillCredit = NPC_ANDORHAL_TOWER_2; + break; + case GO_ANDORHAL_TOWER_3: + uiKillCredit = NPC_ANDORHAL_TOWER_3; + break; + case GO_ANDORHAL_TOWER_4: + uiKillCredit = NPC_ANDORHAL_TOWER_4; + break; + } + if (uiKillCredit) + pPlayer->KilledMonsterCredit(uiKillCredit); + } + return true; +} + +/*###### +## go_scourge_enclosure +######*/ + +enum +{ + SPELL_GYMER_LOCK_EXPLOSION = 55529, + NPC_GYMER_LOCK_DUMMY = 29928 +}; + +bool GOUse_go_scourge_enclosure(Player* pPlayer, GameObject* pGo) +{ + std::list m_lResearchersList; + GetCreatureListWithEntryInGrid(m_lResearchersList, pGo, NPC_GYMER_LOCK_DUMMY, 15.0f); + if (!m_lResearchersList.empty()) + { + for (std::list::iterator itr = m_lResearchersList.begin(); itr != m_lResearchersList.end(); ++itr) + { + (*itr)->CastSpell((*itr), SPELL_GYMER_LOCK_EXPLOSION, true); + } + } + pPlayer->KilledMonsterCredit(NPC_GYMER_LOCK_DUMMY); + return true; +} + +/*###### +## go_lab_work_reagents +######*/ + +enum +{ + QUEST_LAB_WORK = 12557, + + SPELL_WIRHERED_BATWING_KILL_CREDIT = 51226, + SPELL_MUDDY_MIRE_MAGGOT_KILL_CREDIT = 51227, + SPELL_AMBERSEED_KILL_CREDIT = 51228, + SPELL_CHILLED_SERPENT_MUCUS_KILL_CREDIT = 51229, + + GO_AMBERSEED = 190459, + GO_CHILLED_SERPENT_MUCUS = 190462, + GO_WITHERED_BATWING = 190473, + GO_MUDDY_MIRE_MAGGOTS = 190478, +}; + +bool GOUse_go_lab_work_reagents(Player* pPlayer, GameObject* pGo) +{ + if (pPlayer->GetQuestStatus(QUEST_LAB_WORK) == QUEST_STATUS_INCOMPLETE) + { + uint32 uiCreditSpellId = 0; + switch (pGo->GetEntry()) + { + case GO_AMBERSEED: uiCreditSpellId = SPELL_AMBERSEED_KILL_CREDIT; break; + case GO_CHILLED_SERPENT_MUCUS: uiCreditSpellId = SPELL_CHILLED_SERPENT_MUCUS_KILL_CREDIT; break; + case GO_WITHERED_BATWING: uiCreditSpellId = SPELL_WIRHERED_BATWING_KILL_CREDIT; break; + case GO_MUDDY_MIRE_MAGGOTS: uiCreditSpellId = SPELL_MUDDY_MIRE_MAGGOT_KILL_CREDIT; break; + } + + if (uiCreditSpellId) + pPlayer->CastSpell(pPlayer, uiCreditSpellId, true); + } + + return false; +} + +void AddSC_go_scripts() +{ + Script* pNewScript; + + pNewScript = new Script; + pNewScript->Name = "go_barov_journal"; + pNewScript->pGOUse = &GOUse_go_barov_journal; + pNewScript->RegisterSelf(); + + pNewScript = new Script; + pNewScript->Name = "go_ethereum_prison"; + pNewScript->pGOUse = &GOUse_go_ethereum_prison; + pNewScript->RegisterSelf(); + + pNewScript = new Script; + pNewScript->Name = "go_ethereum_stasis"; + pNewScript->pGOUse = &GOUse_go_ethereum_stasis; + pNewScript->RegisterSelf(); + + pNewScript = new Script; + pNewScript->Name = "go_jump_a_tron"; + pNewScript->pGOUse = &GOUse_go_jump_a_tron; + pNewScript->RegisterSelf(); + + pNewScript = new Script; + pNewScript->Name = "go_mysterious_snow_mound"; + pNewScript->pGOUse = &GOUse_go_mysterious_snow_mound; + pNewScript->RegisterSelf(); + + pNewScript = new Script; + pNewScript->Name = "go_tele_to_dalaran_crystal"; + pNewScript->pGOUse = &GOUse_go_tele_to_dalaran_crystal; + pNewScript->RegisterSelf(); + + pNewScript = new Script; + pNewScript->Name = "go_tele_to_violet_stand"; + pNewScript->pGOUse = &GOUse_go_tele_to_violet_stand; + pNewScript->RegisterSelf(); + + pNewScript = new Script; + pNewScript->Name = "go_andorhal_tower"; + pNewScript->pGOUse = &GOUse_go_andorhal_tower; + pNewScript->RegisterSelf(); + + pNewScript = new Script; + pNewScript->Name = "go_scourge_enclosure"; + pNewScript->pGOUse = &GOUse_go_scourge_enclosure; + pNewScript->RegisterSelf(); + + pNewScript = new Script; + pNewScript->Name = "go_lab_work_reagents"; + pNewScript->pGOUse = &GOUse_go_lab_work_reagents; + pNewScript->RegisterSelf(); +} diff --git a/src/modules/SD2/scripts/world/guards.cpp b/src/modules/SD2/scripts/world/guards.cpp new file mode 100644 index 000000000..aba06fb2c --- /dev/null +++ b/src/modules/SD2/scripts/world/guards.cpp @@ -0,0 +1,386 @@ +/** + * ScriptDev2 is an extension for mangos providing enhanced features for + * area triggers, creatures, game objects, instances, items, and spells beyond + * the default database scripting in mangos. + * + * Copyright (C) 2006-2013 ScriptDev2 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * World of Warcraft, and all World of Warcraft or Warcraft art, images, + * and lore are copyrighted by Blizzard Entertainment, Inc. + */ + +/* ScriptData +SDName: Guards +SD%Complete: 100 +SDComment: CombatAI should be organized better for future. +SDCategory: Guards +EndScriptData */ + +/* ContentData +guard_azuremyst +guard_bluffwatcher +guard_contested +guard_darnassus +guard_dunmorogh +guard_durotar +guard_elwynnforest +guard_eversong +guard_exodar +guard_ironforge +guard_mulgore +guard_orgrimmar +guard_shattrath +guard_shattrath_aldor +guard_shattrath_scryer +guard_silvermoon +guard_stormwind +guard_teldrassil +guard_tirisfal +guard_undercity +EndContentData */ + +#include "precompiled.h" +#include "guard_ai.h" + +CreatureAI* GetAI_guard_azuremyst(Creature* pCreature) +{ + return new guardAI(pCreature); +} + +CreatureAI* GetAI_guard_bluffwatcher(Creature* pCreature) +{ + return new guardAI(pCreature); +} + +CreatureAI* GetAI_guard_contested(Creature* pCreature) +{ + return new guardAI(pCreature); +} + +CreatureAI* GetAI_guard_darnassus(Creature* pCreature) +{ + return new guardAI(pCreature); +} + +CreatureAI* GetAI_guard_dunmorogh(Creature* pCreature) +{ + return new guardAI(pCreature); +} + +CreatureAI* GetAI_guard_durotar(Creature* pCreature) +{ + return new guardAI(pCreature); +} + +CreatureAI* GetAI_guard_elwynnforest(Creature* pCreature) +{ + return new guardAI(pCreature); +} + +CreatureAI* GetAI_guard_eversong(Creature* pCreature) +{ + return new guardAI(pCreature); +} + +CreatureAI* GetAI_guard_exodar(Creature* pCreature) +{ + return new guardAI(pCreature); +} + +CreatureAI* GetAI_guard_ironforge(Creature* pCreature) +{ + return new guardAI(pCreature); +} + +CreatureAI* GetAI_guard_mulgore(Creature* pCreature) +{ + return new guardAI(pCreature); +} + +CreatureAI* GetAI_guard_orgrimmar(Creature* pCreature) +{ + return new guardAI_orgrimmar(pCreature); +} + +CreatureAI* GetAI_guard_shattrath(Creature* pCreature) +{ + return new guardAI(pCreature); +} + +/******************************************************* + * guard_shattrath_aldor + *******************************************************/ + +struct guard_shattrath_aldorAI : public guardAI +{ + guard_shattrath_aldorAI(Creature* pCreature) : guardAI(pCreature) { Reset(); } + + uint32 m_uiExile_Timer; + uint32 m_uiBanish_Timer; + ObjectGuid m_playerGuid; + bool m_bCanTeleport; + + void Reset() override + { + m_uiBanish_Timer = 5000; + m_uiExile_Timer = 8500; + m_playerGuid.Clear(); + m_bCanTeleport = false; + } + + void UpdateAI(const uint32 uiDiff) override + { + if (!m_creature->SelectHostileTarget() || !m_creature->getVictim()) + { return; } + + if (m_bCanTeleport) + { + if (m_uiExile_Timer < uiDiff) + { + if (Player* pTarget = m_creature->GetMap()->GetPlayer(m_playerGuid)) + { + pTarget->CastSpell(pTarget, SPELL_EXILE, true); + pTarget->CastSpell(pTarget, SPELL_BANISH_TELEPORT, true); + } + + m_playerGuid.Clear(); + m_uiExile_Timer = 8500; + m_bCanTeleport = false; + } + else + { m_uiExile_Timer -= uiDiff; } + } + else if (m_uiBanish_Timer < uiDiff) + { + Unit* pVictim = m_creature->getVictim(); + + if (pVictim && pVictim->GetTypeId() == TYPEID_PLAYER) + { + DoCastSpellIfCan(pVictim, SPELL_BANISHED_SHATTRATH_A); + m_uiBanish_Timer = 9000; + m_playerGuid = pVictim->GetObjectGuid(); + m_bCanTeleport = true; + } + } + else + { m_uiBanish_Timer -= uiDiff; } + + DoMeleeAttackIfReady(); + } +}; + +CreatureAI* GetAI_guard_shattrath_aldor(Creature* pCreature) +{ + return new guard_shattrath_aldorAI(pCreature); +} + +/******************************************************* + * guard_shattrath_scryer + *******************************************************/ + +struct guard_shattrath_scryerAI : public guardAI +{ + guard_shattrath_scryerAI(Creature* pCreature) : guardAI(pCreature) { Reset(); } + + uint32 m_uiExile_Timer; + uint32 m_uiBanish_Timer; + ObjectGuid m_playerGuid; + bool m_bCanTeleport; + + void Reset() override + { + m_uiBanish_Timer = 5000; + m_uiExile_Timer = 8500; + m_playerGuid.Clear(); + m_bCanTeleport = false; + } + + void UpdateAI(const uint32 uiDiff) override + { + if (!m_creature->SelectHostileTarget() || !m_creature->getVictim()) + { return; } + + if (m_bCanTeleport) + { + if (m_uiExile_Timer < uiDiff) + { + if (Player* pTarget = m_creature->GetMap()->GetPlayer(m_playerGuid)) + { + pTarget->CastSpell(pTarget, SPELL_EXILE, true); + pTarget->CastSpell(pTarget, SPELL_BANISH_TELEPORT, true); + } + + m_playerGuid.Clear(); + m_uiExile_Timer = 8500; + m_bCanTeleport = false; + } + else + { m_uiExile_Timer -= uiDiff; } + } + else if (m_uiBanish_Timer < uiDiff) + { + Unit* pVictim = m_creature->getVictim(); + + if (pVictim && pVictim->GetTypeId() == TYPEID_PLAYER) + { + DoCastSpellIfCan(pVictim, SPELL_BANISHED_SHATTRATH_S); + m_uiBanish_Timer = 9000; + m_playerGuid = pVictim->GetObjectGuid(); + m_bCanTeleport = true; + } + } + else + { m_uiBanish_Timer -= uiDiff; } + + DoMeleeAttackIfReady(); + } +}; + +CreatureAI* GetAI_guard_shattrath_scryer(Creature* pCreature) +{ + return new guard_shattrath_scryerAI(pCreature); +} + +CreatureAI* GetAI_guard_silvermoon(Creature* pCreature) +{ + return new guardAI(pCreature); +} + +CreatureAI* GetAI_guard_stormwind(Creature* pCreature) +{ + return new guardAI_stormwind(pCreature); +} + +CreatureAI* GetAI_guard_teldrassil(Creature* pCreature) +{ + return new guardAI(pCreature); +} + +CreatureAI* GetAI_guard_tirisfal(Creature* pCreature) +{ + return new guardAI(pCreature); +} + +CreatureAI* GetAI_guard_undercity(Creature* pCreature) +{ + return new guardAI(pCreature); +} + +void AddSC_guards() +{ + Script* pNewScript; + + pNewScript = new Script; + pNewScript->Name = "guard_azuremyst"; + pNewScript->GetAI = &GetAI_guard_azuremyst; + pNewScript->RegisterSelf(); + + pNewScript = new Script; + pNewScript->Name = "guard_bluffwatcher"; + pNewScript->GetAI = &GetAI_guard_bluffwatcher; + pNewScript->RegisterSelf(); + + pNewScript = new Script; + pNewScript->Name = "guard_contested"; + pNewScript->GetAI = &GetAI_guard_contested; + pNewScript->RegisterSelf(); + + pNewScript = new Script; + pNewScript->Name = "guard_darnassus"; + pNewScript->GetAI = &GetAI_guard_darnassus; + pNewScript->RegisterSelf(); + + pNewScript = new Script; + pNewScript->Name = "guard_dunmorogh"; + pNewScript->GetAI = &GetAI_guard_dunmorogh; + pNewScript->RegisterSelf(); + + pNewScript = new Script; + pNewScript->Name = "guard_durotar"; + pNewScript->GetAI = &GetAI_guard_durotar; + pNewScript->RegisterSelf(); + + pNewScript = new Script; + pNewScript->Name = "guard_elwynnforest"; + pNewScript->GetAI = &GetAI_guard_elwynnforest; + pNewScript->RegisterSelf(); + + pNewScript = new Script; + pNewScript->Name = "guard_eversong"; + pNewScript->GetAI = &GetAI_guard_eversong; + pNewScript->RegisterSelf(); + + pNewScript = new Script; + pNewScript->Name = "guard_exodar"; + pNewScript->GetAI = &GetAI_guard_exodar; + pNewScript->RegisterSelf(); + + pNewScript = new Script; + pNewScript->Name = "guard_ironforge"; + pNewScript->GetAI = &GetAI_guard_ironforge; + pNewScript->RegisterSelf(); + + pNewScript = new Script; + pNewScript->Name = "guard_mulgore"; + pNewScript->GetAI = &GetAI_guard_mulgore; + pNewScript->RegisterSelf(); + + pNewScript = new Script; + pNewScript->Name = "guard_orgrimmar"; + pNewScript->GetAI = &GetAI_guard_orgrimmar; + pNewScript->RegisterSelf(); + + pNewScript = new Script; + pNewScript->Name = "guard_shattrath"; + pNewScript->GetAI = &GetAI_guard_shattrath; + pNewScript->RegisterSelf(); + + pNewScript = new Script; + pNewScript->Name = "guard_shattrath_aldor"; + pNewScript->GetAI = &GetAI_guard_shattrath_aldor; + pNewScript->RegisterSelf(); + + pNewScript = new Script; + pNewScript->Name = "guard_shattrath_scryer"; + pNewScript->GetAI = &GetAI_guard_shattrath_scryer; + pNewScript->RegisterSelf(); + + pNewScript = new Script; + pNewScript->Name = "guard_silvermoon"; + pNewScript->GetAI = &GetAI_guard_silvermoon; + pNewScript->RegisterSelf(); + + pNewScript = new Script; + pNewScript->Name = "guard_stormwind"; + pNewScript->GetAI = &GetAI_guard_stormwind; + pNewScript->RegisterSelf(); + + pNewScript = new Script; + pNewScript->Name = "guard_teldrassil"; + pNewScript->GetAI = &GetAI_guard_teldrassil; + pNewScript->RegisterSelf(); + + pNewScript = new Script; + pNewScript->Name = "guard_tirisfal"; + pNewScript->GetAI = &GetAI_guard_tirisfal; + pNewScript->RegisterSelf(); + + pNewScript = new Script; + pNewScript->Name = "guard_undercity"; + pNewScript->GetAI = &GetAI_guard_undercity; + pNewScript->RegisterSelf(); +} diff --git a/src/modules/SD2/scripts/world/item_scripts.cpp b/src/modules/SD2/scripts/world/item_scripts.cpp new file mode 100644 index 000000000..4c189db30 --- /dev/null +++ b/src/modules/SD2/scripts/world/item_scripts.cpp @@ -0,0 +1,162 @@ +/** + * ScriptDev2 is an extension for mangos providing enhanced features for + * area triggers, creatures, game objects, instances, items, and spells beyond + * the default database scripting in mangos. + * + * Copyright (C) 2006-2013 ScriptDev2 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * World of Warcraft, and all World of Warcraft or Warcraft art, images, + * and lore are copyrighted by Blizzard Entertainment, Inc. + */ + +/* 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_arcane_charges Prevent use if player is not flying (cannot cast while on ground) +item_flying_machine(i34060,i34061) Engineering crafted flying machines +item_gor_dreks_ointment(i30175) Protecting Our Own(q10488) +EndContentData */ + +#include "precompiled.h" +#include "Spell.h" + +/*##### +# item_arcane_charges +#####*/ + +enum +{ + SPELL_ARCANE_CHARGES = 45072 +}; + +bool ItemUse_item_arcane_charges(Player* pPlayer, Item* pItem, const SpellCastTargets& /*pTargets*/) +{ + if (pPlayer->IsTaxiFlying()) + { return false; } + + pPlayer->SendEquipError(EQUIP_ERR_NONE, pItem, NULL); + + if (const SpellEntry* pSpellInfo = GetSpellStore()->LookupEntry(SPELL_ARCANE_CHARGES)) + Spell::SendCastResult(pPlayer, pSpellInfo, 1, SPELL_FAILED_NOT_ON_GROUND); + + return true; +} + +/*##### +# item_flying_machine +#####*/ + +bool ItemUse_item_flying_machine(Player* pPlayer, Item* pItem, const SpellCastTargets& /*pTargets*/) +{ + uint32 itemId = pItem->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("SD2: Player attempt to use item %u, but did not meet riding requirement", itemId); + pPlayer->SendEquipError(EQUIP_ERR_CANT_EQUIP_SKILL, pItem, NULL); + return true; +} + +/*##### +# item_gor_dreks_ointment +#####*/ + +enum +{ + NPC_TH_DIRE_WOLF = 20748, + SPELL_GORDREKS_OINTMENT = 32578 +}; + +bool ItemUse_item_gor_dreks_ointment(Player* pPlayer, Item* pItem, const SpellCastTargets& pTargets) +{ + if (pTargets.getUnitTarget() && pTargets.getUnitTarget()->GetTypeId() == TYPEID_UNIT && pTargets.getUnitTarget()->HasAura(SPELL_GORDREKS_OINTMENT)) + { + pPlayer->SendEquipError(EQUIP_ERR_NONE, pItem, NULL); + + if (const SpellEntry* pSpellInfo = GetSpellStore()->LookupEntry(SPELL_GORDREKS_OINTMENT)) + Spell::SendCastResult(pPlayer, pSpellInfo, 1, SPELL_FAILED_TARGET_AURASTATE); + + return true; + } + + return false; +} + +/*##### +# item_petrov_cluster_bombs +#####*/ + +enum +{ + SPELL_PETROV_BOMB = 42406, + AREA_ID_SHATTERED_STRAITS = 4064, + ZONE_ID_HOWLING = 495 +}; + +bool ItemUse_item_petrov_cluster_bombs(Player* pPlayer, Item* pItem, const SpellCastTargets& /*pTargets*/) +{ + if (pPlayer->GetZoneId() != ZONE_ID_HOWLING) + return false; + + if (!pPlayer->GetTransport() || pPlayer->GetAreaId() != AREA_ID_SHATTERED_STRAITS) + { + pPlayer->SendEquipError(EQUIP_ERR_NONE, pItem, NULL); + + if (const SpellEntry* pSpellInfo = GetSpellStore()->LookupEntry(SPELL_PETROV_BOMB)) + Spell::SendCastResult(pPlayer, pSpellInfo, 1, SPELL_FAILED_NOT_HERE); + + return true; + } + + return false; +} + +void AddSC_item_scripts() +{ + Script* pNewScript; + + pNewScript = new Script; + pNewScript->Name = "item_arcane_charges"; + pNewScript->pItemUse = &ItemUse_item_arcane_charges; + pNewScript->RegisterSelf(); + + pNewScript = new Script; + pNewScript->Name = "item_flying_machine"; + pNewScript->pItemUse = &ItemUse_item_flying_machine; + pNewScript->RegisterSelf(); + + pNewScript = new Script; + pNewScript->Name = "item_gor_dreks_ointment"; + pNewScript->pItemUse = &ItemUse_item_gor_dreks_ointment; + pNewScript->RegisterSelf(); + + pNewScript = new Script; + pNewScript->Name = "item_petrov_cluster_bombs"; + pNewScript->pItemUse = &ItemUse_item_petrov_cluster_bombs; + pNewScript->RegisterSelf(); +} diff --git a/src/modules/SD2/scripts/world/mob_generic_creature.cpp b/src/modules/SD2/scripts/world/mob_generic_creature.cpp new file mode 100644 index 000000000..d61cc14e7 --- /dev/null +++ b/src/modules/SD2/scripts/world/mob_generic_creature.cpp @@ -0,0 +1,195 @@ +/** + * ScriptDev2 is an extension for mangos providing enhanced features for + * area triggers, creatures, game objects, instances, items, and spells beyond + * the default database scripting in mangos. + * + * Copyright (C) 2006-2013 ScriptDev2 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * World of Warcraft, and all World of Warcraft or Warcraft art, images, + * and lore are copyrighted by Blizzard Entertainment, Inc. + */ + +/** + * 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 generic_creatureAI : public ScriptedAI +{ + generic_creatureAI(Creature* pCreature) : ScriptedAI(pCreature) {Reset();} + + uint32 GlobalCooldown; // This variable acts like the global cooldown that players have (1.5 seconds) + uint32 BuffTimer; // This variable keeps track of buffs + bool IsSelfRooted; + + void Reset() override + { + GlobalCooldown = 0; + BuffTimer = 0; // Rebuff as soon as we can + IsSelfRooted = false; + } + + void Aggro(Unit* who) override + { + if (!m_creature->CanReachWithMeleeAttack(who)) + { + IsSelfRooted = true; + } + } + + void UpdateAI(const uint32 diff) override + { + // 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 (!m_creature->SelectHostileTarget() || !m_creature->getVictim()) + { + return; + } + + // Return if we already cast a spell + if (m_creature->IsNonMeleeSpellCasted(false)) + { + return; + } + + // If we are within range melee the target + if (m_creature->CanReachWithMeleeAttack(m_creature->getVictim())) + { + // Make sure our attack is ready + if (m_creature->isAttackReady()) + { + bool Healing = false; + SpellEntry const* info = NULL; + + // Select a healing spell if less than 30% hp + if (m_creature->GetHealthPercent() < 30.0f) + { + info = SelectSpell(m_creature, -1, -1, SELECT_TARGET_ANY_FRIEND, 0, 0, 0, 0, SELECT_EFFECT_HEALING); + } + + // No healing spell available, select a hostile spell + if (info) { Healing = true; } + else { info = SelectSpell(m_creature->getVictim(), -1, -1, SELECT_TARGET_ANY_ENEMY, 0, 0, 0, 0, SELECT_EFFECT_DONTCARE); } + + // 50% chance if elite or higher, 20% chance if not, to replace our white hit with a spell + if (info && (rand() % (m_creature->GetCreatureInfo()->Rank > 1 ? 2 : 5) == 0) && !GlobalCooldown) + { + // Cast the spell + if (Healing) { DoCastSpell(m_creature, info); } + else { DoCastSpell(m_creature->getVictim(), info); } + + // Set our global cooldown + GlobalCooldown = GENERIC_CREATURE_COOLDOWN; + } + else { m_creature->AttackerStateUpdate(m_creature->getVictim()); } + + m_creature->resetAttackTimer(); + } + } + else + { + bool Healing = false; + SpellEntry const* info = NULL; + + // Select a healing spell if less than 30% hp ONLY 33% of the time + if (m_creature->GetHealthPercent() < 30.0f && !urand(0, 2)) + { + info = SelectSpell(m_creature, -1, -1, SELECT_TARGET_ANY_FRIEND, 0, 0, 0, 0, SELECT_EFFECT_HEALING); + } + + // No healing spell available, See if we can cast a ranged spell (Range must be greater than ATTACK_DISTANCE) + if (info) { Healing = true; } + else { info = SelectSpell(m_creature->getVictim(), -1, -1, SELECT_TARGET_ANY_ENEMY, 0, 0, ATTACK_DISTANCE, 0, SELECT_EFFECT_DONTCARE); } + + // Found a spell, check if we arn't on cooldown + if (info && !GlobalCooldown) + { + // If we are currently moving stop us and set the movement generator + if (!IsSelfRooted) + { + IsSelfRooted = true; + } + + // Cast spell + if (Healing) { DoCastSpell(m_creature, info); } + else { DoCastSpell(m_creature->getVictim(), info); } + + // Set our global cooldown + GlobalCooldown = GENERIC_CREATURE_COOLDOWN; + }// If no spells available and we arn't moving run to target + else if (IsSelfRooted) + { + // Cancel our current spell and then allow movement agian + m_creature->InterruptNonMeleeSpells(false); + IsSelfRooted = false; + } + } + } +}; + +CreatureAI* GetAI_generic_creature(Creature* pCreature) +{ + return new generic_creatureAI(pCreature); +} + +void AddSC_generic_creature() +{ + Script* pNewScript; + + pNewScript = new Script; + pNewScript->Name = "generic_creature"; + pNewScript->GetAI = &GetAI_generic_creature; + pNewScript->RegisterSelf(false); +} diff --git a/src/modules/SD2/scripts/world/npc_professions.cpp b/src/modules/SD2/scripts/world/npc_professions.cpp new file mode 100644 index 000000000..5a6ab39ae --- /dev/null +++ b/src/modules/SD2/scripts/world/npc_professions.cpp @@ -0,0 +1,1130 @@ +/** + * ScriptDev2 is an extension for mangos providing enhanced features for + * area triggers, creatures, game objects, instances, items, and spells beyond + * the default database scripting in mangos. + * + * Copyright (C) 2006-2013 ScriptDev2 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * World of Warcraft, and all World of Warcraft or Warcraft art, images, + * and lore are copyrighted by Blizzard Entertainment, Inc. + */ + +/** + * 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" + +/** + * Notes + * + * - 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 requires 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_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 + +/*### +# formulas to calculate unlearning cost +###*/ + +int32 GetLearningCost(Player* /*pPlayer*/) // tailor, alchemy +{ + return 200000; +} + +int32 GetUnlearnCostHigh(Player* /*pPlayer*/) // tailor, alchemy +{ + return 1500000; +} + +int32 GetUnlearnCostMedium(Player* pPlayer) // blacksmith, leatherwork +{ + uint32 level = pPlayer->getLevel(); + + if (level < 51) + { + return 250000; + } + else if (level < 66) + { + return 500000; + } + else + { + return 1000000; + } +} + +int32 GetUnlearnCostLow(Player* pPlayer) // blacksmith +{ + if (pPlayer->getLevel() < 66) + { + return 50000; + } + else + { + return 100000; + } +} + +/*### +# unlearning related profession spells +###*/ + +bool EquippedOk(Player* pPlayer, uint32 spellId) +{ + SpellEntry const* spell = GetSpellStore()->LookupEntry(spellId); + + if (!spell) + { + return false; + } + + for (int i = 0; i < 3; ++i) + { + SpellEffectEntry const* pSpellEffect = spell->GetSpellEffect(SpellEffectIndex(i)); + if (!pSpellEffect) + continue; + + uint32 reqSpell = pSpellEffect->EffectTriggerSpell; + if (!reqSpell) + { + continue; + } + + Item* pItem; + for (int j = EQUIPMENT_SLOT_START; j < EQUIPMENT_SLOT_END; ++j) + { + pItem = pPlayer->GetItemByPos(INVENTORY_SLOT_BAG_0, j); + if (pItem) + if (pItem->GetProto()->RequiredSpell == reqSpell) + { + // pPlayer has item equipped that require specialty. Not allow to unlearn, player has to unequip first + debug_log("SD2: player attempt to unlearn spell %u, but item %u is equipped.", reqSpell, pItem->GetProto()->ItemId); + return false; + } + } + } + return true; +} + +void ProfessionUnlearnSpells(Player* pPlayer, uint32 type) +{ + switch (type) + { + case 36436: // S_UNLEARN_WEAPON + pPlayer->removeSpell(36125); // Light Earthforged Blade + pPlayer->removeSpell(36128); // Light Emberforged Hammer + pPlayer->removeSpell(36126); // Light Skyforged Axe + break; + case 36435: // S_UNLEARN_ARMOR + pPlayer->removeSpell(36122); // Earthforged Leggings + pPlayer->removeSpell(36129); // Heavy Earthforged Breastplate + pPlayer->removeSpell(36130); // Stormforged Hauberk + pPlayer->removeSpell(34533); // Breastplate of Kings + pPlayer->removeSpell(34529); // Nether Chain Shirt + pPlayer->removeSpell(34534); // Bulwark of Kings + pPlayer->removeSpell(36257); // Bulwark of the Ancient Kings + pPlayer->removeSpell(36256); // Embrace of the Twisting Nether + pPlayer->removeSpell(34530); // Twisting Nether Chain Shirt + pPlayer->removeSpell(36124); // Windforged Leggings + break; + case 36441: // S_UNLEARN_HAMMER + pPlayer->removeSpell(36262); // Dragonstrike + pPlayer->removeSpell(34546); // Dragonmaw + pPlayer->removeSpell(34545); // Drakefist Hammer + pPlayer->removeSpell(36136); // Lavaforged Warhammer + pPlayer->removeSpell(34547); // Thunder + pPlayer->removeSpell(34567); // Deep Thunder + pPlayer->removeSpell(36263); // Stormherald + pPlayer->removeSpell(36137); // Great Earthforged Hammer + break; + case 36439: // S_UNLEARN_AXE + pPlayer->removeSpell(36260); // Wicked Edge of the Planes + pPlayer->removeSpell(34562); // Black Planar Edge + pPlayer->removeSpell(34541); // The Planar Edge + pPlayer->removeSpell(36134); // Stormforged Axe + pPlayer->removeSpell(36135); // Skyforged Great Axe + pPlayer->removeSpell(36261); // Bloodmoon + pPlayer->removeSpell(34543); // Lunar Crescent + pPlayer->removeSpell(34544); // Mooncleaver + break; + case 36438: // S_UNLEARN_SWORD + pPlayer->removeSpell(36258); // Blazefury + pPlayer->removeSpell(34537); // Blazeguard + pPlayer->removeSpell(34535); // Fireguard + pPlayer->removeSpell(36131); // Windforged Rapier + pPlayer->removeSpell(36133); // Stoneforged Claymore + pPlayer->removeSpell(34538); // Lionheart Blade + pPlayer->removeSpell(34540); // Lionheart Champion + pPlayer->removeSpell(36259); // Lionheart Executioner + break; + case 36434: // S_UNLEARN_DRAGON + pPlayer->removeSpell(36076); // Dragonstrike Leggings + pPlayer->removeSpell(36079); // Golden Dragonstrike Breastplate + pPlayer->removeSpell(35576); // Ebon Netherscale Belt + pPlayer->removeSpell(35577); // Ebon Netherscale Bracers + pPlayer->removeSpell(35575); // Ebon Netherscale Breastplate + pPlayer->removeSpell(35582); // Netherstrike Belt + pPlayer->removeSpell(35584); // Netherstrike Bracers + pPlayer->removeSpell(35580); // Netherstrike Breastplate + break; + case 36328: // S_UNLEARN_ELEMENTAL + pPlayer->removeSpell(36074); // Blackstorm Leggings + pPlayer->removeSpell(36077); // Primalstorm Breastplate + pPlayer->removeSpell(35590); // Primalstrike Belt + pPlayer->removeSpell(35591); // Primalstrike Bracers + pPlayer->removeSpell(35589); // Primalstrike Vest + break; + case 36433: // S_UNLEARN_TRIBAL + pPlayer->removeSpell(35585); // Windhawk Hauberk + pPlayer->removeSpell(35587); // Windhawk Belt + pPlayer->removeSpell(35588); // Windhawk Bracers + pPlayer->removeSpell(36075); // Wildfeather Leggings + pPlayer->removeSpell(36078); // Living Crystal Breastplate + break; + case 41299: // S_UNLEARN_SPELLFIRE + pPlayer->removeSpell(26752); // Spellfire Belt + pPlayer->removeSpell(26753); // Spellfire Gloves + pPlayer->removeSpell(26754); // Spellfire Robe + break; + case 41558: // S_UNLEARN_MOONCLOTH + pPlayer->removeSpell(26760); // Primal Mooncloth Belt + pPlayer->removeSpell(26761); // Primal Mooncloth Shoulders + pPlayer->removeSpell(26762); // Primal Mooncloth Robe + break; + case 41559: // S_UNLEARN_SHADOWEAVE + pPlayer->removeSpell(26756); // Frozen Shadoweave Shoulders + pPlayer->removeSpell(26757); // Frozen Shadoweave Boots + pPlayer->removeSpell(26758); // Frozen Shadoweave Robe + break; + } +} + +/*### +# 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()) + { + pPlayer->PrepareQuestMenu(pCreature->GetObjectGuid()); + } + if (pCreature->IsVendor()) + { + 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) + { + switch (eCreature) + { + case 11145: // Myolor Sunderfury + case 11176: // Krathok Moltenfist + if (!pPlayer->HasSpell(S_ARMOR) && !pPlayer->HasSpell(S_WEAPON) && pPlayer->GetReputationRank(REP_ARMOR) == REP_FRIENDLY) + { + pPlayer->ADD_GOSSIP_ITEM(GOSSIP_ICON_CHAT, GOSSIP_ARMOR_LEARN, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 1); + } + if (!pPlayer->HasSpell(S_WEAPON) && !pPlayer->HasSpell(S_ARMOR) && pPlayer->GetReputationRank(REP_WEAPON) == REP_FRIENDLY) + { + pPlayer->ADD_GOSSIP_ITEM(GOSSIP_ICON_CHAT, GOSSIP_WEAPON_LEARN, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 2); + } + break; + case 11146: // Ironus Coldsteel + case 11178: // Borgosh Corebender + if (pPlayer->HasSpell(S_WEAPON)) + { + pPlayer->ADD_GOSSIP_ITEM(GOSSIP_ICON_CHAT, GOSSIP_WEAPON_UNLEARN, GOSSIP_SENDER_UNLEARN, GOSSIP_ACTION_INFO_DEF + 3); + } + break; + case 5164: // Grumnus Steelshaper + case 11177: // Okothos Ironrager + if (pPlayer->HasSpell(S_ARMOR)) + { + pPlayer->ADD_GOSSIP_ITEM(GOSSIP_ICON_CHAT, GOSSIP_ARMOR_UNLEARN, GOSSIP_SENDER_UNLEARN, GOSSIP_ACTION_INFO_DEF + 4); + } + break; + } + } + // WEAPONSMITH SPEC + if (pPlayer->HasSpell(S_WEAPON) && pPlayer->getLevel() > 49 && pPlayer->GetBaseSkillValue(SKILL_BLACKSMITHING) >= 250) + { + switch (eCreature) + { + case 11191: // Lilith the Lithe + if (!HasWeaponSub(pPlayer)) + { + pPlayer->ADD_GOSSIP_ITEM(GOSSIP_ICON_CHAT, GOSSIP_LEARN_HAMMER, GOSSIP_SENDER_LEARN, GOSSIP_ACTION_INFO_DEF + 5); + } + if (pPlayer->HasSpell(S_HAMMER)) + { + pPlayer->ADD_GOSSIP_ITEM(GOSSIP_ICON_CHAT, GOSSIP_UNLEARN_HAMMER, GOSSIP_SENDER_UNLEARN, GOSSIP_ACTION_INFO_DEF + 8); + } + break; + case 11192: // Kilram + if (!HasWeaponSub(pPlayer)) + { + pPlayer->ADD_GOSSIP_ITEM(GOSSIP_ICON_CHAT, GOSSIP_LEARN_AXE, GOSSIP_SENDER_LEARN, GOSSIP_ACTION_INFO_DEF + 6); + } + if (pPlayer->HasSpell(S_AXE)) + { + pPlayer->ADD_GOSSIP_ITEM(GOSSIP_ICON_CHAT, GOSSIP_UNLEARN_AXE, GOSSIP_SENDER_UNLEARN, GOSSIP_ACTION_INFO_DEF + 9); + } + break; + case 11193: // Seril Scourgebane + if (!HasWeaponSub(pPlayer)) + { + pPlayer->ADD_GOSSIP_ITEM(GOSSIP_ICON_CHAT, GOSSIP_LEARN_SWORD, GOSSIP_SENDER_LEARN, GOSSIP_ACTION_INFO_DEF + 7); + } + if (pPlayer->HasSpell(S_SWORD)) + { + pPlayer->ADD_GOSSIP_ITEM(GOSSIP_ICON_CHAT, GOSSIP_UNLEARN_SWORD, GOSSIP_SENDER_UNLEARN, GOSSIP_ACTION_INFO_DEF + 10); + } + break; + } + } + + pPlayer->SEND_GOSSIP_MENU(pPlayer->GetGossipTextId(pCreature), pCreature->GetObjectGuid()); + return true; +} + +void SendActionMenu_npc_prof_blacksmith(Player* pPlayer, Creature* pCreature, uint32 uiAction) +{ + switch (uiAction) + { + case GOSSIP_ACTION_TRADE: + pPlayer->SEND_VENDORLIST(pCreature->GetObjectGuid()); + break; + case GOSSIP_ACTION_TRAIN: + pPlayer->SEND_TRAINERLIST(pCreature->GetObjectGuid()); + break; + // Learn Armor/Weapon + case GOSSIP_ACTION_INFO_DEF + 1: + if (!pPlayer->HasSpell(S_ARMOR)) + { + pPlayer->CastSpell(pPlayer, S_LEARN_ARMOR, true); + // pCreature->CastSpell(pPlayer, S_REP_ARMOR, true); + } + pPlayer->CLOSE_GOSSIP_MENU(); + break; + case GOSSIP_ACTION_INFO_DEF + 2: + if (!pPlayer->HasSpell(S_WEAPON)) + { + pPlayer->CastSpell(pPlayer, S_LEARN_WEAPON, true); + // pCreature->CastSpell(pPlayer, S_REP_WEAPON, true); + } + pPlayer->CLOSE_GOSSIP_MENU(); + break; + // Unlearn Armor/Weapon + case GOSSIP_ACTION_INFO_DEF + 3: + if (HasWeaponSub(pPlayer)) + { + // unknown textID (TALK_MUST_UNLEARN_WEAPON) + pPlayer->SEND_GOSSIP_MENU(pPlayer->GetGossipTextId(pCreature), pCreature->GetObjectGuid()); + } + else if (EquippedOk(pPlayer, S_UNLEARN_WEAPON)) + { + if (pPlayer->GetMoney() >= uint32(GetUnlearnCostLow(pPlayer))) + { + pPlayer->CastSpell(pPlayer, S_UNLEARN_WEAPON, true); + ProfessionUnlearnSpells(pPlayer, S_UNLEARN_WEAPON); + pPlayer->ModifyMoney(-GetUnlearnCostLow(pPlayer)); + pCreature->CastSpell(pPlayer, S_REP_ARMOR, true); + pPlayer->CLOSE_GOSSIP_MENU(); + } + else + { + pPlayer->SendBuyError(BUY_ERR_NOT_ENOUGHT_MONEY, pCreature, 0, 0); + } + } + else + { + pPlayer->SendEquipError(EQUIP_ERR_CANT_DO_RIGHT_NOW, NULL, NULL); + pPlayer->CLOSE_GOSSIP_MENU(); + } + break; + case GOSSIP_ACTION_INFO_DEF + 4: + if (EquippedOk(pPlayer, S_UNLEARN_ARMOR)) + { + if (pPlayer->GetMoney() >= uint32(GetUnlearnCostLow(pPlayer))) + { + pPlayer->CastSpell(pPlayer, S_UNLEARN_ARMOR, true); + ProfessionUnlearnSpells(pPlayer, S_UNLEARN_ARMOR); + pPlayer->ModifyMoney(-GetUnlearnCostLow(pPlayer)); + pCreature->CastSpell(pPlayer, S_REP_WEAPON, true); + } + else + { + pPlayer->SendBuyError(BUY_ERR_NOT_ENOUGHT_MONEY, pCreature, 0, 0); + } + } + else + { + pPlayer->SendEquipError(EQUIP_ERR_CANT_DO_RIGHT_NOW, NULL, NULL); + } + pPlayer->CLOSE_GOSSIP_MENU(); + break; + // Learn Hammer/Axe/Sword + case GOSSIP_ACTION_INFO_DEF + 5: + pPlayer->CastSpell(pPlayer, S_LEARN_HAMMER, true); + pPlayer->CLOSE_GOSSIP_MENU(); + break; + case GOSSIP_ACTION_INFO_DEF + 6: + pPlayer->CastSpell(pPlayer, S_LEARN_AXE, true); + pPlayer->CLOSE_GOSSIP_MENU(); + break; + case GOSSIP_ACTION_INFO_DEF + 7: + pPlayer->CastSpell(pPlayer, S_LEARN_SWORD, true); + pPlayer->CLOSE_GOSSIP_MENU(); + break; + // Unlearn Hammer/Axe/Sword + case GOSSIP_ACTION_INFO_DEF + 8: + if (EquippedOk(pPlayer, S_UNLEARN_HAMMER)) + { + if (pPlayer->GetMoney() >= uint32(GetUnlearnCostMedium(pPlayer))) + { + pPlayer->CastSpell(pPlayer, S_UNLEARN_HAMMER, true); + ProfessionUnlearnSpells(pPlayer, S_UNLEARN_HAMMER); + pPlayer->ModifyMoney(-GetUnlearnCostMedium(pPlayer)); + } + else + { + pPlayer->SendBuyError(BUY_ERR_NOT_ENOUGHT_MONEY, pCreature, 0, 0); + } + } + else + { + pPlayer->SendEquipError(EQUIP_ERR_CANT_DO_RIGHT_NOW, NULL, NULL); + } + pPlayer->CLOSE_GOSSIP_MENU(); + break; + case GOSSIP_ACTION_INFO_DEF + 9: + if (EquippedOk(pPlayer, S_UNLEARN_AXE)) + { + if (pPlayer->GetMoney() >= uint32(GetUnlearnCostMedium(pPlayer))) + { + pPlayer->CastSpell(pPlayer, S_UNLEARN_AXE, true); + ProfessionUnlearnSpells(pPlayer, S_UNLEARN_AXE); + pPlayer->ModifyMoney(-GetUnlearnCostMedium(pPlayer)); + } + else + { + pPlayer->SendBuyError(BUY_ERR_NOT_ENOUGHT_MONEY, pCreature, 0, 0); + } + } + else + { + pPlayer->SendEquipError(EQUIP_ERR_CANT_DO_RIGHT_NOW, NULL, NULL); + } + pPlayer->CLOSE_GOSSIP_MENU(); + break; + case GOSSIP_ACTION_INFO_DEF + 10: + if (EquippedOk(pPlayer, S_UNLEARN_SWORD)) + { + if (pPlayer->GetMoney() >= uint32(GetUnlearnCostMedium(pPlayer))) + { + pPlayer->CastSpell(pPlayer, S_UNLEARN_SWORD, true); + ProfessionUnlearnSpells(pPlayer, S_UNLEARN_SWORD); + pPlayer->ModifyMoney(-GetUnlearnCostMedium(pPlayer)); + } + else + { + pPlayer->SendBuyError(BUY_ERR_NOT_ENOUGHT_MONEY, pCreature, 0, 0); + } + } + else + { + pPlayer->SendEquipError(EQUIP_ERR_CANT_DO_RIGHT_NOW, NULL, NULL); + } + pPlayer->CLOSE_GOSSIP_MENU(); + break; + } +} + +void SendConfirmLearn_npc_prof_blacksmith(Player* pPlayer, Creature* pCreature, uint32 uiAction) +{ + if (uiAction) + { + uint32 eCreature = pCreature->GetEntry(); + switch (eCreature) + { + case 11191: + pPlayer->ADD_GOSSIP_ITEM(GOSSIP_ICON_CHAT, GOSSIP_LEARN_HAMMER, GOSSIP_SENDER_CHECK, uiAction); + // unknown textID (TALK_HAMMER_LEARN) + pPlayer->SEND_GOSSIP_MENU(pPlayer->GetGossipTextId(pCreature), pCreature->GetObjectGuid()); + break; + case 11192: + pPlayer->ADD_GOSSIP_ITEM(GOSSIP_ICON_CHAT, GOSSIP_LEARN_AXE, GOSSIP_SENDER_CHECK, uiAction); + // unknown textID (TALK_AXE_LEARN) + pPlayer->SEND_GOSSIP_MENU(pPlayer->GetGossipTextId(pCreature), pCreature->GetObjectGuid()); + break; + case 11193: + pPlayer->ADD_GOSSIP_ITEM(GOSSIP_ICON_CHAT, GOSSIP_LEARN_SWORD, GOSSIP_SENDER_CHECK, uiAction); + // unknown textID (TALK_SWORD_LEARN) + pPlayer->SEND_GOSSIP_MENU(pPlayer->GetGossipTextId(pCreature), pCreature->GetObjectGuid()); + break; + } + } +} + +void SendConfirmUnlearn_npc_prof_blacksmith(Player* pPlayer, Creature* pCreature, uint32 uiAction) +{ + if (uiAction) + { + uint32 eCreature = pCreature->GetEntry(); + switch (eCreature) + { + case 11146: // Ironus Coldsteel + case 11178: // Borgosh Corebender + case 5164: // Grumnus Steelshaper + case 11177: // Okothos Ironrager + pPlayer->ADD_GOSSIP_ITEM_EXTENDED(0, GOSSIP_UNLEARN_SMITH_SPEC, GOSSIP_SENDER_CHECK, uiAction, BOX_UNLEARN_ARMORORWEAPON, GetUnlearnCostLow(pPlayer), false); + // unknown textID (TALK_UNLEARN_AXEORWEAPON) + pPlayer->SEND_GOSSIP_MENU(pPlayer->GetGossipTextId(pCreature), pCreature->GetObjectGuid()); + break; + + case 11191: + pPlayer->ADD_GOSSIP_ITEM_EXTENDED(0, GOSSIP_UNLEARN_HAMMER, GOSSIP_SENDER_CHECK, uiAction, BOX_UNLEARN_WEAPON_SPEC, GetUnlearnCostMedium(pPlayer), false); + // unknown textID (TALK_HAMMER_UNLEARN) + pPlayer->SEND_GOSSIP_MENU(pPlayer->GetGossipTextId(pCreature), pCreature->GetObjectGuid()); + break; + case 11192: + pPlayer->ADD_GOSSIP_ITEM_EXTENDED(0, GOSSIP_UNLEARN_AXE, GOSSIP_SENDER_CHECK, uiAction, BOX_UNLEARN_WEAPON_SPEC, GetUnlearnCostMedium(pPlayer), false); + // unknown textID (TALK_AXE_UNLEARN) + pPlayer->SEND_GOSSIP_MENU(pPlayer->GetGossipTextId(pCreature), pCreature->GetObjectGuid()); + break; + case 11193: + pPlayer->ADD_GOSSIP_ITEM_EXTENDED(0, GOSSIP_UNLEARN_SWORD, GOSSIP_SENDER_CHECK, uiAction, BOX_UNLEARN_WEAPON_SPEC, GetUnlearnCostMedium(pPlayer), false); + // unknown textID (TALK_SWORD_UNLEARN) + pPlayer->SEND_GOSSIP_MENU(pPlayer->GetGossipTextId(pCreature), pCreature->GetObjectGuid()); + break; + } + } +} + +bool GossipSelect_npc_prof_blacksmith(Player* pPlayer, Creature* pCreature, uint32 uiSender, uint32 uiAction) +{ + switch (uiSender) + { + case GOSSIP_SENDER_MAIN: + SendActionMenu_npc_prof_blacksmith(pPlayer, pCreature, uiAction); + break; + case GOSSIP_SENDER_LEARN: + SendConfirmLearn_npc_prof_blacksmith(pPlayer, pCreature, uiAction); + break; + case GOSSIP_SENDER_UNLEARN: + SendConfirmUnlearn_npc_prof_blacksmith(pPlayer, pCreature, uiAction); + break; + case GOSSIP_SENDER_CHECK: + SendActionMenu_npc_prof_blacksmith(pPlayer, pCreature, uiAction); + break; + } + return true; +} + +/*bool QuestComplete_npc_prof_blacksmith(Player* pPlayer, Creature* pCreature, const Quest* pQuest) +{ + if ((pQuest->GetQuestId() == 5283) || (pQuest->GetQuestId() == 5301)) // armorsmith + pCreature->CastSpell(pPlayer, 17451, true); + + if ((pQuest->GetQuestId() == 5284) || (pQuest->GetQuestId() == 5302)) // weaponsmith + pCreature->CastSpell(pPlayer, 17452, true); + + return true; +}*/ + +/*### +# start menues leatherworking +###*/ + +bool GossipHello_npc_prof_leather(Player* pPlayer, Creature* pCreature) +{ + if (pCreature->IsQuestGiver()) + { + pPlayer->PrepareQuestMenu(pCreature->GetObjectGuid()); + } + if (pCreature->IsVendor()) + { + 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) + { + case 7866: // Peter Galen + case 7867: // Thorkaf Dragoneye + if (pPlayer->HasSpell(S_DRAGON)) + { + pPlayer->ADD_GOSSIP_ITEM(GOSSIP_ICON_CHAT, GOSSIP_UNLEARN_DRAGON, GOSSIP_SENDER_UNLEARN, GOSSIP_ACTION_INFO_DEF + 1); + } + break; + case 7868: // Sarah Tanner + case 7869: // Brumn Winterhoof + if (pPlayer->HasSpell(S_ELEMENTAL)) + { + pPlayer->ADD_GOSSIP_ITEM(GOSSIP_ICON_CHAT, GOSSIP_UNLEARN_ELEMENTAL, GOSSIP_SENDER_UNLEARN, GOSSIP_ACTION_INFO_DEF + 2); + } + break; + case 7870: // Caryssia Moonhunter + case 7871: // Se'Jib + if (pPlayer->HasSpell(S_TRIBAL)) + { + pPlayer->ADD_GOSSIP_ITEM(GOSSIP_ICON_CHAT, GOSSIP_UNLEARN_TRIBAL, GOSSIP_SENDER_UNLEARN, GOSSIP_ACTION_INFO_DEF + 3); + } + break; + } + } + + pPlayer->SEND_GOSSIP_MENU(pPlayer->GetGossipTextId(pCreature), pCreature->GetObjectGuid()); + return true; +} + +void SendActionMenu_npc_prof_leather(Player* pPlayer, Creature* pCreature, uint32 uiAction) +{ + switch (uiAction) + { + case GOSSIP_ACTION_TRADE: + pPlayer->SEND_VENDORLIST(pCreature->GetObjectGuid()); + break; + case GOSSIP_ACTION_TRAIN: + pPlayer->SEND_TRAINERLIST(pCreature->GetObjectGuid()); + break; + // Unlearn Leather + case GOSSIP_ACTION_INFO_DEF + 1: + if (EquippedOk(pPlayer, S_UNLEARN_DRAGON)) + { + if (pPlayer->GetMoney() >= uint32(GetUnlearnCostMedium(pPlayer))) + { + pPlayer->CastSpell(pPlayer, S_UNLEARN_DRAGON, true); + ProfessionUnlearnSpells(pPlayer, S_UNLEARN_DRAGON); + pPlayer->ModifyMoney(-GetUnlearnCostMedium(pPlayer)); + } + else + { + pPlayer->SendBuyError(BUY_ERR_NOT_ENOUGHT_MONEY, pCreature, 0, 0); + } + } + else + { + pPlayer->SendEquipError(EQUIP_ERR_CANT_DO_RIGHT_NOW, NULL, NULL); + } + pPlayer->CLOSE_GOSSIP_MENU(); + break; + case GOSSIP_ACTION_INFO_DEF + 2: + if (EquippedOk(pPlayer, S_UNLEARN_ELEMENTAL)) + { + if (pPlayer->GetMoney() >= uint32(GetUnlearnCostMedium(pPlayer))) + { + pPlayer->CastSpell(pPlayer, S_UNLEARN_ELEMENTAL, true); + ProfessionUnlearnSpells(pPlayer, S_UNLEARN_ELEMENTAL); + pPlayer->ModifyMoney(-GetUnlearnCostMedium(pPlayer)); + } + else + { + pPlayer->SendBuyError(BUY_ERR_NOT_ENOUGHT_MONEY, pCreature, 0, 0); + } + } + else + { + pPlayer->SendEquipError(EQUIP_ERR_CANT_DO_RIGHT_NOW, NULL, NULL); + } + pPlayer->CLOSE_GOSSIP_MENU(); + break; + case GOSSIP_ACTION_INFO_DEF + 3: + if (EquippedOk(pPlayer, S_UNLEARN_TRIBAL)) + { + if (pPlayer->GetMoney() >= uint32(GetUnlearnCostMedium(pPlayer))) + { + pPlayer->CastSpell(pPlayer, S_UNLEARN_TRIBAL, true); + ProfessionUnlearnSpells(pPlayer, S_UNLEARN_TRIBAL); + pPlayer->ModifyMoney(-GetUnlearnCostMedium(pPlayer)); + } + else + { + pPlayer->SendBuyError(BUY_ERR_NOT_ENOUGHT_MONEY, pCreature, 0, 0); + } + } + else + { + pPlayer->SendEquipError(EQUIP_ERR_CANT_DO_RIGHT_NOW, NULL, NULL); + } + pPlayer->CLOSE_GOSSIP_MENU(); + break; + } +} + +void SendConfirmUnlearn_npc_prof_leather(Player* pPlayer, Creature* pCreature, uint32 uiAction) +{ + if (uiAction) + { + uint32 eCreature = pCreature->GetEntry(); + switch (eCreature) + { + case 7866: // Peter Galen + case 7867: // Thorkaf Dragoneye + pPlayer->ADD_GOSSIP_ITEM_EXTENDED(0, GOSSIP_UNLEARN_DRAGON, GOSSIP_SENDER_CHECK, uiAction, BOX_UNLEARN_LEATHER_SPEC, GetUnlearnCostMedium(pPlayer), false); + // unknown textID () + pPlayer->SEND_GOSSIP_MENU(pPlayer->GetGossipTextId(pCreature), pCreature->GetObjectGuid()); + break; + case 7868: // Sarah Tanner + case 7869: // Brumn Winterhoof + pPlayer->ADD_GOSSIP_ITEM_EXTENDED(0, GOSSIP_UNLEARN_ELEMENTAL, GOSSIP_SENDER_CHECK, uiAction, BOX_UNLEARN_LEATHER_SPEC, GetUnlearnCostMedium(pPlayer), false); + // unknown textID () + pPlayer->SEND_GOSSIP_MENU(pPlayer->GetGossipTextId(pCreature), pCreature->GetObjectGuid()); + break; + case 7870: // Caryssia Moonhunter + case 7871: // Se'Jib + pPlayer->ADD_GOSSIP_ITEM_EXTENDED(0, GOSSIP_UNLEARN_TRIBAL, GOSSIP_SENDER_CHECK, uiAction, BOX_UNLEARN_LEATHER_SPEC, GetUnlearnCostMedium(pPlayer), false); + // unknown textID () + pPlayer->SEND_GOSSIP_MENU(pPlayer->GetGossipTextId(pCreature), pCreature->GetObjectGuid()); + break; + } + } +} + +bool GossipSelect_npc_prof_leather(Player* pPlayer, Creature* pCreature, uint32 uiSender, uint32 uiAction) +{ + switch (uiSender) + { + case GOSSIP_SENDER_MAIN: + SendActionMenu_npc_prof_leather(pPlayer, pCreature, uiAction); + break; + case GOSSIP_SENDER_UNLEARN: + SendConfirmUnlearn_npc_prof_leather(pPlayer, pCreature, uiAction); + break; + case GOSSIP_SENDER_CHECK: + SendActionMenu_npc_prof_leather(pPlayer, pCreature, uiAction); + break; + } + 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()) + { pPlayer->PrepareQuestMenu(pCreature->GetObjectGuid()); } + if (pCreature->IsVendor()) + { 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) + { + if (pPlayer->GetQuestRewardStatus(10831) || pPlayer->GetQuestRewardStatus(10832) || pPlayer->GetQuestRewardStatus(10833)) + { + switch (eCreature) + { + case 22213: // Gidge Spellweaver + if (!HasTailorSpell(pPlayer)) + { pPlayer->ADD_GOSSIP_ITEM(GOSSIP_ICON_CHAT, GOSSIP_LEARN_SPELLFIRE, GOSSIP_SENDER_LEARN, GOSSIP_ACTION_INFO_DEF + 1); } + if (pPlayer->HasSpell(S_SPELLFIRE)) + { pPlayer->ADD_GOSSIP_ITEM(GOSSIP_ICON_CHAT, GOSSIP_UNLEARN_SPELLFIRE, GOSSIP_SENDER_UNLEARN, GOSSIP_ACTION_INFO_DEF + 4); } + break; + case 22208: // Nasmara Moonsong + if (!HasTailorSpell(pPlayer)) + { pPlayer->ADD_GOSSIP_ITEM(GOSSIP_ICON_CHAT, GOSSIP_LEARN_MOONCLOTH, GOSSIP_SENDER_LEARN, GOSSIP_ACTION_INFO_DEF + 2); } + if (pPlayer->HasSpell(S_MOONCLOTH)) + { pPlayer->ADD_GOSSIP_ITEM(GOSSIP_ICON_CHAT, GOSSIP_UNLEARN_MOONCLOTH, GOSSIP_SENDER_UNLEARN, GOSSIP_ACTION_INFO_DEF + 5); } + break; + case 22212: // Andrion Darkspinner + if (!HasTailorSpell(pPlayer)) + { pPlayer->ADD_GOSSIP_ITEM(GOSSIP_ICON_CHAT, GOSSIP_LEARN_SHADOWEAVE, GOSSIP_SENDER_LEARN, GOSSIP_ACTION_INFO_DEF + 3); } + if (pPlayer->HasSpell(S_SHADOWEAVE)) + { pPlayer->ADD_GOSSIP_ITEM(GOSSIP_ICON_CHAT, GOSSIP_UNLEARN_SHADOWEAVE, GOSSIP_SENDER_UNLEARN, GOSSIP_ACTION_INFO_DEF + 6); } + break; + } + } + } + + pPlayer->SEND_GOSSIP_MENU(pPlayer->GetGossipTextId(pCreature), pCreature->GetObjectGuid()); + return true; +} + +void SendActionMenu_npc_prof_tailor(Player* pPlayer, Creature* pCreature, uint32 uiAction) +{ + switch (uiAction) + { + case GOSSIP_ACTION_TRADE: + pPlayer->SEND_VENDORLIST(pCreature->GetObjectGuid()); + break; + case GOSSIP_ACTION_TRAIN: + pPlayer->SEND_TRAINERLIST(pCreature->GetObjectGuid()); + break; + // Learn Tailor + case GOSSIP_ACTION_INFO_DEF + 1: + if (!pPlayer->HasSpell(S_SPELLFIRE) && pPlayer->GetMoney() >= uint32(GetLearningCost(pPlayer))) + { + pPlayer->CastSpell(pPlayer, S_LEARN_SPELLFIRE, true); + pPlayer->ModifyMoney(-GetLearningCost(pPlayer)); + } + else + { pPlayer->SendBuyError(BUY_ERR_NOT_ENOUGHT_MONEY, pCreature, 0, 0); } + pPlayer->CLOSE_GOSSIP_MENU(); + break; + case GOSSIP_ACTION_INFO_DEF + 2: + if (!pPlayer->HasSpell(S_MOONCLOTH) && pPlayer->GetMoney() >= uint32(GetLearningCost(pPlayer))) + { + pPlayer->CastSpell(pPlayer, S_LEARN_MOONCLOTH, true); + pPlayer->ModifyMoney(-GetLearningCost(pPlayer)); + } + else + { pPlayer->SendBuyError(BUY_ERR_NOT_ENOUGHT_MONEY, pCreature, 0, 0); } + pPlayer->CLOSE_GOSSIP_MENU(); + break; + case GOSSIP_ACTION_INFO_DEF + 3: + if (!pPlayer->HasSpell(S_SHADOWEAVE) && pPlayer->GetMoney() >= uint32(GetLearningCost(pPlayer))) + { + pPlayer->CastSpell(pPlayer, S_LEARN_SHADOWEAVE, true); + pPlayer->ModifyMoney(-GetLearningCost(pPlayer)); + } + else + { pPlayer->SendBuyError(BUY_ERR_NOT_ENOUGHT_MONEY, pCreature, 0, 0); } + pPlayer->CLOSE_GOSSIP_MENU(); + break; + // Unlearn Tailor + case GOSSIP_ACTION_INFO_DEF + 4: + if (EquippedOk(pPlayer, S_UNLEARN_SPELLFIRE)) + { + if (pPlayer->GetMoney() >= uint32(GetUnlearnCostHigh(pPlayer))) + { + pPlayer->CastSpell(pPlayer, S_UNLEARN_SPELLFIRE, true); + ProfessionUnlearnSpells(pPlayer, S_UNLEARN_SPELLFIRE); + pPlayer->ModifyMoney(-GetUnlearnCostHigh(pPlayer)); + } + else + { pPlayer->SendBuyError(BUY_ERR_NOT_ENOUGHT_MONEY, pCreature, 0, 0); } + } + else + { pPlayer->SendEquipError(EQUIP_ERR_CANT_DO_RIGHT_NOW, NULL, NULL); } + pPlayer->CLOSE_GOSSIP_MENU(); + break; + case GOSSIP_ACTION_INFO_DEF + 5: + if (EquippedOk(pPlayer, S_UNLEARN_MOONCLOTH)) + { + if (pPlayer->GetMoney() >= uint32(GetUnlearnCostHigh(pPlayer))) + { + pPlayer->CastSpell(pPlayer, S_UNLEARN_MOONCLOTH, true); + ProfessionUnlearnSpells(pPlayer, S_UNLEARN_MOONCLOTH); + pPlayer->ModifyMoney(-GetUnlearnCostHigh(pPlayer)); + } + else + { pPlayer->SendBuyError(BUY_ERR_NOT_ENOUGHT_MONEY, pCreature, 0, 0); } + } + else + { pPlayer->SendEquipError(EQUIP_ERR_CANT_DO_RIGHT_NOW, NULL, NULL); } + pPlayer->CLOSE_GOSSIP_MENU(); + break; + case GOSSIP_ACTION_INFO_DEF + 6: + if (EquippedOk(pPlayer, S_UNLEARN_SHADOWEAVE)) + { + if (pPlayer->GetMoney() >= uint32(GetUnlearnCostHigh(pPlayer))) + { + pPlayer->CastSpell(pPlayer, S_UNLEARN_SHADOWEAVE, true); + ProfessionUnlearnSpells(pPlayer, S_UNLEARN_SHADOWEAVE); + pPlayer->ModifyMoney(-GetUnlearnCostHigh(pPlayer)); + } + else + { pPlayer->SendBuyError(BUY_ERR_NOT_ENOUGHT_MONEY, pCreature, 0, 0); } + } + else + { pPlayer->SendEquipError(EQUIP_ERR_CANT_DO_RIGHT_NOW, NULL, NULL); } + pPlayer->CLOSE_GOSSIP_MENU(); + break; + } +} + +void SendConfirmLearn_npc_prof_tailor(Player* pPlayer, Creature* pCreature, uint32 uiAction) +{ + if (uiAction) + { + uint32 eCreature = pCreature->GetEntry(); + switch (eCreature) + { + case 22213: + pPlayer->ADD_GOSSIP_ITEM(GOSSIP_ICON_CHAT, GOSSIP_LEARN_SPELLFIRE, GOSSIP_SENDER_CHECK, uiAction); + // unknown textID () + pPlayer->SEND_GOSSIP_MENU(pPlayer->GetGossipTextId(pCreature), pCreature->GetObjectGuid()); + break; + case 22208: + pPlayer->ADD_GOSSIP_ITEM(GOSSIP_ICON_CHAT, GOSSIP_LEARN_MOONCLOTH, GOSSIP_SENDER_CHECK, uiAction); + // unknown textID () + pPlayer->SEND_GOSSIP_MENU(pPlayer->GetGossipTextId(pCreature), pCreature->GetObjectGuid()); + break; + case 22212: + pPlayer->ADD_GOSSIP_ITEM(GOSSIP_ICON_CHAT, GOSSIP_LEARN_SHADOWEAVE, GOSSIP_SENDER_CHECK, uiAction); + // unknown textID () + pPlayer->SEND_GOSSIP_MENU(pPlayer->GetGossipTextId(pCreature), pCreature->GetObjectGuid()); + break; + } + } +} + +void SendConfirmUnlearn_npc_prof_tailor(Player* pPlayer, Creature* pCreature, uint32 uiAction) +{ + if (uiAction) + { + uint32 eCreature = pCreature->GetEntry(); + switch (eCreature) + { + case 22213: // Gidge Spellweaver + pPlayer->ADD_GOSSIP_ITEM_EXTENDED(0, GOSSIP_UNLEARN_SPELLFIRE, GOSSIP_SENDER_CHECK, uiAction, BOX_UNLEARN_TAILOR_SPEC, GetUnlearnCostHigh(pPlayer), false); + // unknown textID () + pPlayer->SEND_GOSSIP_MENU(pPlayer->GetGossipTextId(pCreature), pCreature->GetObjectGuid()); + break; + case 22208: // Nasmara Moonsong + pPlayer->ADD_GOSSIP_ITEM_EXTENDED(0, GOSSIP_UNLEARN_MOONCLOTH, GOSSIP_SENDER_CHECK, uiAction, BOX_UNLEARN_TAILOR_SPEC, GetUnlearnCostHigh(pPlayer), false); + // unknown textID () + pPlayer->SEND_GOSSIP_MENU(pPlayer->GetGossipTextId(pCreature), pCreature->GetObjectGuid()); + break; + case 22212: // Andrion Darkspinner + pPlayer->ADD_GOSSIP_ITEM_EXTENDED(0, GOSSIP_UNLEARN_SHADOWEAVE, GOSSIP_SENDER_CHECK, uiAction, BOX_UNLEARN_TAILOR_SPEC, GetUnlearnCostHigh(pPlayer), false); + // unknown textID () + pPlayer->SEND_GOSSIP_MENU(pPlayer->GetGossipTextId(pCreature), pCreature->GetObjectGuid()); + break; + } + } +} + +bool GossipSelect_npc_prof_tailor(Player* pPlayer, Creature* pCreature, uint32 uiSender, uint32 uiAction) +{ + switch (uiSender) + { + case GOSSIP_SENDER_MAIN: SendActionMenu_npc_prof_tailor(pPlayer, pCreature, uiAction); break; + case GOSSIP_SENDER_LEARN: SendConfirmLearn_npc_prof_tailor(pPlayer, pCreature, uiAction); break; + case GOSSIP_SENDER_UNLEARN: SendConfirmUnlearn_npc_prof_tailor(pPlayer, pCreature, uiAction); break; + case GOSSIP_SENDER_CHECK: SendActionMenu_npc_prof_tailor(pPlayer, pCreature, uiAction); break; + } + return true; +} + +/*### +# start menues for GO (engineering and leatherworking) +###*/ + +/*bool GOUse_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->GetObjectGuid()); + + return true; +}*/ + +/*### +# +###*/ + +void AddSC_npc_professions() +{ + Script* pNewScript; + + pNewScript = new Script; + pNewScript->Name = "npc_prof_blacksmith"; + pNewScript->pGossipHello = &GossipHello_npc_prof_blacksmith; + pNewScript->pGossipSelect = &GossipSelect_npc_prof_blacksmith; + pNewScript->RegisterSelf(); + + pNewScript = new Script; + pNewScript->Name = "npc_prof_leather"; + pNewScript->pGossipHello = &GossipHello_npc_prof_leather; + pNewScript->pGossipSelect = &GossipSelect_npc_prof_leather; + pNewScript->RegisterSelf(); + + pNewScript = new Script; + pNewScript->Name = "npc_prof_tailor"; + pNewScript->pGossipHello = &GossipHello_npc_prof_tailor; + pNewScript->pGossipSelect = &GossipSelect_npc_prof_tailor; + pNewScript->RegisterSelf(); + + /*pNewScript = new Script; + pNewScript->Name = "go_soothsaying_for_dummies"; + pNewScript->pGOUse = &GOUse_go_soothsaying_for_dummies; + // pNewScript->pGossipSelect = &GossipSelect_go_soothsaying_for_dummies; + pNewScript->RegisterSelf();*/ +} diff --git a/src/modules/SD2/scripts/world/npcs_special.cpp b/src/modules/SD2/scripts/world/npcs_special.cpp new file mode 100644 index 000000000..8028b3f7b --- /dev/null +++ b/src/modules/SD2/scripts/world/npcs_special.cpp @@ -0,0 +1,1510 @@ +/** + * ScriptDev2 is an extension for mangos providing enhanced features for + * area triggers, creatures, game objects, instances, items, and spells beyond + * the default database scripting in mangos. + * + * Copyright (C) 2006-2013 ScriptDev2 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * World of Warcraft, and all World of Warcraft or Warcraft art, images, + * and lore are copyrighted by Blizzard Entertainment, Inc. + */ + +/** + * ScriptData + * SDName: Npcs_Special + * SD%Complete: 100 + * SDComment: To be used for special NPCs that are located globally. + * SDCategory: NPCs + * EndScriptData + */ + +#include "precompiled.h" +#include "escort_ai.h" +#include "pet_ai.h" +#include "ObjectMgr.h" +#include "GameEventMgr.h" + +/** + * 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!) + * npc_dancing_flames 100% midsummer event NPC + * npc_guardian 100% guardianAI used to prevent players from accessing off-limits areas. Not in use by SD2 + * npc_garments_of_quests 100% NPC's related to all Garments of-quests 5621, 5624, 5625, 5648, 5650 + * npc_injured_patient 80% patients for triage-quests (6622 and 6624) + * npc_doctor 100% Gustaf Vanhowzen and Gregory Victor, quest 6622 and 6624 (Triage) + * npc_innkeeper 25% ScriptName not assigned. Innkeepers in general. + * npc_spring_rabbit 1% Used for pet "Spring Rabbit" of Noblegarden + * npc_redemption_target 100% Used for the paladin quests: 1779,1781,9600,9685 + * EndContentData + */ + +/*######## +# 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 +{ + 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) + {2615, 15242, SPAWNTYPE_ALARMBOT}, // Air Force Alarm Bot (Horde) + {21974, 21976, SPAWNTYPE_ALARMBOT}, // Air Force Alarm Bot (Area 52) + {21993, 15242, SPAWNTYPE_ALARMBOT}, // Air Force Guard Post (Horde - Bat Rider) + {21996, 15241, SPAWNTYPE_ALARMBOT}, // Air Force Guard Post (Alliance - Gryphon) + {21997, 21976, SPAWNTYPE_ALARMBOT}, // Air Force Guard Post (Goblin - Area 52 - Zeppelin) + {21999, 15241, SPAWNTYPE_TRIPWIRE_ROOFTOP}, // Air Force Trip Wire - Rooftop (Alliance) + {22001, 15242, SPAWNTYPE_TRIPWIRE_ROOFTOP}, // Air Force Trip Wire - Rooftop (Horde) + {22002, 15242, SPAWNTYPE_TRIPWIRE_ROOFTOP}, // Air Force Trip Wire - Ground (Horde) + {22003, 15241, SPAWNTYPE_TRIPWIRE_ROOFTOP}, // Air Force Trip Wire - Ground (Alliance) + {22063, 21976, SPAWNTYPE_TRIPWIRE_ROOFTOP}, // Air Force Trip Wire - Rooftop (Goblin - Area 52) + {22065, 22064, SPAWNTYPE_ALARMBOT}, // Air Force Guard Post (Ethereal - Stormspire) + {22066, 22067, SPAWNTYPE_ALARMBOT}, // Air Force Guard Post (Scryer - Dragonhawk) + {22068, 22064, SPAWNTYPE_TRIPWIRE_ROOFTOP}, // Air Force Trip Wire - Rooftop (Ethereal - Stormspire) + {22069, 22064, SPAWNTYPE_ALARMBOT}, // Air Force Alarm Bot (Stormspire) + {22070, 22067, SPAWNTYPE_TRIPWIRE_ROOFTOP}, // Air Force Trip Wire - Rooftop (Scryer) + {22071, 22067, SPAWNTYPE_ALARMBOT}, // Air Force Alarm Bot (Scryer) + {22078, 22077, SPAWNTYPE_ALARMBOT}, // Air Force Alarm Bot (Aldor) + {22079, 22077, SPAWNTYPE_ALARMBOT}, // Air Force Guard Post (Aldor - Gryphon) + {22080, 22077, SPAWNTYPE_TRIPWIRE_ROOFTOP}, // Air Force Trip Wire - Rooftop (Aldor) + {22086, 22085, SPAWNTYPE_ALARMBOT}, // Air Force Alarm Bot (Sporeggar) + {22087, 22085, SPAWNTYPE_ALARMBOT}, // Air Force Guard Post (Sporeggar - Spore Bat) + {22088, 22085, SPAWNTYPE_TRIPWIRE_ROOFTOP}, // Air Force Trip Wire - Rooftop (Sporeggar) + {22090, 22089, SPAWNTYPE_ALARMBOT}, // Air Force Guard Post (Toshley's Station - Flying Machine) + {22124, 22122, SPAWNTYPE_ALARMBOT}, // Air Force Alarm Bot (Cenarion) + {22125, 22122, SPAWNTYPE_ALARMBOT}, // Air Force Guard Post (Cenarion - Stormcrow) + {22126, 22122, SPAWNTYPE_ALARMBOT} // Air Force Trip Wire - Rooftop (Cenarion Expedition) +}; + +struct npc_air_force_botsAI : public ScriptedAI +{ + npc_air_force_botsAI(Creature* pCreature) : ScriptedAI(pCreature) + { + m_pSpawnAssoc = NULL; + + // find the correct spawnhandling + for (uint8 i = 0; i < countof(m_aSpawnAssociations); ++i) + { + if (m_aSpawnAssociations[i].m_uiThisCreatureEntry == pCreature->GetEntry()) + { + m_pSpawnAssoc = &m_aSpawnAssociations[i]; + break; + } + } + + if (!m_pSpawnAssoc) + { error_db_log("SD2: 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) + { + error_db_log("SD2: Creature template entry %u does not exist in DB, which is required by npc_air_force_bots", m_pSpawnAssoc->m_uiSpawnedCreatureEntry); + m_pSpawnAssoc = NULL; + return; + } + } + } + + SpawnAssociation* m_pSpawnAssoc; + ObjectGuid m_spawnedGuid; + + void Reset() override { } + + Creature* SummonGuard() + { + Creature* pSummoned = m_creature->SummonCreature(m_pSpawnAssoc->m_uiSpawnedCreatureEntry, 0.0f, 0.0f, 0.0f, 0.0f, TEMPSUMMON_TIMED_OOC_DESPAWN, 300000); + + if (pSummoned) + { m_spawnedGuid = pSummoned->GetObjectGuid(); } + else + { + error_db_log("SD2: npc_air_force_bots: wasn't able to spawn creature %u", m_pSpawnAssoc->m_uiSpawnedCreatureEntry); + m_pSpawnAssoc = NULL; + } + + return pSummoned; + } + + Creature* GetSummonedGuard() + { + Creature* pCreature = m_creature->GetMap()->GetCreature(m_spawnedGuid); + + if (pCreature && pCreature->IsAlive()) + { return pCreature; } + + return NULL; + } + + void MoveInLineOfSight(Unit* pWho) override + { + if (!m_pSpawnAssoc) + { return; } + + if (pWho->IsTargetableForAttack() && m_creature->IsHostileTo(pWho)) + { + Player* pPlayerTarget = pWho->GetTypeId() == TYPEID_PLAYER ? (Player*)pWho : NULL; + + // airforce guards only spawn for players + if (!pPlayerTarget) + { return; } + + Creature* pLastSpawnedGuard = m_spawnedGuid ? GetSummonedGuard() : NULL; + + // prevent calling GetCreature at next MoveInLineOfSight call - speedup + if (!pLastSpawnedGuard) + { m_spawnedGuid.Clear(); } + + switch (m_pSpawnAssoc->m_SpawnType) + { + case SPAWNTYPE_ALARMBOT: + { + if (!pWho->IsWithinDistInMap(m_creature, RANGE_GUARDS_MARK)) + { return; } + + Aura* pMarkAura = pWho->GetAura(SPELL_GUARDS_MARK, EFFECT_INDEX_0); + if (pMarkAura) + { + // the target wasn't able to move out of our range within 25 seconds + if (!pLastSpawnedGuard) + { + pLastSpawnedGuard = SummonGuard(); + + if (!pLastSpawnedGuard) + { return; } + } + + if (pMarkAura->GetAuraDuration() < AURA_DURATION_TIME_LEFT) + { + if (!pLastSpawnedGuard->getVictim()) + { pLastSpawnedGuard->AI()->AttackStart(pWho); } + } + } + else + { + if (!pLastSpawnedGuard) + { pLastSpawnedGuard = SummonGuard(); } + + if (!pLastSpawnedGuard) + { return; } + + pLastSpawnedGuard->CastSpell(pWho, SPELL_GUARDS_MARK, true); + } + break; + } + case SPAWNTYPE_TRIPWIRE_ROOFTOP: + { + 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()) + { + if (!pLastSpawnedGuard->getVictim()) + { pLastSpawnedGuard->AI()->AttackStart(pWho); } + } + break; + } + } + } + } +}; + +CreatureAI* GetAI_npc_air_force_bots(Creature* pCreature) +{ + return new npc_air_force_botsAI(pCreature); +} + +/*######## +# npc_chicken_cluck +#########*/ + +enum +{ + EMOTE_A_HELLO = -1000204, + EMOTE_H_HELLO = -1000205, + EMOTE_CLUCK_TEXT2 = -1000206, + + QUEST_CLUCK = 3861, + FACTION_FRIENDLY = 35, + FACTION_CHICKEN = 31 +}; + +struct npc_chicken_cluckAI : public ScriptedAI +{ + npc_chicken_cluckAI(Creature* pCreature) : ScriptedAI(pCreature) {Reset();} + + uint32 m_uiResetFlagTimer; + + void Reset() override + { + m_uiResetFlagTimer = 120000; + + m_creature->setFaction(FACTION_CHICKEN); + m_creature->RemoveFlag(UNIT_NPC_FLAGS, UNIT_NPC_FLAG_QUESTGIVER); + } + + void ReceiveEmote(Player* pPlayer, uint32 uiEmote) override + { + if (uiEmote == TEXTEMOTE_CHICKEN) + { + if (!urand(0, 29)) + { + if (pPlayer->GetQuestStatus(QUEST_CLUCK) == QUEST_STATUS_NONE) + { + m_creature->SetFlag(UNIT_NPC_FLAGS, UNIT_NPC_FLAG_QUESTGIVER); + m_creature->setFaction(FACTION_FRIENDLY); + + DoScriptText(EMOTE_A_HELLO, m_creature); + + /* are there any difference in texts, after 3.x ? + if (pPlayer->GetTeam() == HORDE) + DoScriptText(EMOTE_H_HELLO, m_creature); + else + DoScriptText(EMOTE_A_HELLO, m_creature); + */ + } + } + } + + if (uiEmote == TEXTEMOTE_CHEER) + { + if (pPlayer->GetQuestStatus(QUEST_CLUCK) == QUEST_STATUS_COMPLETE) + { + m_creature->SetFlag(UNIT_NPC_FLAGS, UNIT_NPC_FLAG_QUESTGIVER); + m_creature->setFaction(FACTION_FRIENDLY); + DoScriptText(EMOTE_CLUCK_TEXT2, m_creature); + } + } + } + + void UpdateAI(const uint32 uiDiff) override + { + // Reset flags after a certain time has passed so that the next player has to start the 'event' again + if (m_creature->HasFlag(UNIT_NPC_FLAGS, UNIT_NPC_FLAG_QUESTGIVER)) + { + if (m_uiResetFlagTimer < uiDiff) + { + EnterEvadeMode(); + } + else + { + m_uiResetFlagTimer -= uiDiff; + } + } + + if (m_creature->SelectHostileTarget() && m_creature->getVictim()) + { + DoMeleeAttackIfReady(); + } + } +}; + +CreatureAI* GetAI_npc_chicken_cluck(Creature* pCreature) +{ + return new npc_chicken_cluckAI(pCreature); +} + +bool QuestAccept_npc_chicken_cluck(Player* /*pPlayer*/, Creature* pCreature, const Quest* pQuest) +{ + if (pQuest->GetQuestId() == QUEST_CLUCK) + { + if (npc_chicken_cluckAI* pChickenAI = dynamic_cast(pCreature->AI())) + { + pChickenAI->Reset(); + } + } + + return true; +} + +bool QuestRewarded_npc_chicken_cluck(Player* /*pPlayer*/, Creature* pCreature, const Quest* pQuest) +{ + if (pQuest->GetQuestId() == QUEST_CLUCK) + { + if (npc_chicken_cluckAI* pChickenAI = dynamic_cast(pCreature->AI())) + { + pChickenAI->Reset(); + } + } + + return true; +} + +/*###### +## npc_dancing_flames +######*/ + +enum +{ + SPELL_FIERY_SEDUCTION = 47057 +}; + +struct npc_dancing_flamesAI : public ScriptedAI +{ + npc_dancing_flamesAI(Creature* pCreature) : ScriptedAI(pCreature) {Reset();} + + void Reset() override {} + + void ReceiveEmote(Player* pPlayer, uint32 uiEmote) override + { + m_creature->SetFacingToObject(pPlayer); + + if (pPlayer->HasAura(SPELL_FIERY_SEDUCTION)) + { pPlayer->RemoveAurasDueToSpell(SPELL_FIERY_SEDUCTION); } + + if (pPlayer->IsMounted()) + { + pPlayer->Unmount(); // doesnt remove mount aura + pPlayer->RemoveSpellsCausingAura(SPELL_AURA_MOUNTED); + } + + switch (uiEmote) + { + case TEXTEMOTE_DANCE: DoCastSpellIfCan(pPlayer, SPELL_FIERY_SEDUCTION); break;// dance -> cast SPELL_FIERY_SEDUCTION + case TEXTEMOTE_WAVE: m_creature->HandleEmote(EMOTE_ONESHOT_WAVE); break;// wave -> wave + case TEXTEMOTE_JOKE: m_creature->HandleEmote(EMOTE_STATE_LAUGH); break;// silly -> laugh(with sound) + case TEXTEMOTE_BOW: m_creature->HandleEmote(EMOTE_ONESHOT_BOW); break;// bow -> bow + case TEXTEMOTE_KISS: m_creature->HandleEmote(TEXTEMOTE_CURTSEY); break;// kiss -> curtsey + } + } +}; + +CreatureAI* GetAI_npc_dancing_flames(Creature* pCreature) +{ + return new npc_dancing_flamesAI(pCreature); +} + +/*###### +## Triage quest +######*/ + +enum +{ + SAY_DOC1 = -1000201, + SAY_DOC2 = -1000202, + SAY_DOC3 = -1000203, + + QUEST_TRIAGE_H = 6622, + QUEST_TRIAGE_A = 6624, + + DOCTOR_ALLIANCE = 12939, + DOCTOR_HORDE = 12920, + ALLIANCE_COORDS = 7, + HORDE_COORDS = 6 +}; + +struct Location +{ + float x, y, z, o; +}; + +static Location AllianceCoords[] = +{ + { -3757.38f, -4533.05f, 14.16f, 3.62f}, // Top-far-right bunk as seen from entrance + { -3754.36f, -4539.13f, 14.16f, 5.13f}, // Top-far-left bunk + { -3749.54f, -4540.25f, 14.28f, 3.34f}, // Far-right bunk + { -3742.10f, -4536.85f, 14.28f, 3.64f}, // Right bunk near entrance + { -3755.89f, -4529.07f, 14.05f, 0.57f}, // Far-left bunk + { -3749.51f, -4527.08f, 14.07f, 5.26f}, // Mid-left bunk + { -3746.37f, -4525.35f, 14.16f, 5.22f}, // Left bunk near entrance +}; + +// alliance run to where +#define A_RUNTOX -3742.96f +#define A_RUNTOY -4531.52f +#define A_RUNTOZ 11.91f + +static Location HordeCoords[] = +{ + { -1013.75f, -3492.59f, 62.62f, 4.34f}, // Left, Behind + { -1017.72f, -3490.92f, 62.62f, 4.34f}, // Right, Behind + { -1015.77f, -3497.15f, 62.82f, 4.34f}, // Left, Mid + { -1019.51f, -3495.49f, 62.82f, 4.34f}, // Right, Mid + { -1017.25f, -3500.85f, 62.98f, 4.34f}, // Left, front + { -1020.95f, -3499.21f, 62.98f, 4.34f} // Right, Front +}; + +// horde run to where +#define H_RUNTOX -1016.44f +#define H_RUNTOY -3508.48f +#define H_RUNTOZ 62.96f + +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 npc_doctorAI : public ScriptedAI +{ + npc_doctorAI(Creature* pCreature) : ScriptedAI(pCreature) {Reset();} + + ObjectGuid m_playerGuid; + + uint32 m_uiSummonPatientTimer; + uint32 m_uiSummonPatientCount; + uint32 m_uiPatientDiedCount; + uint32 m_uiPatientSavedCount; + + bool m_bIsEventInProgress; + + GuidList m_lPatientGuids; + std::vector m_vPatientSummonCoordinates; + + void Reset() override + { + m_playerGuid.Clear(); + + m_uiSummonPatientTimer = 10000; + m_uiSummonPatientCount = 0; + m_uiPatientDiedCount = 0; + m_uiPatientSavedCount = 0; + + m_lPatientGuids.clear(); + m_vPatientSummonCoordinates.clear(); + + m_bIsEventInProgress = false; + + m_creature->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE); + } + + void BeginEvent(Player* pPlayer); + void PatientDied(Location* pPoint); + void PatientSaved(Creature* pSoldier, Player* pPlayer, Location* pPoint); + void UpdateAI(const uint32 uiDiff) override; +}; + +/*##### +## npc_injured_patient (handles all the patients, no matter Horde or Alliance) +#####*/ + +struct npc_injured_patientAI : public ScriptedAI +{ + npc_injured_patientAI(Creature* pCreature) : ScriptedAI(pCreature) {Reset();} + + ObjectGuid m_doctorGuid; + Location* m_pCoord; + + void Reset() override + { + m_doctorGuid.Clear(); + m_pCoord = 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->SetStandState(UNIT_STAND_STATE_DEAD); + + switch (m_creature->GetEntry()) + { + // lower max health + case 12923: + case 12938: // Injured Soldier + m_creature->SetHealth(uint32(m_creature->GetMaxHealth()*.75)); + break; + case 12924: + case 12936: // Badly injured Soldier + m_creature->SetHealth(uint32(m_creature->GetMaxHealth()*.50)); + break; + case 12925: + case 12937: // Critically injured Soldier + m_creature->SetHealth(uint32(m_creature->GetMaxHealth()*.25)); + break; + } + } + + void SpellHit(Unit* pCaster, const SpellEntry* pSpell) override + { + if (pCaster->GetTypeId() == TYPEID_PLAYER && m_creature->IsAlive() && pSpell->Id == 20804) + { + Player* pPlayer = static_cast(pCaster); + if (pPlayer->GetQuestStatus(6624) == QUEST_STATUS_INCOMPLETE || pPlayer->GetQuestStatus(6622) == QUEST_STATUS_INCOMPLETE) + { + if (Creature* pDoctor = m_creature->GetMap()->GetCreature(m_doctorGuid)) + { + if (npc_doctorAI* pDocAI = dynamic_cast(pDoctor->AI())) + { + pDocAI->PatientSaved(m_creature, pPlayer, m_pCoord); + } + } + } + // 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->SetStandState(UNIT_STAND_STATE_STAND); + + switch (urand(0, 2)) + { + case 0: + DoScriptText(SAY_DOC1, m_creature); + break; + case 1: + DoScriptText(SAY_DOC2, m_creature); + break; + case 2: + DoScriptText(SAY_DOC3, m_creature); + break; + } + + m_creature->SetWalk(false); + + switch (m_creature->GetEntry()) + { + case 12923: + case 12924: + case 12925: + m_creature->GetMotionMaster()->MovePoint(0, H_RUNTOX, H_RUNTOY, H_RUNTOZ); + break; + case 12936: + case 12937: + case 12938: + m_creature->GetMotionMaster()->MovePoint(0, A_RUNTOX, A_RUNTOY, A_RUNTOZ); + break; + } + } + } + + void UpdateAI(const uint32 uiDiff) override + { + // lower HP on every world tick makes it a useful counter, not officlone though + uint32 uiHPLose = uint32(0.05f * uiDiff); + if (m_creature->IsAlive() && m_creature->GetHealth() > 1 + uiHPLose) + { + m_creature->SetHealth(m_creature->GetHealth() - uiHPLose); + } + + if (m_creature->IsAlive() && m_creature->GetHealth() <= 1 + uiHPLose) + { + 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, UNIT_DYNFLAG_DEAD); + + if (Creature* pDoctor = m_creature->GetMap()->GetCreature(m_doctorGuid)) + { + if (npc_doctorAI* pDocAI = dynamic_cast(pDoctor->AI())) + { + pDocAI->PatientDied(m_pCoord); + } + } + } + } +}; + +CreatureAI* GetAI_npc_injured_patient(Creature* pCreature) +{ + return new npc_injured_patientAI(pCreature); +} + +/* +npc_doctor (continue) +*/ + +void npc_doctorAI::BeginEvent(Player* pPlayer) +{ + m_playerGuid = pPlayer->GetObjectGuid(); + + m_uiSummonPatientTimer = 10000; + m_uiSummonPatientCount = 0; + m_uiPatientDiedCount = 0; + m_uiPatientSavedCount = 0; + + switch (m_creature->GetEntry()) + { + case DOCTOR_ALLIANCE: + for (uint8 i = 0; i < ALLIANCE_COORDS; ++i) + { + m_vPatientSummonCoordinates.push_back(&AllianceCoords[i]); + } + break; + case DOCTOR_HORDE: + for (uint8 i = 0; i < HORDE_COORDS; ++i) + { + m_vPatientSummonCoordinates.push_back(&HordeCoords[i]); + } + break; + } + + m_bIsEventInProgress = true; + m_creature->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE); +} + +void npc_doctorAI::PatientDied(Location* pPoint) +{ + Player* pPlayer = m_creature->GetMap()->GetPlayer(m_playerGuid); + + if (pPlayer && (pPlayer->GetQuestStatus(6624) == QUEST_STATUS_INCOMPLETE || pPlayer->GetQuestStatus(6622) == QUEST_STATUS_INCOMPLETE)) + { + ++m_uiPatientDiedCount; + + if (m_uiPatientDiedCount > 5 && m_bIsEventInProgress) + { + if (pPlayer->GetQuestStatus(QUEST_TRIAGE_A) == QUEST_STATUS_INCOMPLETE) + { + pPlayer->FailQuest(QUEST_TRIAGE_A); + } + else if (pPlayer->GetQuestStatus(QUEST_TRIAGE_H) == QUEST_STATUS_INCOMPLETE) + { + pPlayer->FailQuest(QUEST_TRIAGE_H); + } + + Reset(); + return; + } + + m_vPatientSummonCoordinates.push_back(pPoint); + } + else + // If no player or player abandon quest in progress + { + Reset(); + } +} + +void npc_doctorAI::PatientSaved(Creature* /*soldier*/, Player* pPlayer, Location* pPoint) +{ + if (pPlayer && m_playerGuid == pPlayer->GetObjectGuid()) + { + if (pPlayer->GetQuestStatus(QUEST_TRIAGE_A) == QUEST_STATUS_INCOMPLETE || pPlayer->GetQuestStatus(QUEST_TRIAGE_H) == QUEST_STATUS_INCOMPLETE) + { + ++m_uiPatientSavedCount; + + if (m_uiPatientSavedCount == 15) + { + for (GuidList::const_iterator itr = m_lPatientGuids.begin(); itr != m_lPatientGuids.end(); ++itr) + { + if (Creature* Patient = m_creature->GetMap()->GetCreature(*itr)) + { + Patient->SetDeathState(JUST_DIED); + } + } + + if (pPlayer->GetQuestStatus(QUEST_TRIAGE_A) == QUEST_STATUS_INCOMPLETE) + { + pPlayer->GroupEventHappens(QUEST_TRIAGE_A, m_creature); + } + else if (pPlayer->GetQuestStatus(QUEST_TRIAGE_H) == QUEST_STATUS_INCOMPLETE) + { + pPlayer->GroupEventHappens(QUEST_TRIAGE_H, m_creature); + } + + Reset(); + return; + } + + m_vPatientSummonCoordinates.push_back(pPoint); + } + } +} + +void npc_doctorAI::UpdateAI(const uint32 uiDiff) +{ + if (m_bIsEventInProgress && m_uiSummonPatientCount >= 20) + { + Reset(); + return; + } + + if (m_bIsEventInProgress && !m_vPatientSummonCoordinates.empty()) + { + if (m_uiSummonPatientTimer < uiDiff) + { + std::vector::iterator itr = m_vPatientSummonCoordinates.begin() + urand(0, m_vPatientSummonCoordinates.size() - 1); + uint32 patientEntry = 0; + + switch (m_creature->GetEntry()) + { + case DOCTOR_ALLIANCE: + patientEntry = AllianceSoldierId[urand(0, 2)]; + break; + case DOCTOR_HORDE: + patientEntry = HordeSoldierId[urand(0, 2)]; + break; + default: + script_error_log("Invalid entry for Triage doctor. Please check your database"); + return; + } + + if (Creature* Patient = m_creature->SummonCreature(patientEntry, (*itr)->x, (*itr)->y, (*itr)->z, (*itr)->o, TEMPSUMMON_TIMED_OOC_DESPAWN, 5000)) + { + // 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); + + m_lPatientGuids.push_back(Patient->GetObjectGuid()); + + if (npc_injured_patientAI* pPatientAI = dynamic_cast(Patient->AI())) + { + pPatientAI->m_doctorGuid = m_creature->GetObjectGuid(); + pPatientAI->m_pCoord = *itr; + m_vPatientSummonCoordinates.erase(itr); + } + } + m_uiSummonPatientTimer = 10000; + ++m_uiSummonPatientCount; + } + else + { + m_uiSummonPatientTimer -= uiDiff; + } + } +} + +bool QuestAccept_npc_doctor(Player* pPlayer, Creature* pCreature, const Quest* pQuest) +{ + if ((pQuest->GetQuestId() == QUEST_TRIAGE_A) || (pQuest->GetQuestId() == QUEST_TRIAGE_H)) + { + if (npc_doctorAI* pDocAI = dynamic_cast(pCreature->AI())) + { + pDocAI->BeginEvent(pPlayer); + } + } + + return true; +} + +CreatureAI* GetAI_npc_doctor(Creature* pCreature) +{ + return new npc_doctorAI(pCreature); +} + +/*###### +## npc_garments_of_quests +######*/ + +enum +{ + 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, + + SAY_COMMON_HEALED = -1000231, + SAY_DG_KEL_THANKS = -1000232, + SAY_DG_KEL_GOODBYE = -1000233, + SAY_ROBERTS_THANKS = -1000256, + SAY_ROBERTS_GOODBYE = -1000257, + SAY_KORJA_THANKS = -1000258, + SAY_KORJA_GOODBYE = -1000259, + SAY_DOLF_THANKS = -1000260, + SAY_DOLF_GOODBYE = -1000261, + SAY_SHAYA_THANKS = -1000262, + SAY_SHAYA_GOODBYE = -1000263, +}; + +struct npc_garments_of_questsAI : public npc_escortAI +{ + npc_garments_of_questsAI(Creature* pCreature) : npc_escortAI(pCreature) { Reset(); } + + ObjectGuid m_playerGuid; + + bool m_bIsHealed; + bool m_bCanRun; + + uint32 m_uiRunAwayTimer; + + void Reset() override + { + m_playerGuid.Clear(); + + m_bIsHealed = false; + m_bCanRun = false; + + m_uiRunAwayTimer = 5000; + + m_creature->SetStandState(UNIT_STAND_STATE_KNEEL); + // expect database to have RegenHealth=0 + m_creature->SetHealth(int(m_creature->GetMaxHealth() * 0.7)); + } + + void SpellHit(Unit* pCaster, const SpellEntry* pSpell) override + { + if (pSpell->Id == SPELL_LESSER_HEAL_R2 || pSpell->Id == SPELL_FORTITUDE_R1) + { + // not while in combat + if (m_creature->IsInCombat()) + { + return; + } + + // nothing to be done now + if (m_bIsHealed && m_bCanRun) + { + return; + } + + if (pCaster->GetTypeId() == TYPEID_PLAYER) + { + switch (m_creature->GetEntry()) + { + case ENTRY_SHAYA: + if (((Player*)pCaster)->GetQuestStatus(QUEST_MOON) == QUEST_STATUS_INCOMPLETE) + { + if (m_bIsHealed && !m_bCanRun && pSpell->Id == SPELL_FORTITUDE_R1) + { + DoScriptText(SAY_SHAYA_THANKS, m_creature, pCaster); + m_bCanRun = true; + } + else if (!m_bIsHealed && pSpell->Id == SPELL_LESSER_HEAL_R2) + { + m_playerGuid = pCaster->GetObjectGuid(); + m_creature->SetStandState(UNIT_STAND_STATE_STAND); + DoScriptText(SAY_COMMON_HEALED, m_creature, pCaster); + m_bIsHealed = true; + } + } + break; + case ENTRY_ROBERTS: + if (((Player*)pCaster)->GetQuestStatus(QUEST_LIGHT_1) == QUEST_STATUS_INCOMPLETE) + { + if (m_bIsHealed && !m_bCanRun && pSpell->Id == SPELL_FORTITUDE_R1) + { + DoScriptText(SAY_ROBERTS_THANKS, m_creature, pCaster); + m_bCanRun = true; + } + else if (!m_bIsHealed && pSpell->Id == SPELL_LESSER_HEAL_R2) + { + m_playerGuid = pCaster->GetObjectGuid(); + m_creature->SetStandState(UNIT_STAND_STATE_STAND); + DoScriptText(SAY_COMMON_HEALED, m_creature, pCaster); + m_bIsHealed = true; + } + } + break; + case ENTRY_DOLF: + if (((Player*)pCaster)->GetQuestStatus(QUEST_LIGHT_2) == QUEST_STATUS_INCOMPLETE) + { + if (m_bIsHealed && !m_bCanRun && pSpell->Id == SPELL_FORTITUDE_R1) + { + DoScriptText(SAY_DOLF_THANKS, m_creature, pCaster); + m_bCanRun = true; + } + else if (!m_bIsHealed && pSpell->Id == SPELL_LESSER_HEAL_R2) + { + m_playerGuid = pCaster->GetObjectGuid(); + m_creature->SetStandState(UNIT_STAND_STATE_STAND); + DoScriptText(SAY_COMMON_HEALED, m_creature, pCaster); + m_bIsHealed = true; + } + } + break; + case ENTRY_KORJA: + if (((Player*)pCaster)->GetQuestStatus(QUEST_SPIRIT) == QUEST_STATUS_INCOMPLETE) + { + if (m_bIsHealed && !m_bCanRun && pSpell->Id == SPELL_FORTITUDE_R1) + { + DoScriptText(SAY_KORJA_THANKS, m_creature, pCaster); + m_bCanRun = true; + } + else if (!m_bIsHealed && pSpell->Id == SPELL_LESSER_HEAL_R2) + { + m_playerGuid = pCaster->GetObjectGuid(); + m_creature->SetStandState(UNIT_STAND_STATE_STAND); + DoScriptText(SAY_COMMON_HEALED, m_creature, pCaster); + m_bIsHealed = true; + } + } + break; + case ENTRY_DG_KEL: + if (((Player*)pCaster)->GetQuestStatus(QUEST_DARKNESS) == QUEST_STATUS_INCOMPLETE) + { + if (m_bIsHealed && !m_bCanRun && pSpell->Id == SPELL_FORTITUDE_R1) + { + DoScriptText(SAY_DG_KEL_THANKS, m_creature, pCaster); + m_bCanRun = true; + } + else if (!m_bIsHealed && pSpell->Id == SPELL_LESSER_HEAL_R2) + { + m_playerGuid = pCaster->GetObjectGuid(); + m_creature->SetStandState(UNIT_STAND_STATE_STAND); + DoScriptText(SAY_COMMON_HEALED, m_creature, pCaster); + m_bIsHealed = true; + } + } + break; + } + + // give quest credit, not expect any special quest objectives + if (m_bCanRun) + { + ((Player*)pCaster)->TalkedToCreature(m_creature->GetEntry(), m_creature->GetObjectGuid()); + } + } + } + } + + void WaypointReached(uint32 /*uiPointId*/) override {} + + void UpdateEscortAI(const uint32 uiDiff) override + { + if (m_bCanRun && !m_creature->IsInCombat()) + { + if (m_uiRunAwayTimer <= uiDiff) + { + if (Player* pPlayer = m_creature->GetMap()->GetPlayer(m_playerGuid)) + { + switch (m_creature->GetEntry()) + { + case ENTRY_SHAYA: + DoScriptText(SAY_SHAYA_GOODBYE, m_creature, pPlayer); + break; + case ENTRY_ROBERTS: + DoScriptText(SAY_ROBERTS_GOODBYE, m_creature, pPlayer); + break; + case ENTRY_DOLF: + DoScriptText(SAY_DOLF_GOODBYE, m_creature, pPlayer); + break; + case ENTRY_KORJA: + DoScriptText(SAY_KORJA_GOODBYE, m_creature, pPlayer); + break; + case ENTRY_DG_KEL: + DoScriptText(SAY_DG_KEL_GOODBYE, m_creature, pPlayer); + break; + } + + Start(true); + } + else + { + EnterEvadeMode(); // something went wrong + } + + m_uiRunAwayTimer = 30000; + } + else + { + m_uiRunAwayTimer -= uiDiff; + } + } + + if (!m_creature->SelectHostileTarget() || !m_creature->getVictim()) + { + return; + } + + DoMeleeAttackIfReady(); + } +}; + +CreatureAI* GetAI_npc_garments_of_quests(Creature* pCreature) +{ + return new npc_garments_of_questsAI(pCreature); +} + +/*###### +## npc_guardian +######*/ + +#define SPELL_DEATHTOUCH 5 + +struct npc_guardianAI : public ScriptedAI +{ + npc_guardianAI(Creature* pCreature) : ScriptedAI(pCreature) {Reset();} + + void Reset() override + { + m_creature->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE); + } + + void UpdateAI(const uint32 /*diff*/) override + { + if (!m_creature->SelectHostileTarget() || !m_creature->getVictim()) + { + return; + } + + if (m_creature->isAttackReady()) + { + m_creature->CastSpell(m_creature->getVictim(), SPELL_DEATHTOUCH, true); + m_creature->resetAttackTimer(); + } + } +}; + +CreatureAI* GetAI_npc_guardian(Creature* pCreature) +{ + return new npc_guardianAI(pCreature); +} + +/*######## +# npc_innkeeper +#########*/ + +// Script applied to all innkeepers by npcflag. +// Are there any known innkeepers that does not hape the options in the below? +// (remember gossipHello is not called unless npcflag|1 is present) + +enum +{ + TEXT_ID_WHAT_TO_DO = 1853, + + SPELL_TRICK_OR_TREAT = 24751, // create item or random buff + SPELL_TRICK_OR_TREATED = 24755, // buff player get when tricked or treated +}; + +#define GOSSIP_ITEM_TRICK_OR_TREAT "Trick or Treat!" +#define GOSSIP_ITEM_WHAT_TO_DO "What can I do at an Inn?" + +bool GossipHello_npc_innkeeper(Player* pPlayer, Creature* pCreature) +{ + pPlayer->PrepareGossipMenu(pCreature, pPlayer->GetDefaultGossipMenuForSource(pCreature)); + + if (IsHolidayActive(HOLIDAY_HALLOWS_END) && !pPlayer->HasAura(SPELL_TRICK_OR_TREATED, EFFECT_INDEX_0)) + { + pPlayer->ADD_GOSSIP_ITEM(GOSSIP_ICON_CHAT, GOSSIP_ITEM_TRICK_OR_TREAT, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 2); + } + + // Should only apply to innkeeper close to start areas. + if (AreaTableEntry const* pAreaEntry = GetAreaEntryByAreaID(pCreature->GetAreaId())) + { + if (pAreaEntry->flags & AREA_FLAG_LOWLEVEL) + { pPlayer->ADD_GOSSIP_ITEM(GOSSIP_ICON_CHAT, GOSSIP_ITEM_WHAT_TO_DO, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 1); } + } + + pPlayer->TalkedToCreature(pCreature->GetEntry(), pCreature->GetObjectGuid()); + pPlayer->SendPreparedGossip(pCreature); + return true; +} + +bool GossipSelect_npc_innkeeper(Player* pPlayer, Creature* pCreature, uint32 /*uiSender*/, uint32 uiAction) +{ + switch (uiAction) + { + case GOSSIP_ACTION_INFO_DEF+1: + pPlayer->SEND_GOSSIP_MENU(TEXT_ID_WHAT_TO_DO, pCreature->GetObjectGuid()); + break; + case GOSSIP_ACTION_INFO_DEF+2: + pPlayer->CLOSE_GOSSIP_MENU(); + pCreature->CastSpell(pPlayer, SPELL_TRICK_OR_TREAT, true); + break; + case GOSSIP_OPTION_VENDOR: + pPlayer->SEND_VENDORLIST(pCreature->GetObjectGuid()); + break; + case GOSSIP_OPTION_INNKEEPER: + pPlayer->CLOSE_GOSSIP_MENU(); + pPlayer->SetBindPoint(pCreature->GetObjectGuid()); + break; + } + + return true; +} + +/*###### +## npc_spring_rabbit +## ATTENTION: This is actually a "fun" script, entirely done without proper source! +######*/ + +enum +{ + NPC_SPRING_RABBIT = 32791, + + SPELL_SPRING_RABBIT_JUMP = 61724, + SPELL_SPRING_RABBIT_WANDER = 61726, + SEPLL_SUMMON_BABY_BUNNY = 61727, + SPELL_SPRING_RABBIT_IN_LOVE = 61728, + SPELL_SPRING_FLING = 61875, +}; + +static const float DIST_START_EVENT = 15.0f; // Guesswork + +struct npc_spring_rabbitAI : public ScriptedPetAI +{ + npc_spring_rabbitAI(Creature* pCreature) : ScriptedPetAI(pCreature) { Reset(); } + + ObjectGuid m_partnerGuid; + uint32 m_uiStep; + uint32 m_uiStepTimer; + float m_fMoveAngle; + + void Reset() override + { + m_uiStep = 0; + m_uiStepTimer = 0; + m_partnerGuid.Clear(); + m_fMoveAngle = 0.0f; + } + + bool CanStartWhatRabbitsDo() { return !m_partnerGuid && !m_uiStepTimer; } + + void StartWhatRabbitsDo(Creature* pPartner) + { + m_partnerGuid = pPartner->GetObjectGuid(); + m_uiStep = 1; + m_uiStepTimer = 30000; + // Calculate meeting position + float m_fMoveAngle = m_creature->GetAngle(pPartner); + float fDist = m_creature->GetDistance(pPartner); + float fX, fY, fZ; + m_creature->GetNearPoint(m_creature, fX, fY, fZ, m_creature->GetObjectBoundingRadius(), fDist * 0.5f - m_creature->GetObjectBoundingRadius(), m_fMoveAngle); + + m_creature->GetMotionMaster()->Clear(); + m_creature->GetMotionMaster()->MovePoint(1, fX, fY, fZ); + } + + // Helper to get the Other Bunnies AI + npc_spring_rabbitAI* GetPartnerAI(Creature* pBunny = NULL) + { + if (!pBunny) + pBunny = m_creature->GetMap()->GetAnyTypeCreature(m_partnerGuid); + + if (!pBunny) + return NULL; + + return dynamic_cast(pBunny->AI()); + } + + // Event Starts when two rabbits see each other + void MoveInLineOfSight(Unit* pWho) override + { + if (m_creature->getVictim()) + return; + + if (pWho->GetTypeId() == TYPEID_UNIT && pWho->GetEntry() == NPC_SPRING_RABBIT && CanStartWhatRabbitsDo() && m_creature->IsFriendlyTo(pWho) && m_creature->IsWithinDistInMap(pWho, DIST_START_EVENT, true)) + { + if (npc_spring_rabbitAI* pOtherBunnyAI = GetPartnerAI((Creature*)pWho)) + { + if (pOtherBunnyAI->CanStartWhatRabbitsDo()) + { + StartWhatRabbitsDo((Creature*)pWho); + pOtherBunnyAI->StartWhatRabbitsDo(m_creature); + } + } + return; + } + + ScriptedPetAI::MoveInLineOfSight(pWho); + } + + bool ReachedMeetingPlace() + { + if (m_uiStep == 3) // Already there + { + m_uiStepTimer = 3000; + m_uiStep = 2; + return true; + } + else + return false; + } + + void MovementInform(uint32 uiMovementType, uint32 uiData) override + { + if (uiMovementType != POINT_MOTION_TYPE || uiData != 1) + return; + + if (!m_partnerGuid) + return; + + m_uiStep = 3; + if (npc_spring_rabbitAI* pOtherBunnyAI = GetPartnerAI()) + { + if (pOtherBunnyAI->ReachedMeetingPlace()) + { + m_creature->SetFacingTo(pOtherBunnyAI->m_creature->GetOrientation()); + m_uiStepTimer = 3000; + } + else + m_creature->SetFacingTo(m_fMoveAngle + M_PI_F * 0.5f); + } + + // m_creature->GetMotionMaster()->MoveRandom(); // does not move around current position, hence not usefull right now + m_creature->GetMotionMaster()->MoveIdle(); + } + + // Overwrite ScriptedPetAI::UpdateAI, to prevent re-following while the event is active! + void UpdateAI(const uint32 uiDiff) override + { + if (!m_partnerGuid || !m_uiStepTimer) + { + ScriptedPetAI::UpdateAI(uiDiff); + return; + } + + if (m_uiStep == 6) + ScriptedPetAI::UpdateAI(uiDiff); // Event nearly finished, do normal following + + if (m_uiStepTimer <= uiDiff) + { + switch (m_uiStep) + { + case 1: // Timer expired, before reached meeting point. Reset. + Reset(); + break; + + case 2: // Called for the rabbit first reached meeting point + if (Creature* pBunny = m_creature->GetMap()->GetAnyTypeCreature(m_partnerGuid)) + pBunny->CastSpell(pBunny, SPELL_SPRING_RABBIT_IN_LOVE, false); + + DoCastSpellIfCan(m_creature, SPELL_SPRING_RABBIT_IN_LOVE); + // no break here + case 3: + m_uiStepTimer = 5000; + m_uiStep += 2; + break; + + case 4: // Called for the rabbit first reached meeting point + DoCastSpellIfCan(m_creature, SEPLL_SUMMON_BABY_BUNNY); + // no break here + case 5: + // Let owner cast achievement related spell + if (Unit* pOwner = m_creature->GetCharmerOrOwner()) + pOwner->CastSpell(pOwner, SPELL_SPRING_FLING, true); + + m_uiStep = 6; + m_uiStepTimer = 30000; + break; + case 6: + m_creature->RemoveAurasDueToSpell(SPELL_SPRING_RABBIT_IN_LOVE); + Reset(); + break; + } + } + else + m_uiStepTimer -= uiDiff; + } +}; + +CreatureAI* GetAI_npc_spring_rabbit(Creature* pCreature) +{ + return new npc_spring_rabbitAI(pCreature); +} + +/*###### +## npc_redemption_target +######*/ + +enum +{ + SAY_HEAL = -1000187, + + SPELL_SYMBOL_OF_LIFE = 8593, + SPELL_SHIMMERING_VESSEL = 31225, + SPELL_REVIVE_SELF = 32343, + + NPC_FURBOLG_SHAMAN = 17542, // draenei side + NPC_BLOOD_KNIGHT = 17768, // blood elf side +}; + +struct npc_redemption_targetAI : public ScriptedAI +{ + npc_redemption_targetAI(Creature* pCreature) : ScriptedAI(pCreature) { Reset(); } + + uint32 m_uiEvadeTimer; + uint32 m_uiHealTimer; + + ObjectGuid m_playerGuid; + + void Reset() override + { + m_uiEvadeTimer = 0; + m_uiHealTimer = 0; + + m_creature->SetFlag(UNIT_DYNAMIC_FLAGS, UNIT_DYNFLAG_DEAD); + m_creature->SetStandState(UNIT_STAND_STATE_DEAD); + } + + void DoReviveSelf(ObjectGuid m_guid) + { + // Wait until he resets again + if (m_uiEvadeTimer) + { + return; + } + + DoCastSpellIfCan(m_creature, SPELL_REVIVE_SELF); + m_creature->SetDeathState(JUST_ALIVED); + m_playerGuid = m_guid; + m_uiHealTimer = 2000; + } + + void UpdateAI(const uint32 uiDiff) override + { + if (m_uiHealTimer) + { + if (m_uiHealTimer <= uiDiff) + { + if (Player* pPlayer = m_creature->GetMap()->GetPlayer(m_playerGuid)) + { + DoScriptText(SAY_HEAL, m_creature, pPlayer); + + // Quests 9600 and 9685 requires kill credit + if (m_creature->GetEntry() == NPC_FURBOLG_SHAMAN || m_creature->GetEntry() == NPC_BLOOD_KNIGHT) + { + pPlayer->KilledMonsterCredit(m_creature->GetEntry(), m_creature->GetObjectGuid()); + } + } + + m_creature->RemoveFlag(UNIT_DYNAMIC_FLAGS, UNIT_DYNFLAG_DEAD); + m_creature->SetStandState(UNIT_STAND_STATE_STAND); + m_uiHealTimer = 0; + m_uiEvadeTimer = 2 * MINUTE * IN_MILLISECONDS; + } + else + { + m_uiHealTimer -= uiDiff; + } + } + + if (m_uiEvadeTimer) + { + if (m_uiEvadeTimer <= uiDiff) + { + EnterEvadeMode(); + m_uiEvadeTimer = 0; + } + else + { + m_uiEvadeTimer -= uiDiff; + } + } + } +}; + +CreatureAI* GetAI_npc_redemption_target(Creature* pCreature) +{ + return new npc_redemption_targetAI(pCreature); +} + +bool EffectDummyCreature_npc_redemption_target(Unit* pCaster, uint32 uiSpellId, SpellEffectIndex uiEffIndex, Creature* pCreatureTarget, ObjectGuid /*originalCasterGuid*/) +{ + // always check spellid and effectindex + if ((uiSpellId == SPELL_SYMBOL_OF_LIFE || uiSpellId == SPELL_SHIMMERING_VESSEL) && uiEffIndex == EFFECT_INDEX_0) + { + if (npc_redemption_targetAI* pTargetAI = dynamic_cast(pCreatureTarget->AI())) + { + pTargetAI->DoReviveSelf(pCaster->GetObjectGuid()); + } + + // always return true when we are handling this spell and effect + return true; + } + + return false; +} + +void AddSC_npcs_special() +{ + Script* pNewScript; + + pNewScript = new Script; + pNewScript->Name = "npc_air_force_bots"; + pNewScript->GetAI = &GetAI_npc_air_force_bots; + pNewScript->RegisterSelf(); + + pNewScript = new Script; + pNewScript->Name = "npc_chicken_cluck"; + pNewScript->GetAI = &GetAI_npc_chicken_cluck; + pNewScript->pQuestAcceptNPC = &QuestAccept_npc_chicken_cluck; + pNewScript->pQuestRewardedNPC = &QuestRewarded_npc_chicken_cluck; + pNewScript->RegisterSelf(); + + pNewScript = new Script; + pNewScript->Name = "npc_dancing_flames"; + pNewScript->GetAI = &GetAI_npc_dancing_flames; + pNewScript->RegisterSelf(); + + pNewScript = new Script; + pNewScript->Name = "npc_injured_patient"; + pNewScript->GetAI = &GetAI_npc_injured_patient; + pNewScript->RegisterSelf(); + + pNewScript = new Script; + pNewScript->Name = "npc_doctor"; + pNewScript->GetAI = &GetAI_npc_doctor; + pNewScript->pQuestAcceptNPC = &QuestAccept_npc_doctor; + pNewScript->RegisterSelf(); + + pNewScript = new Script; + pNewScript->Name = "npc_garments_of_quests"; + pNewScript->GetAI = &GetAI_npc_garments_of_quests; + pNewScript->RegisterSelf(); + + pNewScript = new Script; + pNewScript->Name = "npc_guardian"; + pNewScript->GetAI = &GetAI_npc_guardian; + pNewScript->RegisterSelf(); + + pNewScript = new Script; + pNewScript->Name = "npc_innkeeper"; + pNewScript->pGossipHello = &GossipHello_npc_innkeeper; + pNewScript->pGossipSelect = &GossipSelect_npc_innkeeper; + pNewScript->RegisterSelf(false); // script and error report disabled, but script can be used for custom needs, adding ScriptName + + pNewScript = new Script; + pNewScript->Name = "npc_spring_rabbit"; + pNewScript->GetAI = &GetAI_npc_spring_rabbit; + pNewScript->RegisterSelf(); + + pNewScript = new Script; + pNewScript->Name = "npc_redemption_target"; + pNewScript->GetAI = &GetAI_npc_redemption_target; + pNewScript->pEffectDummyNPC = &EffectDummyCreature_npc_redemption_target; + pNewScript->RegisterSelf(); +} diff --git a/src/modules/SD2/scripts/world/spell_scripts.cpp b/src/modules/SD2/scripts/world/spell_scripts.cpp new file mode 100644 index 000000000..d6a2f2312 --- /dev/null +++ b/src/modules/SD2/scripts/world/spell_scripts.cpp @@ -0,0 +1,1060 @@ +/** + * ScriptDev2 is an extension for mangos providing enhanced features for + * area triggers, creatures, game objects, instances, items, and spells beyond + * the default database scripting in mangos. + * + * Copyright (C) 2006-2013 ScriptDev2 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * World of Warcraft, and all World of Warcraft or Warcraft art, images, + * and lore are copyrighted by Blizzard Entertainment, Inc. + */ + +/** + * ScriptData + * SDName: Spell_Scripts + * SD%Complete: 100 + * SDComment: Spell scripts for dummy effects (if script need access/interaction with parts of other AI or instance, add it in related source files). + * SDCategory: Spell + * EndScriptData + */ + +/* ContentData +spell 8913 +spell 19512 +spell 21014 +spell 21050 +spell 29528 +spell 29866 +spell 34665 +spell 37136 +spell 39246 +spell 43340 +spell 44935 +spell 45109 +spell 45111 +spell 46023 +spell 46770 +spell 47575 +spell 50706 +spell 51331 +spell 51332 +spell 51366 +spell 52090 +spell 56099 +EndContentData */ + +#include "precompiled.h" + +/** + * When you make a spell effect: + * - always check spell id and effect index + * - always return true when the spell is handled by script + */ + +enum +{ + // quest 9452 + SPELL_CAST_FISHING_NET = 29866, + GO_RED_SNAPPER = 181616, + NPC_ANGRY_MURLOC = 17102, + ITEM_RED_SNAPPER = 23614, + // SPELL_SUMMON_TEST = 49214 // ! Just wrong spell name? It summon correct creature (17102)but does not appear to be used. + + // quest 11472 + SPELL_ANUNIAQS_NET = 21014, + GO_TASTY_REEF_FISH = 186949, + NPC_REEF_SHARK = 24637, + ITEM_TASTY_REEF_FISH = 34127, +}; + +bool EffectDummyGameObj_spell_dummy_go(Unit* pCaster, uint32 uiSpellId, SpellEffectIndex uiEffIndex, GameObject* pGOTarget, ObjectGuid /*originalCasterGuid*/) +{ + switch (uiSpellId) + { + case SPELL_ANUNIAQS_NET: + { + if (uiEffIndex == EFFECT_INDEX_0) + { + if (pGOTarget->GetRespawnTime() != 0 || pGOTarget->GetEntry() != GO_TASTY_REEF_FISH || pCaster->GetTypeId() != TYPEID_PLAYER) + return true; + + if (urand(0, 3)) + { + if (Item* pItem = ((Player*)pCaster)->StoreNewItemInInventorySlot(ITEM_TASTY_REEF_FISH, 1)) + ((Player*)pCaster)->SendNewItem(pItem, 1, true, false); + } + else + { + if (Creature* pShark = pCaster->SummonCreature(NPC_REEF_SHARK, pGOTarget->GetPositionX(), pGOTarget->GetPositionY(), pGOTarget->GetPositionZ(), 0.0f, TEMPSUMMON_TIMED_OOC_DESPAWN, 30000)) + pShark->AI()->AttackStart(pCaster); + } + + pGOTarget->SetLootState(GO_JUST_DEACTIVATED); + return true; + } + return true; + } + case SPELL_CAST_FISHING_NET: + { + if (uiEffIndex == EFFECT_INDEX_0) + { + if (pGOTarget->GetRespawnTime() != 0 || pGOTarget->GetEntry() != GO_RED_SNAPPER || pCaster->GetTypeId() != TYPEID_PLAYER) + { return true; } + + if (urand(0, 2)) + { + if (Creature* pMurloc = pCaster->SummonCreature(NPC_ANGRY_MURLOC, pCaster->GetPositionX(), pCaster->GetPositionY() + 20.0f, pCaster->GetPositionZ(), 0.0f, TEMPSUMMON_TIMED_OOC_DESPAWN, 10000)) + { pMurloc->AI()->AttackStart(pCaster); } + } + else + { + if (Item* pItem = ((Player*)pCaster)->StoreNewItemInInventorySlot(ITEM_RED_SNAPPER, 1)) + { ((Player*)pCaster)->SendNewItem(pItem, 1, true, false); } + } + + pGOTarget->SetLootState(GO_JUST_DEACTIVATED); + return true; + } + return true; + } + } + + return false; +} + +enum +{ + // quest 9629 + SPELL_TAG_MURLOC = 30877, + SPELL_TAG_MURLOC_PROC = 30875, + NPC_BLACKSILT_MURLOC = 17326, + NPC_TAGGED_MURLOC = 17654, + + // quest 9447 + SPELL_HEALING_SALVE = 29314, + SPELL_HEALING_SALVE_DUMMY = 29319, + NPC_MAGHAR_GRUNT = 16846, + + // quest 10190 + SPELL_RECHARGING_BATTERY = 34219, + NPC_DRAINED_PHASE_HUNTER = 19595, + + // target hulking helboar + SPELL_ADMINISTER_ANTIDOTE = 34665, + NPC_HELBOAR = 16880, + NPC_DREADTUSK = 16992, + + // quest 6124/6129 + SPELL_APPLY_SALVE = 19512, + SPELL_SICKLY_AURA = 19502, + + NPC_SICKLY_DEER = 12298, + NPC_SICKLY_GAZELLE = 12296, + + NPC_CURED_DEER = 12299, + NPC_CURED_GAZELLE = 12297, + + // quest 12906/13422 + SPELL_DISCIPLINING_ROD = 56033, + SAY_RAND_WORK1 = -1000555, + SAY_RAND_WORK2 = -1000556, + SAY_RAND_WORK3 = -1000557, + SAY_RAND_ATTACK1 = -1000558, + SAY_RAND_ATTACK2 = -1000559, + SAY_RAND_ATTACK3 = -1000560, + + // target morbent fel + SPELL_SACRED_CLEANSING = 8913, + NPC_MORBENT = 1200, + NPC_WEAKENED_MORBENT = 24782, + + // quest 11515 + SPELL_FEL_SIPHON_DUMMY = 44936, + NPC_FELBLOOD_INITIATE = 24918, + NPC_EMACIATED_FELBLOOD = 24955, + + // target nestlewood owlkin + SPELL_INOCULATE_OWLKIN = 29528, + NPC_OWLKIN = 16518, + NPC_OWLKIN_INOC = 16534, + + // target for quest 12166) + SPELL_LIQUID_FIRE = 46770, + SPELL_LIQUID_FIRE_AURA = 47972, + + NPC_ELK = 26616, + NPC_GRIZZLY = 26643, + + NPC_ELK_BUNNY = 27111, + NPC_GRIZZLY_BUNNY = 27112, + + // for quest 12516 + SPELL_MODIFIED_MOJO = 50706, + + NPC_PROPHET_OF_SSERATUS = 28068, + NPC_WEAK_PROPHET_OF_SSERATUS = 28151, + + // for quest 12459 + SPELL_SEEDS_OF_NATURES_WRATH = 49587, + + NPC_REANIMATED_FROSTWYRM = 26841, + NPC_TURGID = 27808, + NPC_DEATHGAZE = 27122, + + NPC_WEAK_REANIMATED_FROSTWYRM = 27821, + NPC_WEAK_TURGID = 27809, + NPC_WEAK_DEATHGAZE = 27807, + + // quest 11982 + SPELL_THROW_BOULDER = 47005, + SPELL_BOULBER_IMPACT = 47007, + SPELL_BOULDER_TOSS_CREDIT = 47009, + + NPC_IRON_RUNESHAPER = 26270, + NPC_RUNE_REAVER = 26268, + + // for quest 11730 + SPELL_ULTRASONIC_SCREWDRIVER = 46023, + SPELL_REPROGRAM_KILL_CREDIT = 46027, + + NPC_COLLECT_A_TRON = 25793, + SPELL_SUMMON_COLLECT_A_TRON = 46034, + + NPC_DEFENDO_TANK = 25758, + SPELL_SUMMON_DEFENDO_TANK = 46058, + + NPC_SCAVENGE_A8 = 25752, + SPELL_SUMMON_SCAVENGE_A8 = 46063, + + NPC_SCAVENGE_B6 = 25792, + SPELL_SUMMON_SCAVENGE_B6 = 46066, + + NPC_SENTRY_BOT = 25753, + SPELL_SUMMON_SENTRY_BOT = 46068, + + // target woodlands walker + SPELL_STRENGTH_ANCIENTS = 47575, + SPELL_CREATE_BARK_WALKERS = 47550, + FACTION_HOSTILE = 16, + + EMOTE_AGGRO = -1000551, + EMOTE_CREATE = -1000552, + + SAY_SPECIMEN = -1000581, + NPC_NEXUS_DRAKE_HATCHLING = 26127, + SPELL_RAELORASZ_FIREBALL = 46704, + + // Quest "Disrupt the Greengill Coast" (11541) + SPELL_ORB_OF_MURLOC_CONTROL = 45109, + SPELL_GREENGILL_SLAVE_FREED = 45110, + SPELL_ENRAGE = 45111, + NPC_FREED_GREENGILL_SLAVE = 25085, + NPC_DARKSPINE_MYRMIDON = 25060, + NPC_DARKSPINE_SIREN = 25073, + + // quest 14107 + SPELL_BLESSING_OF_PEACE = 66719, + NPC_FALLEN_HERO_SPIRIT = 32149, + NPC_FALLEN_HERO_SPIRIT_PROXY = 35055, + SAY_BLESS_1 = -1000594, + SAY_BLESS_2 = -1000595, + SAY_BLESS_3 = -1000596, + SAY_BLESS_4 = -1000597, + SAY_BLESS_5 = -1000598, + + // quest "The Big Bone Worm" 10930 + SPELL_FUMPING = 39246, + SPELL_SUMMON_HAISHULUD = 39248, + NPC_SAND_GNOME = 22483, + NPC_MATURE_BONE_SIFTER = 22482, + + // quest 12813, by item 40587 + SPELL_DARKMENDER_TINCTURE = 52741, + SPELL_SUMMON_CORRUPTED_SCARLET = 54415, + NPC_CORPSES_RISE_CREDIT_BUNNY = 29398, + + // quest 12659, item 38731 + SPELL_AHUNAES_KNIFE = 52090, + NPC_SCALPS_KILL_CREDIT_BUNNY = 28622, + + // quest 13549 + SPELL_TAILS_UP_GENDER_MASTER = 62110, + SPELL_TAILS_UP_AURA = 62109, + SPELL_FORCE_LEOPARD_SUMMON = 62117, + SPELL_FORCE_BEAR_SUMMON = 62118, + NPC_FROST_LEOPARD = 29327, + NPC_ICEPAW_BEAR = 29319, + NPC_LEOPARD_KILL_CREDIT = 33005, + NPC_BEAR_KILL_CREDIT = 33006, + SAY_ITS_FEMALE = -1000642, + SAY_ITS_MALE = -1000643, + + // quest 9849, item 24501 + SPELL_THROW_GORDAWG_BOULDER = 32001, + NPC_MINION_OF_GUROK = 18181, + + // quest 12589 + SPELL_HIT_APPLE = 51331, + SPELL_MISS_APPLE = 51332, + SPELL_MISS_APPLE_HIT_BIRD = 51366, + SPELL_APPLE_FALLS_TO_GROUND = 51371, + NPC_APPLE = 28053, + NPC_LUCKY_WILHELM = 28054, + NPC_DROSTAN = 28328, + SAY_LUCKY_HIT_1 = -1000644, + SAY_LUCKY_HIT_2 = -1000645, + SAY_LUCKY_HIT_3 = -1000646, + SAY_LUCKY_HIT_APPLE = -1000647, + SAY_DROSTAN_GOT_LUCKY_1 = -1000648, + SAY_DROSTAN_GOT_LUCKY_2 = -1000649, + SAY_DROSTAN_HIT_BIRD_1 = -1000650, + SAY_DROSTAN_HIT_BIRD_2 = -1000651, + + // quest 11314, item 33606 + SPELL_LURIELLES_PENDANT = 43340, + NPC_CHILL_NYMPH = 23678, + NPC_LURIELLE = 24117, + FACTION_FRIENDLY = 35, + SAY_FREE_1 = -1000781, + SAY_FREE_2 = -1000782, + SAY_FREE_3 = -1000783, + + // npcs that are only interactable while dead + SPELL_SHROUD_OF_DEATH = 10848, + SPELL_SPIRIT_PARTICLES = 17327, + NPC_FRANCLORN_FORGEWRIGHT = 8888, + NPC_GAERIYAN = 9299, + NPC_GANJO = 26924, + + // quest 11521 + SPELL_EXPOSE_RAZORTHORN_ROOT = 44935, + SPELL_SUMMON_RAZORTHORN_ROOT = 44941, + NPC_RAZORTHORN_RAVAGER = 24922, + GO_RAZORTHORN_DIRT_MOUND = 187073, + + // for quest 10584 + SPELL_PROTOVOLTAIC_MAGNETO_COLLECTOR = 37136, + NPC_ENCASED_ELECTROMENTAL = 21731, + + // quest 6661 + SPELL_MELODIOUS_RAPTURE = 21050, + SPELL_MELODIOUS_RAPTURE_VISUAL = 21051, + NPC_DEEPRUN_RAT = 13016, + NPC_ENTHRALLED_DEEPRUN_RAT = 13017, +}; + +bool EffectAuraDummy_spell_aura_dummy_npc(const Aura* pAura, bool bApply) +{ + switch (pAura->GetId()) + { + case SPELL_BLESSING_OF_PEACE: + { + Creature* pCreature = (Creature*)pAura->GetTarget(); + + if (!pCreature || pCreature->GetEntry() != NPC_FALLEN_HERO_SPIRIT) + return true; + + if (pAura->GetEffIndex() != EFFECT_INDEX_0) + return true; + + if (bApply) + { + switch (urand(0, 4)) + { + case 0: DoScriptText(SAY_BLESS_1, pCreature); break; + case 1: DoScriptText(SAY_BLESS_2, pCreature); break; + case 2: DoScriptText(SAY_BLESS_3, pCreature); break; + case 3: DoScriptText(SAY_BLESS_4, pCreature); break; + case 4: DoScriptText(SAY_BLESS_5, pCreature); break; + } + } + else + { + if (Player* pPlayer = (Player*)pAura->GetCaster()) + { + pPlayer->KilledMonsterCredit(NPC_FALLEN_HERO_SPIRIT_PROXY, pCreature->GetObjectGuid()); + pCreature->ForcedDespawn(); + } + } + + return true; + } + case SPELL_HEALING_SALVE: + { + if (pAura->GetEffIndex() != EFFECT_INDEX_0) + { return true; } + + if (bApply) + { + if (Unit* pCaster = pAura->GetCaster()) + { pCaster->CastSpell(pAura->GetTarget(), SPELL_HEALING_SALVE_DUMMY, true); } + } + + return true; + } + case SPELL_HEALING_SALVE_DUMMY: + { + if (pAura->GetEffIndex() != EFFECT_INDEX_0) + { return true; } + + if (!bApply) + { + Creature* pCreature = (Creature*)pAura->GetTarget(); + + pCreature->UpdateEntry(NPC_MAGHAR_GRUNT); + + if (pCreature->getStandState() == UNIT_STAND_STATE_KNEEL) + { pCreature->SetStandState(UNIT_STAND_STATE_STAND); } + + pCreature->ForcedDespawn(60 * IN_MILLISECONDS); + } + + return true; + } + case SPELL_RECHARGING_BATTERY: + { + if (pAura->GetEffIndex() != EFFECT_INDEX_0) + { return true; } + + if (!bApply) + { + if (pAura->GetTarget()->HasAuraState(AURA_STATE_HEALTHLESS_20_PERCENT)) + { ((Creature*)pAura->GetTarget())->UpdateEntry(NPC_DRAINED_PHASE_HUNTER); } + } + + return true; + } + case SPELL_TAG_MURLOC: + { + Creature* pCreature = (Creature*)pAura->GetTarget(); + + if (pAura->GetEffIndex() != EFFECT_INDEX_0) + { return true; } + + if (bApply) + { + if (pCreature->GetEntry() == NPC_BLACKSILT_MURLOC) + { + if (Unit* pCaster = pAura->GetCaster()) + { pCaster->CastSpell(pCreature, SPELL_TAG_MURLOC_PROC, true); } + } + } + else + { + if (pCreature->GetEntry() == NPC_TAGGED_MURLOC) + { pCreature->ForcedDespawn(); } + } + + return true; + } + case SPELL_RAELORASZ_FIREBALL: + { + if (pAura->GetEffIndex() != EFFECT_INDEX_0) + return true; + + if (Unit* pCaster = pAura->GetCaster()) + DoScriptText(SAY_SPECIMEN, pCaster); + + Unit* pTarget = pAura->GetTarget(); + if (pTarget->GetTypeId() == TYPEID_UNIT) + { + Creature* pCreature = (Creature*)pTarget; + + if (pCreature->GetEntry() == NPC_NEXUS_DRAKE_HATCHLING) + { + pCreature->SetStandState(UNIT_STAND_STATE_SLEEP); + pCreature->ForcedDespawn(3000); + } + } + return true; + } + case SPELL_ENRAGE: + { + if (!bApply || pAura->GetTarget()->GetTypeId() != TYPEID_UNIT) + { return false; } + + Creature* pTarget = (Creature*)pAura->GetTarget(); + + if (Creature* pCreature = GetClosestCreatureWithEntry(pTarget, NPC_DARKSPINE_MYRMIDON, 25.0f)) + { + pTarget->AI()->AttackStart(pCreature); + return true; + } + + if (Creature* pCreature = GetClosestCreatureWithEntry(pTarget, NPC_DARKSPINE_SIREN, 25.0f)) + { + pTarget->AI()->AttackStart(pCreature); + return true; + } + + return false; + } + case SPELL_SHROUD_OF_DEATH: + case SPELL_SPIRIT_PARTICLES: + { + Creature* pCreature = (Creature*)pAura->GetTarget(); + + if (!pCreature || (pCreature->GetEntry() != NPC_FRANCLORN_FORGEWRIGHT && pCreature->GetEntry() != NPC_GAERIYAN && pCreature->GetEntry() != NPC_GANJO)) + { return false; } + + if (bApply) + { pCreature->m_AuraFlags |= UNIT_AURAFLAG_ALIVE_INVISIBLE; } + else + { pCreature->m_AuraFlags &= ~UNIT_AURAFLAG_ALIVE_INVISIBLE; } + + return false; + } + case SPELL_PROTOVOLTAIC_MAGNETO_COLLECTOR: + { + if (pAura->GetEffIndex() != EFFECT_INDEX_0) + { return true; } + + Unit* pTarget = pAura->GetTarget(); + if (bApply && pTarget->GetTypeId() == TYPEID_UNIT) + { ((Creature*)pTarget)->UpdateEntry(NPC_ENCASED_ELECTROMENTAL); } + return true; + } + } + + return false; +} + +bool EffectDummyCreature_spell_dummy_npc(Unit* pCaster, uint32 uiSpellId, SpellEffectIndex uiEffIndex, Creature* pCreatureTarget, ObjectGuid /*originalCasterGuid*/) +{ + switch (uiSpellId) + { + case SPELL_ADMINISTER_ANTIDOTE: + { + if (uiEffIndex == EFFECT_INDEX_0) + { + if (pCreatureTarget->GetEntry() != NPC_HELBOAR) + { return true; } + + // possible needs check for quest state, to not have any effect when quest really complete + + pCreatureTarget->UpdateEntry(NPC_DREADTUSK); + return true; + } + return true; + } + case SPELL_APPLY_SALVE: + { + if (uiEffIndex == EFFECT_INDEX_0) + { + if (pCaster->GetTypeId() != TYPEID_PLAYER) + { return true; } + + if (pCreatureTarget->GetEntry() != NPC_SICKLY_DEER && pCreatureTarget->GetEntry() != NPC_SICKLY_GAZELLE) + { return true; } + + // Update entry, remove aura, set the kill credit and despawn + uint32 uiUpdateEntry = pCreatureTarget->GetEntry() == NPC_SICKLY_DEER ? NPC_CURED_DEER : NPC_CURED_GAZELLE; + pCreatureTarget->RemoveAurasDueToSpell(SPELL_SICKLY_AURA); + pCreatureTarget->UpdateEntry(uiUpdateEntry); + ((Player*)pCaster)->KilledMonsterCredit(uiUpdateEntry); + pCreatureTarget->ForcedDespawn(20000); + + return true; + } + return true; + } + case SPELL_DARKMENDER_TINCTURE: + { + if (uiEffIndex == EFFECT_INDEX_0) + { + if (pCaster->GetTypeId() != TYPEID_PLAYER) + return true; + + // TODO: find/fix visual for effect, no related spells found doing this + + pCreatureTarget->CastSpell(pCreatureTarget, SPELL_SUMMON_CORRUPTED_SCARLET, true); + + ((Player*)pCaster)->KilledMonsterCredit(NPC_CORPSES_RISE_CREDIT_BUNNY); + + pCreatureTarget->ForcedDespawn(); + return true; + } + return true; + } + case SPELL_DISCIPLINING_ROD: + { + if (uiEffIndex == EFFECT_INDEX_0) + { + if (pCreatureTarget->getStandState() == UNIT_STAND_STATE_STAND) + return true; + + switch (urand(1, 2)) + { + case 1: + { + switch (urand(1, 3)) + { + case 1: DoScriptText(SAY_RAND_ATTACK1, pCreatureTarget); break; + case 2: DoScriptText(SAY_RAND_ATTACK2, pCreatureTarget); break; + case 3: DoScriptText(SAY_RAND_ATTACK3, pCreatureTarget); break; + } + + pCreatureTarget->SetStandState(UNIT_STAND_STATE_STAND); + pCreatureTarget->AI()->AttackStart(pCaster); + break; + } + case 2: + { + switch (urand(1, 3)) + { + case 1: DoScriptText(SAY_RAND_WORK1, pCreatureTarget); break; + case 2: DoScriptText(SAY_RAND_WORK2, pCreatureTarget); break; + case 3: DoScriptText(SAY_RAND_WORK3, pCreatureTarget); break; + } + + pCreatureTarget->SetStandState(UNIT_STAND_STATE_STAND); + pCreatureTarget->HandleEmote(EMOTE_STATE_WORK); + break; + } + } + + return true; + } + return true; + } + case SPELL_INOCULATE_OWLKIN: + { + if (uiEffIndex == EFFECT_INDEX_0) + { + if (pCreatureTarget->GetEntry() != NPC_OWLKIN) + { return true; } + + pCreatureTarget->UpdateEntry(NPC_OWLKIN_INOC); + ((Player*)pCaster)->KilledMonsterCredit(NPC_OWLKIN_INOC); + + // set despawn timer, since we want to remove creature after a short time + pCreatureTarget->ForcedDespawn(15000); + + return true; + } + return true; + } + case SPELL_LIQUID_FIRE: + { + if (uiEffIndex == EFFECT_INDEX_0) + { + if (pCaster->GetTypeId() == TYPEID_PLAYER) + { + if (pCreatureTarget->HasAura(SPELL_LIQUID_FIRE_AURA)) + return true; + + if (pCreatureTarget->GetEntry() == NPC_ELK) + { + pCreatureTarget->CastSpell(pCreatureTarget, SPELL_LIQUID_FIRE_AURA, true); + ((Player*)pCaster)->KilledMonsterCredit(NPC_ELK_BUNNY); + } + else if (pCreatureTarget->GetEntry() == NPC_GRIZZLY) + { + pCreatureTarget->CastSpell(pCreatureTarget, SPELL_LIQUID_FIRE_AURA, true); + ((Player*)pCaster)->KilledMonsterCredit(NPC_GRIZZLY_BUNNY); + } + } + return true; + } + return true; + } + case SPELL_MODIFIED_MOJO: + { + if (uiEffIndex == EFFECT_INDEX_0) + { + if (pCreatureTarget->GetEntry() != NPC_PROPHET_OF_SSERATUS) + return true; + + // Apparently done before updateEntry, so need to make a way to handle that + // "Mmm, more mojo" + // "%s drinks the Mojo" + // "NOOOOOOOOOOOOooooooo...............!" + + pCreatureTarget->UpdateEntry(NPC_WEAK_PROPHET_OF_SSERATUS); + return true; + } + return true; + } + case SPELL_FEL_SIPHON_DUMMY: + { + if (uiEffIndex == EFFECT_INDEX_0) + { + if (pCreatureTarget->GetEntry() != NPC_FELBLOOD_INITIATE) + { return true; } + + pCreatureTarget->UpdateEntry(NPC_EMACIATED_FELBLOOD); + return true; + } + return true; + } + case SPELL_SACRED_CLEANSING: + { + if (uiEffIndex == EFFECT_INDEX_1) + { + if (pCreatureTarget->GetEntry() != NPC_MORBENT) + { return true; } + + pCreatureTarget->UpdateEntry(NPC_WEAKENED_MORBENT); + return true; + } + return true; + } + case SPELL_SEEDS_OF_NATURES_WRATH: + { + if (uiEffIndex == EFFECT_INDEX_0) + { + uint32 uiNewEntry = 0; + + switch (pCreatureTarget->GetEntry()) + { + case NPC_REANIMATED_FROSTWYRM: uiNewEntry = NPC_WEAK_REANIMATED_FROSTWYRM; break; + case NPC_TURGID: uiNewEntry = NPC_WEAK_TURGID; break; + case NPC_DEATHGAZE: uiNewEntry = NPC_WEAK_DEATHGAZE; break; + } + + if (uiNewEntry) + pCreatureTarget->UpdateEntry(uiNewEntry); + + return true; + } + return true; + } + case SPELL_STRENGTH_ANCIENTS: + { + if (uiEffIndex == EFFECT_INDEX_0) + { + if (pCaster->GetTypeId() == TYPEID_PLAYER) + { + if (urand(0, 1)) + { + DoScriptText(EMOTE_AGGRO, pCreatureTarget); + pCreatureTarget->setFaction(FACTION_HOSTILE); + pCreatureTarget->AI()->AttackStart(pCaster); + } + else + { + DoScriptText(EMOTE_CREATE, pCreatureTarget); + pCaster->CastSpell(pCaster, SPELL_CREATE_BARK_WALKERS, true); + pCreatureTarget->ForcedDespawn(5000); + } + } + return true; + } + return true; + } + case SPELL_TAG_MURLOC_PROC: + { + if (uiEffIndex == EFFECT_INDEX_0) + { + if (pCreatureTarget->GetEntry() == NPC_BLACKSILT_MURLOC) + { pCreatureTarget->UpdateEntry(NPC_TAGGED_MURLOC); } + } + return true; + } + case SPELL_THROW_BOULDER: + { + if (uiEffIndex == EFFECT_INDEX_0) + { + if (pCaster->GetTypeId() != TYPEID_PLAYER) + return true; + + if (pCreatureTarget->GetEntry() != NPC_IRON_RUNESHAPER && pCreatureTarget->GetEntry() != NPC_RUNE_REAVER) + return true; + + pCreatureTarget->CastSpell(pCreatureTarget, SPELL_BOULBER_IMPACT, true); + pCaster->CastSpell(pCaster, SPELL_BOULDER_TOSS_CREDIT, true); + + return true; + } + return true; + } + case SPELL_ULTRASONIC_SCREWDRIVER: + { + if (uiEffIndex == EFFECT_INDEX_0) + { + if (pCreatureTarget->IsCorpse()) + { + uint32 newSpellId = 0; + + switch (pCreatureTarget->GetEntry()) + { + case NPC_COLLECT_A_TRON: newSpellId = SPELL_SUMMON_COLLECT_A_TRON; break; + case NPC_DEFENDO_TANK: newSpellId = SPELL_SUMMON_DEFENDO_TANK; break; + case NPC_SCAVENGE_A8: newSpellId = SPELL_SUMMON_SCAVENGE_A8; break; + case NPC_SCAVENGE_B6: newSpellId = SPELL_SUMMON_SCAVENGE_B6; break; + case NPC_SENTRY_BOT: newSpellId = SPELL_SUMMON_SENTRY_BOT; break; + } + + if (const SpellEntry* pSpell = GetSpellStore()->LookupEntry(newSpellId)) + { + pCaster->CastSpell(pCreatureTarget, pSpell->Id, true); + + if (Pet* pPet = pCaster->FindGuardianWithEntry(pSpell->GetEffectMiscValue(SpellEffectIndex(uiEffIndex)))) + pPet->CastSpell(pCaster, SPELL_REPROGRAM_KILL_CREDIT, true); + + pCreatureTarget->ForcedDespawn(); + } + } + return true; + } + return true; + } + case SPELL_ORB_OF_MURLOC_CONTROL: + { + pCreatureTarget->CastSpell(pCaster, SPELL_GREENGILL_SLAVE_FREED, true); + + // Freed Greengill Slave + pCreatureTarget->UpdateEntry(NPC_FREED_GREENGILL_SLAVE); + + pCreatureTarget->CastSpell(pCreatureTarget, SPELL_ENRAGE, true); + + return true; + } + case SPELL_FUMPING: + { + if (uiEffIndex == EFFECT_INDEX_2) + { + switch (urand(0, 2)) + { + case 0: + { + pCaster->CastSpell(pCreatureTarget, SPELL_SUMMON_HAISHULUD, true); + break; + } + case 1: + { + for (int i = 0; i < 2; ++i) + { + if (Creature* pSandGnome = pCaster->SummonCreature(NPC_SAND_GNOME, pCreatureTarget->GetPositionX(), pCreatureTarget->GetPositionY(), pCreatureTarget->GetPositionZ(), 0.0f, TEMPSUMMON_TIMED_OOC_DESPAWN, 30000)) + { pSandGnome->AI()->AttackStart(pCaster); } + } + break; + } + case 2: + { + for (int i = 0; i < 2; ++i) + { + if (Creature* pMatureBoneSifter = pCaster->SummonCreature(NPC_MATURE_BONE_SIFTER, pCreatureTarget->GetPositionX(), pCreatureTarget->GetPositionY(), pCreatureTarget->GetPositionZ(), 0.0f, TEMPSUMMON_TIMED_OOC_DESPAWN, 30000)) + { pMatureBoneSifter->AI()->AttackStart(pCaster); } + } + break; + } + } + pCreatureTarget->ForcedDespawn(); + } + return true; + } + case SPELL_AHUNAES_KNIFE: + { + if (uiEffIndex == EFFECT_INDEX_0) + { + if (pCaster->GetTypeId() != TYPEID_PLAYER) + return true; + + ((Player*)pCaster)->KilledMonsterCredit(NPC_SCALPS_KILL_CREDIT_BUNNY); + pCreatureTarget->ForcedDespawn(); + return true; + } + return true; + } + case SPELL_TAILS_UP_GENDER_MASTER: + { + if (uiEffIndex == EFFECT_INDEX_0) + { + bool isMale = urand(0, 1); + Player* pPlayer = pCreatureTarget->GetLootRecipient(); + + if (isMale) + DoScriptText(SAY_ITS_MALE, pCreatureTarget, pPlayer); + else + DoScriptText(SAY_ITS_FEMALE, pCreatureTarget, pPlayer); + + switch (pCreatureTarget->GetEntry()) + { + case NPC_FROST_LEOPARD: + { + if (isMale) + pCreatureTarget->CastSpell(pCreatureTarget, SPELL_TAILS_UP_AURA, true); + else + { + pPlayer->KilledMonsterCredit(NPC_LEOPARD_KILL_CREDIT, pCreatureTarget->GetObjectGuid()); + pCreatureTarget->CastSpell(pPlayer, SPELL_FORCE_LEOPARD_SUMMON, true); + pCreatureTarget->ForcedDespawn(); + } + + break; + } + case NPC_ICEPAW_BEAR: + { + if (isMale) + pCreatureTarget->CastSpell(pCreatureTarget, SPELL_TAILS_UP_AURA, true); + else + { + pPlayer->KilledMonsterCredit(NPC_BEAR_KILL_CREDIT, pCreatureTarget->GetObjectGuid()); + pCreatureTarget->CastSpell(pPlayer, SPELL_FORCE_BEAR_SUMMON, true); + pCreatureTarget->ForcedDespawn(); + } + + break; + } + } + return true; + } + return true; + } + case SPELL_THROW_GORDAWG_BOULDER: + { + if (uiEffIndex == EFFECT_INDEX_0) + { + for (int i = 0; i < 3; ++i) + { + if (irand(i, 2)) // 2-3 summons + { pCreatureTarget->SummonCreature(NPC_MINION_OF_GUROK, 0.0f, 0.0f, 0.0f, 0.0f, TEMPSUMMON_CORPSE_DESPAWN, 5000); } + } + + if (pCreatureTarget->getVictim()) + { + pCaster->DealDamage(pCreatureTarget, pCreatureTarget->GetMaxHealth(), NULL, DIRECT_DAMAGE, SPELL_SCHOOL_MASK_NORMAL, NULL, false); + return true; + } + + // If not in combat, no xp or loot + pCreatureTarget->SetDeathState(JUST_DIED); + pCreatureTarget->SetHealth(0); + return true; + } + return true; + } + case SPELL_HIT_APPLE: + { + if (uiEffIndex == EFFECT_INDEX_0) + { + if (pCaster->GetTypeId() == TYPEID_PLAYER) + ((Player*)pCaster)->KilledMonsterCredit(pCreatureTarget->GetEntry(), pCreatureTarget->GetObjectGuid()); + + pCreatureTarget->CastSpell(pCreatureTarget, SPELL_APPLE_FALLS_TO_GROUND, false); + + if (Creature* pLuckyWilhelm = GetClosestCreatureWithEntry(pCreatureTarget, NPC_LUCKY_WILHELM, 2 * INTERACTION_DISTANCE)) + DoScriptText(SAY_LUCKY_HIT_APPLE, pLuckyWilhelm); + } + return true; + } + case SPELL_MISS_APPLE: + { + if (uiEffIndex == EFFECT_INDEX_0) + { + switch (urand(1, 3)) + { + case 1: DoScriptText(SAY_LUCKY_HIT_1, pCreatureTarget); break; + case 2: DoScriptText(SAY_LUCKY_HIT_2, pCreatureTarget); break; + case 3: DoScriptText(SAY_LUCKY_HIT_3, pCreatureTarget); break; + } + + if (Creature* pDrostan = GetClosestCreatureWithEntry(pCreatureTarget, NPC_DROSTAN, 4 * INTERACTION_DISTANCE)) + DoScriptText(urand(0, 1) ? SAY_DROSTAN_GOT_LUCKY_1 : SAY_DROSTAN_GOT_LUCKY_2, pDrostan); + } + return true; + } + case SPELL_MISS_APPLE_HIT_BIRD: + { + if (uiEffIndex == EFFECT_INDEX_0) + { + if (Creature* pDrostan = GetClosestCreatureWithEntry(pCreatureTarget, NPC_DROSTAN, 5 * INTERACTION_DISTANCE)) + DoScriptText(urand(0, 1) ? SAY_DROSTAN_HIT_BIRD_1 : SAY_DROSTAN_HIT_BIRD_2, pDrostan); + + pCreatureTarget->DealDamage(pCreatureTarget, pCreatureTarget->GetHealth(), NULL, DIRECT_DAMAGE, SPELL_SCHOOL_MASK_NORMAL, NULL, false); + } + return true; + } + case SPELL_LURIELLES_PENDANT: + { + if (uiEffIndex == EFFECT_INDEX_0) + { + if (pCreatureTarget->GetEntry() != NPC_CHILL_NYMPH || pCaster->GetTypeId() != TYPEID_PLAYER) + return true; + + switch (urand(0, 2)) + { + case 0: DoScriptText(SAY_FREE_1, pCreatureTarget); break; + case 1: DoScriptText(SAY_FREE_2, pCreatureTarget); break; + case 2: DoScriptText(SAY_FREE_3, pCreatureTarget); break; + } + + ((Player*)pCaster)->KilledMonsterCredit(NPC_LURIELLE); + pCreatureTarget->SetFactionTemporary(FACTION_FRIENDLY, TEMPFACTION_RESTORE_RESPAWN); + pCreatureTarget->DeleteThreatList(); + pCreatureTarget->AttackStop(true); + pCreatureTarget->GetMotionMaster()->MoveFleeing(pCaster, 7); + pCreatureTarget->ForcedDespawn(7 * IN_MILLISECONDS); + } + return true; + } + case SPELL_EXPOSE_RAZORTHORN_ROOT: + { + if (uiEffIndex == EFFECT_INDEX_0) + { + if (pCreatureTarget->GetEntry() != NPC_RAZORTHORN_RAVAGER) + { return true; } + + if (GameObject* pMound = GetClosestGameObjectWithEntry(pCreatureTarget, GO_RAZORTHORN_DIRT_MOUND, 20.0f)) + { + if (pMound->GetRespawnTime() != 0) + { return true; } + + pCreatureTarget->CastSpell(pCreatureTarget, SPELL_SUMMON_RAZORTHORN_ROOT, true); + pMound->SetLootState(GO_JUST_DEACTIVATED); + } + } + return true; + } + case SPELL_MELODIOUS_RAPTURE: + { + if (uiEffIndex == EFFECT_INDEX_0) + { + if (pCaster->GetTypeId() != TYPEID_PLAYER && pCreatureTarget->GetEntry() != NPC_DEEPRUN_RAT) + { + return true; + } + + pCreatureTarget->UpdateEntry(NPC_ENTHRALLED_DEEPRUN_RAT); + pCreatureTarget->CastSpell(pCreatureTarget, SPELL_MELODIOUS_RAPTURE_VISUAL, false); + pCreatureTarget->GetMotionMaster()->MoveFollow(pCaster, frand(0.5f, 3.0f), frand(M_PI_F * 0.8f, M_PI_F * 1.2f)); + + ((Player*)pCaster)->KilledMonsterCredit(NPC_ENTHRALLED_DEEPRUN_RAT); + } + return true; + } + } + + return false; +} + +void AddSC_spell_scripts() +{ + Script* pNewScript; + + pNewScript = new Script; + pNewScript->Name = "spell_dummy_go"; + pNewScript->pEffectDummyGO = &EffectDummyGameObj_spell_dummy_go; + pNewScript->RegisterSelf(); + + pNewScript = new Script; + pNewScript->Name = "spell_dummy_npc"; + pNewScript->pEffectDummyNPC = &EffectDummyCreature_spell_dummy_npc; + pNewScript->pEffectAuraDummy = &EffectAuraDummy_spell_aura_dummy_npc; + pNewScript->RegisterSelf(); +} diff --git a/src/modules/SD2/scripts/world/world_map_scripts.cpp b/src/modules/SD2/scripts/world/world_map_scripts.cpp new file mode 100644 index 000000000..2c687d075 --- /dev/null +++ b/src/modules/SD2/scripts/world/world_map_scripts.cpp @@ -0,0 +1,236 @@ +/** + * ScriptDev2 is an extension for mangos providing enhanced features for + * area triggers, creatures, game objects, instances, items, and spells beyond + * the default database scripting in mangos. + * + * Copyright (C) 2006-2013 ScriptDev2 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * World of Warcraft, and all World of Warcraft or Warcraft art, images, + * and lore are copyrighted by Blizzard Entertainment, Inc. + */ + +/* ScriptData +SDName: world_map_scripts +SD%Complete: 100 +SDComment: Quest support: 4740, 11538 +SDCategory: World Map Scripts +EndScriptData */ + +#include "precompiled.h" +#include "world_map_scripts.h" + +/* ********************************************************* + * EASTERN KINGDOMS + */ +struct world_map_eastern_kingdoms : public ScriptedMap +{ + world_map_eastern_kingdoms(Map* pMap) : ScriptedMap(pMap) {} + + void OnCreatureCreate(Creature* pCreature) + { + switch (pCreature->GetEntry()) + { + case NPC_JONATHAN: + case NPC_WRYNN: + case NPC_BOLVAR: + case NPC_PRESTOR: + case NPC_WINDSOR: + m_mNpcEntryGuidStore[pCreature->GetEntry()] = pCreature->GetObjectGuid(); + } + } + + void SetData(uint32 /*uiType*/, uint32 /*uiData*/) {} +}; + +InstanceData* GetInstanceData_world_map_eastern_kingdoms(Map* pMap) +{ + return new world_map_eastern_kingdoms(pMap); +} + +/* ********************************************************* + * KALIMDOR + */ +struct world_map_kalimdor : public ScriptedMap +{ + world_map_kalimdor(Map* pMap) : ScriptedMap(pMap) { Initialize(); } + + uint8 m_uiMurkdeepAdds_KilledAddCount; + + void Initialize() + { + m_uiMurkdeepAdds_KilledAddCount = 0; + } + + void OnCreatureCreate(Creature* pCreature) + { + if (pCreature->GetEntry() == NPC_MURKDEEP) + { + m_mNpcEntryGuidStore[NPC_MURKDEEP] = pCreature->GetObjectGuid(); + } + } + + void OnCreatureDeath(Creature* pCreature) + { + switch (pCreature->GetEntry()) + { + case NPC_GREYMIST_COASTRUNNNER: + if (pCreature->IsTemporarySummon()) // Only count the ones summoned for Murkdeep quest + { + ++m_uiMurkdeepAdds_KilledAddCount; + + // If all 3 coastrunners are killed, summon 2 warriors + if (m_uiMurkdeepAdds_KilledAddCount == 3) + { + float fX, fY, fZ; + for (uint8 i = 0; i < 2; ++i) + { + pCreature->GetRandomPoint(aSpawnLocations[POS_IDX_MURKDEEP_SPAWN][0], aSpawnLocations[POS_IDX_MURKDEEP_SPAWN][1], aSpawnLocations[POS_IDX_MURKDEEP_SPAWN][2], 5.0f, fX, fY, fZ); + + if (Creature* pTemp = pCreature->SummonCreature(NPC_GREYMIST_WARRIOR, fX, fY, fZ, aSpawnLocations[POS_IDX_MURKDEEP_SPAWN][3], TEMPSUMMON_DEAD_DESPAWN, 0)) + { + pTemp->SetWalk(false); + pTemp->GetRandomPoint(aSpawnLocations[POS_IDX_MURKDEEP_MOVE][0], aSpawnLocations[POS_IDX_MURKDEEP_MOVE][1], aSpawnLocations[POS_IDX_MURKDEEP_MOVE][2], 5.0f, fX, fY, fZ); + pTemp->GetMotionMaster()->MovePoint(0, fX, fY, fZ); + } + } + + m_uiMurkdeepAdds_KilledAddCount = 0; + } + } + break; + case NPC_GREYMIST_WARRIOR: + if (pCreature->IsTemporarySummon()) // Only count the ones summoned for Murkdeep quest + { + ++m_uiMurkdeepAdds_KilledAddCount; + + // After the 2 warriors are killed, Murkdeep spawns, along with a hunter + if (m_uiMurkdeepAdds_KilledAddCount == 2) + { + float fX, fY, fZ; + for (uint8 i = 0; i < 2; ++i) + { + pCreature->GetRandomPoint(aSpawnLocations[POS_IDX_MURKDEEP_SPAWN][0], aSpawnLocations[POS_IDX_MURKDEEP_SPAWN][1], aSpawnLocations[POS_IDX_MURKDEEP_SPAWN][2], 5.0f, fX, fY, fZ); + + if (Creature* pTemp = pCreature->SummonCreature(!i ? NPC_MURKDEEP : NPC_GREYMIST_HUNTER, fX, fY, fZ, aSpawnLocations[POS_IDX_MURKDEEP_SPAWN][3], TEMPSUMMON_DEAD_DESPAWN, 0)) + { + pTemp->SetWalk(false); + pTemp->GetRandomPoint(aSpawnLocations[POS_IDX_MURKDEEP_MOVE][0], aSpawnLocations[POS_IDX_MURKDEEP_MOVE][1], aSpawnLocations[POS_IDX_MURKDEEP_MOVE][2], 5.0f, fX, fY, fZ); + pTemp->GetMotionMaster()->MovePoint(0, fX, fY, fZ); + } + } + + m_uiMurkdeepAdds_KilledAddCount = 0; + } + } + break; + } + } + + void SetData(uint32 /*uiType*/, uint32 /*uiData*/) {} +}; + +InstanceData* GetInstanceData_world_map_kalimdor(Map* pMap) +{ + return new world_map_kalimdor(pMap); +} + +/* ********************************************************* + * OUTLAND + */ +struct world_map_outland : public ScriptedMap +{ + world_map_outland(Map* pMap) : ScriptedMap(pMap) { Initialize(); } + + uint8 m_uiEmissaryOfHate_KilledAddCount; + + void Initialize() + { + m_uiEmissaryOfHate_KilledAddCount = 0; + } + + void OnCreatureCreate(Creature* pCreature) + { + if (pCreature->GetEntry() == NPC_EMISSARY_OF_HATE) + { m_mNpcEntryGuidStore[NPC_EMISSARY_OF_HATE] = pCreature->GetObjectGuid(); } + } + + void OnCreatureDeath(Creature* pCreature) + { + switch (pCreature->GetEntry()) + { + case NPC_IRESPEAKER: + case NPC_UNLEASHED_HELLION: + if (!GetSingleCreatureFromStorage(NPC_EMISSARY_OF_HATE, true)) + { + ++m_uiEmissaryOfHate_KilledAddCount; + if (m_uiEmissaryOfHate_KilledAddCount == 6) + { + pCreature->SummonCreature(NPC_EMISSARY_OF_HATE, aSpawnLocations[POS_IDX_EMISSARY_SPAWN][0], aSpawnLocations[POS_IDX_EMISSARY_SPAWN][1], aSpawnLocations[POS_IDX_EMISSARY_SPAWN][2], aSpawnLocations[POS_IDX_EMISSARY_SPAWN][3], TEMPSUMMON_DEAD_DESPAWN, 0); + m_uiEmissaryOfHate_KilledAddCount = 0; + } + } + break; + } + } + + void SetData(uint32 /*uiType*/, uint32 /*uiData*/) {} +}; + +InstanceData* GetInstanceData_world_map_outland(Map* pMap) +{ + return new world_map_outland(pMap); +} + +/* ********************************************************* + * NORTHREND + */ +struct world_map_northrend : public ScriptedMap +{ + world_map_northrend(Map* pMap) : ScriptedMap(pMap) {} + + void SetData(uint32 /*uiType*/, uint32 /*uiData*/) {} +}; + +InstanceData* GetInstanceData_world_map_northrend(Map* pMap) +{ + return new world_map_northrend(pMap); +} + +void AddSC_world_map_scripts() +{ + Script* pNewScript; + + pNewScript = new Script; + pNewScript->Name = "world_map_eastern_kingdoms"; + pNewScript->GetInstanceData = &GetInstanceData_world_map_eastern_kingdoms; + pNewScript->RegisterSelf(); + + pNewScript = new Script; + pNewScript->Name = "world_map_kalimdor"; + pNewScript->GetInstanceData = &GetInstanceData_world_map_kalimdor; + pNewScript->RegisterSelf(); + + pNewScript = new Script; + pNewScript->Name = "world_map_outland"; + pNewScript->GetInstanceData = &GetInstanceData_world_map_outland; + pNewScript->RegisterSelf(); + + pNewScript = new Script; + pNewScript->Name = "world_map_northrend"; + pNewScript->GetInstanceData = &GetInstanceData_world_map_northrend; + pNewScript->RegisterSelf(); +} diff --git a/src/modules/SD2/scripts/world/world_map_scripts.h b/src/modules/SD2/scripts/world/world_map_scripts.h new file mode 100644 index 000000000..514214cd3 --- /dev/null +++ b/src/modules/SD2/scripts/world/world_map_scripts.h @@ -0,0 +1,66 @@ +/** + * ScriptDev2 is an extension for mangos providing enhanced features for + * area triggers, creatures, game objects, instances, items, and spells beyond + * the default database scripting in mangos. + * + * Copyright (C) 2006-2013 ScriptDev2 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * World of Warcraft, and all World of Warcraft or Warcraft art, images, + * and lore are copyrighted by Blizzard Entertainment, Inc. + */ + +#ifndef DEF_WORLD_MAP_SCRIPTS_H +#define DEF_WORLD_MAP_SCRIPTS_H + +enum +{ + // Quest 4740 + NPC_GREYMIST_COASTRUNNNER = 2202, + NPC_GREYMIST_WARRIOR = 2205, + NPC_GREYMIST_HUNTER = 2206, + NPC_MURKDEEP = 10323, + QUEST_WANTED_MURKDEEP = 4740, + + // Quest 6403 + NPC_JONATHAN = 466, + NPC_WRYNN = 1747, + NPC_BOLVAR = 1748, + NPC_PRESTOR = 1749, + NPC_WINDSOR = 12580, + + // Quest 11538 + NPC_EMISSARY_OF_HATE = 25003, + NPC_IRESPEAKER = 24999, + NPC_UNLEASHED_HELLION = 25002, +}; + +enum SpawnIndexes +{ + POS_IDX_EMISSARY_SPAWN = 0, // Not used in Classic, but keep here for code change simplification + POS_IDX_MURKDEEP_SPAWN = 1, + POS_IDX_MURKDEEP_MOVE = 2, + POS_IDX_MAX = 3 +}; + +static const float aSpawnLocations[POS_IDX_MAX][4] = +{ + {12583.019f, -6916.194f, 4.601f, 6.18f}, // Emissary of Hate, guesswork + {4981.031f, 597.955f, -1.361f, 4.82f}, // Murkdeep spawn, guesswork + {4988.970f, 547.002f, 5.379f, 0.0f}, // Murkdeep move, guesswork +}; + +#endif diff --git a/src/modules/SD2/sd2_revision_nr.h b/src/modules/SD2/sd2_revision_nr.h new file mode 100644 index 000000000..b23689775 --- /dev/null +++ b/src/modules/SD2/sd2_revision_nr.h @@ -0,0 +1,29 @@ +/** + * ScriptDev2 is an extension for mangos providing enhanced features for + * area triggers, creatures, game objects, instances, items, and spells beyond + * the default database scripting in mangos. + * + * Copyright (C) 2006-2013 ScriptDev2 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * World of Warcraft, and all World of Warcraft or Warcraft art, images, + * and lore are copyrighted by Blizzard Entertainment, Inc. + */ + +#ifndef __SD2_REVISION_NR_H__ +#define __SD2_REVISION_NR_H__ + #define SD2_REVISION_NR "Rel20.2929" +#endif // __SD2_REVISION_NR_H__ diff --git a/src/modules/SD2/sd2_revision_sql.h b/src/modules/SD2/sd2_revision_sql.h new file mode 100644 index 000000000..a3aaea168 --- /dev/null +++ b/src/modules/SD2/sd2_revision_sql.h @@ -0,0 +1,30 @@ +/** + * ScriptDev2 is an extension for mangos providing enhanced features for + * area triggers, creatures, game objects, instances, items, and spells beyond + * the default database scripting in mangos. + * + * Copyright (C) 2006-2013 ScriptDev2 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * World of Warcraft, and all World of Warcraft or Warcraft art, images, + * and lore are copyrighted by Blizzard Entertainment, Inc. + */ + +#ifndef __SD2_REVISION_SQL_H__ +#define __SD2_REVISION_SQL_H__ +#define REVISION_DB_SCRIPTDEV2 "required__scriptdev2" +#define REVISION_DB_SD2_MANGOS "required__mangos" +#endif // __SD2_REVISION_SQL_H__ diff --git a/src/modules/SD2/sql/mangos_scriptname_clear.sql b/src/modules/SD2/sql/mangos_scriptname_clear.sql new file mode 100644 index 000000000..9be32f4ea --- /dev/null +++ b/src/modules/SD2/sql/mangos_scriptname_clear.sql @@ -0,0 +1,10 @@ +-- Clear all ScriptNames +-- This will clear all ScriptNames from any table in the World-Database + +TRUNCATE scripted_areatrigger; +TRUNCATE scripted_event_id; +UPDATE creature_template SET ScriptName=''; +UPDATE gameobject_template SET ScriptName=''; +UPDATE item_template SET ScriptName=''; +UPDATE instance_template SET ScriptName=''; +UPDATE world_template SET ScriptName=''; diff --git a/src/modules/SD2/sql/mangos_scriptname_full.sql b/src/modules/SD2/sql/mangos_scriptname_full.sql new file mode 100644 index 000000000..5f32e4890 --- /dev/null +++ b/src/modules/SD2/sql/mangos_scriptname_full.sql @@ -0,0 +1,1418 @@ +/* */ + +/* AREATRIGGER */ +DELETE FROM scripted_areatrigger WHERE entry=4591; +INSERT INTO scripted_areatrigger VALUES (4591,'at_coilfang_waterfall'); +DELETE FROM scripted_areatrigger WHERE entry=4560; +INSERT INTO scripted_areatrigger VALUES (4560,'at_legion_teleporter'); +DELETE FROM scripted_areatrigger WHERE entry=3066; +INSERT INTO scripted_areatrigger VALUES (3066,'at_ravenholdt'); +DELETE FROM scripted_areatrigger WHERE entry IN (4871,4872,4873); +INSERT INTO scripted_areatrigger VALUES +(4871,'at_warsong_farms'), +(4872,'at_warsong_farms'), +(4873,'at_warsong_farms'); +DELETE FROM scripted_areatrigger WHERE entry IN (5046,5047); +INSERT INTO scripted_areatrigger VALUES +(5046,'at_waygate'), +(5047,'at_waygate'); +DELETE FROM scripted_areatrigger WHERE entry BETWEEN 5284 AND 5287; +INSERT INTO scripted_areatrigger VALUES +(5284,'at_aldurthar_gate'), +(5285,'at_aldurthar_gate'), +(5286,'at_aldurthar_gate'), +(5287,'at_aldurthar_gate'); +DELETE FROM scripted_areatrigger WHERE entry IN (4112,4113); +INSERT INTO scripted_areatrigger VALUES +(4112,'at_naxxramas'), +(4113,'at_naxxramas'); +DELETE FROM scripted_areatrigger WHERE entry=5108; +INSERT INTO scripted_areatrigger VALUES (5108,'at_stormwright_shelf'); +DELETE FROM scripted_areatrigger WHERE entry IN (3546,3547,3548,3549,3550,3552); +INSERT INTO scripted_areatrigger VALUES +(3546,'at_childrens_week_spot'), -- Darnassian bank +(3547,'at_childrens_week_spot'), -- Undercity - thone room +(3548,'at_childrens_week_spot'), -- Stonewrought Dam +(3549,'at_childrens_week_spot'), -- The Mor'shan Rampart +(3550,'at_childrens_week_spot'), -- Ratchet Docks +(3552,'at_childrens_week_spot'); -- Westfall Lighthouse +DELETE FROM scripted_areatrigger WHERE entry IN (2026,2046); +INSERT INTO scripted_areatrigger VALUES +(2026,'at_blackrock_spire'), +(2046,'at_blackrock_spire'); +DELETE FROM scripted_areatrigger WHERE entry=5030; +INSERT INTO scripted_areatrigger VALUES (5030,'at_spearborn_encampment'); +DELETE FROM scripted_areatrigger WHERE entry IN (3958,3960); +INSERT INTO scripted_areatrigger VALUES +(3958,'at_zulgurub'), +(3960,'at_zulgurub'); +DELETE FROM scripted_areatrigger WHERE entry=3626; +INSERT INTO scripted_areatrigger VALUES (3626,'at_vaelastrasz'); +DELETE FROM scripted_areatrigger WHERE entry=4937; +INSERT INTO scripted_areatrigger VALUES (4937,'at_sunwell_plateau'); +DELETE FROM scripted_areatrigger WHERE entry=4524; +INSERT INTO scripted_areatrigger VALUES (4524,'at_shattered_halls'); +DELETE FROM scripted_areatrigger WHERE entry BETWEEN 1726 AND 1740; +INSERT INTO scripted_areatrigger VALUES +(1726,'at_scent_larkorwi'), +(1727,'at_scent_larkorwi'), +(1728,'at_scent_larkorwi'), +(1729,'at_scent_larkorwi'), +(1730,'at_scent_larkorwi'), +(1731,'at_scent_larkorwi'), +(1732,'at_scent_larkorwi'), +(1733,'at_scent_larkorwi'), +(1734,'at_scent_larkorwi'), +(1735,'at_scent_larkorwi'), +(1736,'at_scent_larkorwi'), +(1737,'at_scent_larkorwi'), +(1738,'at_scent_larkorwi'), +(1739,'at_scent_larkorwi'), +(1740,'at_scent_larkorwi'); +DELETE FROM scripted_areatrigger WHERE entry IN (5604,5709,5732); +INSERT INTO scripted_areatrigger VALUES +(5604,'at_icecrown_citadel'), +(5709,'at_icecrown_citadel'), +(5732,'at_icecrown_citadel'); +DELETE FROM scripted_areatrigger WHERE entry in (4288,4485); +INSERT INTO scripted_areatrigger VALUES +(4288,'at_dark_portal'), +(4485,'at_dark_portal'); +DELETE FROM scripted_areatrigger WHERE entry=1966; +INSERT INTO scripted_areatrigger VALUES (1966,'at_murkdeep'); +DELETE FROM scripted_areatrigger WHERE entry=4047; +INSERT INTO scripted_areatrigger VALUES (4047,'at_temple_ahnqiraj'); + + +/* BATTLEGROUNDS */ +UPDATE creature_template SET ScriptName='npc_spirit_guide' WHERE entry IN (13116, 13117); + +/* WORLD BOSS */ +UPDATE creature_template SET ScriptName='boss_ysondre' WHERE entry=14887; +UPDATE creature_template SET ScriptName='boss_emeriss' WHERE entry=14889; +UPDATE creature_template SET ScriptName='boss_taerar' WHERE entry=14890; +UPDATE creature_template SET ScriptName='boss_azuregos' WHERE entry=6109; +UPDATE creature_template SET ScriptName='boss_lethon' WHERE entry=14888; +UPDATE creature_template SET ScriptName='npc_spirit_shade' WHERE entry=15261; + +/* GO */ +UPDATE gameobject_template SET ScriptName='go_barov_journal' WHERE entry=180794; +UPDATE gameobject_template SET ScriptName='go_ethereum_prison' WHERE entry BETWEEN 184418 AND 184431; +UPDATE gameobject_template SET ScriptName='go_ethereum_stasis' WHERE entry BETWEEN 185465 AND 185467; +UPDATE gameobject_template SET ScriptName='go_ethereum_stasis' WHERE entry=184595; +UPDATE gameobject_template SET ScriptName='go_ethereum_stasis' WHERE entry BETWEEN 185461 AND 185464; +UPDATE gameobject_template SET ScriptName='go_jump_a_tron' WHERE entry=183146; +UPDATE gameobject_template SET ScriptName='go_mysterious_snow_mound' WHERE entry=195308; +UPDATE gameobject_template SET ScriptName='go_tele_to_dalaran_crystal' WHERE entry=191230; +UPDATE gameobject_template SET ScriptName='go_tele_to_violet_stand' WHERE entry=191229; +UPDATE gameobject_template SET ScriptName='go_andorhal_tower' WHERE entry IN (176094,176095,176096,176097); +UPDATE gameobject_template SET ScriptName='go_scourge_enclosure' WHERE entry=191548; +UPDATE gameobject_template SET ScriptName='go_veil_skith_cage' WHERE entry IN (185202,185203,185204,185205); +UPDATE gameobject_template SET ScriptName='go_lab_work_reagents' WHERE entry IN (190462, 190473, 190478, 190459); + +/* GUARD */ +UPDATE creature_template SET ScriptName='guard_azuremyst' WHERE entry=18038; +UPDATE creature_template SET ScriptName='guard_orgrimmar' WHERE entry=3296; +UPDATE creature_template SET ScriptName='guard_stormwind' WHERE entry IN (68,1976); +UPDATE creature_template SET ScriptName='guard_contested' WHERE entry IN (9460,4624,3502,11190,15184); +UPDATE creature_template SET ScriptName='guard_elwynnforest' WHERE entry=1423; +UPDATE creature_template SET ScriptName='guard_eversong' WHERE entry=16221; +UPDATE creature_template SET ScriptName='guard_darnassus' WHERE entry=4262; +UPDATE creature_template SET ScriptName='guard_teldrassil' WHERE entry=3571; +UPDATE creature_template SET ScriptName='guard_ironforge' WHERE entry=5595; +UPDATE creature_template SET ScriptName='guard_dunmorogh' WHERE entry IN (727,13076); +UPDATE creature_template SET ScriptName='guard_undercity' WHERE entry=5624; +UPDATE creature_template SET ScriptName='guard_bluffwatcher' WHERE entry=3084; +UPDATE creature_template SET ScriptName='guard_durotar' WHERE entry=5953; +UPDATE creature_template SET ScriptName='guard_mulgore' WHERE entry IN (3212,3215,3217,3218,3219,3220,3221,3222,3223,3224); +UPDATE creature_template SET ScriptName='guard_dunmorogh' WHERE entry IN (727,13076); +UPDATE creature_template SET ScriptName='guard_tirisfal' WHERE entry IN (1735,1738,2210,1744,1745,5725,1743,2209,1746,1742); +UPDATE creature_template SET ScriptName='guard_silvermoon' WHERE entry=16222; +UPDATE creature_template SET ScriptName='guard_exodar' WHERE entry=16733; +UPDATE creature_template SET ScriptName='guard_shattrath' WHERE entry=19687; +UPDATE creature_template SET ScriptName='guard_shattrath_aldor' WHERE entry=18549; +UPDATE creature_template SET ScriptName='guard_shattrath_scryer' WHERE entry=18568; + +/* ITEM */ +UPDATE item_template SET ScriptName='item_arcane_charges' WHERE entry=34475; +UPDATE item_template SET ScriptName='item_flying_machine' WHERE entry IN (34060,34061); +UPDATE item_template SET ScriptName='item_gor_dreks_ointment' WHERE entry=30175; +UPDATE item_template SET ScriptName='item_petrov_cluster_bombs' WHERE entry=33098; + +/* NPC (usually creatures to be found in more than one specific zone) */ +UPDATE creature_template SET ScriptName='npc_air_force_bots' WHERE entry IN (2614, 2615, 21974, 21993, 21996, 21997, 21999, 22001, 22002, 22003, 22063, 22065, 22066, 22068, 22069, 22070, 22071, 22078, 22079, 22080, 22086, 22087, 22088, 22090, 22124, 22125, 22126); +UPDATE creature_template SET ScriptName='npc_chicken_cluck' WHERE entry=620; +UPDATE creature_template SET ScriptName='npc_dancing_flames' WHERE entry=25305; +UPDATE creature_template SET ScriptName='npc_garments_of_quests' WHERE entry IN (12429,12423,12427,12430,12428); +UPDATE creature_template SET ScriptName='npc_guardian' WHERE entry=5764; +UPDATE creature_template SET ScriptName='npc_doctor' WHERE entry IN (12939,12920); +UPDATE creature_template SET ScriptName='npc_injured_patient' WHERE entry IN (12936,12937,12938,12923,12924,12925); +UPDATE creature_template SET ScriptName='npc_prof_blacksmith' WHERE entry IN (5164,11145,11146,11176,11177,11178,11191,11192,11193); +UPDATE creature_template SET ScriptName='npc_prof_leather' WHERE entry IN (7866,7867,7868,7869,7870,7871); +UPDATE creature_template SET ScriptName='npc_prof_tailor' WHERE entry IN (22208,22212,22213); +-- disabled, but can be used for custom +-- UPDATE creature_template SET ScriptName='' WHERE npcflag!=npcflag|65536 AND ScriptName='npc_innkeeper'; +-- UPDATE creature_template SET ScriptName='npc_innkeeper' WHERE npcflag=npcflag|65536; +UPDATE creature_template SET ScriptName='npc_spring_rabbit' WHERE entry=32791; +UPDATE creature_template SET ScriptName='npc_redemption_target' WHERE entry IN (6172,6177,17542,17768); + +/* SPELL */ +UPDATE creature_template SET ScriptName='spell_dummy_npc' WHERE entry IN ( +-- eastern kingdoms +1200,8888,13016, +-- kalimdor +9299,12296,12298, +-- outland +16880,16518,16847,17157,17326,17654,18879,21729,22105,24918,24922,25084,25085, +-- northrend +23678,25752,25753,25758,25792,25793,26268,26270,26421,26616,26643,26841,26924,27122,27808,28053,28054,28068,28093,28465,28600,29319,29327,29329,29330,29338,30146,32149); + +UPDATE gameobject_template SET ScriptName='spell_dummy_go' WHERE entry IN (181616,186949); + +/* WORLD MAP SCRIPTS */ +DELETE FROM world_template WHERE map IN (0, 1, 530, 571, 609); +INSERT INTO world_template VALUES +(0, 'world_map_eastern_kingdoms'), +(1, 'world_map_kalimdor'), +(530, 'world_map_outland'), +(571, 'world_map_northrend'), +(609, 'world_map_ebon_hold'); + +/* */ +/* ZONE */ +/* */ + +/* ALTERAC MOUNTAINS */ + +/* ALTERAC VALLEY */ + +/* ARATHI HIGHLANDS */ +UPDATE creature_template SET ScriptName='npc_professor_phizzlethorpe' WHERE entry=2768; +UPDATE creature_template SET ScriptName='npc_kinelory' WHERE entry=2713; + +/* ASHENVALE */ +UPDATE creature_template SET ScriptName='npc_muglash' WHERE entry=12717; +UPDATE gameobject_template SET ScriptName='go_naga_brazier' WHERE entry=178247; +UPDATE creature_template SET ScriptName='npc_ruul_snowhoof' WHERE entry=12818; +UPDATE creature_template SET ScriptName='npc_torek' WHERE entry=12858; +UPDATE creature_template SET ScriptName='npc_feero_ironhand' WHERE entry=4484; + +/* */ +/* AUCHINDOUN */ +/* */ + +/* MANA TOMBS */ +UPDATE creature_template SET ScriptName='boss_pandemonius' WHERE entry=18341; +UPDATE creature_template SET ScriptName='boss_nexusprince_shaffar' WHERE entry=18344; + +/* AUCHENAI CRYPTS */ +UPDATE creature_template SET ScriptName='boss_exarch_maladaar' WHERE entry=18373; +UPDATE creature_template SET ScriptName='mob_stolen_soul' WHERE entry=18441; +UPDATE creature_template SET ScriptName='boss_shirrak' WHERE entry=18371; + +/* SETHEKK HALLS */ +UPDATE instance_template SET ScriptName='instance_sethekk_halls' WHERE map=556; +UPDATE creature_template SET ScriptName='boss_talon_king_ikiss' WHERE entry=18473; +UPDATE creature_template SET ScriptName='boss_darkweaver_syth' WHERE entry=18472; +UPDATE creature_template SET ScriptName='boss_anzu' WHERE entry=23035; +DELETE FROM scripted_event_id WHERE id=14797; +INSERT INTO scripted_event_id VALUES +(14797,'event_spell_summon_raven_god'); + +/* SHADOW LABYRINTH */ +UPDATE instance_template SET ScriptName='instance_shadow_labyrinth' WHERE map=555; +UPDATE creature_template SET ScriptName='boss_murmur' WHERE entry=18708; +UPDATE creature_template SET ScriptName='boss_grandmaster_vorpil' WHERE entry=18732; +UPDATE creature_template SET ScriptName='boss_blackheart_the_inciter' WHERE entry=18667; +UPDATE creature_template SET ScriptName='boss_ambassador_hellmaw' WHERE entry=18731; +UPDATE creature_template SET ScriptName='npc_void_traveler' WHERE entry=19226; + +/* */ +/* AZJOL-NERUB */ +/* */ + +/* AHN'KAHET */ +UPDATE creature_template SET ScriptName='boss_amanitar' WHERE entry=30258; +UPDATE creature_template SET ScriptName='npc_amanitar_mushroom' WHERE entry IN (30391,30435); +UPDATE creature_template SET ScriptName='boss_jedoga' WHERE entry=29310; +UPDATE creature_template SET ScriptName='npc_twilight_volunteer' WHERE entry=30385; +UPDATE creature_template SET ScriptName='boss_nadox' WHERE entry=29309; +UPDATE creature_template SET ScriptName='mob_ahnkahar_egg' WHERE entry IN (30172,30173); +UPDATE creature_template SET ScriptName='boss_taldaram' WHERE entry=29308; +UPDATE gameobject_template SET ScriptName='go_nerubian_device' WHERE entry IN (193093,193094); +UPDATE creature_template SET ScriptName='boss_volazj' WHERE entry=29311; +UPDATE instance_template SET ScriptName='instance_ahnkahet' WHERE map=619; + +/* AZJOL-NERUB */ +UPDATE creature_template SET ScriptName='boss_anubarak' WHERE entry=29120; +UPDATE creature_template SET ScriptName='npc_impale_target' WHERE entry=29184; +UPDATE creature_template SET ScriptName='boss_hadronox' WHERE entry=28921; +UPDATE creature_template SET ScriptName='boss_krikthir' WHERE entry=28684; +UPDATE instance_template SET ScriptName='instance_azjol-nerub' WHERE map=601; + +/* AZSHARA */ +UPDATE creature_template SET ScriptName='npc_rizzle_sprysprocket' WHERE entry=23002; +UPDATE creature_template SET ScriptName='npc_depth_charge' WHERE entry=23025; +UPDATE gameobject_template SET ScriptName='go_southfury_moonstone' WHERE entry=185566; +UPDATE creature_template SET ScriptName='mobs_spitelashes' WHERE entry IN (6190,6193,6194,6195,6196); +UPDATE creature_template SET ScriptName='npc_loramus_thalipedes' WHERE entry=7783; + +/* AZUREMYST ISLE */ +UPDATE creature_template SET ScriptName='npc_draenei_survivor' WHERE entry=16483; +UPDATE creature_template SET ScriptName='npc_engineer_spark_overgrind' WHERE entry=17243; +UPDATE creature_template SET ScriptName='npc_injured_draenei' WHERE entry=16971; +UPDATE creature_template SET ScriptName='npc_magwin' WHERE entry=17312; + +/* BADLANDS */ + +/* BARRENS */ +UPDATE creature_template SET ScriptName='npc_beaten_corpse' WHERE entry=10668; +UPDATE creature_template SET ScriptName='npc_gilthares' WHERE entry=3465; +UPDATE creature_template SET ScriptName='npc_taskmaster_fizzule' WHERE entry=7233; +UPDATE creature_template SET ScriptName='npc_twiggy_flathead' WHERE entry=6248; +DELETE FROM scripted_areatrigger WHERE entry=522; +INSERT INTO scripted_areatrigger VALUES (522,'at_twiggy_flathead'); +UPDATE creature_template SET ScriptName='npc_wizzlecranks_shredder' WHERE entry=3439; + +/* BLACK TEMPLE */ +UPDATE instance_template SET ScriptName='instance_black_temple' WHERE map=564; +UPDATE creature_template SET ScriptName='npc_akama_shade' WHERE entry=22990; -- Akama at Shade of Akama +UPDATE creature_template SET ScriptName='npc_akama_illidan' WHERE entry=23089; -- Akama at Illidan +UPDATE creature_template SET ScriptName='mob_illidari_council' WHERE entry=23426; -- Illidari Council controller mob +UPDATE creature_template SET ScriptName='mob_blood_elf_council_voice_trigger' WHERE entry=23499; -- Voice Trigger Mob (Controls Aggro + Enrage yells) +UPDATE creature_template SET ScriptName='boss_veras_darkshadow' WHERE entry=22952; -- Rogue of Illidari Council +UPDATE creature_template SET ScriptName='boss_teron_gorefiend' WHERE entry=22871; -- Teron Gorefiend +UPDATE creature_template SET ScriptName='boss_supremus' WHERE entry=22898; -- Supremus +UPDATE creature_template SET ScriptName='boss_shade_of_akama' WHERE entry=22841; -- Shade of Akama +UPDATE creature_template SET ScriptName='boss_reliquary_of_souls' WHERE entry=22856; -- Reliquary Controller Mob +UPDATE creature_template SET ScriptName='boss_essence_of_suffering' WHERE entry=23418; -- Essence Of Suffering +UPDATE creature_template SET ScriptName='boss_essence_of_desire' WHERE entry=23419; -- Essence of Desire +UPDATE creature_template SET ScriptName='boss_essence_of_anger' WHERE entry=23420; -- Essence of Anger +UPDATE creature_template SET ScriptName='boss_najentus' WHERE entry=22887; -- High Warlord Naj'entus +UPDATE creature_template SET ScriptName='boss_gurtogg_bloodboil' WHERE entry=22948; -- Gurtogg Bloodboil +UPDATE creature_template SET ScriptName='boss_mother_shahraz' WHERE entry=22947; -- Mother Shahraz +UPDATE creature_template SET ScriptName='boss_lady_malande' WHERE entry=22951; -- Priest <3 at Illidari Council +UPDATE creature_template SET ScriptName='boss_illidan_stormrage' WHERE entry=22917; -- Illidan The Betrayer! +UPDATE creature_template SET ScriptName='boss_high_nethermancer_zerevor' WHERE entry=22950; -- Mage at Illidari Council +UPDATE creature_template SET ScriptName='boss_gathios_the_shatterer' WHERE entry=22949; -- Paladin at Illidari Council +UPDATE creature_template SET ScriptName='boss_maiev_shadowsong' WHERE entry=23197; -- Maiev Shadowsong +UPDATE creature_template SET ScriptName='mob_flame_of_azzinoth' WHERE entry=22997; -- Flame of Azzinoth (Illidan Phase 2) +UPDATE creature_template SET ScriptName='mob_blade_of_azzinoth' WHERE entry=22996; -- Blade of Azzinoth (Illidan Phase 2) +UPDATE creature_template SET ScriptName='mob_cage_trap_trigger' WHERE entry=23304; -- Cage Trap mob in Illidan Phase 3/4 Normal +UPDATE creature_template SET ScriptName='mob_shadow_demon' WHERE entry=23375; -- Shadow Demon in Illidan Demon Form +UPDATE creature_template SET ScriptName='npc_volcano' WHERE entry=23085; -- Supremus Volcano +UPDATE creature_template SET ScriptName='molten_flame' WHERE entry=23095; -- Molten Flame in SUpremus +UPDATE creature_template SET ScriptName='mob_ashtongue_channeler' WHERE entry=23421; -- Ashtongue CHanneler in Shade of AKama +UPDATE creature_template SET ScriptName='mob_ashtongue_sorcerer' WHERE entry=23215; -- Ashtongue Sorcerer in Shade of Akama +UPDATE creature_template SET ScriptName='npc_spirit_of_olum' WHERE entry=23411; +UPDATE creature_template SET ScriptName='npc_enslaved_soul' WHERE entry=23469; + +/* BLACKFATHOM DEPTHS */ +UPDATE instance_template SET ScriptName='instance_blackfathom_deeps' WHERE map=48; +UPDATE gameobject_template SET ScriptName='go_fire_of_akumai' WHERE entry IN (21118,21119,21120,21121); + +/* BLACKROCK CAVERNS */ +UPDATE instance_template SET ScriptName='instance_blackrock_caverns' WHERE map=645; +UPDATE creature_template SET ScriptName='boss_romogg' WHERE entry=39665; +UPDATE creature_template SET ScriptName='boss_corla' WHERE entry=39679; +UPDATE creature_template SET ScriptName='boss_karsh_steelbender' WHERE entry=39698; +UPDATE creature_template SET ScriptName='boss_beauty' WHERE entry=39700; +UPDATE creature_template SET ScriptName='boss_lord_obsidius' WHERE entry=39705; + +/* BLACKROCK DEPTHS */ +DELETE FROM scripted_areatrigger WHERE entry=1526; +INSERT INTO scripted_areatrigger VALUES (1526,'at_ring_of_law'); +UPDATE instance_template SET ScriptName='instance_blackrock_depths' WHERE map =230; +UPDATE creature_template SET ScriptName='boss_emperor_dagran_thaurissan' WHERE entry=9019; +UPDATE creature_template SET ScriptName='boss_moira_bronzebeard' WHERE entry=8929; +UPDATE creature_template SET ScriptName='boss_ambassador_flamelash' WHERE entry=9156; +UPDATE creature_template SET ScriptName='boss_doomrel' WHERE entry=9039; +UPDATE creature_template SET ScriptName='boss_general_angerforge' WHERE entry=9033; +UPDATE creature_template SET ScriptName='boss_high_interrogator_gerstahn' WHERE entry=9018; +UPDATE creature_template SET ScriptName='boss_coren_direbrew' WHERE entry=23872; +UPDATE creature_template SET ScriptName='npc_grimstone' WHERE entry=10096; +UPDATE creature_template SET ScriptName='npc_theldren_trigger' WHERE entry=16079; +UPDATE creature_template SET ScriptName='npc_kharan_mighthammer' WHERE entry=9021; +UPDATE creature_template SET ScriptName='npc_rocknot' WHERE entry=9503; +UPDATE creature_template SET ScriptName='npc_marshal_windsor' WHERE entry=9023; +UPDATE creature_template SET ScriptName='npc_dughal_stormwing' WHERE entry=9022; +UPDATE creature_template SET ScriptName='npc_tobias_seecher' WHERE entry=9679; +UPDATE gameobject_template SET ScriptName='go_shadowforge_brazier' WHERE entry IN (174744, 174745); +UPDATE gameobject_template SET ScriptName='go_relic_coffer_door' WHERE entry IN (174554, 174555, 174556, 174557, 174558, 174559, 174560, 174561, 174562, 174563, 174564, 174566); + +/* BLACKROCK SPIRE */ +UPDATE instance_template SET ScriptName='instance_blackrock_spire' WHERE map=229; +UPDATE creature_template SET ScriptName='boss_overlord_wyrmthalak' WHERE entry=9568; +UPDATE creature_template SET ScriptName='boss_gyth' WHERE entry=10339; +UPDATE creature_template SET ScriptName='boss_pyroguard_emberseer' WHERE entry=9816; +DELETE FROM scripted_event_id WHERE id=4884; +INSERT INTO scripted_event_id VALUES +(4884,'event_spell_altar_emberseer'); +UPDATE gameobject_template SET ScriptName='go_father_flame' WHERE entry=175245; + +/* BLACKWING LAIR */ +UPDATE instance_template SET ScriptName='instance_blackwing_lair' WHERE map=469; +UPDATE creature_template SET ScriptName='boss_razorgore' WHERE entry=12435; +UPDATE gameobject_template SET ScriptName='go_black_dragon_egg' WHERE entry=177807; +UPDATE creature_template SET ScriptName='boss_vaelastrasz' WHERE entry=13020; +UPDATE creature_template SET ScriptName='boss_broodlord' WHERE entry=12017; +UPDATE creature_template SET ScriptName='boss_firemaw' WHERE entry=11983; +UPDATE creature_template SET ScriptName='boss_ebonroc' WHERE entry=14601; +UPDATE creature_template SET ScriptName='boss_flamegor' WHERE entry=11981; +UPDATE creature_template SET ScriptName='boss_chromaggus' WHERE entry=14020; +UPDATE creature_template SET ScriptName='boss_victor_nefarius' WHERE entry=10162; +UPDATE creature_template SET ScriptName='boss_nefarian' WHERE entry=11583; + +/* BLADE'S EDGE MOUNTAINS */ +UPDATE creature_template SET ScriptName='mobs_nether_drake' WHERE entry IN (20021,21817,21820,21821,21823); +UPDATE creature_template SET ScriptName='npc_daranelle' WHERE entry=21469; +UPDATE creature_template SET ScriptName='npc_bloodmaul_stout_trigger' WHERE entry=21241; +UPDATE creature_template SET ScriptName='npc_simon_game_bunny' WHERE entry=22923; + +/* BLASTED LANDS */ +UPDATE creature_template SET ScriptName='npc_fallen_hero_of_horde' WHERE entry=7572; + +/* BLOODMYST ISLE */ +UPDATE creature_template SET ScriptName='mob_webbed_creature' WHERE entry=17680; + +/* BOREAN TUNDRA */ +UPDATE creature_template SET ScriptName='npc_nesingwary_trapper' WHERE entry=25835; +UPDATE creature_template SET ScriptName='npc_oil_stained_wolf' WHERE entry=25791; +UPDATE creature_template SET ScriptName='npc_sinkhole_kill_credit' WHERE entry IN (26248,26249); +UPDATE creature_template SET ScriptName='npc_lurgglbr' WHERE entry=25208; + +/* BURNING STEPPES */ +UPDATE creature_template SET ScriptName='npc_ragged_john' WHERE entry=9563; +UPDATE creature_template SET ScriptName='npc_grark_lorkrub' WHERE entry=9520; + +/* */ +/* CAVERNS OF TIME */ +/* */ + +/* MT. HYJAL */ +UPDATE instance_template SET ScriptName='instance_hyjal' WHERE map=534; +UPDATE creature_template SET ScriptName='npc_tyrande_whisperwind' WHERE entry=17948; +UPDATE creature_template SET ScriptName='npc_thrall' WHERE entry=17852; +UPDATE creature_template SET ScriptName='npc_jaina_proudmoore' WHERE entry=17772; +UPDATE creature_template SET ScriptName='boss_archimonde' WHERE entry=17968; +UPDATE creature_template SET ScriptName='npc_doomfire_spirit' WHERE entry=18104; + +/* OLD HILLSBRAD */ +UPDATE instance_template SET ScriptName='instance_old_hillsbrad' WHERE map=560; +UPDATE creature_template SET ScriptName='npc_erozion' WHERE entry=18723; +UPDATE creature_template SET ScriptName='npc_taretha' WHERE entry=18887; +UPDATE creature_template SET ScriptName='npc_thrall_old_hillsbrad' WHERE entry=17876; +DELETE FROM scripted_event_id WHERE id=11111; +INSERT INTO scripted_event_id VALUES +(11111,'event_go_barrel_old_hillsbrad'); + +/* THE CULLING OF STRATHOLME */ +UPDATE instance_template SET ScriptName='instance_culling_of_stratholme' WHERE map=595; +UPDATE creature_template SET ScriptName='npc_chromie' WHERE entry IN (26527, 27915); +UPDATE creature_template SET ScriptName='spell_dummy_npc_crates_bunny' WHERE entry=30996; + +/* THE DARK PORTAL */ +UPDATE creature_template SET ScriptName='boss_chrono_lord_deja' WHERE entry=17879; +UPDATE creature_template SET ScriptName='boss_aeonus' WHERE entry=17881; +UPDATE creature_template SET ScriptName='boss_temporus' WHERE entry=17880; +UPDATE instance_template SET ScriptName='instance_dark_portal' WHERE map=269; +UPDATE creature_template SET ScriptName='npc_medivh_black_morass' WHERE entry=15608; +UPDATE creature_template SET ScriptName='npc_time_rift' WHERE entry=17838; + +/* */ +/* COILFANG RESERVOIR */ +/* */ + +/* THE SLAVE PENS */ +UPDATE creature_template SET ScriptName='boss_ahune' WHERE entry=25740; +UPDATE creature_template SET ScriptName='npc_frozen_core' WHERE entry=25865; +UPDATE creature_template SET ScriptName='npc_ice_spear_bunny' WHERE entry=25985; + +/* THE UNDERBOG */ +UPDATE creature_template SET ScriptName='mob_underbog_mushroom' WHERE entry=17990; +UPDATE creature_template SET ScriptName='boss_hungarfen' WHERE entry=17770; + +/* THE STEAMVAULT */ +UPDATE instance_template SET ScriptName='instance_steam_vault' WHERE map=545; +UPDATE creature_template SET ScriptName='boss_hydromancer_thespia' WHERE entry=17797; +UPDATE creature_template SET ScriptName='boss_mekgineer_steamrigger' WHERE entry=17796; +UPDATE creature_template SET ScriptName='boss_warlord_kalithresh' WHERE entry=17798; +UPDATE gameobject_template SET ScriptName='go_main_chambers_access_panel' WHERE entry IN (184125,184126); +UPDATE creature_template SET ScriptName='mob_naga_distiller' WHERE entry=17954; +UPDATE creature_template SET ScriptName='mob_steamrigger_mechanic' WHERE entry=17951; + +/* SERPENTSHRINE CAVERN */ +UPDATE instance_template SET ScriptName='instance_serpent_shrine' WHERE map=548; +UPDATE creature_template SET ScriptName='boss_hydross_the_unstable' WHERE entry=21216; +/* Leotheras the Blind event */ +UPDATE creature_template SET ScriptName='boss_leotheras_the_blind' WHERE entry=21215; +/* Fathom-lord Karathress event */ +UPDATE creature_template SET ScriptName='boss_fathomlord_karathress' WHERE entry=21214; +UPDATE creature_template SET ScriptName='boss_fathomguard_sharkkis' WHERE entry=21966; +UPDATE creature_template SET ScriptName='boss_fathomguard_tidalvess' WHERE entry=21965; +UPDATE creature_template SET ScriptName='boss_fathomguard_caribdis' WHERE entry=21964; +/* Morogrim Tidewalker event */ +UPDATE creature_template SET ScriptName='boss_morogrim_tidewalker' WHERE entry=21213; +UPDATE creature_template SET ScriptName='mob_water_globule' WHERE entry=21913; +/* Lady Vashj event */ +UPDATE creature_template SET ScriptName='boss_lady_vashj' WHERE entry=21212; +UPDATE creature_template SET ScriptName='mob_enchanted_elemental' WHERE entry=21958; +UPDATE gameobject_template SET ScriptName='go_shield_generator' WHERE entry IN (185051,185052,185053,185054); +/* The Lurker Below event */ +UPDATE gameobject_template SET ScriptName='go_strange_pool' WHERE entry=184956; +UPDATE creature_template SET ScriptName='boss_the_lurker_below' WHERE entry=21217; + +/* CRYSTALSONG FOREST */ + +/* */ +/* CRUSADER COLISEUM */ +/* */ + +/* TRAIL OF THE CHAMPION */ + +/* TRIAL OF THE CRUSADER */ +UPDATE instance_template SET ScriptName='instance_trial_of_the_crusader' WHERE map=649; +UPDATE creature_template SET ScriptName='npc_barrett_ramsey' WHERE entry IN (34816, 35035, 35766, 35770, 35771); +UPDATE creature_template SET ScriptName='npc_beast_combat_stalker' WHERE entry=36549; +UPDATE creature_template SET ScriptName='boss_gormok' WHERE entry=34796; +UPDATE creature_template SET ScriptName='boss_acidmaw' WHERE entry=35144; +UPDATE creature_template SET ScriptName='boss_dreadscale' WHERE entry=34799; +UPDATE creature_template SET ScriptName='boss_icehowl' WHERE entry=34797; +UPDATE creature_template SET ScriptName='boss_jaraxxus' WHERE entry=34780; +UPDATE creature_template SET ScriptName='boss_anubarak_trial' WHERE entry=34564; +UPDATE creature_template SET ScriptName='boss_fjola' WHERE entry=34497; +UPDATE creature_template SET ScriptName='boss_eydis' WHERE entry=34496; +UPDATE creature_template SET ScriptName='npc_anubarak_spike' WHERE entry=34660; +UPDATE creature_template SET ScriptName='npc_frost_sphere' WHERE entry=34606; +UPDATE creature_template SET ScriptName='npc_nerubian_borrow' WHERE entry=34862; + +/* DALARAN */ +UPDATE creature_template SET ScriptName='npc_dalaran_guardian_mage' WHERE entry IN (29255, 29254); + +/* DARKSHORE */ +UPDATE creature_template SET ScriptName='npc_kerlonian' WHERE entry=11218; +UPDATE creature_template SET ScriptName='npc_prospector_remtravel' WHERE entry=2917; +UPDATE creature_template SET ScriptName='npc_threshwackonator' WHERE entry=6669; +UPDATE creature_template SET ScriptName='npc_volcor' WHERE entry=3692; +UPDATE creature_template SET ScriptName='npc_therylune' WHERE entry=3584; +UPDATE creature_template SET ScriptName='npc_rabid_bear' WHERE entry=2164; + +/* DARNASSUS */ + +/* DEADMINES */ +UPDATE creature_template SET ScriptName='boss_mr_smite' WHERE entry=646; +UPDATE instance_template SET ScriptName='instance_deadmines' WHERE map=36; +UPDATE gameobject_template SET ScriptName='go_defias_cannon' WHERE entry=16398; + +/* DEADWIND PASS */ + +/* DESOLACE */ +UPDATE creature_template SET ScriptName='npc_aged_dying_ancient_kodo' WHERE entry IN (4700, 4701, 4702, 11627); +UPDATE creature_template SET ScriptName='npc_dalinda_malem' WHERE entry=5644; +UPDATE creature_template SET ScriptName='npc_melizza_brimbuzzle' WHERE entry=12277; + +/* DIRE MAUL */ +UPDATE instance_template SET ScriptName='instance_dire_maul' WHERE map=429; + +/* DRAGONBLIGHT */ +UPDATE creature_template SET ScriptName='npc_destructive_ward' WHERE entry=27430; + +/* DRAK'THARON KEEP */ +UPDATE creature_template SET ScriptName='boss_novos' WHERE entry=26631; +UPDATE creature_template SET ScriptName='npc_crystal_channel_target' WHERE entry=26712; +UPDATE creature_template SET ScriptName='boss_tharonja' WHERE entry=26632; +UPDATE creature_template SET ScriptName='boss_trollgore' WHERE entry=26630; +UPDATE instance_template SET ScriptName='instance_draktharon_keep' WHERE map=600; + +/* DUN MOROGH */ + +/* DUROTAR */ +UPDATE creature_template SET ScriptName='npc_lazy_peon' WHERE entry=10556; + +/* DUSKWOOD */ + +/* DUSTWALLOW MARSH */ +UPDATE creature_template SET ScriptName='mobs_risen_husk_spirit' WHERE entry IN (23554,23555); +UPDATE creature_template SET ScriptName='npc_ogron' WHERE entry=4983; +UPDATE creature_template SET ScriptName='npc_morokk' WHERE entry=4500; +UPDATE creature_template SET ScriptName='npc_restless_apparition' WHERE entry=23861; +UPDATE creature_template SET ScriptName='npc_private_hendel' WHERE entry=4966; +UPDATE creature_template SET ScriptName='npc_stinky_ignatz' WHERE entry=4880; +DELETE FROM scripted_areatrigger WHERE entry=4752; +INSERT INTO scripted_areatrigger VALUES +(4752,'at_nats_landing'); + +/* EASTERN PLAGUELANDS */ +UPDATE creature_template SET ScriptName='npc_eris_havenfire' WHERE entry=14494; + +/* EBON HOLD */ +UPDATE creature_template SET ScriptName='npc_death_knight_initiate' WHERE entry=28406; +UPDATE creature_template SET ScriptName='npc_unworthy_initiate_anchor' WHERE entry=29521; +UPDATE creature_template SET ScriptName='npc_unworthy_initiate' WHERE entry IN (29519,29520,29565,29566,29567); +UPDATE gameobject_template SET ScriptName='go_acherus_soul_prison' WHERE entry IN (191577,191580,191581,191582,191583,191584,191585,191586,191587,191588,191589,191590); +UPDATE creature_template SET ScriptName='npc_a_special_surprise' WHERE entry IN (29032,29061,29065,29067,29068,29070,29074,29072,29073,29071); +UPDATE creature_template SET ScriptName='npc_koltira_deathweaver' WHERE entry=28912; +UPDATE creature_template SET ScriptName='npc_eye_of_acherus' WHERE entry=28511; +UPDATE creature_template SET ScriptName='npc_scarlet_ghoul' WHERE entry=28845; +UPDATE creature_template SET ScriptName='npc_highlord_darion_mograine' WHERE entry=29173; +UPDATE creature_template SET ScriptName='npc_fellow_death_knight' WHERE entry IN (29199, 29204, 29200); +UPDATE creature_template SET ScriptName='npc_lich_king_light_dawn' WHERE entry=29183; +UPDATE creature_template SET ScriptName='npc_acherus_deathcharger' WHERE entry=28782; + +/* ELWYNN FOREST */ +UPDATE creature_template SET ScriptName='npc_hogger' WHERE entry=448; + +/* EVERSONG WOODS */ +UPDATE creature_template SET ScriptName='npc_kelerun_bloodmourn' WHERE entry=17807; +UPDATE gameobject_template SET ScriptName='go_harbinger_second_trial' WHERE entry=182052; +UPDATE creature_template SET ScriptName='npc_prospector_anvilward' WHERE entry=15420; +UPDATE creature_template SET ScriptName='npc_apprentice_mirveda' WHERE entry=15402; +UPDATE creature_template SET ScriptName='npc_infused_crystal' WHERE entry=16364; + +/* FELWOOD */ +DELETE FROM scripted_event_id WHERE id=8328; +INSERT INTO scripted_event_id VALUES +(8328, 'npc_kroshius'); +UPDATE creature_template SET ScriptName='npc_kitten' WHERE entry=9937; +UPDATE creature_template SET ScriptName='npc_corrupt_saber' WHERE entry=10042; +UPDATE creature_template SET ScriptName='npc_niby_the_almighty' WHERE entry=14469; +UPDATE creature_template SET ScriptName='npc_kroshius' WHERE entry=14467; + +/* FERALAS */ +UPDATE creature_template SET ScriptName='npc_oox22fe' WHERE entry=7807; + +/* GHOSTLANDS */ +UPDATE creature_template SET ScriptName='npc_ranger_lilatha' WHERE entry=16295; + +/* GNOMEREGAN */ +UPDATE creature_template SET ScriptName='boss_thermaplugg' WHERE entry=7800; +UPDATE gameobject_template SET ScriptName='go_gnomeface_button' WHERE entry BETWEEN 142214 AND 142219; +UPDATE creature_template SET ScriptName='npc_blastmaster_emi_shortfuse' WHERE entry=7998; +UPDATE creature_template SET ScriptName='npc_kernobee' WHERE entry=7850; +UPDATE instance_template SET ScriptName='instance_gnomeregan' WHERE map=90; + +/* GRIZZLY HILLS */ +UPDATE creature_template SET ScriptName='npc_depleted_war_golem' WHERE entry=27017; + +/* GRUUL'S LAIR */ +UPDATE instance_template SET ScriptName='instance_gruuls_lair' WHERE map =565; +UPDATE creature_template SET ScriptName='boss_gruul' WHERE entry=19044; +/* Maulgar and Event */ +UPDATE creature_template SET ScriptName='boss_high_king_maulgar' WHERE entry=18831; +UPDATE creature_template SET ScriptName='boss_kiggler_the_crazed' WHERE entry=18835; +UPDATE creature_template SET ScriptName='boss_blindeye_the_seer' WHERE entry=18836; +UPDATE creature_template SET ScriptName='boss_olm_the_summoner' WHERE entry=18834; +UPDATE creature_template SET ScriptName='boss_krosh_firehand' WHERE entry=18832; + +/* GUNDRAK */ +UPDATE creature_template SET ScriptName='boss_drakkari_colossus' WHERE entry=29307; +UPDATE creature_template SET ScriptName='boss_drakkari_elemental' WHERE entry=29573; +UPDATE creature_template SET ScriptName='npc_living_mojo' WHERE entry=29830; +UPDATE creature_template SET ScriptName='boss_eck' WHERE entry=29932; +UPDATE creature_template SET ScriptName='boss_galdarah' WHERE entry=29306; +UPDATE creature_template SET ScriptName='boss_moorabi' WHERE entry=29305; +UPDATE creature_template SET ScriptName='boss_sladran' WHERE entry=29304; +UPDATE gameobject_template SET ScriptName='go_gundrak_altar' WHERE entry IN (192518, 192519, 192520); +UPDATE instance_template SET ScriptName='instance_gundrak' WHERE map=604; + +/* */ +/* HELLFIRE CITADEL */ +/* */ + +/* BLOOD FURNACE */ +/* The Maker,Broggok,Kelidan,Broggok's cloud */ +UPDATE creature_template SET ScriptName='boss_the_maker' WHERE entry=17381; +UPDATE creature_template SET ScriptName='boss_broggok' WHERE entry=17380; +UPDATE creature_template SET ScriptName='boss_kelidan_the_breaker' WHERE entry=17377; +UPDATE creature_template SET ScriptName='mob_broggok_poisoncloud' WHERE entry=17662; +UPDATE creature_template SET ScriptName='mob_shadowmoon_channeler' WHERE entry=17653; +UPDATE gameobject_template SET ScriptName='go_prison_cell_lever' WHERE entry=181982; +UPDATE instance_template SET ScriptName='instance_blood_furnace' WHERE map=542; + +/* HELLFIRE RAMPARTS */ +/* Vazruden,Omor the Unscarred,Watchkeeper Gargolmar */ +UPDATE creature_template SET ScriptName='boss_omor_the_unscarred' WHERE entry=17308; +UPDATE creature_template SET ScriptName='boss_watchkeeper_gargolmar' WHERE entry=17306; +UPDATE creature_template SET ScriptName='boss_vazruden_herald' WHERE entry=17307; +UPDATE creature_template SET ScriptName='boss_vazruden' WHERE entry=17537; +UPDATE instance_template SET ScriptName='instance_ramparts' WHERE map=543; + +/* SHATTERED HALLS */ +/* Nethekurse and his spawned shadowfissure */ +UPDATE creature_template SET ScriptName='boss_grand_warlock_nethekurse' WHERE entry=16807; +UPDATE creature_template SET ScriptName='boss_warbringer_omrogg' WHERE entry=16809; +UPDATE creature_template SET ScriptName='mob_fel_orc_convert' WHERE entry=17083; +UPDATE creature_template SET ScriptName='mob_lesser_shadow_fissure' WHERE entry=17471; +UPDATE creature_template SET ScriptName='mob_omrogg_heads' WHERE entry IN (19523,19524); +UPDATE creature_template SET ScriptName='boss_warchief_kargath_bladefist' WHERE entry=16808; +UPDATE instance_template SET ScriptName='instance_shattered_halls' WHERE map=540; + +/* MAGTHERIDON'S LAIR */ +UPDATE instance_template SET ScriptName='instance_magtheridons_lair' WHERE map=544; +UPDATE gameobject_template SET ScriptName='go_manticron_cube' WHERE entry=181713; +UPDATE creature_template SET ScriptName='boss_magtheridon' WHERE entry=17257; +UPDATE creature_template SET ScriptName='mob_abyssal' WHERE entry=17454; +UPDATE creature_template SET ScriptName='mob_hellfire_channeler' WHERE entry=17256; +UPDATE creature_template SET ScriptName='npc_target_trigger' WHERE entry=17474; + +/* HELLFIRE PENINSULA */ +UPDATE creature_template SET ScriptName='boss_doomlord_kazzak' WHERE entry=18728; +UPDATE creature_template SET ScriptName='npc_aeranas' WHERE entry=17085; +UPDATE creature_template SET ScriptName='npc_ancestral_wolf' WHERE entry=17077; +UPDATE creature_template SET ScriptName='npc_demoniac_scryer' WHERE entry=22258; +UPDATE creature_template SET ScriptName='npc_wounded_blood_elf' WHERE entry=16993; +UPDATE creature_template SET ScriptName='npc_fel_guard_hound' WHERE entry=21847; + +/* HILLSBRAD FOOTHILLS */ + +/* HINTERLANDS */ +UPDATE creature_template SET ScriptName='npc_00x09hl' WHERE entry=7806; +UPDATE creature_template SET ScriptName='npc_rinji' WHERE entry=7780; + +/* HOWLING FJORD */ +DELETE FROM scripted_areatrigger WHERE entry IN (4778,4779); +INSERT INTO scripted_areatrigger VALUES +(4778,'at_ancient_male_vrykul'), +(4779,'at_nifflevar'); +UPDATE creature_template SET ScriptName='npc_ancient_male_vrykul' WHERE entry=24314; +UPDATE creature_template SET ScriptName='npc_daegarn' WHERE entry=24151; +UPDATE creature_template SET ScriptName='npc_silvermoon_harry' WHERE entry=24539; +UPDATE creature_template SET ScriptName='npc_lich_king_village' WHERE entry=24248; +UPDATE creature_template SET ScriptName='npc_king_ymiron' WHERE entry=24321; + +/* */ +/* ICECROWN CITADEL */ +/* */ + +/* ICECROWN CITADEL */ +UPDATE instance_template SET ScriptName='instance_icecrown_citadel' WHERE map=631; +UPDATE creature_template SET ScriptName='boss_lord_marrowgar' WHERE entry=36612; +UPDATE creature_template SET ScriptName='boss_lady_deathwhisper' WHERE entry=36855; +UPDATE creature_template SET ScriptName='boss_deathbringer_saurfang' WHERE entry=37813; +UPDATE creature_template SET ScriptName='npc_queen_lanathel_intro' WHERE entry=38004; +UPDATE creature_template SET ScriptName='npc_blood_orb_control' WHERE entry=38008; +UPDATE creature_template SET ScriptName='npc_ball_of_flame' WHERE entry IN (38332,38451); +UPDATE creature_template SET ScriptName='npc_kinetic_bomb' WHERE entry=38454; +UPDATE creature_template SET ScriptName='npc_dark_nucleus' WHERE entry=38369; +UPDATE creature_template SET ScriptName='boss_taldaram_icc' WHERE entry=37973; +UPDATE creature_template SET ScriptName='boss_keleseth_icc' WHERE entry=37972; +UPDATE creature_template SET ScriptName='boss_valanar_icc' WHERE entry=37970; +UPDATE creature_template SET ScriptName='boss_blood_queen_lanathel' WHERE entry=37955; +UPDATE creature_template SET ScriptName='boss_sindragosa' WHERE entry=36853; +UPDATE creature_template SET ScriptName='npc_rimefang_icc' WHERE entry=37533; +UPDATE creature_template SET ScriptName='npc_spinestalker_icc' WHERE entry=37534; +UPDATE creature_template SET ScriptName='mob_frost_bomb' WHERE entry=37186; +UPDATE creature_template SET ScriptName='boss_festergut' WHERE entry=36626; +UPDATE creature_template SET ScriptName='boss_rotface' WHERE entry=36627; +UPDATE creature_template SET ScriptName='mob_little_ooze' WHERE entry=36897; +UPDATE creature_template SET ScriptName='mob_big_ooze' WHERE entry=36899; +UPDATE creature_template SET ScriptName='boss_valithria_dreamwalker' WHERE entry=36789; +UPDATE creature_template SET ScriptName='boss_professor_putricide' WHERE entry=36678; +UPDATE creature_template SET ScriptName='boss_the_lich_king_icc' WHERE entry=36597; +DELETE FROM scripted_event_id WHERE id IN (23426,23438); +INSERT INTO scripted_event_id VALUES +(23426,'event_gameobject_citadel_valve'), +(23438,'event_gameobject_citadel_valve'); + +/* FORGE OF SOULS */ +UPDATE creature_template SET ScriptName='boss_bronjahm' WHERE entry=36497; +UPDATE creature_template SET ScriptName='npc_corrupted_soul_fragment' WHERE entry=36535; +UPDATE creature_template SET ScriptName='boss_devourer_of_souls' WHERE entry=36502; +UPDATE instance_template SET ScriptName='instance_forge_of_souls' WHERE map=632; + +/* HALLS OF REFLECTION */ + +/* PIT OF SARON */ +UPDATE instance_template SET ScriptName='instance_pit_of_saron' WHERE map=658; +UPDATE creature_template SET ScriptName='boss_forgemaster_garfrost' WHERE entry=36494; +UPDATE creature_template SET ScriptName='boss_krick' WHERE entry=36477; +UPDATE creature_template SET ScriptName='boss_ick' WHERE entry=36476; +UPDATE creature_template SET ScriptName='npc_exploding_orb' WHERE entry=36610; +UPDATE creature_template SET ScriptName='npc_ymirjar_deathbringer' WHERE entry=36892; +UPDATE creature_template SET ScriptName='npc_collapsing_icicle' WHERE entry=36847; +UPDATE creature_template SET ScriptName='boss_tyrannus' WHERE entry=36658; +UPDATE creature_template SET ScriptName='boss_rimefang_pos' WHERE entry=36661; +DELETE FROM scripted_areatrigger WHERE entry IN (5578,5581); +INSERT INTO scripted_areatrigger VALUES +(5578,'at_pit_of_saron'), +(5581,'at_pit_of_saron'); + +/* ICECROWN */ + +/* IRONFORGE */ + +/* ISLE OF QUEL'DANAS */ +UPDATE creature_template SET ScriptName='npc_converted_sentry' WHERE entry=24981; + +/* KARAZHAN */ +UPDATE instance_template SET ScriptName='instance_karazhan' WHERE map=532; +UPDATE creature_template SET ScriptName='boss_midnight' WHERE entry=16151; +UPDATE creature_template SET ScriptName='boss_attumen' WHERE entry IN (15550,16152); +UPDATE creature_template SET ScriptName='boss_moroes' WHERE entry=15687; +UPDATE creature_template SET ScriptName='boss_maiden_of_virtue' WHERE entry=16457; +UPDATE creature_template SET ScriptName='boss_curator' WHERE entry=15691; +UPDATE creature_template SET ScriptName='boss_julianne' WHERE entry=17534; +UPDATE creature_template SET ScriptName='boss_romulo' WHERE entry=17533; +UPDATE creature_template SET ScriptName='boss_dorothee' WHERE entry=17535; +UPDATE creature_template SET ScriptName='boss_strawman' WHERE entry=17543; +UPDATE creature_template SET ScriptName='boss_tinhead' WHERE entry=17547; +UPDATE creature_template SET ScriptName='boss_roar' WHERE entry=17546; +UPDATE creature_template SET ScriptName='boss_crone' WHERE entry=18168; +UPDATE creature_template SET ScriptName='boss_terestian_illhoof' WHERE entry=15688; +UPDATE creature_template SET ScriptName='boss_shade_of_aran' WHERE entry=16524; +UPDATE creature_template SET ScriptName='boss_netherspite' WHERE entry=15689; +UPDATE creature_template SET ScriptName='boss_malchezaar' WHERE entry=15690; +UPDATE creature_template SET ScriptName='boss_nightbane' WHERE entry=17225; +UPDATE creature_template SET ScriptName='boss_bigbadwolf' WHERE entry=17521; +UPDATE creature_template SET ScriptName='mob_demon_chain' WHERE entry=17248; +UPDATE creature_template SET ScriptName='npc_fiendish_portal' WHERE entry=17265; +UPDATE creature_template SET ScriptName='npc_shade_of_aran_blizzard' WHERE entry=17161; +UPDATE creature_template SET ScriptName='npc_netherspite_portal' WHERE entry IN (17367,17368,17369); +UPDATE creature_template SET ScriptName='npc_infernal_target' WHERE entry=17644; +UPDATE creature_template SET ScriptName='npc_berthold' WHERE entry=16153; +UPDATE creature_template SET ScriptName='npc_barnes' WHERE entry=16812; +UPDATE creature_template SET ScriptName='npc_grandmother' WHERE entry=17603; +UPDATE creature_template SET ScriptName='npc_image_of_medivh' WHERE entry=17651; +UPDATE creature_template SET ScriptName='npc_image_arcanagos' WHERE entry=17652; +UPDATE creature_template SET ScriptName='npc_echo_of_medivh' WHERE entry=16816; +UPDATE creature_template SET ScriptName='npc_king_llane' WHERE entry=21684; +UPDATE creature_template SET ScriptName='npc_warchief_blackhand' WHERE entry=21752; +UPDATE creature_template SET ScriptName='npc_human_conjurer' WHERE entry=21683; +UPDATE creature_template SET ScriptName='npc_orc_warlock' WHERE entry=21750; +UPDATE creature_template SET ScriptName='npc_human_footman' WHERE entry=17211; +UPDATE creature_template SET ScriptName='npc_orc_grunt' WHERE entry=17469; +UPDATE creature_template SET ScriptName='npc_water_elemental' WHERE entry=21160; +UPDATE creature_template SET ScriptName='npc_summoned_daemon' WHERE entry=21726; +UPDATE creature_template SET ScriptName='npc_human_charger' WHERE entry=21664; +UPDATE creature_template SET ScriptName='npc_orc_wolf' WHERE entry=21748; +UPDATE creature_template SET ScriptName='npc_human_cleric' WHERE entry=21682; +UPDATE creature_template SET ScriptName='npc_orc_necrolyte' WHERE entry=21747; +DELETE FROM scripted_event_id WHERE id IN (10591,10951); +INSERT INTO scripted_event_id VALUES +(10591,'event_spell_summon_nightbane'), +(10951,'event_spell_medivh_journal'); + +/* LOCH MODAN */ +UPDATE creature_template SET ScriptName='npc_mountaineer_pebblebitty' WHERE entry=3836; +UPDATE creature_template SET ScriptName='npc_miran' WHERE entry=1379; + +/* MAGISTER'S TERRACE */ +UPDATE instance_template SET ScriptName='instance_magisters_terrace' WHERE map=585; +UPDATE creature_template SET ScriptName='boss_selin_fireheart' WHERE entry=24723; +UPDATE creature_template SET ScriptName='mob_fel_crystal' WHERE entry=24722; +UPDATE creature_template SET ScriptName='boss_vexallus' WHERE entry=24744; +UPDATE creature_template SET ScriptName='mob_pure_energy' WHERE entry=24745; +UPDATE creature_template SET ScriptName='boss_priestess_delrissa' WHERE entry=24560; +UPDATE creature_template SET ScriptName='npc_kagani_nightstrike' WHERE entry=24557; +UPDATE creature_template SET ScriptName='npc_ellris_duskhallow' WHERE entry=24558; +UPDATE creature_template SET ScriptName='npc_eramas_brightblaze' WHERE entry=24554; +UPDATE creature_template SET ScriptName='npc_yazzai' WHERE entry=24561; +UPDATE creature_template SET ScriptName='npc_warlord_salaris' WHERE entry=24559; +UPDATE creature_template SET ScriptName='npc_garaxxas' WHERE entry=24555; +UPDATE creature_template SET ScriptName='npc_apoko' WHERE entry=24553; +UPDATE creature_template SET ScriptName='npc_zelfan' WHERE entry=24556; +UPDATE creature_template SET ScriptName='boss_felblood_kaelthas' WHERE entry=24664; +UPDATE creature_template SET ScriptName='mob_arcane_sphere' WHERE entry=24708; +UPDATE creature_template SET ScriptName='mob_felkael_phoenix' WHERE entry=24674; +UPDATE creature_template SET ScriptName='mob_felkael_phoenix_egg' WHERE entry=24675; +UPDATE creature_template SET ScriptName='npc_kalecgos' WHERE entry=24844; +DELETE FROM scripted_event_id WHERE id=16547; +INSERT INTO scripted_event_id VALUES +(16547,'event_go_scrying_orb'); + +/* MARAUDON */ +UPDATE creature_template SET ScriptName='boss_noxxion' WHERE entry=13282; + +/* MOLTEN CORE */ +UPDATE instance_template SET ScriptName='instance_molten_core' WHERE map=409; +UPDATE creature_template SET ScriptName='boss_lucifron' WHERE entry=12118; +UPDATE creature_template SET ScriptName='boss_magmadar' WHERE entry=11982; +UPDATE creature_template SET ScriptName='boss_gehennas' WHERE entry=12259; +UPDATE creature_template SET ScriptName='boss_garr' WHERE entry=12057; +UPDATE creature_template SET ScriptName='boss_baron_geddon' WHERE entry=12056; +UPDATE creature_template SET ScriptName='boss_shazzrah' WHERE entry=12264; +UPDATE creature_template SET ScriptName='boss_golemagg' WHERE entry=11988; +UPDATE creature_template SET ScriptName='boss_sulfuron' WHERE entry=12098; +UPDATE creature_template SET ScriptName='boss_majordomo' WHERE entry=12018; +UPDATE creature_template SET ScriptName='boss_ragnaros' WHERE entry=11502; +UPDATE creature_template SET ScriptName='mob_firesworn' WHERE entry=12099; +UPDATE creature_template SET ScriptName='mob_core_rager' WHERE entry=11672; +UPDATE creature_template SET ScriptName='mob_flamewaker_priest' WHERE entry=11662; + +/* MOONGLADE */ +UPDATE creature_template SET ScriptName='npc_clintar_dw_spirit' WHERE entry=22916; +UPDATE creature_template SET ScriptName='npc_keeper_remulos' WHERE entry=11832; +UPDATE creature_template SET ScriptName='boss_eranikus' WHERE entry=15491; + +/* MULGORE */ +UPDATE creature_template SET ScriptName='npc_kyle_the_frenzied' WHERE entry=23616; + +/* NAGRAND */ +UPDATE creature_template SET ScriptName='mob_lump' WHERE entry=18351; +UPDATE creature_template SET ScriptName='npc_maghar_captive' WHERE entry=18210; +UPDATE creature_template SET ScriptName='npc_creditmarker_visit_with_ancestors' WHERE entry IN (18840,18841,18842,18843); + +/* NAXXRAMAS */ +UPDATE instance_template SET ScriptName='instance_naxxramas' WHERE map=533; +UPDATE creature_template SET ScriptName='boss_anubrekhan' WHERE entry=15956; +UPDATE creature_template SET ScriptName='boss_faerlina' WHERE entry=15953; +UPDATE creature_template SET ScriptName='boss_maexxna' WHERE entry=15952; +UPDATE creature_template SET ScriptName='npc_web_wrap' WHERE entry=16486; +UPDATE creature_template SET ScriptName='boss_noth' WHERE entry=15954; +UPDATE creature_template SET ScriptName='boss_heigan' WHERE entry=15936; +UPDATE creature_template SET ScriptName='boss_loatheb' WHERE entry=16011; +UPDATE creature_template SET ScriptName='boss_razuvious' WHERE entry=16061; +UPDATE creature_template SET ScriptName='boss_gothik' WHERE entry=16060; +UPDATE creature_template SET ScriptName='spell_anchor' WHERE entry=16137; +UPDATE creature_template SET ScriptName='boss_thane_korthazz' WHERE entry=16064; +UPDATE creature_template SET ScriptName='boss_sir_zeliek' WHERE entry=16063; +UPDATE creature_template SET ScriptName='boss_lady_blaumeux' WHERE entry=16065; +UPDATE creature_template SET ScriptName='boss_rivendare_naxx' WHERE entry=30549; +UPDATE creature_template SET ScriptName='boss_patchwerk' WHERE entry=16028; +UPDATE creature_template SET ScriptName='boss_grobbulus' WHERE entry=15931; +UPDATE creature_template SET ScriptName='boss_gluth' WHERE entry=15932; +UPDATE creature_template SET ScriptName='boss_thaddius' WHERE entry=15928; +UPDATE creature_template SET ScriptName='boss_stalagg' WHERE entry=15929; +UPDATE creature_template SET ScriptName='boss_feugen' WHERE entry=15930; +UPDATE creature_template SET ScriptName='npc_tesla_coil' WHERE entry=16218; +UPDATE creature_template SET ScriptName='boss_sapphiron' WHERE entry=15989; +UPDATE gameobject_template SET ScriptName='go_sapphiron_birth' WHERE entry=181356; +UPDATE creature_template SET ScriptName='boss_kelthuzad' WHERE entry=15990; + +/* NETHERSTORM */ +DELETE FROM scripted_areatrigger WHERE entry=4497; +INSERT INTO scripted_areatrigger VALUES (4497,'at_commander_dawnforge'); +UPDATE gameobject_template SET ScriptName='go_manaforge_control_console' WHERE entry IN (183770,183956,184311,184312); +UPDATE creature_template SET ScriptName='npc_manaforge_control_console' WHERE entry IN (20209,20417,20418,20440); +UPDATE creature_template SET ScriptName='npc_commander_dawnforge' WHERE entry=19831; +UPDATE creature_template SET ScriptName='npc_bessy' WHERE entry=20415; +UPDATE creature_template SET ScriptName='npc_maxx_a_million' WHERE entry=19589; +UPDATE creature_template SET ScriptName='npc_zeppit' WHERE entry=22484; +UPDATE creature_template SET ScriptName='npc_protectorate_demolitionist' WHERE entry=20802; +UPDATE creature_template SET ScriptName='npc_captured_vanguard' WHERE entry=20763; + +/* */ +/* THE NEXUS */ +/* */ + +/* EYE OF ETERNITY */ +UPDATE instance_template SET ScriptName='instance_eye_of_eternity' WHERE map=616; +UPDATE creature_template SET ScriptName='boss_malygos' WHERE entry=28859; +UPDATE creature_template SET ScriptName='npc_power_spark' WHERE entry=30084; +UPDATE creature_template SET ScriptName='npc_wyrmrest_skytalon' WHERE entry=30161; +DELETE FROM scripted_event_id WHERE id=20711; +INSERT INTO scripted_event_id VALUES +(20711,'event_go_focusing_iris'); + +/* NEXUS */ +UPDATE creature_template SET ScriptName='boss_anomalus' WHERE entry=26763; +UPDATE creature_template SET ScriptName='mob_chaotic_rift' WHERE entry=26918; +UPDATE creature_template SET ScriptName='boss_keristrasza' WHERE entry=26723; +UPDATE creature_template SET ScriptName='boss_ormorok' WHERE entry=26794; +UPDATE creature_template SET ScriptName='npc_crystal_spike_trigger' WHERE entry IN (27101, 27079); +UPDATE creature_template SET ScriptName='boss_telestra' WHERE entry=26731; +UPDATE gameobject_template SET ScriptName='go_containment_sphere' WHERE entry IN (188526, 188527, 188528); +UPDATE instance_template SET ScriptName='instance_nexus' WHERE map=576; + +/* OCULUS */ +UPDATE instance_template SET ScriptName='instance_oculus' WHERE map=578; +UPDATE creature_template SET ScriptName='boss_eregos' WHERE entry=27656; +UPDATE creature_template SET ScriptName='boss_urom' WHERE entry=27655; +UPDATE creature_template SET ScriptName='boss_varos' WHERE entry=27447; +UPDATE creature_template SET ScriptName='npc_azure_ring_captain' WHERE entry=28236; +UPDATE creature_template SET ScriptName='npc_arcane_beam' WHERE entry=28239; +UPDATE creature_template SET ScriptName='npc_centrifuge_core' WHERE entry=28183; +UPDATE creature_template SET ScriptName='npc_planar_anomaly' WHERE entry=30879; +UPDATE creature_template SET ScriptName='npc_oculus_drake' WHERE entry IN (27756, 27692, 27755); +DELETE FROM scripted_event_id WHERE id IN (10665,12229,18454,18455); +INSERT INTO scripted_event_id VALUES +(10665,'event_spell_call_captain'), +(12229,'event_spell_call_captain'), +(18454,'event_spell_call_captain'), +(18455,'event_spell_call_captain'); + +/* OBSIDIAN SANCTUM */ +UPDATE instance_template SET ScriptName='instance_obsidian_sanctum' WHERE map=615; +UPDATE creature_template SET ScriptName='boss_sartharion' WHERE entry=28860; +UPDATE creature_template SET ScriptName='mob_vesperon' WHERE entry=30449; +UPDATE creature_template SET ScriptName='mob_shadron' WHERE entry=30451; +UPDATE creature_template SET ScriptName='mob_tenebron' WHERE entry=30452; +UPDATE creature_template SET ScriptName='mob_twilight_eggs' WHERE entry IN (30882,31204); +UPDATE creature_template SET ScriptName='npc_tenebron_egg_controller' WHERE entry=31138; +UPDATE creature_template SET ScriptName='npc_flame_tsunami' WHERE entry=30616; +UPDATE creature_template SET ScriptName='npc_fire_cyclone' WHERE entry=30648; + +/* ONYXIA'S LAIR */ +UPDATE instance_template SET ScriptName='instance_onyxias_lair' WHERE map=249; +UPDATE creature_template SET ScriptName='boss_onyxia' WHERE entry=10184; + +/* ORGRIMMAR */ +UPDATE creature_template SET ScriptName='npc_shenthul' WHERE entry=3401; +UPDATE creature_template SET ScriptName='npc_thrall_warchief' WHERE entry=4949; + +/* RAGEFIRE CHASM */ + +/* RAZORFEN DOWNS */ +UPDATE creature_template SET ScriptName='npc_belnistrasz' WHERE entry=8516; + +/* RAZORFEN KRAUL */ +UPDATE instance_template SET ScriptName='instance_razorfen_kraul' WHERE map=47; +UPDATE creature_template SET ScriptName='npc_willix_the_importer' WHERE entry=4508; +UPDATE creature_template SET ScriptName='npc_snufflenose_gopher' WHERE entry=4781; + +/* REDRIDGE MOUNTAINS */ +UPDATE creature_template SET ScriptName='npc_corporal_keeshan' WHERE entry=349; + +/* RUBY SANCTUM */ +UPDATE instance_template SET ScriptName='instance_ruby_sanctum' WHERE map=724; +UPDATE creature_template SET ScriptName='boss_baltharus' WHERE entry=39751; +UPDATE creature_template SET ScriptName='boss_saviana' WHERE entry=39747; +UPDATE creature_template SET ScriptName='boss_zarithrian' WHERE entry=39746; +UPDATE creature_template SET ScriptName='npc_baltharus_clone' WHERE entry=39899; +UPDATE creature_template SET ScriptName='boss_halion_real' WHERE entry=39863; +UPDATE creature_template SET ScriptName='boss_halion_twilight' WHERE entry=40142; + +/* RUINS OF AHN'QIRAJ */ +UPDATE instance_template SET ScriptName='instance_ruins_of_ahnqiraj' WHERE map=509; +UPDATE creature_template SET ScriptName='mob_anubisath_guardian' WHERE entry=15355; +UPDATE creature_template SET ScriptName='boss_kurinnaxx' WHERE entry=15348; +UPDATE creature_template SET ScriptName='boss_ayamiss' WHERE entry=15369; +UPDATE creature_template SET ScriptName='boss_moam' WHERE entry=15340; +UPDATE creature_template SET ScriptName='boss_ossirian' WHERE entry=15339; +UPDATE gameobject_template SET ScriptName='go_ossirian_crystal' WHERE entry=180619; +UPDATE creature_template SET ScriptName='npc_hive_zara_larva' WHERE entry=15555; +UPDATE creature_template SET ScriptName='boss_buru' WHERE entry=15370; +UPDATE creature_template SET ScriptName='npc_buru_egg' WHERE entry=15514; +UPDATE creature_template SET ScriptName='npc_general_andorov' WHERE entry=15471; +UPDATE creature_template SET ScriptName='npc_kaldorei_elite' WHERE entry=15473; + +/* SCARLET MONASTERY */ +UPDATE instance_template SET ScriptName='instance_scarlet_monastery' WHERE map=189; +UPDATE creature_template SET ScriptName='boss_arcanist_doan' WHERE entry=6487; +UPDATE creature_template SET ScriptName='boss_herod' WHERE entry=3975; +UPDATE creature_template SET ScriptName='boss_high_inquisitor_whitemane' WHERE entry=3977; +UPDATE creature_template SET ScriptName='boss_scarlet_commander_mograine' WHERE entry=3976; +UPDATE creature_template SET ScriptName='mob_scarlet_trainee' WHERE entry=6575; +UPDATE creature_template SET ScriptName='boss_headless_horseman' WHERE entry=23682; + +/* SCHOLOMANCE */ +UPDATE instance_template SET ScriptName='instance_scholomance' WHERE map=289; +UPDATE creature_template SET ScriptName='boss_darkmaster_gandling' WHERE entry=1853; +UPDATE creature_template SET ScriptName='boss_jandice_barov' WHERE entry=10503; +DELETE FROM scripted_event_id WHERE id IN (5618, 5619, 5620, 5621, 5622, 5623); +INSERT INTO scripted_event_id VALUES +(5618,'event_spell_gandling_shadow_portal'), +(5619,'event_spell_gandling_shadow_portal'), +(5620,'event_spell_gandling_shadow_portal'), +(5621,'event_spell_gandling_shadow_portal'), +(5622,'event_spell_gandling_shadow_portal'), +(5623,'event_spell_gandling_shadow_portal'); + +/* SEARING GORGE */ + +/* SHADOWFANG KEEP */ +UPDATE instance_template SET ScriptName='instance_shadowfang_keep' WHERE map=33; +UPDATE creature_template SET ScriptName='npc_shadowfang_prisoner' WHERE entry IN (3849,3850); +UPDATE creature_template SET ScriptName='npc_arugal' WHERE entry=10000; +UPDATE creature_template SET ScriptName='npc_deathstalker_vincent' WHERE entry=4444; +UPDATE creature_template SET ScriptName='mob_arugal_voidwalker' WHERE entry=4627; +UPDATE creature_template SET ScriptName='boss_arugal' WHERE entry=4275; +UPDATE creature_template SET ScriptName='npc_apothecary_hummel' WHERE entry=36296; +UPDATE creature_template SET ScriptName='npc_valentine_boss_manager' WHERE entry=36643; + +/* SHADOWMOON VALLEY */ +UPDATE creature_template SET ScriptName='boss_doomwalker' WHERE entry=17711; +UPDATE creature_template SET ScriptName='npc_dragonmaw_peon' WHERE entry=22252; +UPDATE creature_template SET ScriptName='mob_mature_netherwing_drake' WHERE entry=21648; +UPDATE creature_template SET ScriptName='mob_enslaved_netherwing_drake' WHERE entry=21722; +UPDATE creature_template SET ScriptName='npc_wilda' WHERE entry=21027; +UPDATE creature_template SET ScriptName='mob_torloth' WHERE entry=22076; +UPDATE creature_template SET ScriptName='npc_totem_of_spirits' WHERE entry=21071; +DELETE FROM scripted_event_id WHERE id IN (13513,13514,13515,13516); +INSERT INTO scripted_event_id VALUES +(13513,'event_spell_soul_captured_credit'), +(13514,'event_spell_soul_captured_credit'), +(13515,'event_spell_soul_captured_credit'), +(13516,'event_spell_soul_captured_credit'); +UPDATE creature_template SET ScriptName='npc_lord_illidan_stormrage' WHERE entry=22083; +UPDATE gameobject_template SET ScriptName='go_crystal_prison' WHERE entry=185126; +UPDATE creature_template SET ScriptName='npc_spawned_oronok_tornheart' WHERE entry=21685; +UPDATE creature_template SET ScriptName='npc_domesticated_felboar' WHERE entry=21195; +UPDATE creature_template SET ScriptName='npc_shadowmoon_tuber_node' WHERE entry=21347; +UPDATE creature_template SET ScriptName='npc_veneratus_spawn_node' WHERE entry=21334; + +/* SHATTRATH */ +UPDATE creature_template SET ScriptName='npc_dirty_larry' WHERE entry=19720; +UPDATE creature_template SET ScriptName='npc_khadgars_servant' WHERE entry=19685; +UPDATE creature_template SET ScriptName='npc_salsalabim' WHERE entry=18584; + +/* SHOLAZAR BASIN */ +UPDATE creature_template SET ScriptName='npc_helice' WHERE entry=28787; +UPDATE creature_template SET ScriptName='npc_injured_rainspeaker' WHERE entry=28217; +UPDATE creature_template SET ScriptName='npc_mosswalker_victim' WHERE entry=28113; + +/* SILITHUS */ +UPDATE creature_template SET ScriptName='npc_anachronos_the_ancient' WHERE entry=15381; +UPDATE gameobject_template SET ScriptName='go_crystalline_tear' WHERE entry=180633; + +/* SILVERMOON */ + +/* SILVERPINE FOREST */ +UPDATE creature_template SET ScriptName='npc_deathstalker_erland' WHERE entry=1978; +UPDATE creature_template SET ScriptName='npc_deathstalker_faerleia' WHERE entry=2058; + +/* STOCKADES */ + +/* STONETALON MOUNTAINS */ +UPDATE creature_template SET ScriptName='npc_kaya' WHERE entry=11856; + +/* STORM PEAKS */ + +/* STORMWIND CITY */ +UPDATE creature_template SET ScriptName='npc_bartleby' WHERE entry=6090; +UPDATE creature_template SET ScriptName='npc_dashel_stonefist' WHERE entry=4961; +UPDATE creature_template SET ScriptName='npc_lady_katrana_prestor' WHERE entry=1749; +UPDATE creature_template SET ScriptName='npc_squire_rowe' WHERE entry=17804; +UPDATE creature_template SET ScriptName='npc_reginald_windsor' WHERE entry =12580; + +/* STRANGLETHORN VALE */ +UPDATE creature_template SET ScriptName='mob_yenniku' WHERE entry=2530; + +/* STRATHOLME */ +UPDATE instance_template SET ScriptName='instance_stratholme' WHERE map=329; +UPDATE creature_template SET ScriptName='boss_dathrohan_balnazzar' WHERE entry=10812; +UPDATE creature_template SET ScriptName='boss_maleki_the_pallid' WHERE entry=10438; +UPDATE creature_template SET ScriptName='boss_cannon_master_willey' WHERE entry=10997; +UPDATE creature_template SET ScriptName='boss_baroness_anastari' WHERE entry=10436; +UPDATE creature_template SET ScriptName='boss_silver_hand_bosses' WHERE entry IN (17910,17911,17912,17913,17914); +UPDATE creature_template SET ScriptName='mobs_spectral_ghostly_citizen' WHERE entry IN (10384,10385); +UPDATE creature_template SET ScriptName='mob_restless_soul' WHERE entry=11122; +UPDATE gameobject_template SET ScriptName='go_gauntlet_gate' WHERE entry=175357; +UPDATE gameobject_template SET ScriptName='go_service_gate' WHERE entry=175368; +UPDATE gameobject_template SET ScriptName='go_stratholme_postbox' WHERE entry IN (176346,176349,176350,176351,176352,176353); + +/* SUNKEN TEMPLE */ +UPDATE instance_template SET ScriptName='instance_sunken_temple' WHERE map=109; +DELETE FROM scripted_areatrigger WHERE entry=4016; +INSERT INTO scripted_areatrigger VALUES (4016,'at_shade_of_eranikus'); +UPDATE creature_template SET ScriptName='npc_malfurion_stormrage' WHERE entry=15362; +DELETE FROM scripted_event_id WHERE id IN (3094,3095,3097,3098,3099,3100); +INSERT INTO scripted_event_id VALUES +(3094,'event_antalarion_statue_activation'), +(3095,'event_antalarion_statue_activation'), +(3097,'event_antalarion_statue_activation'), +(3098,'event_antalarion_statue_activation'), +(3099,'event_antalarion_statue_activation'), +(3100,'event_antalarion_statue_activation'); +UPDATE creature_template SET ScriptName='npc_shade_of_hakkar' WHERE entry=8440; +UPDATE gameobject_template SET ScriptName='go_eternal_flame' WHERE entry IN (148418,148419,148420,148421); +DELETE FROM scripted_event_id WHERE id=8502; +INSERT INTO scripted_event_id VALUES +(8502,'event_avatar_of_hakkar'); + +/* SUNWELL PLATEAU */ +UPDATE instance_template SET ScriptName='instance_sunwell_plateau' WHERE map=580; +UPDATE creature_template SET ScriptName='boss_brutallus' WHERE entry=24882; +UPDATE creature_template SET ScriptName='boss_kalecgos' WHERE entry=24850; +UPDATE creature_template SET ScriptName='boss_kalecgos_humanoid' WHERE entry=24891; +UPDATE creature_template SET ScriptName='boss_sathrovarr' WHERE entry=24892; +DELETE FROM scripted_areatrigger WHERE entry=4853; +INSERT INTO scripted_areatrigger VALUES (4853,'at_madrigosa'); +UPDATE creature_template SET ScriptName='boss_alythess' WHERE entry=25166; +UPDATE creature_template SET ScriptName='boss_sacrolash' WHERE entry=25165; +UPDATE creature_template SET ScriptName='npc_shadow_image' WHERE entry=25214; +UPDATE creature_template SET ScriptName='boss_muru' WHERE entry=25741; +UPDATE creature_template SET ScriptName='boss_entropius' WHERE entry=25840; +UPDATE creature_template SET ScriptName='npc_portal_target' WHERE entry=25770; +UPDATE creature_template SET ScriptName='npc_void_sentinel_summoner' WHERE entry=25782; +UPDATE creature_template SET ScriptName='boss_kiljaeden' WHERE entry=25315; +UPDATE creature_template SET ScriptName='npc_kiljaeden_controller' WHERE entry=25608; +UPDATE creature_template SET ScriptName='spell_dummy_npc_brutallus_cloud' WHERE entry=25703; +UPDATE creature_template SET ScriptName='boss_felmyst' WHERE entry=25038; +UPDATE creature_template SET ScriptName='npc_shield_orb' WHERE entry=25502; +UPDATE creature_template SET ScriptName='npc_power_blue_flight' WHERE entry=25653; +UPDATE creature_template SET ScriptName='npc_demonic_vapor' WHERE entry=25265; + +/* SWAMP OF SORROWS */ +UPDATE creature_template SET ScriptName='npc_galen_goodward' WHERE entry=5391; + +/* TANARIS */ +UPDATE creature_template SET ScriptName='mob_aquementas' WHERE entry=9453; +UPDATE creature_template SET ScriptName='npc_custodian_of_time' WHERE entry=20129; +UPDATE creature_template SET ScriptName='npc_oox17tn' WHERE entry=7784; +UPDATE creature_template SET ScriptName='npc_stone_watcher_of_norgannon' WHERE entry=7918; +UPDATE creature_template SET ScriptName='npc_tooga' WHERE entry=5955; + +/* TELDRASSIL */ +UPDATE creature_template SET ScriptName='npc_mist' WHERE entry=3568; + +/* */ +/* TEMPEST KEEP */ +/* */ + +/* THE MECHANAR */ +UPDATE creature_template SET ScriptName='boss_nethermancer_sepethrea' WHERE entry=19221; +UPDATE creature_template SET ScriptName='boss_pathaleon_the_calculator' WHERE entry=19220; +UPDATE creature_template SET ScriptName='mob_nether_wraith' WHERE entry=21062; +UPDATE instance_template SET ScriptName='instance_mechanar' WHERE map=554; + +/* THE BOTANICA */ +UPDATE creature_template SET ScriptName='boss_high_botanist_freywinn' WHERE entry=17975; +UPDATE creature_template SET ScriptName='boss_laj' WHERE entry=17980; +UPDATE creature_template SET ScriptName='boss_warp_splinter' WHERE entry=17977; +UPDATE creature_template SET ScriptName='mob_warp_splinter_treant' WHERE entry=19949; + +/* THE ARCATRAZ */ +UPDATE instance_template SET ScriptName='instance_arcatraz' WHERE map=552; +UPDATE creature_template SET ScriptName='boss_harbinger_skyriss' WHERE entry=20912; +UPDATE creature_template SET ScriptName='boss_dalliah' WHERE entry=20885; +UPDATE creature_template SET ScriptName='boss_soccothrates' WHERE entry=20886; +UPDATE creature_template SET ScriptName='npc_warden_mellichar' WHERE entry=20904; +UPDATE creature_template SET ScriptName='npc_millhouse_manastorm' WHERE entry=20977; + +/* THE EYE */ +UPDATE instance_template SET ScriptName='instance_the_eye' WHERE map=550; +/* Al'ar event */ +UPDATE creature_template SET ScriptName='boss_alar' WHERE entry=19514; +/* Void Reaver event */ +UPDATE creature_template SET ScriptName='boss_void_reaver' WHERE entry=19516; +/* Astromancer event */ +UPDATE creature_template SET ScriptName='boss_high_astromancer_solarian' WHERE entry=18805; +UPDATE creature_template SET ScriptName='mob_solarium_priest' WHERE entry=18806; +/* Kael'thas event */ +UPDATE creature_template SET ScriptName='boss_kaelthas' WHERE entry=19622; +UPDATE creature_template SET ScriptName='boss_thaladred_the_darkener' WHERE entry=20064; +UPDATE creature_template SET ScriptName='boss_lord_sanguinar' WHERE entry=20060; +UPDATE creature_template SET ScriptName='boss_grand_astromancer_capernian' WHERE entry=20062; +UPDATE creature_template SET ScriptName='boss_master_engineer_telonicus' WHERE entry=20063; +UPDATE creature_template SET ScriptName='mob_phoenix_tk' WHERE entry=21362; +UPDATE creature_template SET ScriptName='mob_phoenix_egg_tk' WHERE entry=21364; + +/* TEMPLE OF AHN'QIRAJ */ +UPDATE instance_template SET ScriptName='instance_temple_of_ahnqiraj' WHERE map=531; +UPDATE creature_template SET ScriptName='boss_cthun' WHERE entry=15727; +UPDATE creature_template SET ScriptName='boss_skeram' WHERE entry=15263; +UPDATE creature_template SET ScriptName='boss_vem' WHERE entry=15544; +UPDATE creature_template SET ScriptName='boss_yauj' WHERE entry=15543; +UPDATE creature_template SET ScriptName='boss_kri' WHERE entry=15511; +UPDATE creature_template SET ScriptName='boss_sartura' WHERE entry=15516; +UPDATE creature_template SET ScriptName='boss_fankriss' WHERE entry=15510; +UPDATE creature_template SET ScriptName='boss_viscidus' WHERE entry=15299; +-- UPDATE creature_template SET ScriptName='boss_glob_of_viscidus' WHERE entry=15667; +UPDATE creature_template SET ScriptName='boss_huhuran' WHERE entry=15509; +UPDATE creature_template SET ScriptName='boss_veklor' WHERE entry=15276; +UPDATE creature_template SET ScriptName='boss_veknilash' WHERE entry=15275; +UPDATE creature_template SET ScriptName='boss_ouro' WHERE entry=15517; +UPDATE creature_template SET ScriptName='npc_ouro_spawner' WHERE entry=15957; +UPDATE creature_template SET ScriptName='boss_eye_of_cthun' WHERE entry=15589; +UPDATE creature_template SET ScriptName='mob_sartura_royal_guard' WHERE entry=15984; +UPDATE creature_template SET ScriptName='mob_giant_claw_tentacle' WHERE entry=15728; +UPDATE creature_template SET ScriptName='mob_anubisath_sentinel' WHERE entry=15264; +DELETE FROM scripted_areatrigger WHERE entry IN (4033,4034); +INSERT INTO scripted_areatrigger VALUES +(4033,'at_stomach_cthun'), +(4034,'at_stomach_cthun'); + +/* TEROKKAR FOREST */ +UPDATE creature_template SET ScriptName='mob_netherweb_victim' WHERE entry=22355; +UPDATE creature_template SET ScriptName='mob_unkor_the_ruthless' WHERE entry=18262; +UPDATE creature_template SET ScriptName='npc_akuno' WHERE entry=22377; +UPDATE creature_template SET ScriptName='npc_hungry_nether_ray' WHERE entry=23439; +UPDATE creature_template SET ScriptName='npc_letoll' WHERE entry=22458; +UPDATE creature_template SET ScriptName='npc_mana_bomb_exp_trigger' WHERE entry=20767; +UPDATE gameobject_template SET ScriptName='go_mana_bomb' WHERE entry=184725; +UPDATE creature_template SET ScriptName='npc_captive_child' WHERE entry=22314; +UPDATE creature_template SET ScriptName='npc_isla_starmane' WHERE entry=18760; +UPDATE creature_template SET ScriptName="npc_skywing" WHERE entry=22424; + +/* THOUSAND NEEDLES */ +UPDATE creature_template SET ScriptName='npc_kanati' WHERE entry=10638; +UPDATE creature_template SET ScriptName='npc_plucky_johnson' WHERE entry=6626; +UPDATE creature_template SET ScriptName='npc_paoka_swiftmountain' WHERE entry=10427; +UPDATE creature_template SET ScriptName='npc_lakota_windsong' WHERE entry=10646; + +/* THUNDER BLUFF */ + +/* TIRISFAL GLADES */ +UPDATE gameobject_template SET ScriptName='go_mausoleum_trigger' WHERE entry=104593; +UPDATE gameobject_template SET ScriptName='go_mausoleum_door' WHERE entry=176594; +UPDATE creature_template SET ScriptName='npc_calvin_montague' WHERE entry=6784; + +/* ULDAMAN */ +DELETE FROM scripted_event_id WHERE id IN (2228,2268); +INSERT INTO scripted_event_id VALUES +(2228,'event_spell_altar_boss_aggro'), +(2268,'event_spell_altar_boss_aggro'); +UPDATE creature_template SET ScriptName='boss_archaedas' WHERE entry=2748; +UPDATE creature_template SET ScriptName='mob_archaeras_add' WHERE entry IN (7309,7076,7077,10120); +UPDATE instance_template SET ScriptName='instance_uldaman' WHERE map=70; + +/* */ +/* ULDUAR */ +/* */ + +/* HALLS OF LIGHTNING */ +UPDATE instance_template SET ScriptName='instance_halls_of_lightning' WHERE map=602; +UPDATE creature_template SET ScriptName='boss_bjarngrim' WHERE entry=28586; +UPDATE creature_template SET ScriptName='mob_stormforged_lieutenant' WHERE entry=29240; +UPDATE creature_template SET ScriptName='boss_volkhan' WHERE entry=28587; +UPDATE creature_template SET ScriptName='mob_molten_golem' WHERE entry=28695; +UPDATE creature_template SET ScriptName='npc_volkhan_anvil' WHERE entry=28823; +UPDATE creature_template SET ScriptName='boss_ionar' WHERE entry=28546; +UPDATE creature_template SET ScriptName='mob_spark_of_ionar' WHERE entry=28926; +UPDATE creature_template SET ScriptName='boss_loken' WHERE entry=28923; + +/* HALLS OF STONE */ +UPDATE instance_template SET ScriptName='instance_halls_of_stone' WHERE map=599; +UPDATE creature_template SET ScriptName='boss_maiden_of_grief' WHERE entry=27975; +UPDATE creature_template SET ScriptName='boss_sjonnir' WHERE entry=27978; +UPDATE creature_template SET ScriptName='npc_brann_hos' WHERE entry=28070; +UPDATE creature_template SET ScriptName='npc_dark_matter' WHERE entry=28235; +UPDATE creature_template SET ScriptName='npc_searing_gaze' WHERE entry=28265; + +/* ULDUAR */ +UPDATE instance_template SET ScriptName='instance_ulduar' WHERE map=603; +UPDATE creature_template SET ScriptName='boss_general_vezax' WHERE entry=33271; +UPDATE creature_template SET ScriptName='boss_auriaya' WHERE entry=33515; +UPDATE creature_template SET ScriptName='boss_feral_defender' WHERE entry=34035; +UPDATE creature_template SET ScriptName='boss_brundir' WHERE entry=32857; +UPDATE creature_template SET ScriptName='boss_molgeim' WHERE entry=32927; +UPDATE creature_template SET ScriptName='boss_steelbreaker' WHERE entry=32867; +UPDATE creature_template SET ScriptName='boss_ignis' WHERE entry=33118; +UPDATE creature_template SET ScriptName='npc_iron_construct' WHERE entry=33121; +UPDATE creature_template SET ScriptName='npc_scorch' WHERE entry=33221; +UPDATE creature_template SET ScriptName='boss_xt_002' WHERE entry=33293; +DELETE FROM scripted_event_id WHERE id IN (9735,21620); +INSERT INTO scripted_event_id VALUES +(9735,'event_spell_saronite_barrier'), +(21620,'event_ulduar'); + +/* UN'GORO CRATER */ +UPDATE creature_template SET ScriptName='npc_ame01' WHERE entry=9623; +UPDATE creature_template SET ScriptName='npc_ringo' WHERE entry=9999; + +/* UNDERCITY */ +UPDATE creature_template SET ScriptName='npc_lady_sylvanas_windrunner' WHERE entry=10181; + +/* */ +/* UTGARDE KEEP */ +/* */ + +/* UTGARDE KEEP */ +UPDATE instance_template SET ScriptName='instance_utgarde_keep' WHERE map=574; +UPDATE creature_template SET ScriptName='mob_dragonflayer_forge_master' WHERE entry=24079; +UPDATE creature_template SET ScriptName='boss_skarvald' WHERE entry=24200; +UPDATE creature_template SET ScriptName='boss_dalronn' WHERE entry=24201; +UPDATE creature_template SET ScriptName='boss_ingvar' WHERE entry=23954; +UPDATE creature_template SET ScriptName='npc_annhylde' WHERE entry=24068; +UPDATE creature_template SET ScriptName='boss_keleseth' WHERE entry=23953; +UPDATE creature_template SET ScriptName='mob_vrykul_skeleton' WHERE entry=23970; + +/* UTGARDE PINNACLE */ +UPDATE creature_template SET ScriptName='boss_gortok' WHERE entry=26687; +UPDATE creature_template SET ScriptName='npc_gortok_subboss' WHERE entry IN (26683,26684,26685,26686); +UPDATE creature_template SET ScriptName='boss_skadi' WHERE entry=26693; +UPDATE creature_template SET ScriptName='npc_grauf' WHERE entry=26893; +UPDATE creature_template SET ScriptName='boss_svala' WHERE entry=29281; +UPDATE creature_template SET ScriptName='boss_ymiron' WHERE entry=26861; +UPDATE instance_template SET ScriptName='instance_pinnacle' WHERE map=575; +DELETE FROM scripted_areatrigger WHERE entry IN (4991,5140); +INSERT INTO scripted_areatrigger VALUES +(4991,'at_skadi'), +(5140,'at_svala_intro'); +DELETE FROM scripted_event_id WHERE id IN (17728,20651); +INSERT INTO scripted_event_id VALUES +(17728,'event_spell_gortok_event'), +(20651,'event_achiev_kings_bane'); + +/* VAULT OF ARCHAVON */ + +/* VIOLET HOLD */ +UPDATE instance_template SET ScriptName='instance_violet_hold' WHERE map=608; +UPDATE gameobject_template SET ScriptName='go_activation_crystal' WHERE entry=193611; +UPDATE creature_template SET ScriptName='npc_door_seal' WHERE entry=30896; +UPDATE creature_template SET ScriptName='npc_sinclari' WHERE entry=30658; +UPDATE creature_template SET ScriptName='npc_prison_event_controller' WHERE entry=30883; +UPDATE creature_template SET ScriptName='npc_teleportation_portal' WHERE entry IN (31011,30679,32174); +UPDATE creature_template SET ScriptName='boss_ichoron' WHERE entry IN (29313,32234); +UPDATE creature_template SET ScriptName='boss_erekem' WHERE entry IN (29315,32226); +UPDATE creature_template SET ScriptName='npc_erekem_guard' WHERE entry IN (29395,32228); + +/* WAILING CAVERNS */ +UPDATE instance_template SET ScriptName='instance_wailing_caverns' WHERE map=43; +UPDATE creature_template SET ScriptName='npc_disciple_of_naralex' WHERE entry=3678; + +/* WESTERN PLAGUELANDS */ +UPDATE creature_template SET ScriptName='npc_the_scourge_cauldron' WHERE entry=11152; +UPDATE creature_template SET ScriptName='npc_anchorite_truuen' WHERE entry=17238; + +/* WESTFALL */ +UPDATE creature_template SET ScriptName='npc_daphne_stilwell' WHERE entry=6182; +UPDATE creature_template SET ScriptName='npc_defias_traitor' WHERE entry=467; + +/* WETLANDS */ +UPDATE creature_template SET ScriptName='npc_tapoke_slim_jahn' WHERE entry=4962; +UPDATE creature_template SET ScriptName='npc_mikhail' WHERE entry=4963; + +/* WINTERSPRING */ +UPDATE creature_template SET ScriptName='npc_ranshalla' WHERE entry=10300; +UPDATE gameobject_template SET ScriptName='go_elune_fire' WHERE entry IN (177417, 177404); + +/* ZANGARMARSH */ +DELETE FROM scripted_event_id WHERE id=11225; +INSERT INTO scripted_event_id VALUES (11225,'event_taxi_stormcrow'); +UPDATE creature_template SET ScriptName='npc_cooshcoosh' WHERE entry=18586; +UPDATE creature_template SET ScriptName='npc_kayra_longmane' WHERE entry=17969; + +/* ZUL'AMAN */ +UPDATE instance_template SET ScriptName='instance_zulaman' WHERE map=568; +UPDATE creature_template SET ScriptName='npc_harrison_jones_za' WHERE entry=24358; +UPDATE gameobject_template SET ScriptName='go_strange_gong' WHERE entry=187359; +UPDATE creature_template SET ScriptName='boss_akilzon' WHERE entry=23574; +UPDATE creature_template SET ScriptName='mob_soaring_eagle' WHERE entry=24858; +UPDATE creature_template SET ScriptName='boss_halazzi' WHERE entry=23577; +UPDATE creature_template SET ScriptName='boss_spirit_lynx' WHERE entry=24143; +UPDATE creature_template SET ScriptName='boss_janalai' WHERE entry=23578; +UPDATE creature_template SET ScriptName='boss_malacrass' WHERE entry=24239; +UPDATE creature_template SET ScriptName='boss_nalorakk' WHERE entry=23576; +UPDATE creature_template SET ScriptName='boss_zuljin' WHERE entry=23863; +UPDATE creature_template SET ScriptName='npc_feather_vortex' WHERE entry=24136; +UPDATE creature_template SET ScriptName='npc_dragonhawk_egg' WHERE entry=23817; +UPDATE creature_template SET ScriptName='npc_janalai_firebomb' WHERE entry=23920; +UPDATE creature_template SET ScriptName='npc_amanishi_hatcher' WHERE entry IN (23818,24504); +UPDATE creature_template SET ScriptName='npc_forest_frog' WHERE entry=24396; + +/* ZUL'DRAK */ +UPDATE creature_template SET ScriptName='npc_gurgthock' WHERE entry=30007; + + +/* ZUL'FARRAK */ +UPDATE instance_template SET ScriptName='instance_zulfarrak' WHERE map=209; +DELETE FROM scripted_event_id WHERE id IN (2488,2609); +INSERT INTO scripted_event_id VALUES +(2488,'event_go_zulfarrak_gong'), +(2609,'event_spell_unlocking'); +DELETE FROM scripted_areatrigger WHERE entry=1447; +INSERT INTO scripted_areatrigger VALUES (1447,'at_zulfarrak'); +UPDATE creature_template SET ScriptName='boss_zumrah' WHERE entry=7271; + +/* ZUL'GURUB */ +UPDATE instance_template SET ScriptName='instance_zulgurub' WHERE map=309; +UPDATE creature_template SET ScriptName='boss_jeklik' WHERE entry=14517; +UPDATE creature_template SET ScriptName='boss_venoxis' WHERE entry=14507; +UPDATE creature_template SET ScriptName='boss_marli' WHERE entry=14510; +UPDATE creature_template SET ScriptName='boss_mandokir' WHERE entry=11382; +UPDATE creature_template SET ScriptName='mob_ohgan' WHERE entry=14988; +UPDATE creature_template SET ScriptName='boss_jindo' WHERE entry=11380; +UPDATE creature_template SET ScriptName='boss_hakkar' WHERE entry=14834; +UPDATE creature_template SET ScriptName='boss_thekal' WHERE entry=14509; +UPDATE creature_template SET ScriptName='boss_arlokk' WHERE entry=14515; +UPDATE gameobject_template SET ScriptName='go_gong_of_bethekk' WHERE entry=180526; +UPDATE creature_template SET ScriptName='boss_hazzarah' WHERE entry=15083; +UPDATE creature_template SET ScriptName='boss_renataki' WHERE entry=15084; +UPDATE creature_template SET ScriptName='mob_zealot_lorkhan' WHERE entry=11347; +UPDATE creature_template SET ScriptName='mob_zealot_zath' WHERE entry=11348; +UPDATE creature_template SET ScriptName='mob_healing_ward' WHERE entry=14987; +UPDATE creature_template SET ScriptName='npc_gurubashi_bat_rider' WHERE entry=14750; + +/* EOF */ diff --git a/src/modules/SD2/sql/old/mangos_old_spells.sql b/src/modules/SD2/sql/old/mangos_old_spells.sql new file mode 100644 index 000000000..8419cacd2 --- /dev/null +++ b/src/modules/SD2/sql/old/mangos_old_spells.sql @@ -0,0 +1,374 @@ +/* Old spell queries from Scriptdev revision 55. Not supported by scriptdev anylonger */ +/* To be used in ordinary database developement. Can be added in any changeset/revision as database developers sees fit */ + +UPDATE `creature_template` SET `spell1` = 116 WHERE `entry` = 946; +UPDATE `creature_template` SET `spell1` = 16415 WHERE `entry` = 4063; +UPDATE `creature_template` SET `spell1` = 205 WHERE `entry` = 1867; +UPDATE `creature_template` SET `spell1` = 705 WHERE `entry` = 1915; +UPDATE `creature_template` SET `spell1` = 145, `spell2` = 134 WHERE `entry` = 1914; +UPDATE `creature_template` SET `spell1` = 837, `spell2` = 122 WHERE `entry` = 1889; +UPDATE `creature_template` SET `spell1` = 8406, `spell2` = 865 WHERE `entry` = 314; +UPDATE `creature_template` SET `spell1` = 6041, `spell2` = 6364 WHERE `entry` = 2570; +UPDATE `creature_template` SET `spell1` = 8407, `spell2` = 12486 WHERE `entry` = 2567; +UPDATE `creature_template` SET `spell1` = 6041 WHERE `entry` = 697; +UPDATE `creature_template` SET `spell1` = 8402, `spell2` = 8423 WHERE `entry` = 1653; +UPDATE `creature_template` SET `spell1` = 8402 WHERE `entry` = 1562; +UPDATE `creature_template` SET `spell1` = 7641, `spell2` = 11707 WHERE `entry` = 1564; +UPDATE `creature_template` SET `spell1` = 1106 WHERE `entry` = 3218; +UPDATE `creature_template` SET `spell1` = 837 WHERE `entry` = 2591; +UPDATE `creature_template` SET `spell1` = 837 WHERE `entry` = 2255; +UPDATE `creature_template` SET `spell1` = 205 WHERE `entry` = 1539; +UPDATE `creature_template` SET `spell1` = 15264, `spell2` = 6060, `spell3` = 988 WHERE `entry` = 4299; +UPDATE `creature_template` SET `spell1` = 984, `spell2` = 1026 WHERE `entry` = 4296; +UPDATE `creature_template` SET `spell1` = 8438, `spell2` = 2601 WHERE `entry` = 4300; +UPDATE `creature_template` SET `spell1` = 1088, `spell2` = 992 WHERE `entry` = 533; +UPDATE `creature_template` SET `spell1` = 7322 WHERE `entry` = 203; +UPDATE `creature_template` SET `spell1` = 1106, `spell2` = 2941 WHERE `entry` = 2577; +UPDATE `creature_template` SET `spell1` = 548, `spell2` = 6535 WHERE `entry` = 3273; +UPDATE `creature_template` SET `spell1` = 529 WHERE `entry` = 1183; +UPDATE `creature_template` SET `spell1` = 992, `spell2` = 705 WHERE `entry` = 436; +UPDATE `creature_template` SET `spell1` = 5177 WHERE `entry` = 7235; +UPDATE `creature_template` SET `spell1` = 5177 WHERE `entry` = 2012; +UPDATE `creature_template` SET `spell1` = 403, `spell2` = 770 WHERE `entry` = 1397; +UPDATE `creature_template` SET `spell1` = 2136 WHERE `entry` = 1174; +UPDATE `creature_template` SET `spell1` = 143 WHERE `entry` = 2018; +UPDATE `creature_template` SET `spell1` = 915, `spell2` = 2606 WHERE `entry` = 1399; +UPDATE `creature_template` SET `spell1` = 915, `spell2` = 8293 WHERE `entry` = 3783; +UPDATE `creature_template` SET `spell1` = 984 WHERE `entry` = 3732; +UPDATE `creature_template` SET `spell1` = 13480 WHERE `entry` = 3725; +UPDATE `creature_template` SET `spell1` = 13480 WHERE `entry` = 3728; +UPDATE `creature_template` SET `spell1` = 9672, `spell2` = 7101 WHERE `entry` = 3662; +UPDATE `creature_template` SET `spell1` = 18089 WHERE `entry` = 2207; +UPDATE `creature_template` SET `spell1` = 8402, `spell2` = 10215 WHERE `entry` = 7026; +UPDATE `creature_template` SET `spell1` = 11659, `spell2` = 8402 WHERE `entry` = 7028; +UPDATE `creature_template` SET `spell1` = 7918, `spell2` = 6685 WHERE `entry` = 7038; +UPDATE `creature_template` SET `spell1` = 676, `spell2` = 53, `spell3` = 0 WHERE `entry` = 6866; +UPDATE `creature_template` SET `spell1` = 53, `spell2` = 0, `spell3` = 0 WHERE `entry` = 122; +UPDATE `creature_template` SET `spell1` = 71, `spell2` = 1671 WHERE `entry` = 449; +UPDATE `creature_template` SET `spell1` = 6554 WHERE `entry` = 121; +UPDATE `creature_template` SET `spell1` = 53 WHERE `entry` = 590; +UPDATE `creature_template` SET `spell1` = 12166 WHERE `entry` = 3232; +UPDATE `creature_template` SET `spell1` = 403 WHERE `entry` = 2953; +UPDATE `creature_template` SET `spell1` = 3385 WHERE `entry` = 2954; +UPDATE `creature_template` SET `spell1` = 15657, `spell2` = 17230, `spell3` = 16509, `spell4` = 0 WHERE `entry` = 1707; +UPDATE `creature_template` SET `spell1` = 6253, `spell2` = 16244, `spell3` = 8242, `spell4` = 16509 WHERE `entry` = 1711; +UPDATE `creature_template` SET `spell1` = 6547, `spell2` = 2590, `spell3` = 6253 WHERE `entry` = 1708; +UPDATE `creature_template` SET `spell1` = 11554, `spell2` = 8242, `spell3` = 6253 WHERE `entry` = 1715; +UPDATE `creature_template` SET `spell1` = 1768, `spell2` = 17230, `spell3` = 16509 WHERE `entry` = 1706; +UPDATE `creature_template` SET `spell1` = 5115 WHERE `entry` = 1729; +UPDATE `creature_template` SET `spell1` = 9915 WHERE `entry` = 1732; +UPDATE `creature_template` SET `spell1` = 143 WHERE `entry` = 1726; +UPDATE `creature_template` SET `spell1` = 143 WHERE `entry` = 619; +UPDATE `creature_template` SET `spell1` = 6660 WHERE `entry` = 657; +UPDATE `creature_template` SET `spell1` = 6660, `spell2` = 6685 WHERE `entry` = 4417; +UPDATE `creature_template` SET `spell1` = 6685 WHERE `entry` = 598; +UPDATE `creature_template` SET `spell1` = 6016 WHERE `entry` = 4416; +UPDATE `creature_template` SET `spell1` = 5115, `spell2` = 6435 WHERE `entry` = 594; +UPDATE `creature_template` SET `spell1` = 9915 WHERE `entry` = 634; +UPDATE `creature_template` SET `spell1` = 205 WHERE `entry` = 1725; +UPDATE `creature_template` SET `spell1` = 133, `spell2` = 205, `spell3` = 113 WHERE `entry` = 4418; +UPDATE `creature_template` SET `spell1` = 6136, `spell2` = 116 WHERE `entry` = 474; +UPDATE `creature_template` SET `spell1` = 3140, `spell2` = 12486, `spell3` = 3443 WHERE `entry` = 910; +UPDATE `creature_template` SET `spell1` = 744, `spell2` = 7992, `spell3` = 2590 WHERE `entry` = 909; +UPDATE `creature_template` SET `spell1` = 8646 WHERE `entry` = 116; +UPDATE `creature_template` SET `spell1` = 53 WHERE `entry` = 504; +UPDATE `creature_template` SET `spell1` = 2764 WHERE `entry` = 95; +UPDATE `creature_template` SET `spell1` = 53, `spell2` = 133 WHERE `entry` = 94; +UPDATE `creature_template` SET `spell1` = 168 WHERE `entry` = 589; +UPDATE `creature_template` SET `spell1` = 8382, `spell2` = 8733 WHERE `entry` = 4819; +UPDATE `creature_template` SET `spell1` = 6145, `spell2` = 71, `spell3` = 22691 WHERE `entry` = 4818; +UPDATE `creature_template` SET `spell1` = 14109, `spell2` = 3358, `spell3` = 8733, `spell4` = 12024 WHERE `entry` = 4820; +UPDATE `creature_template` SET `spell1` = 7357 WHERE `entry` = 4359; +UPDATE `creature_template` SET `spell1` = 7357 WHERE `entry` = 1024; +UPDATE `creature_template` SET `spell1` = 1777, `spell2` = 6533 WHERE `entry` = 1028; +UPDATE `creature_template` SET `spell1` = 10177 WHERE `entry` = 1418; +UPDATE `creature_template` SET `spell1` = 744, `spell2` = 1707 WHERE `entry` = 1026; +UPDATE `creature_template` SET `spell1` = 7372, `spell2` = 2457 WHERE `entry` = 1027; +UPDATE `creature_template` SET `spell1` = 15869 WHERE `entry` = 1029; +UPDATE `creature_template` SET `spell1` = 7357 WHERE `entry` = 747; +UPDATE `creature_template` SET `spell1` = 7357 WHERE `entry` = 171; +UPDATE `creature_template` SET `spell1` = 7357 WHERE `entry` = 544; +UPDATE `creature_template` SET `spell1` = 7357 WHERE `entry` = 545; +UPDATE `creature_template` SET `spell1` = 7357 WHERE `entry` = 578; +UPDATE `creature_template` SET `spell1` = 744, `spell2` = 865 WHERE `entry` = 127; +UPDATE `creature_template` SET `spell1` = 13519, `spell2` = 6074 WHERE `entry` = 517; +UPDATE `creature_template` SET `spell1` = 10277 WHERE `entry` = 458; +UPDATE `creature_template` SET `spell1` = 7357 WHERE `entry` = 126; +UPDATE `creature_template` SET `spell1` = 205, `spell2` = 331 WHERE `entry` = 548; +UPDATE `creature_template` SET `spell1` = 2589 WHERE `entry` = 732; +UPDATE `creature_template` SET `spell1` = 3368 WHERE `entry` = 46; +UPDATE `creature_template` SET `spell1` = 6268 WHERE `entry` = 1083; +UPDATE `creature_template` SET `spell1` = 3393 WHERE `entry` = 422; +UPDATE `creature_template` SET `spell1` = 3288 WHERE `entry` = 1011; +UPDATE `creature_template` SET `spell1` = 8016 WHERE `entry` = 1008; +UPDATE `creature_template` SET `spell1` = 8016 WHERE `entry` = 1007; +UPDATE `creature_template` SET `spell1` = 205 WHERE `entry` = 1009; +UPDATE `creature_template` SET `spell1` = 547, `spell2` = 548 WHERE `entry` = 1013; +UPDATE `creature_template` SET `spell1` = 744 WHERE `entry` = 434; +UPDATE `creature_template` SET `spell1` = 3150 WHERE `entry` = 433; +UPDATE `creature_template` SET `spell1` = 6205 WHERE `entry` = 432; +UPDATE `creature_template` SET `spell1` = 705 WHERE `entry` = 429; +UPDATE `creature_template` SET `spell1` = 71, `spell2` = 8380, `spell3` = 8629 WHERE `entry` = 568; +UPDATE `creature_template` SET `spell1` = 744 WHERE `entry` = 579; +UPDATE `creature_template` SET `spell1` = 6730, `spell2` = 6016 WHERE `entry` = 448; +UPDATE `creature_template` SET `spell1` = 6730, `spell2` = 6016 WHERE `entry` = 500; +UPDATE `creature_template` SET `spell1` = 8016, `spell2` = 0 WHERE `entry` = 123; +UPDATE `creature_template` SET `spell1` = 1160 WHERE `entry` = 124; +UPDATE `creature_template` SET `spell1` = 3368 WHERE `entry` = 501; +UPDATE `creature_template` SET `spell1` = 3237 WHERE `entry` = 1674; +UPDATE `creature_template` SET `spell1` = 3237 WHERE `entry` = 1772; +UPDATE `creature_template` SET `spell1` = 3237, `spell2` = 695 WHERE `entry` = 1773; +UPDATE `creature_template` SET `spell1` = 3237 WHERE `entry` = 1939; +UPDATE `creature_template` SET `spell1` = 3237 WHERE `entry` = 1940; +UPDATE `creature_template` SET `spell1` = 3237 WHERE `entry` = 1942; +UPDATE `creature_template` SET `spell1` = 3237 WHERE `entry` = 1944; +UPDATE `creature_template` SET `spell1` = 2480 WHERE `entry` = 3112; +UPDATE `creature_template` SET `spell1` = 5280 WHERE `entry` = 3111; +UPDATE `creature_template` SET `spell1` = 3248 WHERE `entry` = 3114; +UPDATE `creature_template` SET `spell1` = 6950, `spell2` = 774 WHERE `entry` = 3113; +UPDATE `creature_template` SET `spell1` = 548, `spell2` = 8045, `spell3` = 8052 WHERE `entry` = 3269; +UPDATE `creature_template` SET `spell1` = 594, `spell2` = 8092, `spell3` = 2052 WHERE `entry` = 3458; +UPDATE `creature_template` SET `spell1` = 594, `spell2` = 598, `spell3` = 8102, `spell4` = 6074 WHERE `entry` = 3271; +UPDATE `creature_template` SET `spell1` = 1978, `spell2` = 3044 WHERE `entry` = 3265; +UPDATE `creature_template` SET `spell1` = 13446, `spell2` = 12323, `spell3` = 13443 WHERE `entry` = 3459; +UPDATE `creature_template` SET `spell1` = 22356 WHERE `entry` = 3267; +UPDATE `creature_template` SET `spell1` = 3427 WHERE `entry` = 712; +UPDATE `creature_template` SET `spell1` = 5164 WHERE `entry` = 446; +UPDATE `creature_template` SET `spell1` = 331, `spell2` = 548 WHERE `entry` = 430; +UPDATE `creature_template` SET `spell1` = 7919 WHERE `entry` = 424; +UPDATE `creature_template` SET `spell1` = 3148 WHERE `entry` = 1115; +UPDATE `creature_template` SET `spell1` = 5164 WHERE `entry` = 1117; +UPDATE `creature_template` SET `spell1` = 53 WHERE `entry` = 1116; +UPDATE `creature_template` SET `spell1` = 5164 WHERE `entry` = 1118; +UPDATE `creature_template` SET `spell1` = 10277 WHERE `entry` = 1162; +UPDATE `creature_template` SET `spell1` = 529 WHERE `entry` = 1166; +UPDATE `creature_template` SET `spell1` = 1776 WHERE `entry` = 1163; +UPDATE `creature_template` SET `spell1` = 11976 WHERE `entry` = 1164; +UPDATE `creature_template` SET `spell1` = 331 WHERE `entry` = 1197; +UPDATE `creature_template` SET `spell1` = 2121 WHERE `entry` = 1165; +UPDATE `creature_template` SET `spell1` = 7405, `spell2` = 71 WHERE `entry` = 1167; +UPDATE `creature_template` SET `spell1` = 11660, `spell2` = 17926, `spell3` = 6215, `spell4` = 11672 WHERE `entry` = 1789; +UPDATE `creature_template` SET `spell1` = 348, `spell2` = 686 WHERE `entry` = 4473; +UPDATE `creature_template` SET `spell1` = 15580, `spell2` = 15613, `spell3` = 23600, `spell4` = 16406 WHERE `entry` = 10391; +UPDATE `creature_template` SET `spell1` = 3416 WHERE `entry` = 531; +UPDATE `creature_template` SET `spell1` = 7369 WHERE `entry` = 1783; +UPDATE `creature_template` SET `spell1` = 838 WHERE `entry` = 7341; +UPDATE `creature_template` SET `spell1` = 15580, `spell2` = 15613, `spell3` = 16406 WHERE `entry` = 10390; +UPDATE `creature_template` SET `spell1` = 9734, `spell2` = 6074 WHERE `entry` = 787; +UPDATE `creature_template` SET `spell1` = 7322, `spell2` = 12486 WHERE `entry` = 203; +UPDATE `creature_template` SET `spell1` = 7992 WHERE `entry` = 1110; +UPDATE `creature_template` SET `spell1` = 17312, `spell2` = 992, `spell3` = 984 WHERE `entry` = 7340; +UPDATE `creature_template` SET `spell1` = 22940, `spell2` = 1411 WHERE `entry` = 1784; +UPDATE `creature_template` SET `spell1` = 705, `spell2` = 1014 WHERE `entry` = 7342; +UPDATE `creature_template` SET `spell1` = 8699 WHERE `entry` = 785; +UPDATE `creature_template` SET `spell1` = 7373 WHERE `entry` = 48; +UPDATE `creature_template` SET `spell1` = 19644, `spell2` = 21949, `spell3` = 19642 WHERE `entry` = 6488; +UPDATE `creature_template` SET `spell1` = 10179, `spell2` = 22645, `spell3` = 13009, `spell4` = 12556 WHERE `entry` = 7358; +UPDATE `creature_template` SET `spell1` = 10148, `spell2` = 12470 WHERE `entry` = 7357; +UPDATE `creature_template` SET `spell1` = 12947, `spell2` = 12946 WHERE `entry` = 7356; +UPDATE `creature_template` SET `spell1` = 12255, `spell2` = 12252, `spell3` = 12254 WHERE `entry` = 7355; +UPDATE `creature_template` SET `spell1` = 10892, `spell2` = 11659 WHERE `entry` = 7354; +UPDATE `creature_template` SET `spell1` = 21667, `spell2` = 21331, `spell3` = 21793 WHERE `entry` = 12225; +UPDATE `creature_template` SET `spell1` = 21080, `spell2` = 8817 WHERE `entry` = 12236; +UPDATE `creature_template` SET `spell1` = 24375, `spell2` = 15580 WHERE `entry` = 12237; +UPDATE `creature_template` SET `spell1` = 21909, `spell2` = 21832, `spell3` = 19128, `spell4` = 21869 WHERE `entry` = 12201; +UPDATE `creature_template` SET `spell1` = 21911, `spell2` = 15584, `spell3` = 21749 WHERE `entry` = 12258; +UPDATE `creature_template` SET `spell1` = 21833, `spell2` = 22334 WHERE `entry` = 13601; +UPDATE `creature_template` SET `spell1` = 21687, `spell2` = 21547, `spell3` = 21707 WHERE `entry` = 13282; +UPDATE `creature_template` SET `spell1` = 9770, `spell2` = 16145, `spell3` = 1604, `spell4` = 9776 WHERE `entry` = 6206; +UPDATE `creature_template` SET `spell1` = 9770, `spell2` = 1604, `spell3` = 12540 WHERE `entry` = 6208; +UPDATE `creature_template` SET `spell1` = 10851 WHERE `entry` = 6209; +UPDATE `creature_template` SET `spell1` = 1604, `spell2` = 6409, `spell3` = 3420 WHERE `entry` = 6215; +UPDATE `creature_template` SET `spell1` = 9459, `spell2` = 1604, `spell3` = 10341, `spell4` = 11638 WHERE `entry` = 6219; +UPDATE `creature_template` SET `spell1` = 16169, `spell2` = 5568, `spell3` = 10887 WHERE `entry` = 6229; +UPDATE `creature_template` SET `spell1` = 3053, `spell2` = 16412 WHERE `entry` = 6228; +UPDATE `creature_template` SET `spell1` = 11082, `spell2` = 11085, `spell3` = 11084 WHERE `entry` = 6235; +UPDATE `creature_template` SET `spell1` = 1604 WHERE `entry` = 7361; +UPDATE `creature_template` SET `spell1` = 6660, `spell2` = 8858 WHERE `entry` = 6407; +UPDATE `creature_template` SET `spell1` = 8211, `spell2` = 10341, `spell3` = 1604, `spell4` = 9459 WHERE `entry` = 6220; +UPDATE `creature_template` SET `spell1` = 10341, `spell2` = 1604, `spell3` = 9459 WHERE `entry` = 6218; +UPDATE `creature_template` SET `spell1` = 1604, `spell2` = 11264, `spell3` = 12024 WHERE `entry` = 7603; +UPDATE `creature_template` SET `spell1` = 6660, `spell2` = 2643, `spell3` = 5116 WHERE `entry` = 6223; +UPDATE `creature_template` SET `spell1` = 13398, `spell2` = 1604, `spell3` = 12024 WHERE `entry` = 6222; +UPDATE `creature_template` SET `spell1` = 11820, `spell2` = 1604 WHERE `entry` = 6234; +UPDATE `creature_template` SET `spell1` = 1604 WHERE `entry` = 6233; +UPDATE `creature_template` SET `spell1` = 11306, `spell2` = 10733 WHERE `entry` = 6226; +UPDATE `creature_template` SET `spell1` = 22519, `spell2` = 11264 WHERE `entry` = 6227; +UPDATE `creature_template` SET `spell1` = 10346, `spell2` = 1604, `spell3` = 17174 WHERE `entry` = 6225; +UPDATE `creature_template` SET `spell1` = 6533, `spell2` = 1604, `spell3` = 11820, `spell4` = 11084 WHERE `entry` = 6230; +UPDATE `creature_template` SET `spell1` = 10341, `spell2` = 9459 WHERE `entry` = 7079; +UPDATE `creature_template` SET `spell1` = 22689, `spell2` = 22662, `spell3` = 19319 WHERE `entry` = 11492; +UPDATE `creature_template` SET `spell1` = 15708, `spell2` = 23511 WHERE `entry` = 14325; +UPDATE `creature_template` SET `spell1` = 10947, `spell2` = 10151 WHERE `entry` = 14324; +UPDATE `creature_template` SET `spell1` = 15580, `spell2` = 15655, `spell3` = 22572, `spell4` = 20691 WHERE `entry` = 14321; +UPDATE `creature_template` SET `spell1` = 15580, `spell2` = 15655, `spell3` = 20691 WHERE `entry` = 14326; +UPDATE `creature_template` SET `spell1` = 15580, `spell2` = 17307, `spell3` = 20691 WHERE `entry` = 14323; +UPDATE `creature_template` SET `spell1` = 22419, `spell2` = 22420, `spell3` = 22421 WHERE `entry` = 13280; +UPDATE `creature_template` SET `spell1` = 22422 WHERE `entry` = 14122; +UPDATE `creature_template` SET `spell1` = 5116, `spell2` = 20904, `spell3` = 14290, `spell4` = 14295 WHERE `entry` = 11488; +UPDATE `creature_template` SET `spell1` = 16128, `spell2` = 15550, `spell3` = 22899 WHERE `entry` = 11496; +UPDATE `creature_template` SET `spell1` = 22909 WHERE `entry` = 14396; +UPDATE `creature_template` SET `spell1` = 15708, `spell2` = 24375 WHERE `entry` = 11501; +UPDATE `creature_template` SET `spell1` = 11668, `spell2` = 14887 WHERE `entry` = 14327; +UPDATE `creature_template` SET `spell1` = 10984 WHERE `entry` = 14506; +UPDATE `creature_template` SET `spell1` = 10894, `spell2` = 10947, `spell3` = 18807 WHERE `entry` = 11487; +UPDATE `creature_template` SET `spell1` = 20691, `spell2` = 22920 WHERE `entry` = 11486; +UPDATE `creature_template` SET `spell1` = 22424, `spell2` = 10151, `spell3` = 16144 WHERE `entry` = 14354; +UPDATE `creature_template` SET `spell1` = 20691, `spell2` = 24375 WHERE `entry` = 11498; +UPDATE `creature_template` SET `spell1` = 15550, `spell2` = 22924, `spell3` = 22994 WHERE `entry` = 11489; +UPDATE `creature_template` SET `spell1` = 22478, `spell2` = 22651 WHERE `entry` = 11490; +UPDATE `creature_template` SET `spell1` = 20832, `spell2` = 16102, `spell3` = 15530, `spell4` = 16170 WHERE `entry` = 11444; +UPDATE `creature_template` SET `spell1` = 22572, `spell2` = 22916 WHERE `entry` = 11450; +UPDATE `creature_template` SET `spell1` = 13737, `spell2` = 20677, `spell3` = 24317 WHERE `entry` = 11441; +UPDATE `creature_template` SET `spell1` = 19474 WHERE `entry` = 11457; +UPDATE `creature_template` SET `spell1` = 22460, `spell2` = 22272 WHERE `entry` = 13197; +UPDATE `creature_template` SET `spell1`=512, `spell2`=1010, `spell3`=0, `spell4`=0 WHERE `entry`=10415; +UPDATE `creature_template` SET `spell1`=1096, `spell2`=0, `spell3`=0, `spell4`=0 WHERE `entry`=10917; +UPDATE `creature_template` SET `spell1`=20603, `spell2`=15398, `spell3`=9256, `spell4`=20741 WHERE `entry`=10813; +UPDATE `creature_template` SET `spell1`=16565, `spell2`=16867, `spell3`=18327, `spell4`=0 WHERE `entry`=10436; +UPDATE `creature_template` SET `spell1`=16866, `spell2`=1604, `spell3`=0, `spell4`=0 WHERE `entry`=10697; +UPDATE `creature_template` SET `spell1`=1604, `spell2`=16866, `spell3`=0, `spell4`=0 WHERE `entry`=10416; +UPDATE `creature_template` SET `spell1`=17439, `spell2`=15584, `spell3`=1604, `spell4`=0 WHERE `entry`=10394; +UPDATE `creature_template` SET `spell1`=1604, `spell2`=5884, `spell3`=0, `spell4`=0 WHERE `entry`=11121; +UPDATE `creature_template` SET `spell1`=16140, `spell2`=1604, `spell3`=0, `spell4`=0 WHERE `entry`=10383; +UPDATE `creature_template` SET `spell1`=1604, `spell2`=3589, `spell3`=0, `spell4`=0 WHERE `entry`=8530; +UPDATE `creature_template` SET `spell1`=23382, `spell2`=15615, `spell3`=16496, `spell4`=0 WHERE `entry`=10997; +UPDATE `creature_template` SET `spell1`=13020, `spell2`=23413, `spell3`=0, `spell4`=0 WHERE `entry`=10425; +UPDATE `creature_template` SET `spell1`=17293, `spell2`=23462, `spell3`=17274, `spell4`=0 WHERE `entry`=10811; +UPDATE `creature_template` SET `spell1`=1604, `spell2`=15749, `spell3`=11972, `spell4`=13534 WHERE `entry`=13118; +UPDATE `creature_template` SET `spell1`=11831, `spell2`=23412, `spell3`=1604, `spell4`=0 WHERE `entry`=10419; +UPDATE `creature_template` SET `spell1`=5101, `spell2`=17143, `spell3`=13005, `spell4`=0 WHERE `entry`=12337; +UPDATE `creature_template` SET `spell1`=13005, `spell2`=1604, `spell3`=20005, `spell4`=0 WHERE `entry`=10421; +UPDATE `creature_template` SET `spell1`=36647, `spell2`=17143, `spell3`=1604, `spell4`=0 WHERE `entry`=10424; +UPDATE `creature_template` SET `spell1`=15749, `spell2`=33871, `spell3`=1604, `spell4`=0 WHERE `entry`=10418; +UPDATE `creature_template` SET `spell1`=17445, `spell2`=1604, `spell3`=39077, `spell4`=0 WHERE `entry`=11120; +UPDATE `creature_template` SET `spell1`=17287, `spell2`=1604, `spell3`=22645, `spell4`=0 WHERE `entry`=10420; +UPDATE `creature_template` SET `spell1`=22645, `spell2`=17165, `spell3`=22947, `spell4`=0 WHERE `entry`=10426; +UPDATE `creature_template` SET `spell1`=36033, `spell2`=17145, `spell3`=1604, `spell4`=0 WHERE `entry`=11043; +UPDATE `creature_template` SET `spell1`=36176, `spell2`=36947, `spell3`=1604, `spell4`=0 WHERE `entry`=10423; +UPDATE `creature_template` SET `spell1`=38094, `spell2`=1604, `spell3`=6788, `spell4`=0 WHERE `entry`=11054; +UPDATE `creature_template` SET `spell1`=15451, `spell2`=16927, `spell3`=16144, `spell4`=15534 WHERE `entry`=10422; +UPDATE `creature_template` SET `spell1`=22412, `spell2`=4962, `spell3`=1604, `spell4`=3589 WHERE `entry`=10413; +UPDATE `creature_template` SET `spell1`=15471, `spell2`=1604, `spell3`=3589, `spell4`=16430 WHERE `entry`=10412; +UPDATE `creature_template` SET `spell1`=16401, `spell2`=12023, `spell3`=1604, `spell4`=20812 WHERE `entry`=8556; +UPDATE `creature_template` SET `spell1`=4629, `spell2`=20549, `spell3`=20812, `spell4`=21949 WHERE `entry`=11859; +UPDATE `creature_template` SET `spell1`=1604, `spell2`=15530, `spell3`=15531, `spell4`=16458 WHERE `entry`=15607; +UPDATE `creature_template` SET `spell1`=16388, `spell2`=0, `spell3`=0, `spell4`=0 WHERE `entry`=10557; +UPDATE `creature_template` SET `spell1`=18200, `spell2`=1604, `spell3`=9791, `spell4`=16458 WHERE `entry`=10407; +UPDATE `creature_template` SET `spell1`=7964, `spell2`=20712, `spell3`=1604, `spell4`=23511 WHERE `entry`=11058; +UPDATE `creature_template` SET `spell1`=16249, `spell2`=16143, `spell3`=16449, `spell4`=0 WHERE `entry`=11136; +UPDATE `creature_template` SET `spell1`=22687, `spell2`=1604, `spell3`=0, `spell4`=0 WHERE `entry`=10385; +UPDATE `creature_template` SET `spell1`=12538, `spell2`=15608, `spell3`=16172, `spell4`=1604 WHERE `entry`=10406; +UPDATE `creature_template` SET `spell1`=17286, `spell2`=1604, `spell3`=20830, `spell4`=0 WHERE `entry`=10812; +UPDATE `creature_template` SET `spell1`=3589, `spell2`=16867, `spell3`=1604, `spell4`=0 WHERE `entry`=8541; +UPDATE `creature_template` SET `spell1`=17685, `spell2`=18663, `spell3`=19643, `spell4`=20812 WHERE `entry`=16101; +UPDATE `creature_template` SET `spell1`=16793, `spell2`=10887, `spell3`=14099, `spell4`=1604 WHERE `entry`=10435; +UPDATE `creature_template` SET `spell1`=15850, `spell2`=16249, `spell3`=20743, `spell4`=16869 WHERE `entry`=10438; +UPDATE `creature_template` SET `spell1`=12734, `spell2`=16172, `spell3`=1604, `spell4`=6788 WHERE `entry`=11032; +UPDATE `creature_template` SET `spell1`=16143, `spell2`=1604, `spell3`=15043, `spell4`=0 WHERE `entry`=10382; +UPDATE `creature_template` SET `spell1`=1604, `spell2`=6788, `spell3`=0, `spell4`=0 WHERE `entry`=11197; +UPDATE `creature_template` SET `spell1`=1604, `spell2`=6788, `spell3`=0, `spell4`=0 WHERE `entry`=11030; +UPDATE `creature_template` SET `spell1`=5568, `spell2`=17307, `spell3`=1604, `spell4`=6788 WHERE `entry`=10439; +UPDATE `creature_template` SET `spell1`=19813, `spell2`=4283, `spell3`=0, `spell4`=0 WHERE `entry`=11658; +UPDATE `creature_template` SET `spell1`=19393, `spell2`=0, `spell3`=0, `spell4`=0 WHERE `entry`=11668; +UPDATE `creature_template` SET `spell1`=25787, `spell2`=0, `spell3`=0, `spell4`=0 WHERE `entry`=12101; +UPDATE `creature_template` SET `spell1`=19364, `spell2`=19365, `spell3`=19369, `spell4`=19367 WHERE `entry`=11673; +UPDATE `creature_template` SET `spell1`=4283, `spell2`=19813, `spell3`=0, `spell4`=0 WHERE `entry`=11659; +UPDATE `creature_template` SET `spell1`=20228, `spell2`=0, `spell3`=0, `spell4`=0 WHERE `entry`=12143; +UPDATE `creature_template` SET `spell1`=20203, `spell2`=0, `spell3`=0, `spell4`=0 WHERE `entry`=11669; +UPDATE `creature_template` SET `spell1`=24668, `spell2`=23952, `spell3`=10960, `spell4`=0 WHERE `entry`=11663; +UPDATE `creature_template` SET `spell1`=23952, `spell2`=20294, `spell3`=0, `spell4`=0 WHERE `entry`=11662; +UPDATE `creature_template` SET `spell1`=20677, `spell2`=20740, `spell3`=0, `spell4`=0 WHERE `entry`=12119; +UPDATE `creature_template` SET `spell1`=20623, `spell2`=0, `spell3`=0, `spell4`=0 WHERE `entry`=11664; +UPDATE `creature_template` SET `spell1`=19778, `spell2`=19771, `spell3`=0, `spell4`=0 WHERE `entry`=11671; +UPDATE `creature_template` SET `spell1`=21081, `spell2`=19730, `spell3`=0, `spell4`=0 WHERE `entry`=11661; +UPDATE `creature_template` SET `spell1`=19626, `spell2`=19630, `spell3`=19631, `spell4`=0 WHERE `entry`=11667; +UPDATE `creature_template` SET `spell1`=19798, `spell2`=19496, `spell3`=0, `spell4`=0 WHERE `entry`=12806; +UPDATE `creature_template` SET `spell1`=19635, `spell2`=0, `spell3`=0, `spell4`=0 WHERE `entry`=11666; +UPDATE `creature_template` SET `spell1`=19642, `spell2`=19644, `spell3`=0, `spell4`=0 WHERE `entry`=12100; +UPDATE `creature_template` SET `spell1`=19641, `spell2`=0, `spell3`=0, `spell4`=0 WHERE `entry`=12076; +UPDATE `creature_template` SET `spell1`=20294, `spell2`=0, `spell3`=0, `spell4`=0 WHERE `entry`=12099; +UPDATE `creature_template` SET `spell1`=18108, `spell2`=0, `spell3`=0, `spell4`=0 WHERE `entry`=12265; +UPDATE `creature_template` SET `spell1`=22689, `spell2`=19319, `spell3`=0, `spell4`=0 WHERE `entry`=11672; +UPDATE `creature_template` SET `spell1`=35178, `spell2`=15749, `spell3`=0, `spell4`=0 WHERE `entry`=10509; +UPDATE `creature_template` SET `spell1`=35178, `spell2`=0, `spell3`=0, `spell4`=0 WHERE `entry`=9819; +UPDATE `creature_template` SET `spell1`=30688, `spell2`=0, `spell3`=0, `spell4`=0 WHERE `entry`=10319; +UPDATE `creature_template` SET `spell1`=20816, `spell2`=0, `spell3`=0, `spell4`=0 WHERE `entry`=9817; +UPDATE `creature_template` SET `spell1`=32192, `spell2`=18108, `spell3`=0, `spell4`=0 WHERE `entry`=9818; +UPDATE `creature_template` SET `spell1`=23462, `spell2`=23341, `spell3`=17274, `spell4`=0 WHERE `entry`=9816; +UPDATE `creature_template` SET `spell1`=24317, `spell2`=0, `spell3`=0, `spell4`=0 WHERE `entry`=10258; +UPDATE `creature_template` SET `spell1`=145, `spell2`=0, `spell3`=0, `spell4`=0 WHERE `entry`=10161; +UPDATE `creature_template` SET `spell1`=0, `spell2`=0, `spell3`=0, `spell4`=0 WHERE `entry`=10683; +UPDATE `creature_template` SET `spell1`=28725, `spell2`=0, `spell3`=0, `spell4`=0 WHERE `entry`=10264; +UPDATE `creature_template` SET `spell1`=12461, `spell2`=22591, `spell3`=16172, `spell4`=24317 WHERE `entry`=10899; +UPDATE `creature_template` SET `spell1`=12461, `spell2`=0, `spell3`=0, `spell4`=0 WHERE `entry`=10317; +UPDATE `creature_template` SET `spell1`=0, `spell2`=0, `spell3`=0, `spell4`=0 WHERE `entry`=10762; +UPDATE `creature_template` SET `spell1`=0, `spell2`=0, `spell3`=0, `spell4`=0 WHERE `entry`=10742; +UPDATE `creature_template` SET `spell1`=30014, `spell2`=32370, `spell3`=0, `spell4`=0 WHERE `entry`=10447; +UPDATE `creature_template` SET `spell1`=22414, `spell2`=23512, `spell3`=0, `spell4`=0 WHERE `entry`=10442; +UPDATE `creature_template` SET `spell1`=20691, `spell2`=15589, `spell3`=23931, `spell4`=0 WHERE `entry`=10429; +UPDATE `creature_template` SET `spell1`=20667, `spell2`=20712, `spell3`=18763, `spell4`=0 WHERE `entry`=10339; +UPDATE `creature_template` SET `spell1`=11286, `spell2`=0, `spell3`=0, `spell4`=0 WHERE `entry`=10318; +UPDATE `creature_template` SET `spell1`=23462, `spell2`=25668, `spell3`=0, `spell4`=0 WHERE `entry`=10372; +UPDATE `creature_template` SET `spell1`=28168, `spell2`=27580, `spell3`=0, `spell4`=0 WHERE `entry`=10371; +UPDATE `creature_template` SET `spell1`=23462, `spell2`=0, `spell3`=0, `spell4`=0 WHERE `entry`=10083; +UPDATE `creature_template` SET `spell1`=20741, `spell2`=24668, `spell3`=0, `spell4`=0 WHERE `entry`=10162; +UPDATE `creature_template` SET `spell1`=17883, `spell2`=16785, `spell3`=0, `spell4`=0 WHERE `entry`=10430; +UPDATE `creature_template` SET `spell1`=23462, `spell2`=20691, `spell3`=23023, `spell4`=23931 WHERE `entry`=10363; +UPDATE `creature_template` SET `spell1`=20276, `spell2`=0, `spell3`=0, `spell4`=0 WHERE `entry`=10814; +UPDATE `creature_template` SET `spell1`=13496, `spell2`=0, `spell3`=0, `spell4`=0 WHERE `entry`=11598; +UPDATE `creature_template` SET `spell1`=27559, `spell2`=20691, `spell3`=12020, `spell4`=30138 WHERE `entry`=10487; +UPDATE `creature_template` SET `spell1`=17715, `spell2`=27559, `spell3`=20276, `spell4`=0 WHERE `entry`=10491; +UPDATE `creature_template` SET `spell1`=11660, `spell2`=18807, `spell3`=0, `spell4`=0 WHERE `entry`=10470; +UPDATE `creature_template` SET `spell1`=18270, `spell2`=0, `spell3`=0, `spell4`=0 WHERE `entry`=10481; +UPDATE `creature_template` SET `spell1`=18116, `spell2`=17695, `spell3`=20741, `spell4`=15584 WHERE `entry`=10433; +UPDATE `creature_template` SET `spell1`=17472, `spell2`=15584, `spell3`=0, `spell4`=0 WHERE `entry`=10482; +UPDATE `creature_template` SET `spell1`=18106, `spell2`=18116, `spell3`=15584, `spell4`=0 WHERE `entry`=11261; +UPDATE `creature_template` SET `spell1`=18116, `spell2`=20741, `spell3`=18144, `spell4`=0 WHERE `entry`=10506; +UPDATE `creature_template` SET `spell1`=11672, `spell2`=12020, `spell3`=18116, `spell4`=0 WHERE `entry`=10505; +UPDATE `creature_template` SET `spell1`=16359, `spell2`=3584, `spell3`=18116, `spell4`=0 WHERE `entry`=10901; +UPDATE `creature_template` SET `spell1`=24375, `spell2`=18813, `spell3`=18116, `spell4`=0 WHERE `entry`=11622; +UPDATE `creature_template` SET `spell1`=15550, `spell2`=20691, `spell3`=18116, `spell4`=0 WHERE `entry`=10507; +UPDATE `creature_template` SET `spell1`=11668, `spell2`=11700, `spell3`=18116, `spell4`=0 WHERE `entry`=10504; +UPDATE `creature_template` SET `spell1`=24673, `spell2`=18270, `spell3`=18116, `spell4`=0 WHERE `entry`=10503; +UPDATE `creature_template` SET `spell1`=15584, `spell2`=17472, `spell3`=0, `spell4`=0 WHERE `entry`=11439; +UPDATE `creature_template` SET `spell1`=18116, `spell2`=19460, `spell3`=6215, `spell4`=15487 WHERE `entry`=10502; +UPDATE `creature_template` SET `spell1`=18702, `spell2`=10212, `spell3`=18116, `spell4`=0 WHERE `entry`=1853; +UPDATE `creature_template` SET `spell1`=16350, `spell2`=21369, `spell3`=8398, `spell4`=18033 WHERE `entry`=10508; +UPDATE `creature_template` SET `spell1`=27224, `spell2`=30900, `spell3`=3609, `spell4`=0 WHERE `entry`=14861; +UPDATE `creature_template` SET `spell1`=18278, `spell2`=29405, `spell3`=12020, `spell4`=0 WHERE `entry`=10498; +UPDATE `creature_template` SET `spell1`=24063, `spell2`=18270, `spell3`=0, `spell4`=0 WHERE `entry`=10495; +UPDATE `creature_template` SET `spell1`=12020, `spell2`=18278, `spell3`=25304, `spell4`=0 WHERE `entry`=11263; +UPDATE `creature_template` SET `spell1`=16469, `spell2`=25349, `spell3`=0, `spell4`=0 WHERE `entry`=11551; +UPDATE `creature_template` SET `spell1`=25304, `spell2`=11660, `spell3`=12020, `spell4`=0 WHERE `entry`=10477; +UPDATE `creature_template` SET `spell1`=25304, `spell2`=12020, `spell3`=10161, `spell4`=0 WHERE `entry`=10472; +UPDATE `creature_template` SET `spell1`=15571, `spell2`=0, `spell3`=0, `spell4`=0 WHERE `entry`=10475; +UPDATE `creature_template` SET `spell1`=25304, `spell2`=10161, `spell3`=10473, `spell4`=0 WHERE `entry`=10469; +UPDATE `creature_template` SET `spell1`=11660, `spell2`=15571, `spell3`=10161, `spell4`=0 WHERE `entry`=11284; +UPDATE `creature_template` SET `spell1`=17689, `spell2`=15571, `spell3`=18270, `spell4`=0 WHERE `entry`=10480; +UPDATE `creature_template` SET `spell1`=15571, `spell2`=30138, `spell3`=0, `spell4`=0 WHERE `entry`=10497; +UPDATE `creature_template` SET `spell1`=15571, `spell2`=30138, `spell3`=0, `spell4`=0 WHERE `entry`=10479; +UPDATE `creature_template` SET `spell1`=17504, `spell2`=12867, `spell3`=0, `spell4`=0 WHERE `entry`=14513; +UPDATE `creature_template` SET `spell1`=17504, `spell2`=12867, `spell3`=24673, `spell4`=0 WHERE `entry`=14520; +UPDATE `creature_template` SET `spell1`=30138, `spell2`=11660, `spell3`=0, `spell4`=0 WHERE `entry`=14521; +UPDATE `creature_template` SET `spell1`=11556, `spell2`=8140, `spell3`=23262, `spell4`=0 WHERE `entry`=14518; +UPDATE `creature_template` SET `spell1`=10894, `spell2`=23244, `spell3`=0, `spell4`=0 WHERE `entry`=14519; +UPDATE `creature_template` SET `spell1`=11556, `spell2`=8140, `spell3`=15571, `spell4`=0 WHERE `entry`=14514; +UPDATE `creature_template` SET `spell1`=15571, `spell2`=23244, `spell3`=0, `spell4`=0 WHERE `entry`=14512; +UPDATE `creature_template` SET `spell1`=12292, `spell2`=15571, `spell3`=28168, `spell4`=0 WHERE `entry`=10488; +UPDATE `creature_template` SET `spell1`=15571, `spell2`=24063, `spell3`=18270, `spell4`=12021 WHERE `entry`=10485; +UPDATE `creature_template` SET `spell1`=10161, `spell2`=13021, `spell3`=20883, `spell4`=0 WHERE `entry`=11257; +UPDATE `creature_template` SET `spell1`=15571, `spell2`=0, `spell3`=0, `spell4`=0 WHERE `entry`=10478; +UPDATE `creature_template` SET `spell1`=17504, `spell2`=26554, `spell3`=35011, `spell4`=0 WHERE `entry`=10486; +UPDATE `creature_template` SET `spell1`=19627, `spell2`=18816, `spell3`=13021, `spell4`=0 WHERE `entry`=10432; +UPDATE `creature_template` SET `spell1`=18807, `spell2`=10876, `spell3`=0, `spell4`=0 WHERE `entry`=10471; +UPDATE `creature_template` SET `spell1`=10876, `spell2`=25304, `spell3`=18647, `spell4`=0 WHERE `entry`=10500; +UPDATE `creature_template` SET `spell1`=29684, `spell2`=25051, `spell3`=0, `spell4`=0 WHERE `entry`=10489; + +UPDATE `creature_template` SET `spell1` = 24317, `spell2` = 19644 WHERE `entry` = 8890; +UPDATE `creature_template` SET `spell1` = 30901, `spell2` = 27581, `spell3` = 34510 WHERE `entry` = 8891; +UPDATE `creature_template` SET `spell1` = 29560, `spell2` = 19644 WHERE `entry` = 8892; +UPDATE `creature_template` SET `spell1` = 40082, `spell2` = 30901 WHERE `entry` = 8893; +UPDATE `creature_template` SET `spell1` = 8812, `spell2` = 10961, `spell3` = 10947 WHERE `entry` = 8894; +UPDATE `creature_template` SET `spell1` = 25292, `spell2` = 1020 WHERE `entry` = 8895; +UPDATE `creature_template` SET `spell1` = 17143, `spell2` = 10293, `spell3` = 25292 WHERE `entry` = 8898; diff --git a/src/modules/SD2/sql/optional/mangos_optional_generic_creature.sql b/src/modules/SD2/sql/optional/mangos_optional_generic_creature.sql new file mode 100644 index 000000000..a787b584e --- /dev/null +++ b/src/modules/SD2/sql/optional/mangos_optional_generic_creature.sql @@ -0,0 +1,283 @@ +/* */ + +/* WORLD CREATURE These are creatures to be found in more than one specific zone */ +/* Spider */ +UPDATE `creature_template` SET `ScriptName` = 'generic_creature' WHERE `entry` IN (14881); + +/* */ +/* ZONE */ +/* */ + +/* ALTERAC MOUNTAINS */ +/* Crushridge Mage, Argus Shadow Mage, Dalaran Summoner, Archmage Ansirem Runeweaver */ +UPDATE `creature_template` SET `ScriptName` = 'generic_creature' WHERE `entry` IN (2255, 2318, 2358, 2543); + +/* ALTERAC VALLEY */ +/* Wildpaw Mystic */ +UPDATE `creature_template` SET `ScriptName` = 'generic_creature' WHERE `entry` IN (11838); + +/* ARATHI HIGHLANDS */ +/* Feeboz, Boulderfist Shaman, Boulderfist Magus, Syndicate Magus, Dark Iron Shadowcaster, Witherbark Axe Thrower, Plains Creeper, Giant Plains Creeper */ +UPDATE `creature_template` SET `ScriptName` = 'generic_creature' WHERE `entry` IN (4063, 2570, 2567, 2591, 2577, 2554, 2563, 2565); + +/* ASHENVALE */ +/* Shadethicket Raincaller, Forsaken Seeker, Dark Strand Cultist, Dark Strand Adept */ +UPDATE `creature_template` SET `ScriptName` = 'generic_creature' WHERE `entry` IN (3783, 3732, 3725, 3728); + + +/* */ +/* AUCHINDOUN */ +/* */ + + + +/* AZSHARA */ +/* Draconic Magelord, Draconic Mageweaver, Archmage Xylem */ +UPDATE `creature_template` SET `ScriptName` = 'generic_creature' WHERE `entry` IN (6129, 6131, 8379); + +/* BADLANDS */ +/* Dustbelcher Ogre Mage, Dustbelcher Mystic, Shadowmage Vivian Lagrave */ +UPDATE `creature_template` SET `ScriptName` = 'generic_creature' WHERE `entry` IN (2720, 2907, 9078); + +/* BARRENS */ +/* Kolkar Stormer, Razormane Geomancer, Razormane Seer, Razormane Mystic, Razormane Hunter, Razormane Warfrenzy, Razormane Water Seeker, Silithid Creeper, Elder Mystic Razorsnout, Horde Axe Thrower */ +UPDATE `creature_template` SET `ScriptName` = 'generic_creature' WHERE `entry` IN (3273, 3269, 3458, 3271, 3265, 3459, 3267, 3250, 3270, 9458); + +/* BLACKFATHOM DEPTHS */ +/* Blindlight Oracle, Blindlight Muckdweller, Blindlight Murloc, Twilight Shadowmage */ +UPDATE `creature_template` SET `ScriptName` = 'generic_creature' WHERE `entry` IN (4820, 4819, 4818, 4813); + + +/* Hedrum the Creeper, Dark Keeper Zimrel */ +UPDATE `creature_template` SET `ScriptName` = 'generic_creature' WHERE `entry` IN (9032, 9441); +/* Anvilrage military */ +UPDATE `creature_template` SET `ScriptName` = 'generic_creature' WHERE `entry` IN (8890, 8891, 8892, 8893, 8894, 8895, 8898); +/* Cave Creeper */ +UPDATE `creature_template` SET `ScriptName` = 'generic_creature' WHERE `entry` IN (8933); + +/* BLACKROCK SPIRE */ +/* Blackhand Dreadweaver,Blackhand Summoner,Blackhand Veteran,Blackhand Elite,Blackhand Assassin,Blackhand Iron Guard,Chromatic Whelp,Chromatic Dragonspawn,Rookery Whelp,Rookery Guardian,Rage Talon Captain,Rage Talon Fire Tongue,Rage Talon Flamescale */ +UPDATE `creature_template` SET `ScriptName` = 'generic_creature' WHERE `entry` IN (9817, 9818, 9819, 10317, 10318, 10319, 10442, 10447, 10161, 10258, 10371, 10372, 10083); +/* Spirestone Battle Mage, Spirestone Mystic, Smolderthorn Mystic, Smolderthorn Axe Thrower, Bloodaxe Summoner , Spire Spider, Spire Spiderling */ +UPDATE `creature_template` SET `ScriptName` = 'generic_creature' WHERE `entry` IN (9197, 9198, 9239, 9267, 9717, 10374, 10375); + + +/* BLACKROCK SPIRE Upper */ +/* Jed Runewatcher,Solakar Flamewreath,Goraluk Anvilcrack */ +UPDATE `creature_template` SET `ScriptName` = 'generic_creature' WHERE `entry`IN (10509,10264,10899); + +/* BLACKWING LAIR */ +/* Trash Mobs */ +UPDATE `creature_template` SET `ScriptName` = 'generic_creature' WHERE `entry` IN (12420); + + +/* BLASTED LANDS */ +/* Dreadmaul Ogre Mage, Bloodmage Drazial, Bloodmage Lynnore, Archmage Allistarj */ +UPDATE `creature_template` SET `ScriptName` = 'generic_creature' WHERE `entry` IN (5975, 7505, 7506, 7666); + +/* BURNING STEPPES */ +/* Blackrock Sorcerer, Blackrock Warlock, Scalding Broodling, Thaurissan Agent, Firegut Ogre Mage */ +UPDATE `creature_template` SET `ScriptName` = 'generic_creature' WHERE `entry` IN (7026, 7028, 7048, 7038, 7034); + + +/* DARKSHORE */ +/* Delmanis the Hated, Greymist Oracle, */ +UPDATE `creature_template` SET `ScriptName` = 'generic_creature' WHERE `entry` IN (3662, 2207); + +/* DEADMINES */ +/* Defias Squallshaper, Defias Magician, Defias Conjurer, Defias Overseer, Defias Watchman, Defias Wizard, Defias Evoker, Defias Pirate, Defias Taskmaster, Defias Miner, Defias Strip Miner, Defias Henchman, */ +UPDATE `creature_template` SET `ScriptName` = 'generic_creature' WHERE `entry` IN (1732, 1726, 619, 634, 1725, 4418, 1729, 657, 4417, 598, 4416, 594); + +/* DEADWIND PASS */ +/* Deadwind Ogre Mage */ +UPDATE `creature_template` SET `ScriptName` = 'generic_creature' WHERE `entry` IN (7379); + +/* DESOLACE */ +/* Burning Blade Shadowmage, Burning Blade Summoner, Mage Hunter */ +UPDATE `creature_template` SET `ScriptName` = 'generic_creature' WHERE `entry` IN (4667, 4668, 4681); + +/* DIRE MAUL */ +UPDATE `creature_template` SET `ScriptName` = 'generic_creature' WHERE `entry` IN (11492, 14325, 14324, 14321, 14326, 14323, 13280, 14122, 11488, 11496, 14396, 11501, 14327, 14506, 11487, 11486, 14354, 11498, 11489, 11490, 11444, 11450, 11441, 11457, 13197); +/* Gordok Mage-Lord, Highborne Summoner */ +UPDATE `creature_template` SET `ScriptName` = 'generic_creature' WHERE `entry` IN (11444, 11466); + +/* DUN MOROGH */ +/* Frostmane Novice, Frostmane Seer, Rockjaw Skullthumper, Rockjaw Bonesnapper, Rockjaw Ambusher, Rockjaw Backbreaker, */ +UPDATE `creature_template` SET `ScriptName` = 'generic_creature' WHERE `entry` IN (946, 1397, 1115, 1117, 1116, 1118); + +/* DUROTAR */ +/* Razormane Scout, Razormane Dustrunner, Razormane Quilboar, Razormane Battleguard, Vile Familiar */ +UPDATE `creature_template` SET `ScriptName` = 'generic_creature' WHERE `entry` IN (3112, 3113, 3111, 3114, 3101); + +/* DUSKWOOD */ +/* Eliza, Nightbane Shadow Weaver, Skeletal Mage, Defias Night Runner, Defias Night Blade, Skeletal Fiend, Skeletal Healer, Skeletal Mage, Skeletal Raider, Skeletal Warder, Skeletal Warrior, Venom Web Spider, Pygmy Venom Web Spider */ +UPDATE `creature_template` SET `ScriptName` = 'generic_creature' WHERE `entry` IN (314, 533, 203, 215, 909, 531, 787, 203, 1110, 785, 48, 217, 539); + +/* DUSTWALLOW MARSH */ +/* Mirefin Murloc, Darkmist Spider, Withervine Creeper, Darkfang Creeper, Darkfang Spider, Giant Darkfang Spider, Archmage Tervosh */ +UPDATE `creature_template` SET `ScriptName` = 'generic_creature' WHERE `entry` IN (4359, 4376, 4382, 4412, 4413, 4415, 4967); + +/* EASTERN PLAGUELANDS */ +/* Crimson Bodyguard, Crimson Courier, Crypt Walker, Hate Shrieker, Cursed Mage, Shadowmage, Dark Summoner, Scarlet Archmage, Archmage Angela Dosantos */ +UPDATE `creature_template` SET `ScriptName` = 'generic_creature' WHERE `entry` IN (13118, 12337, 8556, 8541, 8524, 8550, 8551, 9451, 16116); + +/* ELWYNN FOREST */ +/* Hogger, Defias Rogue Wizard, Defias Enchanter, Defias Bodyguard, Defias Bandit, Defias Cutpurse, Murloc Lurker, Murloc Forager, Forest Spider, Mine Spider */ +UPDATE `creature_template` SET `ScriptName` = 'generic_creature' WHERE `entry` IN (448, 474, 910, 6866, 116, 94, 732, 46, 30, 43); + +/* FELWOOD */ +/* Timbermaw Mystic */ +UPDATE `creature_template` SET `ScriptName` = 'generic_creature' WHERE `entry` IN (11552); + +/* FERALAS */ +/* Gordunni Ogre Mage, Gordunni Mage-Lord, Woodpaw Mystic, Gordok Ogre-Mage */ +UPDATE `creature_template` SET `ScriptName` = 'generic_creature' WHERE `entry` IN (5237, 5239, 5254, 11443); + +/* GNOMEREGAN */ +UPDATE `creature_template` SET `ScriptName` = 'generic_creature' WHERE `entry` IN (6206, 6208, 6209, 6215, 6219, 6229, 6228, 6235, 7361, 6407, 6220, 6218, 7603, 6223, 6222, 6234, 6233, 6226, 6227, 6225, 6230, 7079); + +/* */ +/* HELLFIRE CITADEL */ +/* */ + +/* Laughing Skull Legionnaire, Laughing Skull Warden, Laughing Skull Rogue, Hellfire Imp, Shadowmoon Channeler, Fel Orc Neophyte, Shadowmoon Technician, Shadowmoon Adept, Hellfire Familiar, Felguard Brute, Shadowmoon Summoner, Shadowmoon Warlock */ +UPDATE `creature_template` SET `ScriptName` = 'generic_creature' WHERE `entry` IN (17626, 17624, 17491, 17477, 17653, 17429, 17414, 17397, 19016, 18894, 17395, 17371); + +/* HELLFIRE RAMPARTS */ +/* Vazruden, Omor the Unscarred, Watchkeeper Gargolmar */ +UPDATE `creature_template` SET `ScriptName` = 'generic_creature' WHERE `entry` IN (17537, 17308, 17306); +/* Hellfire Watcher , Bonechewer Hungerer, Bonechewer Beastmaster, Bonechewer Ravener, Bonechewer Ripper, Bonechewer Destroyer, Bleeding Hollow Archer, Bleeding Hollow Darkcaster, Hellfire Sentry , Bleeding Hollow Scryer */ +UPDATE `creature_template` SET `ScriptName` = 'generic_creature' WHERE `entry` IN (17309, 17259, 17455, 17264, 17281, 17271, 17270, 17269, 17517, 17478); + +/* SHATTERED HALLS */ +/* Warchief Kargath Bladefist */ +UPDATE `creature_template` SET `ScriptName` = 'generic_creature' WHERE `entry` IN (16808); +/* Shattered Hand Centurion, Shadowmoon Darkcaster, Shattered Hand Champion, Sharpshooter Guard, Shattered Hand Assassin, Warbringer O'mrogg, Grand Warlock Nethekurse, Shattered Hand Sharpshooter, Shattered Hand Legionnaire, Shattered Hand Reaver, Shattered Hand Brawler */ +UPDATE `creature_template` SET `ScriptName` = 'generic_creature' WHERE `entry` IN (17465, 17694, 17671, 17622, 17695, 16809, 16807, 16704, 16700, 16699, 16593); + + +/* HILLSBRAD FOOTHILLS */ +/* Syndicate Shadow Mage, Elder Moss Creeper, Giant Moss Creeper(also AM), Forest Moss Creeper, Writhing Mage */ +UPDATE `creature_template` SET `ScriptName` = 'generic_creature' WHERE `entry` IN (2244, 2348, 2349, 2350, 7075); + +/* HINTERLANDS */ +/* Vilebranch Axe Thrower, Mystic Yayo'jin */ +UPDATE `creature_template` SET `ScriptName` = 'generic_creature' WHERE `entry` IN (2639, 14739); + +/* LOCH MODAN */ +/* Mo'grosh Mystic, Tunnel Rat Geomancer, Magosh , Stonesplinter Scout, Stonesplinter Seer, Stonesplinter Geomancer, Stonesplinter Skullthumper, Stonesplinter Bonesnapper, Stonesplinter Shaman, Stonesplinter Digger, */ +UPDATE `creature_template` SET `ScriptName` = 'generic_creature' WHERE `entry` IN (1183, 1174, 1399, 1162, 1166, 1165, 1163, 1164, 1197, 1167); + +/* MULGORE */ +/* Bristleback Shaman, Bristleback Interloper, Bristleback Quilboar, Bristleback Battleboar, Bristleback Interloper, */ +UPDATE `creature_template` SET `ScriptName` = 'generic_creature' WHERE `entry` IN (2953, 3232, 2952, 2954, 3232); + +/* NAXXRAMAS */ +/* Dread Creeper, Archmage Tarsis Kir-Moldir */ +UPDATE `creature_template` SET `ScriptName` = 'generic_creature' WHERE `entry` IN (15974, 16381); + +/* ONYXIA'S LAIR */ +/* Onyxian Whelp */ +UPDATE `creature_template` SET `ScriptName` = 'generic_creature' WHERE `entry` IN (11262); + +/* RAZORFEN DOWNS */ +/* Mordresh Fire Eye, Plaguemaw the Rotting, Tuten\'kash, Ragglesnout, Glutton */ +UPDATE `creature_template` SET `ScriptName` = 'generic_creature' WHERE `entry` IN (7357, 7356, 7355, 7354, 8567); +/* Skeletal Frostweaver, Skeletal Shadowcaster, Skeletal Summoner, */ +UPDATE `creature_template` SET `ScriptName` = 'generic_creature' WHERE `entry` IN (7341, 7340, 7342); + +/* REDRIDGE MOUNTAINS */ +/* Blackrock Shadowcaster, Murloc Minor Tidecaller, Murloc Nightcrawler, Murloc Tidecaller, Murloc Scout, Murloc Shorestriker, Shadowhide Darkweaver, Rabid Shadowhide Gnoll, Shadowhide Gnoll, Shadowhide Brute, Shadowhide Warrior, Shadowhide Assassin, Redridge Mystic, Redridge Poacher, Redridge Thrasher, Redridge Basher, Black Dragon Whelp, Tarantula, Greater Tarantula, Blackrock Summoner */ +UPDATE `creature_template` SET `ScriptName` = 'generic_creature' WHERE `entry` IN (436, 548, 544, 545, 578, 1083, 422, 429, 434, 433, 432, 568, 579, 430, 424, 712, 446, 441, 442, 505, 4463); + +/* SCARLET MONASTERY */ +/* Fallen Champion, Scarlet Chaplain, Scarlet Adept, Scarlet Wizard, */ +UPDATE `creature_template` SET `ScriptName` = 'generic_creature' WHERE `entry` IN (6488, 4299, 4296, 4300); + +/* SCHOLOMANCE */ +/* Kirtonos the Herald, Rattlegore, Marduk Blackpool, Risen Guard, Risen Bonewarder, Risen Lackey, Risen Aberration, Risen Warrior, Risen Protector, Risen Construct, Risen Guardian, Diseased Ghoul, Ragged Ghoul, Spectral Projection, Spectral Teacher, Necrofiend, Scholomance Neophyte, Scholomance Acolyte, Scholomance Occultist */ +UPDATE `creature_template` SET `ScriptName` = 'generic_creature' WHERE `entry` IN (10506, 11622, 10433, 10489, 10491, 10482, 10485, 10486, 10487, 10488, 11598, 10495, 10497, 11263, 10500, 11551, 10470, 10471, 10472); +/* Scholomance Student, Scholomance Necromancer, Scholomance Adept, Scholomance Handler, Splintered Skeleton, Skulking Corpse, Unstable Corpse, Reanimated Corpse, Aspect of Banality, Aspect of Corruption, Aspect of Malice, Aspect of Shadow, Blood Steward of Kirtonos, Spectral Tutor, Dark Shade, Corrupted Spirit, Malicious Spirit, Banal Spirit, Scholomance Dark Summoner */ +UPDATE `creature_template` SET `ScriptName` = 'generic_creature' WHERE `entry` IN (10475, 10477, 10469, 11257, 10478, 10479, 10480, 10481, 14518, 14519, 14520, 14521, 14861, 10498, 11284, 14512, 14513, 14514, 11582); + +/* SEARING GORGE */ +/* Glassweb Spider, Searing Lava Spider, Greater Lava Spider */ +UPDATE `creature_template` SET `ScriptName` = 'generic_creature' WHERE `entry` IN (5856, 5857, 5858); + +/* SHADOWFANG KEEP */ +/* Archmage Arugal */ +UPDATE `creature_template` SET `ScriptName` = 'generic_creature' WHERE `entry` IN (4275); + + +/* SILITHUS */ +/* Orgrimmar Legion Axe Thrower, Stormwind Archmage */ +UPDATE `creature_template` SET `ScriptName` = 'generic_creature' WHERE `entry` IN (15617, 15859); + +/* SILVERPINE FOREST */ +/* Dalaran Apprentice, Dalaran Conjuror, Dalaran Mage, Dalaran Wizard, Rot Hide Mystic, Rot Hide Gladerunner, Rot Hide Brute, Rot Hide Plague Weaver, Rot Hide Savage, Rot Hide Bruiser, Mist Creeper, Dalaran Apprentice, Dalaran Wizard, Dalaran Protector, Dalaran Warder, Dalaran Mage, Dalaran Conjuror, Lake Creeper, Elder Lake Creeper, Vile Fin Shorecreeper, Nightlash, Archmage Ataeric */ +UPDATE `creature_template` SET `ScriptName` = 'generic_creature' WHERE `entry` IN (1867, 1915, 1914, 1889, 1773, 1772, 1939, 1940, 1942, 1944, 1781, 1867, 1889, 1912, 1913, 1914, 1915, 1955, 1956, 1957, 1983, 2120); + +/* STOCKADES */ +/* Defias Captive, Defias Convict, Defias Inmate, Defias Insurgent, Defias Prisoner, */ +UPDATE `creature_template` SET `ScriptName` = 'generic_creature' WHERE `entry` IN (1707, 1711, 1708, 1715, 1706); + +/* STONETALON MOUNTAINS */ +/* Deepmoss Creeper */ +UPDATE `creature_template` SET `ScriptName` = 'generic_creature' WHERE `entry` IN (4005); + +/* STRANGLETHORN VALE */ +/* Bloodscalp Shaman, Bloodsail Elder Magus, Bloodsail Mage, Bloodsail Warlock, Bloodscalp Axe Thrower, Skullsplitter Axe Thrower, Bloodscalp Mystic, Skullsplitter Mystic */ +UPDATE `creature_template` SET `ScriptName` = 'generic_creature' WHERE `entry` IN (697, 1653, 1562, 1564, 694, 696, 701, 780); + +/* STRATHOLME */ +/* Jarien, Aurius, Ash'ari Crystal, Elder Farwhisper, Mindless Undead */ +UPDATE `creature_template` SET `ScriptName` = 'generic_creature' WHERE `entry` IN (16101, 10917, 10415, 15607, 11030); + +/* SUNKEN TEMPLE */ +/* Zekkis, Kazkaz the Unholy, Spawn of Hakkar, Shade of Eranikus, Jammal'an the Prophet, Ogom the Wretched, Zolo, Gasher, Loro, Hukku, Zul'Lor, Mijan, Morphaz, Weaver, Dreamscythe, Hazzas, Avatar of Hakkar, Atal'alarion */ +UPDATE `creature_template` SET `ScriptName` = 'generic_creature' WHERE `entry` IN (5400, 5401, 5708, 5709, 5710, 5711, 5712, 5713, 5714, 5715, 5716, 5717, 5719, 5720, 5721, 5722, 8443, 8580); +/* Jade, Murk Slitherer, Murk Spitter, Murk Worm, Saturated Ooze, Fungal Ooze, Cursed Atal'ai, Atal'ai Witch Doctor, Enthralled Atal'ai, Mummified Atal'ai, Unliving Atal'ai, Atal'ai Priest, Atal'ai Corpse Eater, Atal'ai Deathwalker, Atal'ai High Priest, Nightmare Scalebane, Nightmare Wyrmkin, Nightmare Wanderer, Hakkari Frostwing, Nightmare Whelp, Atal'ai Skeleton, Hakkari Sapper, Hakkari Minion, Hakkari Bloodkeeper, Nightmare Suppressor */ +UPDATE `creature_template` SET `ScriptName` = 'generic_creature' WHERE `entry` IN (1063, 5224, 5225, 5226, 5228, 5235, 5243, 5259, 5261, 5263, 5267, 5269, 5270, 5271, 5273, 5277, 5280, 5283, 5291, 8319, 8324, 8336, 8437, 8438, 8497); + +/* SWAMP OF SORROWS */ +/* Marsh Murloc, Adolescent Whelp, Dreaming Whelp, Wyrmkin Dreamwalker, Scalebane Captain, Deathstrike Tarantula */ +UPDATE `creature_template` SET `ScriptName` = 'generic_creature' WHERE `entry` IN (747, 740, 741, 743, 745, 769); + +/* TANARIS */ +/* Dunemaul Ogre Mage, Wastewander Shadow Mage, Sandfury Axe Thrower */ +UPDATE `creature_template` SET `ScriptName` = 'generic_creature' WHERE `entry` IN (5473, 5617, 5646); + +/* TELDRASSIL */ +/* Gnarlpine Mystic, Gnarlpine Pathfinder, Bloodfeather Sorceress, Webwood Spider, Giant Webwood Spider */ +UPDATE `creature_template` SET `ScriptName` = 'generic_creature' WHERE `entry` IN (7235, 2012, 2018, 1986, 2001); + +/* TIRISFAL GLADES */ +/* Scarlet Neophyte, Rot Hide Gnoll, Young Night Web Spider, Night Web Spider, Vicious Night Web Spider */ +UPDATE `creature_template` SET `ScriptName` = 'generic_creature' WHERE `entry` IN (1539, 1674, 1504, 1505, 1555); + +/* UN'GORO CRATER */ +/* Tar Creeper */ +UPDATE `creature_template` SET `ScriptName` = 'generic_creature' WHERE `entry` IN (6527); + +/* WAILING CAVERNS */ +/* Deviate Creeper */ +UPDATE `creature_template` SET `ScriptName` = 'generic_creature' WHERE `entry` IN (3632); + +/* WESTERN PLAGUELANDS */ +/* Skeletal Acolyte, Skeletal Flayer, Skeletal Sorcerer, Scarlet Mage, Araj the Summoner */ +UPDATE `creature_template` SET `ScriptName` = 'generic_creature' WHERE `entry` IN (1789, 1783, 1784, 1826, 1852); + +/* WESTFALL */ +/* Defias Pillager, Defias Smuggler, Defias Trapper, Defias Highwayman, Defias Knuckleduster, Defias Pathstalker, Defias Looter, Murloc Oracle, Murloc Hunter, Murloc Warrior, Murloc Tidehunter, Murloc Coastrunner, Riverpaw Scout, Riverpaw Mongrel, Riverpaw Brute, Riverpaw Herbalist, Defias Renegade Mage, Riverpaw Mystic */ +UPDATE `creature_template` SET `ScriptName` = 'generic_creature' WHERE `entry` IN (589, 95, 504, 122, 449, 121, 590, 517, 458, 171, 127, 126, 500, 123, 124, 501, 450, 453); + +/* WETLANDS */ +/* Bluegill Forager, Bluegill Murloc, Bluegill Muckdweller, Bluegill Raider, Bluegill Warrior, Bluegill Oracle, Mosshide Mistweaver, Mosshide Mystic, Mosshide Trapper, Mosshide Mongrel, Mosshide Gnoll, Fen Creeper, Red Whelp, Lost Whelp, Flamesnorting Whelp, Crimson Whelp */ +UPDATE `creature_template` SET `ScriptName` = 'generic_creature' WHERE `entry` IN (1026, 1024, 1028, 1418, 1027, 1029, 1009, 1013, 1011, 1008, 1007, 1040, 1042, 1043, 1044, 1069); + +/* WINTERSPRING */ +/* Cobalt Mageweaver, Chillwind Chimaera */ +UPDATE `creature_template` SET `ScriptName` = 'generic_creature' WHERE `entry` IN (7437, 7448); + + +/* EOF */ diff --git a/src/modules/SD2/sql/scriptdev2_create_database.sql b/src/modules/SD2/sql/scriptdev2_create_database.sql new file mode 100644 index 000000000..2aa367fd5 --- /dev/null +++ b/src/modules/SD2/sql/scriptdev2_create_database.sql @@ -0,0 +1,3 @@ +CREATE DATABASE `scriptdev2` DEFAULT CHARACTER SET utf8 COLLATE utf8_general_ci; + +GRANT SELECT, INSERT, UPDATE, DELETE, CREATE, DROP, ALTER, LOCK TABLES ON `scriptdev2`.* TO 'mangos'@'localhost'; diff --git a/src/modules/SD2/sql/scriptdev2_create_structure_mysql.sql b/src/modules/SD2/sql/scriptdev2_create_structure_mysql.sql new file mode 100644 index 000000000..7d4fc10b6 --- /dev/null +++ b/src/modules/SD2/sql/scriptdev2_create_structure_mysql.sql @@ -0,0 +1,72 @@ +DROP TABLE IF EXISTS `custom_texts`; +CREATE TABLE `custom_texts` ( + `entry` mediumint(8) NOT NULL, + `content_default` text NOT NULL, + `content_loc1` text, + `content_loc2` text, + `content_loc3` text, + `content_loc4` text, + `content_loc5` text, + `content_loc6` text, + `content_loc7` text, + `content_loc8` text, + `sound` mediumint(8) unsigned NOT NULL DEFAULT '0', + `type` tinyint(3) unsigned NOT NULL DEFAULT '0', + `language` tinyint(3) unsigned NOT NULL DEFAULT '0', + `emote` smallint(5) unsigned NOT NULL DEFAULT '0', + `comment` text, + PRIMARY KEY (`entry`) +) ENGINE=MyISAM DEFAULT CHARSET=utf8 ROW_FORMAT=FIXED COMMENT='Custom Texts'; + +DROP TABLE IF EXISTS `gossip_texts`; +CREATE TABLE `gossip_texts` ( + `entry` mediumint(8) NOT NULL, + `content_default` text NOT NULL, + `content_loc1` text, + `content_loc2` text, + `content_loc3` text, + `content_loc4` text, + `content_loc5` text, + `content_loc6` text, + `content_loc7` text, + `content_loc8` text, + `comment` text, + PRIMARY KEY (`entry`) +) ENGINE=MyISAM DEFAULT CHARSET=utf8 ROW_FORMAT=FIXED COMMENT='Gossip Texts'; + +DROP TABLE IF EXISTS `script_texts`; +CREATE TABLE `script_texts` ( + `entry` mediumint(8) NOT NULL, + `content_default` text NOT NULL, + `content_loc1` text, + `content_loc2` text, + `content_loc3` text, + `content_loc4` text, + `content_loc5` text, + `content_loc6` text, + `content_loc7` text, + `content_loc8` text, + `sound` mediumint(8) unsigned NOT NULL DEFAULT '0', + `type` tinyint(3) unsigned NOT NULL DEFAULT '0', + `language` tinyint(3) unsigned NOT NULL DEFAULT '0', + `emote` smallint(5) unsigned NOT NULL DEFAULT '0', + `comment` text, + PRIMARY KEY (`entry`) +) ENGINE=MyISAM DEFAULT CHARSET=utf8 ROW_FORMAT=FIXED COMMENT='Script Texts'; + +DROP TABLE IF EXISTS script_waypoint; +CREATE TABLE script_waypoint ( + entry mediumint(8) unsigned NOT NULL DEFAULT '0' COMMENT 'creature_template entry', + pointid mediumint(8) unsigned NOT NULL DEFAULT '0', + location_x float NOT NULL DEFAULT '0', + location_y float NOT NULL DEFAULT '0', + location_z float NOT NULL DEFAULT '0', + waittime int(10) unsigned NOT NULL DEFAULT '0' COMMENT 'waittime in millisecs', + point_comment text, + PRIMARY KEY (entry, pointid) +) ENGINE=MyISAM DEFAULT CHARSET=utf8 ROW_FORMAT=FIXED COMMENT='Script Creature waypoints'; + +DROP TABLE IF EXISTS `sd2_db_version`; +CREATE TABLE `sd2_db_version` ( + `version` varchar(255) NOT NULL default '' COMMENT 'Database version string' +) ENGINE=MyISAM DEFAULT CHARSET=utf8; diff --git a/src/modules/SD2/sql/scriptdev2_create_structure_pgsql.sql b/src/modules/SD2/sql/scriptdev2_create_structure_pgsql.sql new file mode 100644 index 000000000..f03fef480 --- /dev/null +++ b/src/modules/SD2/sql/scriptdev2_create_structure_pgsql.sql @@ -0,0 +1,67 @@ +CREATE TABLE custom_texts ( + entry bigint NOT NULL, + content_default text NOT NULL, + content_loc1 text, + content_loc2 text, + content_loc3 text, + content_loc4 text, + content_loc5 text, + content_loc6 text, + content_loc7 text, + content_loc8 text, + sound integer DEFAULT 0 NOT NULL, + type smallint DEFAULT (0)::smallint NOT NULL, + language smallint DEFAULT (0)::smallint NOT NULL, + emote smallint DEFAULT (0)::smallint NOT NULL, + comment text, + PRIMARY KEY(entry) +); + +CREATE TABLE gossip_texts ( + entry bigint NOT NULL, + content_default text NOT NULL, + content_loc1 text, + content_loc2 text, + content_loc3 text, + content_loc4 text, + content_loc5 text, + content_loc6 text, + content_loc7 text, + content_loc8 text, + comment text, + PRIMARY KEY(entry) +); + +CREATE TABLE script_texts ( + entry bigint NOT NULL, + content_default text NOT NULL, + content_loc1 text, + content_loc2 text, + content_loc3 text, + content_loc4 text, + content_loc5 text, + content_loc6 text, + content_loc7 text, + content_loc8 text, + sound integer DEFAULT 0 NOT NULL, + type smallint DEFAULT (0)::smallint NOT NULL, + language smallint DEFAULT (0)::smallint NOT NULL, + emote smallint DEFAULT (0)::smallint NOT NULL, + comment text, + PRIMARY KEY(entry) +); + +CREATE TABLE script_waypoint ( + entry bigint NOT NULL, + pointid bigint NOT NULL, + location_x float DEFAULT (0)::float NOT NULL, + location_y float DEFAULT (0)::float NOT NULL, + location_z float DEFAULT (0)::float NOT NULL, + waittime integer DEFAULT 0 NOT NULL, + point_comment text, + PRIMARY KEY(entry,pointid) +); + +CREATE TABLE sd2_db_version ( + version varchar(255) NOT NULL DEFAULT '' +); diff --git a/src/modules/SD2/sql/scriptdev2_script_full.sql b/src/modules/SD2/sql/scriptdev2_script_full.sql new file mode 100644 index 000000000..363e6b516 --- /dev/null +++ b/src/modules/SD2/sql/scriptdev2_script_full.sql @@ -0,0 +1,7479 @@ +-- +-- Version data (part used in command .server info ) +-- + +DELETE FROM sd2_db_version; +INSERT INTO sd2_db_version (version) VALUES ('ScriptDev2 (for CMaNGOS c12716+) '); + +-- +-- Below contains data for table `script_texts` mainly used in C++ parts. +-- valid entries for table are between -1000000 and -1999999 +-- + +TRUNCATE script_texts; + +-- +-- -1 000 000 First 100 entries are reserved for special use, do not add regular text here. +-- + +INSERT INTO script_texts (entry,content_default,sound,type,language,emote,comment) VALUES +(-1000000,'',0,0,0,0,'DEFAULT_TEXT'), +(-1000001,'%s goes into a killing frenzy!',0,2,0,0,'EMOTE_GENERIC_FRENZY_KILL'), +(-1000002,'%s goes into a frenzy!',0,2,0,0,'EMOTE_GENERIC_FRENZY'), +(-1000003,'%s becomes enraged!',0,2,0,0,'EMOTE_GENERIC_ENRAGED'), +(-1000004,'%s goes into a berserker rage!',0,2,0,0,'EMOTE_GENERIC_BERSERK'), +(-1000005,'%s goes into a frenzy!',0,3,0,0,'EMOTE_BOSS_GENERIC_FRENZY'), +(-1000006,'%s becomes enraged!',0,3,0,0,'EMOTE_BOSS_GENERIC_ENRAGED'); + +-- +-- Normal text entries below. Say/Yell/Whisper/Emote for any regular world object. +-- Text entries for text used by creatures in instances are using map id as part of entry. +-- Example: for map 430, the database text entry for all creatures normally on this map start with -1430 +-- Values decrement as they are made. +-- For creatures outside instance, use -1 000 100 and below. Decrement value as they are made. +-- +-- Comment should contain a unique name that can be easily identified later by using sql queries like for example +-- SELECT * FROM script_texts WHERE comment LIKE '%defias%'; +-- Place the define used in script itself at the end of the comment. +-- +-- Do not change entry without a very good reason. Localization projects depends on using entries that does not change! +-- Example: UPDATE script_texts SET content_loc1 = 'the translated text' WHERE entry = -1000100; +-- + +-- -1 000 100 GENERAL MAPS (not instance maps) +INSERT INTO script_texts (entry,content_default,sound,type,language,emote,comment) VALUES +(-1000100,'Come, little ones. Face me!',0,1,0,0,'azuregos SAY_TELEPORT'), + +(-1000101,'Follow me, $N. I\'ll take you to the Defias hideout. But you better protect me or I am as good as dead',0,0,7,0,'defias traitor SAY_START'), +(-1000102,'The entrance is hidden here in Moonbrook. Keep your eyes peeled for thieves. They want me dead.',0,0,7,0,'defias traitor SAY_PROGRESS'), +(-1000103,'You can go tell Stoutmantle this is where the Defias Gang is holed up, $N.',0,0,7,0,'defias traitor SAY_END'), +(-1000104,'$N coming in fast! Prepare to fight!',0,0,7,0,'defias traitor SAY_AGGRO_1'), +(-1000105,'Help!',0,0,7,0,'defias traitor SAY_AGGRO_2'), + +(-1000106,'Everyone ready?',0,0,1,0,'torek SAY_READY'), +(-1000107,'Ok, Lets move out!',0,0,1,0,'torek SAY_MOVE'), +(-1000108,'Prepare yourselves. Silverwing is just around the bend.',0,0,1,0,'torek SAY_PREPARE'), +(-1000109,'Silverwing is ours!',0,0,1,0,'torek SAY_WIN'), +(-1000110,'Go report that the outpost is taken. We will remain here.',0,0,1,0,'torek SAY_END'), + +(-1000111,'Our house is this way, through the thicket.',0,0,7,0,'magwin SAY_START'), +(-1000112,'Help me!',0,0,7,0,'magwin SAY_AGGRO'), +(-1000113,'My poor family. Everything has been destroyed.',0,0,7,0,'magwin SAY_PROGRESS'), +(-1000114,'Father! Father! You\'re alive!',0,0,7,0,'magwin SAY_END1'), +(-1000115,'You can thank $N for getting me back here safely, father.',0,0,7,0,'magwin SAY_END2'), +(-1000116,'%s hugs her father.',0,2,7,0,'magwin EMOTE_HUG'), + +(-1000117,'Thank you for agreeing to help. Now, let\'s get out of here $N.',0,0,1,0,'wounded elf SAY_ELF_START'), +(-1000118,'Over there! They\'re following us!',0,0,1,0,'wounded elf SAY_ELF_SUMMON1'), +(-1000119,'Allow me a moment to rest. The journey taxes what little strength I have.',0,0,1,16,'wounded elf SAY_ELF_RESTING'), +(-1000120,'Did you hear something?',0,0,1,0,'wounded elf SAY_ELF_SUMMON2'), +(-1000121,'Falcon Watch, at last! Now, where\'s my... Oh no! My pack, it\'s missing! Where has -',0,0,1,0,'wounded elf SAY_ELF_COMPLETE'), +(-1000122,'You won\'t keep me from getting to Falcon Watch!',0,0,1,0,'wounded elf SAY_ELF_AGGRO'), + +(-1000123,'Ready when you are, $c.',0,0,0,15,'big will SAY_BIG_WILL_READY'), +(-1000124,'The Affray has begun. $n, get ready to fight!',0,0,0,0,'twiggy SAY_TWIGGY_BEGIN'), +(-1000125,'You! Enter the fray!',0,0,0,0,'twiggy SAY_TWIGGY_FRAY'), +(-1000126,'Challenger is down!',0,0,0,0,'twiggy SAY_TWIGGY_DOWN'), +(-1000127,'The Affray is over.',0,0,0,0,'twiggy SAY_TWIGGY_OVER'), + +(-1000128,'We need you to send reinforcements to Manaforge Duro, Ardonis. This is not a request, it\'s an order.',0,0,0,0,'dawnforge SAY_COMMANDER_DAWNFORGE_1'), +(-1000129,'You cannot be serious! We are severely understaffed and can barely keep this manaforge functional!',0,0,0,0,'dawnforge SAY_ARCANIST_ARDONIS_1'), +(-1000130,'You will do as ordered. Manaforge Duro has come under heavy attack by mana creatures and the situation is out of control. Failure to comply will not be tolerated!',0,0,0,0,'dawnforge SAY_COMMANDER_DAWNFORGE_2'), +(-1000131,'Indeed, it is not a request.',0,0,0,0,'dawnforge SAY_PATHALEON_CULATOR_IMAGE_1'), +(-1000132,'My lord!',0,0,0,0,'dawnforge SAY_COMMANDER_DAWNFORGE_3'), +(-1000133,'Duro will be reinforced! Ultris was a complete disaster. I will NOT have that mistake repeated!',0,0,0,0,'dawnforge PATHALEON_CULATOR_IMAGE_2'), +(-1000134,'We\'ve had too many setbacks along the way: Hellfire Citadel, Fallen Sky Ridge, Firewing Point... Prince Kael\'thas will tolerate no further delays. I will tolerate nothing other than complete success!',0,0,0,0,'dawnforge PATHALEON_CULATOR_IMAGE_2_1'), +(-1000135,'I am returning to Tempest Keep. See to it that I do not have reason to return!',0,0,0,0,'dawnforge PATHALEON_CULATOR_IMAGE_2_2' ), +(-1000136,'Yes, my lord.',0,0,0,0,'dawnforge COMMANDER_DAWNFORGE_4 SAY_ARCANIST_ARDONIS_2'), +(-1000137,'See to it, Ardonis!',0,0,0,0,'dawnforge COMMANDER_DAWNFORGE_5'), + +(-1000138,'Avruu\'s magic... it still controls me. You must fight me, mortal. It\'s the only way to break the spell!',0,0,0,0,'aeranas SAY_SUMMON'), +(-1000139,'Avruu\'s magic is broken! I\'m free once again!',0,0,0,0,'aeranas SAY_FREE'), + +(-1000140,'Let\'s go.',0,0,1,0,'lilatha SAY_START'), +(-1000141,'$N, let\'s use the antechamber to the right.',0,0,1,0,'lilatha SAY_PROGRESS1'), +(-1000142,'I can see the light at the end of the tunnel!',0,0,1,0,'lilatha SAY_PROGRESS2'), +(-1000143,'There\'s Farstrider Enclave now, $C. Not far to go... Look out! Troll ambush!!',0,0,1,0,'lilatha SAY_PROGRESS3'), +(-1000144,'Thank you for saving my life and bringing me back to safety, $N',0,0,1,0,'lilatha SAY_END1'), +(-1000145,'Captain Helios, I\'ve been rescued from the Amani Catacombs. Reporting for duty, sir!',0,0,1,0,'lilatha SAY_END2'), +(-1000146,'Liatha, get someone to look at those injuries. Thank you for bringing her back safely.',0,0,1,0,'lilatha CAPTAIN_ANSWER'), + +(-1000147,'I remember well the sting of defeat at the conclusion of the Third War. I have waited far too long for my revenge. Now the shadow of the Legion falls over this world. It is only a matter of time until all of your failed creation... is undone.',11332,1,0,0,'kazzak SAY_INTRO'), +(-1000148,'The Legion will conquer all!',11333,1,0,0,'kazzak SAY_AGGRO1'), +(-1000149,'All mortals will perish!',11334,1,0,0,'kazzak SAY_AGGRO2'), +(-1000150,'All life must be eradicated!',11335,1,0,0,'kazzak SAY_SURPREME1'), +(-1000151,'I\'ll rip the flesh from your bones!',11336,1,0,0,'kazzak SAY_SURPREME2'), +(-1000152,'Kirel Narak!',11337,1,0,0,'kazzak SAY_KILL1'), +(-1000153,'Contemptible wretch!',11338,1,0,0,'kazzak SAY_KILL2'), +(-1000154,'The universe will be remade.',11339,1,0,0,'kazzak SAY_KILL3'), +(-1000155,'The Legion... will never... fall.',11340,1,0,0,'kazzak SAY_DEATH'), + +(-1000156,'Bloodmaul Brew? Me favorite!',0,0,0,0,'bladespire ogre SAY_BREW_1'), + +(-1000157,'Invaders, you dangle upon the precipice of oblivion! The Burning Legion comes and with it comes your end.',0,1,0,0,'kazzak SAY_RAND1'), +(-1000158,'Impudent whelps, you only delay the inevitable. Where one has fallen, ten shall rise. Such is the will of Kazzak...',0,1,0,0,'kazzak SAY_RAND2'), + +(-1000159,'Do not proceed. You will be eliminated!',11344,1,0,0,'doomwalker SAY_AGGRO'), +(-1000160,'Tectonic disruption commencing.',11345,1,0,0,'doomwalker SAY_EARTHQUAKE_1'), +(-1000161,'Magnitude set. Release.',11346,1,0,0,'doomwalker SAY_EARTHQUAKE_2'), +(-1000162,'Trajectory locked.',11347,1,0,0,'doomwalker SAY_OVERRUN_1'), +(-1000163,'Engage maximum speed.',11348,1,0,0,'doomwalker SAY_OVERRUN_2'), +(-1000164,'Threat level zero.',11349,1,0,0,'doomwalker SAY_SLAY_1'), +(-1000165,'Directive accomplished.',11350,1,0,0,'doomwalker SAY_SLAY_2'), +(-1000166,'Target exterminated.',11351,1,0,0,'doomwalker SAY_SLAY_3'), +(-1000167,'System failure in five, f-o-u-r...',11352,1,0,0,'doomwalker SAY_DEATH'), + +(-1000168,'Who dares awaken Aquementas?',0,1,0,0,'aquementas AGGRO_YELL_AQUE'), + +(-1000169,'Muahahahaha! You fool! You\'ve released me from my banishment in the interstices between space and time!',0,1,0,0,'nether_drake SAY_NIHIL_1'), +(-1000170,'All of Draenor shall quick beneath my feet! I will destroy this world and reshape it in my image!',0,1,0,0,'nether_drake SAY_NIHIL_2'), +(-1000171,'Where shall I begin? I cannot bother myself with a worm such as yourself. There is a world to be conquered!',0,1,0,0,'nether_drake SAY_NIHIL_3'), +(-1000172,'No doubt the fools that banished me are long dead. I shall take wing survey my demense. Pray to whatever gods you hold dear that we do not meet again.',0,1,0,0,'nether_drake SAY_NIHIL_4'), +(-1000173,'NOOOOooooooo!',0,1,0,0,'nether_drake SAY_NIHIL_INTERRUPT'), + +(-1000174,'Good $N, you are under the spell\'s influence. I must analyze it quickly, then we can talk.',0,0,7,0,'daranelle SAY_SPELL_INFLUENCE'), + +(-1000175,'Thank you, mortal.',0,0,11,0,' SAY_JUST_EATEN'), + +(-1000176,'The last thing I remember is the ship falling and us getting into the pods. I\'ll go see how I can help. Thank you!',0,0,7,0,'draenei_survivor SAY_HEAL1'), +(-1000177,'$C, Where am I? Who are you? Oh no! What happened to the ship?',0,0,7,0,'draenei_survivor SAY_HEAL2'), +(-1000178,'$C You saved me! I owe you a debt that I can never repay. I\'ll go see if I can help the others.',0,0,7,0,'draenei_survivor SAY_HEAL3'), +(-1000179,'Ugh... what is this place? Is that all that\'s left of the ship over there?',0,0,7,0,'draenei_survivor SAY_HEAL4'), +(-1000180,'Oh, the pain...',0,0,7,0,'draenei_survivor SAY_HELP1'), +(-1000181,'Everything hurts, Please make it stop...',0,0,7,0,'draenei_survivor SAY_HELP2'), +(-1000182,'Ughhh... I hurt. Can you help me?',0,0,7,0,'draenei_survivor SAY_HELP3'), +(-1000183,'I don\'t know if I can make it, please help me...',0,0,7,0,'draenei_survivor SAY_HELP4'), + +(-1000184,'Yes Master, all goes along as planned.',0,0,7,0,'engineer_spark SAY_TEXT'), +(-1000185,'%s puts the shell to his ear.',0,2,7,0,'engineer_spark EMOTE_SHELL'), +(-1000186,'Now I cut you!',0,1,7,0,'engineer_spark SAY_ATTACK'), + +(-1000187,'Thank you, dear $C, you just saved my life.',0,0,0,0,'npc_redemption_target SAY_HEAL'), + +(-1000188,'Deployment sucessful. Trespassers will be neutralized.',0,0,0,0,'converted_sentry SAY_CONVERTED_1'), +(-1000189,'Objective acquired. Initiating security routines.',0,0,0,0,'converted_sentry SAY_CONVERTED_2'), + +(-1000190,'In Nagrand, food hunt ogre!',0,0,0,0,' SAY_LUMP_0'), +(-1000191,'You taste good with maybe a little salt and pepper.',0,0,0,0,' SAY_LUMP_1'), +(-1000192,'OK, OK! Lump give up!',0,0,0,0,' SAY_LUMP_DEFEAT'), + +(-1000193,'%s looks down at the discarded necklace. In her sadness, the lady incants a glamour, which beckons forth Highborne spirits. The chamber resonates with their ancient song about the Sin\'dorei...',10896,2,1,0,'lady_sylvanas EMOTE_LAMENT_START'), + +(-1000194,'I give up! Please don\'t kill me!',0,0,0,0,'unkor SAY_SUBMIT'), + +(-1000195,'Thank you again, $N. I\'ll make my way to the road now. When you can, find Terenthis and let him know we escaped.',0,0,0,1,'volcor SAY_ESCAPE'), + +(-1000196,'Belore...',0,0,1,0,'lady_sylvanas SAY_LAMENT_END'), +(-1000197,'%s kneels down and pick up the amulet.',0,2,1,16,'lady_sylvanas EMOTE_LAMENT_END'), + +(-1000198,'Taste blade, mongrel!',0,0,0,0,'SAY_GUARD_SIL_AGGRO1'), +(-1000199,'Please tell me that you didn\'t just do what I think you just did. Please tell me that I\'m not going to have to hurt you...',0,0,0,0,'SAY_GUARD_SIL_AGGRO2'), +(-1000200,'As if we don\'t have enough problems, you go and create more!',0,0,0,0,'SAY_GUARD_SIL_AGGRO3'), + +(-1000201,'I\'m saved! Thank you, doctor!',0,0,0,0,'injured_patient SAY_DOC1'), +(-1000202,'HOORAY! I AM SAVED!',0,0,0,0,'injured_patient SAY_DOC2'), +(-1000203,'Sweet, sweet embrace... take me...',0,0,0,0,'injured_patient SAY_DOC3'), + +(-1000204,'%s looks up at you quizzically. Maybe you should inspect it?',0,2,0,0,'cluck EMOTE_A_HELLO'), +(-1000205,'%s looks at you unexpectadly.',0,2,0,0,'cluck EMOTE_H_HELLO'), +(-1000206,'%s starts pecking at the feed.',0,2,0,0,'cluck EMOTE_CLUCK_TEXT2'), + +(-1000207,'Mmm. Me thirsty!',0,0,0,0,'bladespire ogre SAY_BREW_2'), +(-1000208,'Ohh, look! Bloodmaul Brew! Mmmm...',0,0,0,0,'bladespire ogre SAY_BREW_3'), + +(-1000209,'Very well. Let\'s see what you have to show me, $N.',0,0,1,0,'anvilward SAY_ANVIL1'), +(-1000210,'What manner of trick is this, $R? If you seek to ambush me, I warn you I will not go down quietly!',0,0,1,0,'anvilward SAY_ANVIL2'), + +(-1000211,'Warning! %s emergency shutdown process initiated by $N. Shutdown will complete in two minutes.',0,2,0,0,'manaforge_control EMOTE_START'), +(-1000212,'Emergency shutdown will complete in one minute.',0,2,0,0,'manaforge_control EMOTE_60'), +(-1000213,'Emergency shutdown will complete in thirty seconds.',0,2,0,0,'manaforge_control EMOTE_30'), +(-1000214,'Emergency shutdown will complete in ten seconds.',0,2,0,0,'manaforge_control EMOTE_10'), +(-1000215,'Emergency shutdown complete.',0,2,0,0,'manaforge_control EMOTE_COMPLETE'), +(-1000216,'Emergency shutdown aborted.',0,2,0,0,'manaforge_control EMOTE_ABORT'), + +(-1000217,'Greetings, $N. I will guide you through the cavern. Please try and keep up.',0,4,0,0,'WHISPER_CUSTODIAN_1'), +(-1000218,'We do not know if the Caverns of Time have always been accessible to mortals. Truly, it is impossible to tell as the Timeless One is in perpetual motion, changing our timeways as he sees fit. What you see now may very well not exist tomorrow. You may wake up and have no memory of this place.',0,4,0,0,'WHISPER_CUSTODIAN_2'), +(-1000219,'It is strange, I know... Most mortals cannot actually comprehend what they see here, as often, what they see is not anchored within their own perception of reality.',0,4,0,0,'WHISPER_CUSTODIAN_3'), +(-1000220,'Follow me, please.',0,4,0,0,'WHISPER_CUSTODIAN_4'), +(-1000221,'There are only two truths to be found here: First, that time is chaotic, always in flux, and completely malleable and second, perception does not dictate reality.',0,4,0,0,'WHISPER_CUSTODIAN_5'), +(-1000222,'As custodians of time, we watch over and care for Nozdormu\'s realm. The master is away at the moment, which means that attempts are being made to dramatically alter time. The master never meddles in the affairs of mortals but instead corrects the alterations made to time by others. He is reactionary in this regard.',0,4,0,0,'WHISPER_CUSTODIAN_6'), +(-1000223,'For normal maintenance of time, the Keepers of Time are sufficient caretakers. We are able to deal with most ordinary disturbances. I speak of little things, such as rogue mages changing something in the past to elevate their status or wealth in the present.',0,4,0,0,'WHISPER_CUSTODIAN_7'), +(-1000224,'These tunnels that you see are called timeways. They are infinite in number. The ones that currently exist in your reality are what the master has deemed as \'trouble spots.\' These trouble spots may differ completely in theme but they always share a cause. That is, their existence is a result of the same temporal disturbance. Remember that should you venture inside one...',0,4,0,0,'WHISPER_CUSTODIAN_8'), +(-1000225,'This timeway is in great disarray! We have agents inside right now attempting to restore order. What information I have indicates that Thrall\'s freedom is in jeopardy. A malevolent organization known as the Infinite Dragonflight is trying to prevent his escape. I fear without outside assistance, all will be lost.',0,4,0,0,'WHISPER_CUSTODIAN_9'), +(-1000226,'We have very little information on this timeway. Sa\'at has been dispatched and is currently inside. The data we have gathered from his correspondence is that the Infinite Dragonflight are once again attempting to alter time. Could it be that the opening of the Dark Portal is being targeted for sabotage? Let us hope not...',0,4,0,0,'WHISPER_CUSTODIAN_10'), +(-1000227,'This timeway is currently collapsing. What that may hold for the past, present and future is currently unknown...',0,4,0,0,'WHISPER_CUSTODIAN_11'), +(-1000228,'The timeways are currently ranked in order from least catastrophic to most catastrophic. Note that they are all classified as catastrophic, meaning that any single one of these timeways collapsing would mean that your world would end. We only classify them in such a way so that the heroes and adventurers that are sent here know which timeway best suits their abilities.',0,4,0,0,'WHISPER_CUSTODIAN_12'), +(-1000229,'All we know of this timeway is that it leads to Mount Hyjal. The Infinite Dragonflight have gone to great lengths to prevent our involvement. We know next to nothing, mortal. Soridormi is currently attempting to break through the timeway\'s defenses but has thus far been unsuccessful. You might be our only hope of breaking through and resolving the conflict.',0,4,0,0,'WHISPER_CUSTODIAN_13'), +(-1000230,'Our time is at an end $N. I would wish you luck, if such a thing existed.',0,4,0,0,'WHISPER_CUSTODIAN_14'), + +(-1000231,'Ah, $GPriest:Priestess; you came along just in time. I appreciate it.',0,0,0,20,'garments SAY_COMMON_HEALED'), +(-1000232,'Thank you! Thank you, $GPriest:Priestess;. Now I can take on those gnolls with your power to back me!',0,0,1,4,'garments SAY_DG_KEL_THANKS'), +(-1000233,'Farewell to you, and may shadow always protect you!',0,0,1,3,'garments SAY_DG_KEL_GOODBYE'), + +(-1000234,'Follow me, stranger. This won\'t take long.',0,0,0,0,'SAY_KHAD_SERV_0'), +(-1000235,'Shattrath was once the draenei capital of this world. Its name means "dwelling of light."',0,4,0,0,'SAY_KHAD_SERV_1'), +(-1000236,'When the Burning Legion turned the orcs against the draenei, the fiercest battle was fought here. The draenei fought tooth and nail, but in the end the city fell.',0,4,0,0,'SAY_KHAD_SERV_2'), +(-1000237,'The city was left in ruins and darkness... until the Sha\'tar arrived.',0,4,0,0,'SAY_KHAD_SERV_3'), +(-1000238,'Let us go into the Lower City. I will warn you that as one of the only safe havens in Outland, Shattrath has attracted droves of refugees from all wars, current and past.',0,4,0,0,'SAY_KHAD_SERV_4'), +(-1000239,'The Sha\'tar, or "born from light" are the naaru that came to Outland to fight the demons of the Burning Legion.',0,4,0,0,'SAY_KHAD_SERV_5'), +(-1000240,'They were drawn to the ruins of Shattrath City where a small remnant of the draenei priesthood conducted its rites inside a ruined temple on this very spot.',0,4,0,0,'SAY_KHAD_SERV_6'), +(-1000241,'The priesthood, known as the Aldor, quickly regained its strength as word spread that the naaru had returned and reconstruction soon began. The ruined temple is now used as an infirmary for injured refugees.',0,4,0,0,'SAY_KHAD_SERV_7'), +(-1000242,'It wouldn\'t be long, however, before the city came under attack once again. This time, the attack came from Illidan\'s armies. A large regiment of blood elves had been sent by Illidan\'s ally, Kael\'thas Sunstrider, to lay waste to the city.',0,4,0,0,'SAY_KHAD_SERV_8'), +(-1000243,'As the regiment of blood elves crossed this very bridge, the Aldor\'s exarchs and vindicators lined up to defend the Terrace of Light. But then the unexpected happened.',0,4,0,0,'SAY_KHAD_SERV_9'), +(-1000244,'The blood elves laid down their weapons in front of the city\'s defenders; their leader, a blood elf elder known as Voren\'thal, stormed into the Terrace of Light and demanded to speak to A\'dal.',0,4,0,0,'SAY_KHAD_SERV_10'), +(-1000245,'As the naaru approached him, Voren\'thal kneeled before him and uttered the following words: "I\'ve seen you in a vision, naaru. My race\'s only hope for survival lies with you. My followers and I are here to serve you."',0,4,0,0,'SAY_KHAD_SERV_11'), +(-1000246,'The defection of Voren\'thal and his followers was the largest loss ever incurred by Kael\'s forces. And these weren\'t just any blood elves. Many of the best and brightest amongst Kael\'s scholars and magisters had been swayed by Voren\'thal\'s influence.',0,4,0,0,'SAY_KHAD_SERV_12'), +(-1000247,'The naaru accepted the defectors, who would become known as the Scryers; their dwelling lies in the platform above. Only those initiated with the Scryers are allowed there.',0,4,0,0,'SAY_KHAD_SERV_13'), +(-1000248,'The Aldor are followers of the Light and forgiveness and redemption are values they understand. However, they found hard to forget the deeds of the blood elves while under Kael\'s command.',0,4,0,0,'SAY_KHAD_SERV_14'), +(-1000249,'Many of the priesthood had been slain by the same magisters who now vowed to serve the naaru. They were not happy to share the city with their former enemies.',0,4,0,0,'SAY_KHAD_SERV_15'), +(-1000250,'The Aldor\'s most holy temple and its surrounding dwellings lie on the terrace above. As a holy site, only the initiated are welcome inside.',0,4,0,0,'SAY_KHAD_SERV_16'), +(-1000251,'The attacks against Shattrath continued, but the city did not fall\, as you can see. On the contrary, the naaru known as Xi\'ri led a successful incursion into Shadowmoon Valley - Illidan\'s doorstep.',0,4,0,0,'SAY_KHAD_SERV_17'), +(-1000252,'There he continues to wage war on Illidan with the assistance of the Aldor and the Scryers. The two factions have not given up on their old feuds, though.',0,4,0,0,'SAY_KHAD_SERV_18'), +(-1000253,'Such is their animosity that they vie for the honor of being sent to assist the naaru there. Each day, that decision is made here by A\'dal. The armies gather here to receive A\'dal\'s blessing before heading to Shadowmoon.',0,4,0,0,'SAY_KHAD_SERV_19'), +(-1000254,'Khadgar should be ready to see you again. Just remember that to serve the Sha\'tar you will most likely have to ally with the Aldor or the Scryers. And seeking the favor of one group will cause the others\' dislike.',0,4,0,0,'SAY_KHAD_SERV_20'), +(-1000255,'Good luck stranger, and welcome to Shattrath City.',0,4,0,0,'SAY_KHAD_SERV_21'), + +(-1000256,'Thank you! Thank you, $GPriest:Priestess;. Now I can take on those murlocs with the Light on my side!',0,0,7,4,'garments SAY_ROBERTS_THANKS'), +(-1000257,'Farewell to you, and may the Light be with you always.',0,0,7,3,'garments SAY_ROBERTS_GOODBYE'), +(-1000258,'Thank you! Thank you, $GPriest:Priestess;. Now I can take on those humans with your power to back me!',0,0,1,4,'garments SAY_KORJA_THANKS'), +(-1000259,'Farewell to you, and may our ancestors be with you always!',0,0,1,3,'garments SAY_KORJA_GOODBYE'), +(-1000260,'Thank you! Thank you, $GPriest:Priestess;. Now I can take on those wendigo with the Light on my side!',0,0,7,4,'garments SAY_DOLF_THANKS'), +(-1000261,'Farewell to you, and may the Light be with you always.',0,0,7,3,'garments SAY_DOLF_GOODBYE'), +(-1000262,'Thank you! Thank you, $GPriest:Priestess;. Now I can take on those corrupt timberlings with Elune\'s power behind me!',0,0,2,4,'garments SAY_SHAYA_THANKS'), +(-1000263,'Farewell to you, and may Elune be with you always.',0,0,2,3,'garments SAY_SHAYA_GOODBYE'), + +(-1000264,'Ok, $N. Follow me to the cave where I\'ll attempt to harness the power of the rune stone into these goggles.',0,0,0,1,'phizzlethorpe SAY_PROGRESS_1'), +(-1000265,'I discovered this cave on our first day here. I believe the energy in the stone can be used to our advantage.',0,0,0,1,'phizzlethorpe SAY_PROGRESS_2'), +(-1000266,'I\'ll begin drawing energy from the stone. Your job, $N, is to defend me. This place is cursed... trust me.',0,0,0,1,'phizzlethorpe SAY_PROGRESS_3'), +(-1000267,'%s begins tinkering with the goggles before the stone.',0,2,0,0,'phizzlethorpe EMOTE_PROGRESS_4'), +(-1000268,'Help!!! Get these things off me so I can get my work done!',0,0,0,0,'phizzlethorpe SAY_AGGRO'), +(-1000269,'Almost done! Just a little longer!',0,0,0,1,'phizzlethorpe SAY_PROGRESS_5'), +(-1000270,'I\'ve done it! I have harnessed the power of the stone into the goggles! Let\'s get out of here!',0,0,0,1,'phizzlethorpe SAY_PROGRESS_6'), +(-1000271,'Phew! Glad to be back from that creepy cave.',0,0,0,1,'phizzlethorpe SAY_PROGRESS_7'), +(-1000272,'%s hands one glowing goggles over to Doctor Draxlegauge.',0,2,0,0,'phizzlethorpe EMOTE_PROGRESS_8'), +(-1000273,'Doctor Draxlegauge will give you further instructions, $N. Many thanks for your help!',0,0,0,1,'phizzlethorpe SAY_PROGRESS_9'), + +(-1000274,'Time to teach you a lesson in manners, little $Gboy:girl;!',0,0,0,0,'larry SAY_START'), +(-1000275,'Now I\'m gonna give you to the count of \'3\' to get out of here before I sick the dogs on you.',0,0,0,0,'larry SAY_COUNT'), +(-1000276,'1...',0,0,0,0,'larry SAY_COUNT_1'), +(-1000277,'2...',0,0,0,0,'larry SAY_COUNT_2'), +(-1000278,'Time to meet your maker!',0,0,0,0,'larry SAY_ATTACK_5'), +(-1000279,'Alright, we give up! Don\'t hurt us!',0,0,0,0,'larry SAY_GIVEUP'), + +(-1000280,'A shadowy, sinister presence has invaded the Emerald Dream. Its power is poised to spill over into our world, $N. We must oppose it! That\'s why I cannot accompany you in person.',0,0,0,1,'clintar SAY_START'), +(-1000281,'The Emerald Dream will never be yours!',0,0,0,0,'clintar SAY_AGGRO_1'), +(-1000282,'Begone from this place!',0,0,0,0,'clintar SAY_AGGRO_2'), +(-1000283,'That\'s the first relic, but there are still two more. Follow me, $N.',0,0,0,0,'clintar SAY_RELIC1'), +(-1000284,'I\'ve recovered the second relic. Take a moment to rest, and then we\'ll continue to the last reliquary.',0,0,0,0,'clintar SAY_RELIC2'), +(-1000285,'We have all three of the relics, but my energy is rapidly fading. We must make our way back to Dreamwarden Lurosa! He will let you know what to do next.',0,0,0,0,'clintar SAY_RELIC3'), +(-1000286,'Lurosa, I am entrusting the Relics of Aviana to $N, who will take them to Morthis Whisperwing. I must return completely to the Emerald Dream now. Do not let $N fail!',0,0,0,1,'clintar SAY_END'), + +(-1000287,'Emergency power activated! Initializing ambulanory motor! CLUCK!',0,0,0,0,'oox SAY_OOX_START'), +(-1000288,'Physical threat detected! Evasive action! CLUCK!',0,0,0,0,'oox SAY_OOX_AGGRO1'), +(-1000289,'Thread analyzed! Activating combat plan beta! CLUCK!',0,0,0,0,'oox SAY_OOX_AGGRO2'), +(-1000290,'CLUCK! Sensors detect spatial anomaly - danger imminent! CLUCK!',0,0,0,0,'oox SAY_OOX_AMBUSH'), +(-1000291,'No one challanges the Wastewander nomads - not even robotic chickens! ATTACK!',0,0,0,0,'oox SAY_OOX17_AMBUSH_REPLY'), +(-1000292,'Cloaking systems online! CLUCK! Engaging cloak for transport to Booty Bay!',0,0,0,0,'oox SAY_OOX_END'), + +(-1000293,'To the house! Stay close to me, no matter what! I have my gun and ammo there!',0,0,7,0,'stilwell SAY_DS_START'), +(-1000294,'We showed that one!',0,0,7,0,'stilwell SAY_DS_DOWN_1'), +(-1000295,'One more down!',0,0,7,0,'stilwell SAY_DS_DOWN_2'), +(-1000296,'We\'ve done it! We won!',0,0,7,0,'stilwell SAY_DS_DOWN_3'), +(-1000297,'Meet me down by the orchard--I just need to put my gun away.',0,0,7,0,'stilwell SAY_DS_PROLOGUE'), + +(-1000298,'Alright, alright I think I can figure out how to operate this thing...',0,0,0,393,'wizzlecrank SAY_START'), +(-1000299,'Arrrgh! This isn\'t right!',0,0,0,0,'wizzlecrank SAY_STARTUP1'), +(-1000300,'Okay, I think I\'ve got it, now. Follow me, $n!',0,0,0,1,'wizzlecrank SAY_STARTUP2'), +(-1000301,'There\'s the stolen shredder! Stop it or Lugwizzle will have our hides!',0,1,0,0,'wizzlecrank SAY_MERCENARY'), +(-1000302,'Looks like we\'re out of woods, eh? Wonder what this does...',0,0,0,0,'wizzlecrank SAY_PROGRESS_1'), +(-1000303,'Come on, don\'t break down on me now!',0,0,0,393,'wizzlecrank SAY_PROGRESS_2'), +(-1000304,'That was a close one! Well, let\'s get going, it\'s still a ways to Ratchet!',0,0,0,0,'wizzlecrank SAY_PROGRESS_3'), +(-1000305,'Hmm... I don\'t think this blinking red light is a good thing...',0,0,0,0,'wizzlecrank SAY_END'), + +(-1000306,'Let\'s get to the others, and keep an eye open for those wolves cutside...',0,0,1,0,'erland SAY_START_1'), +(-1000307,'Be careful, $N. Those wolves like to hide among the trees.',0,0,1,0,'erland SAY_START_2'), +(-1000308,'A $C attacks!',0,0,1,0,'erland SAY_AGGRO_1'), +(-1000309,'Beware! I am under attack!',0,0,1,0,'erland SAY_AGGRO_2'), +(-1000310,'Oh no! A $C is upon us!',0,0,1,0,'erland SAY_AGGRO_3'), +(-1000311,'We\'re almost there!',0,0,1,0,'erland SAY_PROGRESS'), +(-1000312,'We made it! Thanks, $N. I couldn\'t have gotten without you.',0,0,1,0,'erland SAY_END'), +(-1000313,'It\'s good to see you again, Erland. What is your report?',0,0,33,1,'erland SAY_RANE'), +(-1000314,'Masses of wolves are to the east, and whoever lived at Malden\'s Orchard is gone.',0,0,1,1,'erland SAY_RANE_REPLY'), +(-1000315,'If I am excused, then I\'d like to check on Quinn...',0,0,1,1,'erland SAY_CHECK_NEXT'), +(-1000316,'Hello, Quinn. How are you faring?',0,0,1,1,'erland SAY_QUINN'), +(-1000317,'I\'ve been better. Ivar the Foul got the better of me...',0,0,33,1,'erland SAY_QUINN_REPLY'), +(-1000318,'Try to take better care of yourself, Quinn. You were lucky this time.',0,0,1,1,'erland SAY_BYE'), + +(-1000319,'Let the trial begin, Bloodwrath, attack!',0,1,1,0,'kelerun SayId1'), +(-1000320,'Champion Lightrend, make me proud!',0,1,1,0,'kelerun SayId2'), +(-1000321,'Show this upstart how a real Blood Knight fights, Swiftblade!',0,1,1,0,'kelerun SayId3'), +(-1000322,'Show $n the meaning of pain, Sunstriker!',0,1,1,0,'kelerun SayId4'), + +(-1000323,'Mist! I feared I would never see you again! Yes, I am well, do not worry for me. You must rest and recover your health.',0,0,7,0,'mist SAY_AT_HOME'), +(-1000324,'%s growls in acknowledgement before straightening and making her way off into the forest.',0,2,0,0,'mist EMOTE_AT_HOME'), + +(-1000325,'"Threshwackonator First Mate unit prepared to follow"',0,2,0,0,'threshwackonator EMOTE_START'), +(-1000326,'YARRR! Swabie, what have ye done?! He\'s gone mad! Baton him down the hatches! Hoist the mast! ARRRR! Every man for hi\'self!',0,0,7,0,'threshwackonator SAY_AT_CLOSE'), + +(-1000327,'Ok, $n, let\'s go find where I left that mysterious fossil. Follow me!',0,0,7,0,'remtravel SAY_REM_START'), +(-1000328,'Now where did I put that mysterious fossil? Ah, maybe up there...',0,0,7,0,'remtravel SAY_REM_RAMP1_1'), +(-1000329,'Hrm, nothing up here.',0,0,7,0,'remtravel SAY_REM_RAMP1_2'), +(-1000330,'No mysterious fossil here... Ah, but my copy of Green Hills of Stranglethorn. What a good book!',0,0,7,0,'remtravel SAY_REM_BOOK'), +(-1000331,'I bet you I left it in the tent!',0,0,7,0,'remtravel SAY_REM_TENT1_1'), +(-1000332,'Oh wait, that\'s Hollee\'s tent... and it\'s empty.',0,0,7,0,'remtravel SAY_REM_TENT1_2'), +(-1000333,'Interesting... I hadn\'t noticed this earlier...',0,0,7,0,'remtravel SAY_REM_MOSS'), +(-1000334,'%s inspects the ancient, mossy stone.',0,2,7,0,'remtravel EMOTE_REM_MOSS'), +(-1000335,'Oh wait! I\'m supposed to be looking for that mysterious fossil!',0,0,7,0,'remtravel SAY_REM_MOSS_PROGRESS'), +(-1000336,'Nope. didn\'t leave the fossil back here!',0,0,7,0,'remtravel SAY_REM_PROGRESS'), +(-1000337,'Ah. I remember now! I gave the mysterious fossil to Hollee! Check with her.',0,0,7,0,'remtravel SAY_REM_REMEMBER'), +(-1000338,'%s goes back to work, oblivious to everything around him.',0,2,7,0,'remtravel EMOTE_REM_END'), +(-1000339,'Something tells me this $r wants the mysterious fossil too. Help!',0,0,7,0,'remtravel SAY_REM_AGGRO'), + +(-1000340,'%s howls in delight at the sight of his lunch!',0,2,0,0,'kyle EMOTE_SEE_LUNCH'), +(-1000341,'%s eats his lunch.',0,2,0,0,'kyle EMOTE_EAT_LUNCH'), +(-1000342,'%s thanks you with a special dance.',0,2,0,0,'kyle EMOTE_DANCE'), + +(-1000343,'Is the way clear? Let\'s get out while we can, $N.',0,0,0,0,'kayra SAY_START'), +(-1000344,'Looks like we won\'t get away so easy. Get ready!',0,0,0,0,'kayra SAY_AMBUSH1'), +(-1000345,'Let\'s keep moving. We\'re not safe here!',0,0,0,0,'kayra SAY_PROGRESS'), +(-1000346,'Look out, $N! Enemies ahead!',0,0,0,0,'kayra SAY_AMBUSH2'), +(-1000347,'We\'re almost to the refuge! Let\'s go.',0,0,0,0,'kayra SAY_END'), + +(-1000348,'Ah...the wondrous sound of kodos. I love the way they make the ground shake... inspect the beast for me.',0,0,0,0,'kodo round SAY_SMEED_HOME_1'), +(-1000349,'Hey, look out with that kodo! You had better inspect that beast before i give you credit!',0,0,0,0,'kodo round SAY_SMEED_HOME_2'), +(-1000350,'That kodo sure is a beauty. Wait a minute, where are my bifocals? Perhaps you should inspect the beast for me.',0,0,0,0,'kodo round SAY_SMEED_HOME_3'), + +(-1000351,'You, there! Hand over that moonstone and nobody gets hurt!',0,1,0,0,'sprysprocket SAY_START'), +(-1000352,'%s takes the Southfury moonstone and escapes into the river. Follow her!',0,3,0,0,'sprysprocket EMOTE_START'), +(-1000353,'Just chill!',0,4,0,0,'sprysprocket SAY_WHISPER_CHILL'), +(-1000354,'Stupid grenade picked a fine time to backfire! So much for high quality goblin engineering!',0,1,0,0,'sprysprocket SAY_GRENADE_FAIL'), +(-1000355,'All right, you win! I surrender! Just don\'t hurt me!',0,1,0,0,'sprysprocket SAY_END'), + +(-1000356,'Okay, okay... gimme a minute to rest now. You gone and beat me up good.',0,0,1,14,'calvin SAY_COMPLETE'), + +(-1000357,'Let\'s go before they find out I\'m free!',0,0,0,1,'KAYA_SAY_START'), +(-1000358,'Look out! We\'re under attack!',0,0,0,0,'KAYA_AMBUSH'), +(-1000359,'Thank you for helping me. I know my way back from here.',0,0,0,0,'KAYA_END'), + +(-1000360,'The strands of LIFE have been severed! The Dreamers must be avenged!',0,1,0,0,' ysondre SAY_AGGRO'), +(-1000361,'Come forth, ye Dreamers - and claim your vengeance!',0,1,0,0,' ysondre SAY_SUMMONDRUIDS'), + +(-1000362,'Let\'s go $N. I am ready to reach Whitereach Post.',0,0,1,0,'paoka SAY_START'), +(-1000363,'Now this looks familiar. If we keep heading east, I think we can... Ahh, Wyvern on the attack!',0,0,1,0,'paoka SAY_WYVERN'), +(-1000364,'Thanks a bunch... I can find my way back to Whitereach Post from here. Be sure to talk with Motega Firemane; perhaps you can keep him from sending me home.',0,0,1,0,'paoka SAY_COMPLETE'), + +(-1000365,'Be on guard... Arnak has some strange power over the Grimtotem... they will not be happy to see me escape.',0,0,1,0,'lakota SAY_LAKO_START'), +(-1000366,'Look out, the Grimtotem are upon us!',0,0,1,0,'lakota SAY_LAKO_LOOK_OUT'), +(-1000367,'Here they come.',0,0,1,0,'lakota SAY_LAKO_HERE_COME'), +(-1000368,'More Grimtotems are coming this way!',0,0,1,0,'lakota SAY_LAKO_MORE'), +(-1000369,'Finally, free at last... I must be going now, thanks for helping me escape. I can get back to Freewind Post by myself.',0,0,1,0,'lakota SAY_LAKO_END'), + +(-1000370,'Stay close, $n. I\'ll need all the help I can get to break out of here. Let\'s go!',0,0,1,1,'gilthares SAY_GIL_START'), +(-1000371,'At last! Free from Northwatch Hold! I need a moment to catch my breath...',0,0,1,5,'gilthares SAY_GIL_AT_LAST'), +(-1000372,'Now i feel better. Let\'s get back to Ratchet. Come on, $n.',0,0,1,23,'gilthares SAY_GIL_PROCEED'), +(-1000373,'Looks like the Southsea Freeboters are heavily entrenched on the coast. This could get rough.',0,0,1,25,'gilthares SAY_GIL_FREEBOOTERS'), +(-1000374,'Help! $C attacking!',0,0,1,0,'gilthares SAY_GIL_AGGRO_1'), +(-1000375,'$C heading this way fast! Time for revenge!',0,0,1,0,'gilthares SAY_GIL_AGGRO_2'), +(-1000376,'$C coming right at us!',0,0,1,0,'gilthares SAY_GIL_AGGRO_3'), +(-1000377,'Get this $C off of me!',0,0,1,0,'gilthares SAY_GIL_AGGRO_4'), +(-1000378,'Almost back to Ratchet! Let\'s keep up the pace...',0,0,1,0,'gilthares SAY_GIL_ALMOST'), +(-1000379,'Ah, the sweet salt air of Ratchet.',0,0,1,0,'gilthares SAY_GIL_SWEET'), +(-1000380,'Captain Brightsun, $N here has freed me! $N, i am certain the Captain will reward your bravery.',0,0,1,66,'gilthares SAY_GIL_FREED'), + +(-1000381,'I sense the tortured spirits, $n. They are this way, come quickly!',0,0,0,1,'wilda SAY_WIL_START'), +(-1000382,'Watch out!',0,0,0,0,'wilda SAY_WIL_AGGRO1'), +(-1000383,'Naga attackers! Defend yourself!',0,0,0,0,'wilda SAY_WIL_AGGRO2'), +(-1000384,'Grant me protection $n, i must break trough their foul magic!',0,0,0,0,'wilda SAY_WIL_PROGRESS1'), +(-1000385,'The naga of Coilskar are exceptionally cruel to their prisoners. It is a miracle that I survived inside that watery prison for as long as I did. Earthmother be praised.',0,0,0,0,'wilda SAY_WIL_PROGRESS2'), +(-1000386,'Now we must find the exit.',0,0,0,0,'wilda SAY_WIL_FIND_EXIT'), +(-1000387,'Lady Vashj must answer for these atrocities. She must be brought to justice!',0,0,0,0,'wilda SAY_WIL_PROGRESS4'), +(-1000388,'The tumultuous nature of the great waterways of Azeroth and Draenor are a direct result of tormented water spirits.',0,0,0,0,'wilda SAY_WIL_PROGRESS5'), +(-1000389,'It shouldn\'t be much further, $n. The exit is just up ahead.',0,0,0,0,'wilda SAY_WIL_JUST_AHEAD'), +(-1000390,'Thank you, $n. Please return to my brethren at the Altar of Damnation, near the Hand of Gul\'dan, and tell them that Wilda is safe. May the Earthmother watch over you...',0,0,0,0,'wilda SAY_WIL_END'), + +(-1000391,'I\'m Thirsty.',0,0,0,0,'tooga SAY_TOOG_THIRST'), +(-1000392,'Torta must be so worried.',0,0,0,0,'tooga SAY_TOOG_WORRIED'), +(-1000393,'Torta, my love! I have returned at long last.',0,0,0,0,'tooga SAY_TOOG_POST_1'), +(-1000394,'You have any idea how long I\'ve been waiting here? And where\'s dinner? All that time gone and nothing to show for it?',0,0,0,0,'tooga SAY_TORT_POST_2'), +(-1000395,'My dearest Torta. I have been gone for so long. Finally we are reunited. At long last our love can blossom again.',0,0,0,0,'tooga SAY_TOOG_POST_3'), +(-1000396,'Enough with the rambling. I am starving! Now, get your dusty shell into that ocean and bring momma some grub.',0,0,0,0,'tooga SAY_TORT_POST_4'), +(-1000397,'Yes Torta. Whatever your heart desires...',0,0,0,0,'tooga SAY_TOOG_POST_5'), +(-1000398,'And try not to get lost this time...',0,0,0,0,'tooga SAY_TORT_POST_6'), + +(-1000399,'Peace is but a fleeting dream! Let the NIGHTMARE reign!',0,1,0,0,'taerar SAY_AGGRO'), +(-1000400,'Children of Madness - I release you upon this world!',0,1,0,0,'taerar SAY_SUMMONSHADE'), + +(-1000401,'Hope is a DISEASE of the soul! This land shall wither and die!',0,1,0,0,'emeriss SAY_AGGRO'), +(-1000402,'Taste your world\'s corruption!',0,1,0,0,'emeriss SAY_CASTCORRUPTION'), + +(-1000403,'Rin\'ji is free!',0,0,0,0,'SAY_RIN_FREE'), +(-1000404,'Attack my sisters! The troll must not escape!',0,0,0,0,'SAY_RIN_BY_OUTRUNNER'), +(-1000405,'Rin\'ji needs help!',0,0,1,0,'SAY_RIN_HELP_1'), +(-1000406,'Rin\'ji is being attacked!',0,0,1,0,'SAY_RIN_HELP_2'), +(-1000407,'Rin\'ji can see road now, $n. Rin\'ji knows the way home.',0,0,1,0,'SAY_RIN_COMPLETE'), +(-1000408,'Rin\'ji will tell you secret now... $n, should go to the Overlook Cliffs. Rin\'ji hid something on island there',0,0,1,0,'SAY_RIN_PROGRESS_1'), +(-1000409,'You find it, you keep it! Don\'t tell no one that Rin\'ji talked to you!',0,0,1,0,'SAY_RIN_PROGRESS_2'), + +(-1000410,'Here they come! Defend yourself!',0,0,1,5,'kanati SAY_KAN_START'), + +(-1000411,'Why don\'t we deal with you now, Hendel? Lady Proudmoore will speak for you back in the tower.',0,0,7,0,'hendel SAY_PROGRESS_1_TER'), +(-1000412,'Please... please... Miss Proudmore. I didn\'t mean to...',0,0,7,0,'hendel SAY_PROGRESS_2_HEN'), +(-1000413,'I apologize for taking so long to get here. I wanted Lady Proudmoore to be present also.',0,0,7,0,'hendel SAY_PROGRESS_3_TER'), +(-1000414,'We can only stay a few moments before returning to the tower. If you wish to speak to us more you may find us there.',0,0,7,0,'hendel SAY_PROGRESS_4_TER'), +(-1000415,'%s, too injured, gives up the chase.',0,2,0,0,'hendel EMOTE_SURRENDER'), + +(-1000416,'Well, I\'m not sure how far I\'ll make it in this state... I\'m feeling kind of faint...',0,0,0,0,'ringo SAY_RIN_START_1'), +(-1000417,'Remember, if I faint again, the water that Spraggle gave you will revive me.',0,0,0,0,'ringo SAY_RIN_START_2'), +(-1000418,'The heat... I can\'t take it...',0,0,0,0,'ringo SAY_FAINT_1'), +(-1000419,'Maybe... you could carry me?',0,0,0,0,'ringo SAY_FAINT_2'), +(-1000420,'Uuuuuuggggghhhhh....',0,0,0,0,'ringo SAY_FAINT_3'), +(-1000421,'I\'m not feeling so well...',0,0,0,0,'ringo SAY_FAINT_4'), +(-1000422,'Where... Where am I?',0,0,0,0,'ringo SAY_WAKE_1'), +(-1000423,'I am feeling a little better now, thank you.',0,0,0,0,'ringo SAY_WAKE_2'), +(-1000424,'Yes, I must go on.',0,0,0,0,'ringo SAY_WAKE_3'), +(-1000425,'How am I feeling? Quite soaked, thank you.',0,0,0,0,'ringo SAY_WAKE_4'), +(-1000426,'Spraggle! I didn\'t think I\'d make it back!',0,0,0,0,'ringo SAY_RIN_END_1'), +(-1000427,'Ringo! You\'re okay!',0,0,0,0,'ringo SAY_SPR_END_2'), +(-1000428,'Oh... I\'m feeling faint...',0,0,0,0,'ringo SAY_RIN_END_3'), +(-1000429,'%s collapses onto the ground.',0,2,0,0,'ringo EMOTE_RIN_END_4'), +(-1000430,'%s stands up after a short pause.',0,2,0,0,'ringo EMOTE_RIN_END_5'), +(-1000431,'Ugh.',0,0,0,0,'ringo SAY_RIN_END_6'), +(-1000432,'Ringo? Wake up! Don\'t worry, I\'ll take care of you.',0,0,0,0,'ringo SAY_SPR_END_7'), +(-1000433,'%s fades away after a long pause.',0,2,0,0,'ringo EMOTE_RIN_END_8'), + +(-1000434,'Liladris has been waiting for me at Maestra\'s Post, so we should make haste, $N.',0,0,0,0,'kerlonian SAY_KER_START'), +(-1000435,'%s looks very sleepy...',0,2,0,0,'kerlonian EMOTE_KER_SLEEP_1'), +(-1000436,'%s suddenly falls asleep',0,2,0,0,'kerlonian EMOTE_KER_SLEEP_2'), +(-1000437,'%s begins to drift off...',0,2,0,0,'kerlonian EMOTE_KER_SLEEP_3'), +(-1000438,'This looks like the perfect place for a nap...',0,0,0,0,'kerlonian SAY_KER_SLEEP_1'), +(-1000439,'Yaaaaawwwwwnnnn...',0,0,0,0,'kerlonian SAY_KER_SLEEP_2'), +(-1000440,'Oh, I am so tired...',0,0,0,0,'kerlonian SAY_KER_SLEEP_3'), +(-1000441,'You don\'t mind if I stop here for a moment, do you?',0,0,0,0,'kerlonian SAY_KER_SLEEP_4'), +(-1000442,'Be on the alert! The Blackwood furbolgs are numerous in the area...',0,0,0,0,'kerlonian SAY_KER_ALERT_1'), +(-1000443,'It\'s quiet... Too quiet...',0,0,0,0,'kerlonian SAY_KER_ALERT_2'), +(-1000444,'Oh, I can see Liladris from here... Tell her I\'m here, won\'t you?',0,0,0,0,'kerlonian SAY_KER_END'), +(-1000445,'%s wakes up!',0,2,0,0,'kerlonian EMOTE_KER_AWAKEN'), + +(-1000446,'A-Me good. Good, A-Me. Follow... follow A-Me. Home. A-Me go home.',0,0,0,0,'ame01 SAY_AME_START'), +(-1000447,'Good... good, A-Me. A-Me good. Home. Find home.',0,0,0,0,'ame01 SAY_AME_PROGRESS'), +(-1000448,'A-Me home! A-Me good! Good A-Me. Home. Home. Home.',0,0,0,0,'ame01 SAY_AME_END'), +(-1000449,'$c, no hurt A-Me. A-Me good.',0,0,0,0,'ame01 SAY_AME_AGGRO1'), +(-1000450,'Danger. Danger! $c try hurt A-Me.',0,0,0,0,'ame01 SAY_AME_AGGRO2'), +(-1000451,'Bad, $c. $c, bad!',0,0,0,0,'ame01 SAY_AME_AGGRO3'), + +(-1000452,'I noticed some fire on that island over there. A human, too. Let\'s go check it out, $n.',0,0,1,0,'ogron SAY_OGR_START'), +(-1000453,'That\'s Reethe alright. Let\'s go see what he has to say, yeah?',0,0,1,1,'ogron SAY_OGR_SPOT'), +(-1000454,'W-what do you want? Just leave me alone...',0,0,0,6,'ogron SAY_OGR_RET_WHAT'), +(-1000455,'I swear. I didn\'t steal anything from you! Here, take some of my supplies, just go away!',0,0,0,27,'ogron SAY_OGR_RET_SWEAR'), +(-1000456,'Just tell us what you know about the Shady Rest Inn, and I won\'t bash your skull in.',0,0,1,0,'ogron SAY_OGR_REPLY_RET'), +(-1000457,'I... Well, I may of taken a little thing or two from the inn... but what would an ogre care about that?',0,0,0,6,'ogron SAY_OGR_RET_TAKEN'), +(-1000458,'Look here, if you don\'t tell me about the fire--',0,0,1,0,'ogron SAY_OGR_TELL_FIRE'), +(-1000459,'Not one step closer, ogre!',0,0,0,27,'ogron SAY_OGR_RET_NOCLOSER'), +(-1000460,'And I don\'t know anything about this fire of yours...',0,0,0,0,'ogron SAY_OGR_RET_NOFIRE'), +(-1000461,'What was that? Did you hear something?',0,0,0,0,'ogron SAY_OGR_RET_HEAR'), +(-1000462,'Paval Reethe! Found you at last. And consorting with ogres now? No fear, even deserters and traitors are afforded some mercy.',0,0,0,0,'ogron SAY_OGR_CAL_FOUND'), +(-1000463,'Private, show Lieutenant Reethe some mercy.',0,0,0,29,'ogron SAY_OGR_CAL_MERCY'), +(-1000464,'Gladly, sir.',0,0,0,0,'ogron SAY_OGR_HALL_GLAD'), +(-1000465,'%s staggers backwards as the arrow lodges itself deeply in his chest.',0,2,0,0,'ogron EMOTE_OGR_RET_ARROW'), +(-1000466,'Ugh... Hallan, didn\'t think you had it in you...',0,0,0,34,'ogron SAY_OGR_RET_ARROW'), +(-1000467,'Now, let\'s clean up the rest of the trash, men!',0,0,0,0,'ogron SAY_OGR_CAL_CLEANUP'), +(-1000468,'Damn it! You\'d better not die on me, human!',0,0,1,0,'ogron SAY_OGR_NODIE'), +(-1000469,'Still with us, Reethe?',0,0,1,0,'ogron SAY_OGR_SURVIVE'), +(-1000470,'Must be your lucky day. Alright, I\'ll talk. Just leave me alone. Look, you\'re not going to believe me, but it wa... oh, Light, looks like the girl could shoot...',0,0,0,0,'ogron SAY_OGR_RET_LUCKY'), +(-1000471,'By the way, thanks for watching my back.',0,0,1,0,'ogron SAY_OGR_THANKS'), + +(-1000472,'1...',0,3,0,0,'mana bomb SAY_COUNT_1'), +(-1000473,'2...',0,3,0,0,'mana bomb SAY_COUNT_2'), +(-1000474,'3...',0,3,0,0,'mana bomb SAY_COUNT_3'), +(-1000475,'4...',0,3,0,0,'mana bomb SAY_COUNT_4'), +(-1000476,'5...',0,3,0,0,'mana bomb SAY_COUNT_5'), + +(-1000477,'Let us leave this place. I\'ve had enough of these madmen!',0,0,0,0,'akuno SAY_AKU_START'), +(-1000478,'You\'ll go nowhere, fool!',0,0,0,0,'akuno SAY_AKU_AMBUSH_A'), +(-1000479,'Beware! More cultists come!',0,0,0,0,'akuno SAY_AKU_AMBUSH_B'), +(-1000480,'You will not escape us so easily!',0,0,0,0,'akuno SAY_AKU_AMBUSH_B_REPLY'), +(-1000481,'I can find my way from here. My friend Mekeda will reward you for your kind actions.',0,0,0,0,'akuno SAY_AKU_COMPLETE'), + +(-1000482,'Look out!',0,0,0,0,'maghar captive SAY_MAG_START'), +(-1000483,'Don\'t let them escape! Kill the strong one first!',0,0,0,0,'maghar captive SAY_MAG_NO_ESCAPE'), +(-1000484,'More of them coming! Watch out!',0,0,0,0,'maghar captive SAY_MAG_MORE'), +(-1000485,'Where do you think you\'re going? Kill them all!',0,0,0,0,'maghar captive SAY_MAG_MORE_REPLY'), +(-1000486,'Ride the lightning, filth!',0,0,0,0,'maghar captive SAY_MAG_LIGHTNING'), +(-1000487,'FROST SHOCK!!!',0,0,0,0,'maghar captive SAY_MAG_SHOCK'), +(-1000488,'It is best that we split up now, in case they send more after us. Hopefully one of us will make it back to Garrosh. Farewell stranger.',0,0,0,0,'maghar captive SAY_MAG_COMPLETE'), + +(-1000489,'Show our guest around Shattrath, will you? Keep an eye out for pickpockets in the lower city.',0,0,0,0,'SAY_KHAD_START'), +(-1000490,'A newcomer to Shattrath! Make sure to drop by later. We can always use a hand with the injured.',0,0,0,0,'SAY_KHAD_INJURED'), +(-1000491,'Be mindful of what you say, this one\'s being shown around by Khadgar\'s pet.',0,0,0,0,'SAY_KHAD_MIND_YOU'), +(-1000492,'Are you joking? I\'m a Scryer, I always watch what i say. More enemies than allies in this city, it seems.',0,0,0,0,'SAY_KHAD_MIND_ALWAYS'), +(-1000493,'Light be with you, $n. And welcome to Shattrath.',0,0,0,0,'SAY_KHAD_ALDOR_GREET'), +(-1000494,'We\'re rather selective of who we befriend, $n. You think you have what it takes?',0,0,0,0,'SAY_KHAD_SCRYER_GREET'), +(-1000495,'Khadgar himself is showing you around? You must have made a good impression, $n.',0,0,0,0,'SAY_KHAD_HAGGARD'), + +(-1000496,'%s lifts its head into the air, as if listening for something.',0,2,0,0,'ancestral wolf EMOTE_WOLF_LIFT_HEAD'), +(-1000497,'%s lets out a howl that rings across the mountains to the north and motions for you to follow.',0,2,0,0,'ancestral wolf EMOTE_WOLF_HOWL'), +(-1000498,'Welcome, kind spirit. What has brought you to us?',0,0,0,0,'ancestral wolf SAY_WOLF_WELCOME'), + +(-1000499,'Puny $r wanna fight %s? Me beat you! Me boss here!',0,0,1,0,'morokk SAY_MOR_CHALLENGE'), +(-1000500,'Me scared! Me run now!',0,1,0,0,'morokk SAY_MOR_SCARED'), + +(-1000501,'Are you sure that you are ready? If we do not have a group of your allies to aid us, we will surely fail.',0,0,1,0,'muglash SAY_MUG_START1'), +(-1000502,'This will be a though fight, $n. Follow me closely.',0,0,1,0,'muglash SAY_MUG_START2'), +(-1000503,'This is the brazier, $n. Put it out. Vorsha is a beast, worthy of praise from no one!',0,0,1,0,'muglash SAY_MUG_BRAZIER'), +(-1000504,'Now we must wait. It won\'t be long before the naga realize what we have done.',0,0,1,0,'muglash SAY_MUG_BRAZIER_WAIT'), +(-1000505,'Be on your guard, $n!',0,0,1,0,'muglash SAY_MUG_ON_GUARD'), +(-1000506,'Perhaps we will get a moment to rest. But i will not give up until we have faced off against Vorsha!',0,0,1,0,'muglash SAY_MUG_REST'), +(-1000507,'We have done it!',0,0,1,0,'muglash SAY_MUG_DONE'), +(-1000508,'You have my deepest gratitude. I thank you.',0,0,1,0,'muglash SAY_MUG_GRATITUDE'), +(-1000509,'I am going to patrol the area for a while longer and ensure that things are truly safe.',0,0,1,0,'muglash SAY_MUG_PATROL'), +(-1000510,'Please return to Zoram\'gar and report our success to the Warsong runner.',0,0,1,0,'muglash SAY_MUG_RETURN'), + +(-1000511,'Aright, listen up! Form a circle around me and move out!',0,0,0,0,'letoll SAY_LE_START'), +(-1000512,'Aright, $r, just keep us safe from harm while we work. We\'ll pay you when we return.',0,0,0,0,'letoll SAY_LE_KEEP_SAFE'), +(-1000513,'The dig site is just north of here.',0,0,0,0,'letoll SAY_LE_NORTH'), +(-1000514,'We\'re here! Start diggin\'!',0,0,0,0,'letoll SAY_LE_ARRIVE'), +(-1000515,'I think there\'s somethin\' buried here, beneath the sand!',0,0,0,0,'letoll SAY_LE_BURIED'), +(-1000516,'Almost got it!',0,0,0,0,'letoll SAY_LE_ALMOST'), +(-1000517,'By brann\'s brittle bananas! What is it!? It... It looks like a drum.',0,0,0,0,'letoll SAY_LE_DRUM'), +(-1000518,'Wow... a drum.',0,0,0,0,'letoll SAY_LE_DRUM_REPLY'), +(-1000519,'This discovery will surely rock the foundation of modern archaeology.',0,0,0,0,'letoll SAY_LE_DISCOVERY'), +(-1000520,'Yea, great. Can we leave now? This desert is giving me hives.',0,0,0,0,'letoll SAY_LE_DISCOVERY_REPLY'), +(-1000521,'Have ye gone mad? You expect me to leave behind a drum without first beatin\' on it? Not this son of Ironforge! No sir!',0,0,0,0,'letoll SAY_LE_NO_LEAVE'), +(-1000522,'This reminds me of that one time where you made us search Silithus for evidence of sand gnomes.',0,0,0,0,'letoll SAY_LE_NO_LEAVE_REPLY1'), +(-1000523,'Or that time when you told us that you\'d discovered the cure for the plague of the 20th century. What is that even? 20th century?',0,0,0,0,'letoll SAY_LE_NO_LEAVE_REPLY2'), +(-1000524,'I don\'t think it can top the one time where he told us that he\'d heard that Artha\'s "cousin\'s" skeleton was frozen beneath a glacier in Winterspring. I\'ll never forgive you for that one, Letoll. I mean honestly... Artha\'s cousin?',0,0,0,0,'letoll SAY_LE_NO_LEAVE_REPLY3'), +(-1000525,'I dunno. It can\'t possibly beat the time he tried to convince us that we\'re all actually a figment of some being\'s imagination and that they only use us for their own personal amusement. That went over well during dinner with the family.',0,0,0,0,'letoll SAY_LE_NO_LEAVE_REPLY4'), +(-1000526,'Shut yer yaps! I\'m gonna bang on this drum and that\'s that!',0,0,0,0,'letoll SAY_LE_SHUT'), +(-1000527,'Say, do you guys hear that?',0,0,0,0,'letoll SAY_LE_REPLY_HEAR'), +(-1000528,'IN YOUR FACE! I told you there was somethin\' here!',0,0,0,0,'letoll SAY_LE_IN_YOUR_FACE'), +(-1000529,'Don\'t just stand there! Help him out!',0,0,0,0,'letoll SAY_LE_HELP_HIM'), +(-1000530,'%s picks up the drum.',0,2,0,0,'letoll EMOTE_LE_PICK_UP'), +(-1000531,'You\'ve been a tremendous help, $r! Let\'s get out of here before more of those things show up! I\'ll let Dwarfowitz know you did the job asked of ya\' admirably.',0,0,0,0,'letoll SAY_LE_THANKS'), + +(-1000532,'At your command, my liege...',0,0,0,0,'torloth TORLOTH_DIALOGUE1'), +(-1000533,'As you desire, Lord Illidan.',0,0,0,0,'torloth TORLOTH_DIALOGUE2'), +(-1000534,'Yes, Lord Illidan, I would sacrifice to you this magnificent physique. On this day you will fall - another victim of Torloth...',0,0,0,0,'torloth TORLOTH_DIALOGUE3'), +(-1000535,'Destroy them, Torloth. Let lose their blood like a river upon this hallowed ground.',0,0,0,0,'lordillidan ILLIDAN_DIALOGUE'), +(-1000536,'What manner of fool dares stand before Illidan Stormrage? Soldiers, destroy these insects!',0,1,0,0,'lordillidan ILLIDAN_SUMMON1'), +(-1000537,'You are no challenge for the Crimson Sigil. Mind breakers, end this nonsense.',0,1,0,0,'lordillidan ILLIDAN_SUMMON2'), +(-1000538,'Run while you still can. The highlords come soon...',0,1,0,0,'lordillidan ILLIDAN_SUMMON3'), +(-1000539,'Torloth your master calls!',0,1,0,0,'lordillidan ILLIDAN_SUMMON4'), +(-1000540,'So you have defeated the Crimson Sigil. You now seek to challenge my rule? Not even Arthas could defeat me, yet you dare to even harbor such thoughts? Then I say to you, come! Come $N! The Black Temple awaits...',0,1,0,0,'lordillidan EVENT_COMPLETED'), + +(-1000541,'%s jumps into the moonwell and goes underwater...',0,2,0,0,'kitten EMOTE_SAB_JUMP'), +(-1000542,'%s follows $n obediertly.',0,2,0,0,'kitten EMOTE_SAB_FOLLOW'), + +(-1000543,'Why have you come here, outsider? You will only find pain! Our fate will be yours...',0,0,0,25,'restless app SAY_RAND_1'), +(-1000544,'It was ... terrible... the demon...',0,0,0,25,'restless app SAY_RAND_2'), +(-1000545,'The darkness... the corruption... they came too quickly for anyone to know...',0,0,0,25,'restless app SAY_RAND_3'), +(-1000546,'The darkness will consume all... all the living...',0,0,0,25,'restless app SAY_RAND_4'), +(-1000547,'It is too late for us, living one. Take yourself and your friend away from here before you both are... claimed...',0,0,0,25,'restless app SAY_RAND_5'), +(-1000548,'It is too late for Jarl... its hold is too strong...',0,0,0,25,'restless app SAY_RAND_6'), +(-1000549,'Go away, whoever you are! Witch Hill is mine... mine!',0,0,0,25,'restless app SAY_RAND_7'), +(-1000550,'The manor... someone else... will soon be consumed...',0,0,0,25,'restless app SAY_RAND_8'), + +(-1000551,'The %s is angered by your request and attacks!',0,2,0,0,'woodlands EMOTE_AGGRO'), +(-1000552,'Breaking off a piece of its bark, the %s hands it to you before departing.',0,2,0,0,'woodlands EMOTE_CREATE'), + +(-1000553,'Be ready, $N. I hear the council returning. Prepare to ambush!',0,0,0,0,'deathstalker_faerleia SAY_START'), +(-1000554,'Well done. A blow to Arugal no doubt!',0,0,0,0,'deathstalker_faerleia SAY_END'), + +(-1000555,'Back... to work...',0,0,0,0,'exhausted vrykul SAY_RAND_WORK1'), +(-1000556,'You treat us worse than animals!',0,0,0,0,'exhausted vrykul SAY_RAND_WORK2'), +(-1000557,'We will have revenge...some day.',0,0,0,0,'exhausted vrykul SAY_RAND_WORK3'), +(-1000558,'Curse you! You will not treat me like a beast!',0,0,0,0,'exhausted vrykul SAY_RAND_ATTACK1'), +(-1000559,'I\'d rather die fighting than live like a slave.',0,0,0,0,'exhausted vrykul SAY_RAND_ATTACK2'), +(-1000560,'Enough! I will teach you some manners, wench!',0,0,0,0,'exhausted vrykul SAY_RAND_ATTACK3'), + +(-1000561,'My wounds are grave. Forgive my slow pace but my injuries won\'t allow me to walk any faster.',0,0,0,0,'SAY_CORPORAL_KEESHAN_1'), +(-1000562,'Ah, fresh air, at last! I need a moment to rest.',0,0,0,0,'SAY_CORPORAL_KEESHAN_2'), +(-1000563,'The Blackrock infestation is thick in these parts. I will do my best to keep the pace. Let\'s go!',0,0,0,0,'SAY_CORPORAL_KEESHAN_3'), +(-1000564,'Marshal Marris, sir. Corporal Keeshan of the 12th Sabre Regiment returned from battle and reporting for duty!',0,0,0,0,'SAY_CORPORAL_KEESHAN_4'), +(-1000565,'Brave adventurer, thank you for rescuing me! I am sure Marshal Marris will reward your kind deed.',0,0,0,0,'SAY_CORPORAL_KEESHAN_5'), + +(-1000566,'Stand back! Stand clear! The infernal will need to be given a wide berth!',0,0,0,0,'SAY_NIBY_1'), +(-1000567,'BOW DOWN TO THE ALMIGHTY! BOW DOWN BEFORE MY INFERNAL DESTRO... chicken?',0,0,0,0,'SAY_NIBY_2'), +(-1000568,'%s rolls on the floor laughing.',0,2,0,0,'EMOTE_IMPSY_1'), +(-1000569,'Niby, you\' re an idiot.',0,0,0,0,'SAY_IMPSY_1'), +(-1000570,'Silence, servant! Vengeance will be mine! Death to Stormwind! Death by chicken!',0,0,0,0,'SAY_NIBY_3'), + +(-1000571,'Help! I\'ve only one hand to defend myself with.',0,0,0,0,'SAY_MIRAN_1'), +(-1000572,'Feel the power of the Dark Iron Dwarves!',0,0,0,0,'SAY_DARK_IRON_DWARF'), +(-1000573,'Send them on! I\'m not afraid of some scrawny beasts!',0,0,0,0,'SAY_MIRAN_2'), +(-1000574,'Ah, here at last! It\'s going to feel so good to get rid of these barrels.',0,0,0,0,'SAY_MIRAN_3'), + +(-1000575,'Together we will fight our way out of here. Are you ready?',0,0,0,0,'Lurgglbr - SAY_START_1'), +(-1000576,'Then we leave.',0,0,0,0,'Lurgglbr - SAY_START_2'), +(-1000577,'This is far enough. I can make it on my own from here.',0,0,0,0,'Lurgglbr - SAY_END_1'), +(-1000578,'Thank you for helping me $r. Please tell the king I am back.',0,0,0,0,'Lurgglbr - SAY_END_2'), + +(-1000579,'There! Destroy him! The Cipher must be recovered!',0,0,0,25,'spirit hunter - SAY_VENERATUS_SPAWN'), + +(-1000580,'Sleep now, young one ...',0,0,0,0,'Raelorasz SAY_SLEEP'), +(-1000581,'A wonderful specimen.',0,0,0,0,'Raeloarsz SAY_SPECIMEN'), + +(-1000582,'Help! Please, You must help me!',0,0,0,0,'Galen - periodic say'), +(-1000583,'Let us leave this place.',0,0,0,0,'Galen - quest accepted'), +(-1000584,'Look out! The $c attacks!',0,0,0,0,'Galen - aggro 1'), +(-1000585,'Help! I\'m under attack!',0,0,0,0,'Galen - aggro 2'), +(-1000586,'Thank you $N. I will remember you always. You can find my strongbox in my camp, north of Stonard.',0,0,0,0,'Galen - quest complete'), +(-1000587,'%s whispers to $N the secret to opening his strongbox.',0,2,0,0,'Galen - emote whisper'), +(-1000588,'%s disappears into the swamp.',0,2,0,0,'Galen - emote disapper'), + +(-1000589,'Kroshius live? Kroshius crush!',0,1,0,0,'SAY_KROSHIUS_REVIVE'), + +(-1000590,'Woot!',0,0,0,0,'Captive Child SAY_THANKS_1'), +(-1000591,'I think those weird bird guys were going to eat us. Gross!',0,0,0,0,'Captive Child SAY_THANKS_2'), +(-1000592,'Yay! We\'re free!',0,0,0,0,'Captive Child SAY_THANKS_3'), +(-1000593,'Gross!',0,0,0,0,'Captive Child SAY_THANKS_4'), + +(-1000594,'At last... now I can rest.',0,0,0,0,'hero spirit SAY_BLESS_1'), +(-1000595,'I\'m so tired. Just let me rest for a moment.',0,0,0,0,'hero spirit SAY_BLESS_2'), +(-1000596,'I can\'t hear the screams anymore. Is this the end?',0,0,0,0,'hero spirit SAY_BLESS_3'), +(-1000597,'My nightmare, is it finally over?',0,0,0,0,'hero spirit SAY_BLESS_4'), +(-1000598,'It was awful... I dreamt I was fighting against my friends.',0,0,0,0,'hero spirit SAY_BLESS_5'), + +(-1000599,'It\'s a miracle! The beast skinned itself!',0,0,0,5,'nesingwary trapper SAY_PHRASE_1'), +(-1000600,'Jackpot!',0,0,0,5,'nesingwary trapper SAY_PHRASE_2'), +(-1000601,'This is the last one i need for that set of Nesingwary steak knives!',0,0,0,5,'nesingwary trapper SAY_PHRASE_3'), +(-1000602,'Silly beasts!',0,0,0,5,'nesingwary trapper SAY_PHRASE_4'), + +(-1000603,'Do not test me, scurvy dog! I\'m trained in the way of the Blood Knights!',0,0,0,0,'silvermoon harry SAY_AGGRO'), +(-1000604,'I\'ll pay! I\'ll pay! Eeeek! Please don\'t hurt me!',0,0,0,0,'silvermoon harry SAY_BEATEN'), + +(-1000605,'We wait until you ready.',0,0,0,0,'rainspeaker SAY_ACCEPT'), +(-1000606,'Home time!',0,0,0,0,'rainspeaker SAY_START'), +(-1000607,'Thanks!',0,0,0,0,'rainspeaker SAY_END_1'), +(-1000608,'Oh no! Some puppy-men followed!',0,0,0,0,'rainspeaker SAY_END_2'), +(-1000609,'Dumb big-tongue lover! You not friend of Frenzyheart no more. Frenzyheart will get you good.',0,1,0,0,'rainspeaker SAY_TRACKER'), + +(-1000610,'The mosswalker victim groans in pain.',0,2,0,0,'mosswalker victim EMOTE_PAIN'), + +(-1000611,'Maybe you make weather better too?',0,0,0,0,'mosswalker victim SAY_RESCUE_1'), +(-1000612,'We saved. You nice, dryskin.',0,0,0,0,'mosswalker victim SAY_RESCUE_2'), +(-1000613,'You save us! Yay for you!',0,0,0,0,'mosswalker victim SAY_RESCUE_3'), +(-1000614,'Thank you! You good!',0,0,0,0,'mosswalker victim SAY_RESCUE_4'), + +(-1000615,'Use my shinies...make weather good again...make undead things go away.',0,0,0,0,'mosswalker victim SAY_DIE_1'), +(-1000616,'We gave shinies to shrine... we not greedy... why this happen?',0,0,0,0,'mosswalker victim SAY_DIE_2'), +(-1000617,'I do something bad? I sorry....',0,0,0,0,'mosswalker victim SAY_DIE_3'), +(-1000618,'We not do anything... to them... I no understand.',0,0,0,0,'mosswalker victim SAY_DIE_4'), +(-1000619,'Thank...you.',0,0,0,0,'mosswalker victim SAY_DIE_5'), +(-1000620,'Please take... my shinies. All done...',0,0,0,0,'mosswalker victim SAY_DIE_6'), + +(-1000621,'All systems on-line. Prepare yourself, we leave shortly.',0,0,0,0,'maxx SAY_START'), +(-1000622,'Be careful in there and come back in one piece!',0,0,0,0,'maxx SAY_ALLEY_FAREWELL'), +(-1000623,'Proceed.',0,0,0,0,'maxx SAY_CONTINUE'), +(-1000624,'You\'re back! Were you able to get all of the machines?',0,0,0,0,'maxx SAY_ALLEY_FINISH'), + +(-1000625,'%s gathers the warp chaser\'s blood.',0,2,0,0,'zeppit EMOTE_GATHER_BLOOD'), + +(-1000626,'Intiating energy collection.',0,0,0,0,'depleted golem SAY_GOLEM_CHARGE'), +(-1000627,'Energy collection complete.',0,0,0,0,'depleted golem SAY_GOLEM_COMPLETE'), + +(-1000628,'%s feeds on the freshly-killed warp chaser.',0,2,0,0,'hungry ray EMOTE_FEED'), + +(-1000629,' Damsel in distress over here!',0,0,0,0,'isla starmane - SAY_ISLA_PERIODIC_1'), +(-1000630,'Hello? Help?',0,0,0,0,'isla starmane - SAY_ISLA_PERIODIC_2'), +(-1000631,'Don\'t leave me in here! Cause if you do I will find you!',0,0,0,0,'isla starmane - SAY_ISLA_PERIODIC_3'), +(-1000632,'Ok, let\'s get out of here!',0,0,0,0,'isla starmane - SAY_ISLA_START'), +(-1000633,'You sure you\'re ready? Take a moment.',0,0,0,0,'isla starmane - SAY_ISLA_WAITING'), +(-1000634,'Alright, let\'s do this!',0,0,0,0,'isla starmane - SAY_ISLA_LEAVE_BUILDING'), + +(-1000635,'So then we too are cursed?',0,0,0,0,'ancient vrykul SAY_VRYKUL_CURSED'), +(-1000636,'%s points to the infant.',0,2,0,0,'ancient vrykul EMOTE_VRYKUL_POINT'), +(-1000637,'%s sobs.',0,2,0,0,'ancient vrykul EMOTE_VRYKUL_SOB'), +(-1000638,'The gods have forsaken us! We must dispose of it before Ymiron is notified!',0,0,0,0,'ancient vrykul SAY_VRYKUL_DISPOSE'), +(-1000639,'NO! You cannot! I beg of you! It is our child!',0,0,0,0,'ancient vrykul SAY_VRYKUL_BEG'), +(-1000640,'Then what are we to do, wife? The others cannot find out. Should they learn of this aberration, we will all be executed.',0,0,0,0,'ancient vrykul SAY_VRYKUL_WHAT'), +(-1000641,'I... I will hide it. I will hide it until I find it a home, far away from here...',0,0,0,0,'ancient vrykul SAY_VRYKUL_HIDE'), + +(-1000642,'It\'s a female.',0,5,0,0,'leopard icepaw SAY_ITS_FEMALE'), +(-1000643,'It\'s an angry male!',0,5,0,0,'leopard icepaw SAY_ITS_MALE'), + +(-1000644,'Ouch! That\'s it, I quit the target business!',0,0,0,0,'SAY_LUCKY_HIT_1'), +(-1000645,'My ear! You grazed my ear!',0,0,0,0,'SAY_LUCKY_HIT_2'), +(-1000646,'Not the \'stache! Now I\'m asymmetrical!',0,0,0,5,'SAY_LUCKY_HIT_3'), +(-1000647,'Good shot!',0,0,0,4,'SAY_LUCKY_HIT_APPLE'), +(-1000648,'Stop whining. You\'ve still got your luck.',0,0,0,0,'SAY_DROSTAN_GOT_LUCKY_1'), +(-1000649,'Bah, it\'s an improvement.',0,0,0,11,'SAY_DROSTAN_GOT_LUCKY_2'), +(-1000650,'Calm down lad, it\'s just a birdshot!',0,0,0,0,'SAY_DROSTAN_HIT_BIRD_1'), +(-1000651,'The only thing hurt is your pride, lad! Buck up!',0,0,0,0,'SAY_DROSTAN_HIT_BIRD_2'), + +(-1000652,'Me so hungry! YUM!',0,0,0,71,'dragonmaw peon SAY_PEON_1'), +(-1000653,'Hey... me not feel so good.',0,0,0,0,'dragonmaw peon SAY_PEON_2'), +(-1000654,'You is bad orc... baaad... or... argh!',0,0,0,0,'dragonmaw peon SAY_PEON_3'), +(-1000655,'Time for eating!?',0,0,0,71,'dragonmaw peon SAY_PEON_4'), +(-1000656,'It put the mutton in the stomach!',0,0,0,71,'dragonmaw peon SAY_PEON_5'), + +(-1000657,'Let\'s get the hell out of here.',0,0,0,5,'helice SAY_HELICE_ACCEPT'), +(-1000658,'Listen up, Venture Company goons! Rule #1: Never keep the prisoner near the explosives.',0,0,0,25,'helice SAY_HELICE_EXPLOSIVES_1'), +(-1000659,'Or THIS is what you get.',0,0,0,0,'helice SAY_HELICE_EXPLODE_1'), +(-1000660,'It\'s getting a little hot over here. Shall we move on?',0,0,0,11,'helice SAY_HELICE_MOVE_ON'), +(-1000661,'Oh, look, it\'s another cartload of explosives! Let\'s help them dispose of it.',0,0,0,25,'helice SAY_HELICE_EXPLOSIVES_2'), +(-1000662,'You really shouldn\'t play with this stuff. Someone could get hurt.',0,0,0,5,'helice SAY_HELICE_EXPLODE_2'), +(-1000663,'We made it! Thank you for getting me out of that hell hole. Tell Hemet to expect me!',0,0,0,4,'helice SAY_HELICE_COMPLETE'), + +(-1000664,'The Destructive Ward gains in power.',0,5,0,0,'destructive ward SAY_WARD_POWERUP'), +(-1000665,'The Destructive Ward is fully charged!',0,5,0,0,'destructive ward SAY_WARD_CHARGED'), + +(-1000666,'I can sense the SHADOW on your hearts. There can be no rest for the wicked!',0,1,0,0,'lethon SAY_LETHON_AGGRO'), +(-1000667,'Your wicked souls shall feed my power!',0,1,0,0,'lethon SAY_LETHON_SHADE'), + +(-1000668,'%s releases the last of its energies into the nearby runestone, successfully reactivating it.',0,2,0,0,'infused crystal SAY_DEFENSE_FINISH'), + +(-1000669,'We will locate the origin of the Nightmare through the fragments you collected, $N. From there, we will pull Eranikus through a rift in the Dream. Steel yourself, $C. We are inviting the embodiment of the Nightmare into our world.',0,0,0,0,'remulos SAY_REMULOS_INTRO_1'), +(-1000670,'To Nighthaven! Keep your army close, champion. ',0,0,0,0,'remulos SAY_REMULOS_INTRO_2'), +(-1000671,'The rift will be opened there, above the Lake Elun\'ara. Prepare yourself, $N. Eranikus entry into our world will be wrought with chaos and strife.',0,0,0,0,'remulos SAY_REMULOS_INTRO_3'), +(-1000672,'He will stop at nothing to get to Malfurion\'s physical manifistation. That must not happen... We must keep the beast occupied long enough for Tyrande to arrive.',0,0,0,0,'remulos SAY_REMULOS_INTRO_4'), +(-1000673,'Defend Nightaven, hero...',0,0,0,0,'remulos SAY_REMULOS_INTRO_5'), +(-1000674,'%s has entered our world',0,3,0,0,'eranikus EMOTE_SUMMON_ERANIKUS'), +(-1000675,'Pitful predictable mortals... You know not what you have done! The master\'s will fulfilled. The Moonglade shall be destroyed and Malfurion along with it!',0,1,0,0,'eranikus SAY_ERANIKUS_SPAWN'), +(-1000676,'Fiend! Face the might of Cenarius!',0,1,0,1,'remulos SAY_REMULOS_TAUNT_1'), +(-1000677,'%s lets loose a sinister laugh.',0,2,0,0,'eranikus EMOTE_ERANIKUS_LAUGH'), +(-1000678,'You are certanly not your father, insect. Should it interest me, I would crush you with but a swipe of my claws. Turn Shan\'do Stormrage over to me and your pitiful life will be spared along with the lives of your people.',0,1,0,0,'eranikus SAY_ERANIKUS_TAUNT_2'), +(-1000679,'Who is the predictable one, beast? Surely you did not think that we would summon you on top of Malfurion? Your redemption comes, Eranikus. You will be cleansed of this madness - this corruption.',0,1,0,1,'remulos SAY_REMULOS_TAUNT_3'), +(-1000680,'My redemption? You are bold, little one. My redemption comes by the will of my god.',0,1,0,0,'eranikus SAY_ERANIKUS_TAUNT_4'), +(-1000681,'%s roars furiously.',0,2,0,0,'eranikus EMOTE_ERANIKUS_ATTACK'), +(-1000682,'Hurry, $N! We must find protective cover!',0,0,0,0,'remulos SAY_REMULOS_DEFEND_1'), +(-1000683,'Please, champion, protect our people.',0,0,0,1,'remulos SAY_REMULOS_DEFEND_2'), +(-1000684,'Rise, servants of the Nightmare! Rise and destroy this world! Let there be no survivors...',0,1,0,0,'eranikus SAY_ERANIKUS_SHADOWS'), +(-1000685,'We will battle these fiends, together! Nighthaven\'s Defenders are also among us. They will fight to the death if asked. Now, quickly, we must drive these aberations back to the Nightmare. Destroy them all!',0,0,0,1,'remulos SAY_REMULOS_DEFEND_3'), +(-1000686,'Where is your savior? How long can you hold out against my attacks?',0,1,0,0,'eranikus SAY_ERANIKUS_ATTACK_1'), +(-1000687,'Defeated my minions? Then face me, mortals!',0,1,0,0,'eranikus SAY_ERANIKUS_ATTACK_2'), +(-1000688,'Remulos, look how easy they fall before me? You can stop this, fool. Turn the druid over to me and it will all be over...',0,1,0,0,'eranikus SAY_ERANIKUS_ATTACK_3'), +(-1000689,'Elune, hear my prayers. Grant us serenity! Watch over our fallen...',0,1,0,0,'tyrande SAY_TYRANDE_APPEAR'), +(-1000690,'Tend to the injuries of the wounded, sisters!',0,0,0,0,'tyrande SAY_TYRANDE_HEAL'), +(-1000691,'Seek absolution, Eranikus. All will be forgiven...',0,1,0,0,'tyrande SAY_TYRANDE_FORGIVEN_1'), +(-1000692,'You will be forgiven, Eranikus. Elune will always love you. Break free of the bonds that command you!',0,1,0,0,'tyrande SAY_TYRANDE_FORGIVEN_2'), +(-1000693,'The grasp of the Old Gods is unmoving. He is consumed by their dark thoughts... I... I... I cannot... cannot channel much longer... Elune aide me.',0,0,0,0,'tyrande SAY_TYRANDE_FORGIVEN_3'), +(-1000694,'IT BURNS! THE PAIN.. SEARING...',0,1,0,0,'eranikus SAY_ERANIKUS_DEFEAT_1'), +(-1000695,'WHY? Why did this happen to... to me? Where were you Tyrande? Where were you when I fell from the grace of Elune?',0,1,0,0,'eranikus SAY_ERANIKUS_DEFEAT_2'), +(-1000696,'I... I feel... I feel the touch of Elune upon my being once more... She smiles upon me... Yes... I...', 0,1,0,0,'eranikus SAY_ERANIKUS_DEFEAT_3'), +(-1000697,'%s is wholly consumed by the Light of Elune. Tranquility sets in over the Moonglade',0,2,0,0,'eranikus EMOTE_ERANIKUS_REDEEM'), +(-1000698,'%s falls to one knee.',0,2,0,0,'tyrande EMOTE_TYRANDE_KNEEL'), +(-1000699,'Praise be to Elune... Eranikus is redeemed.',0,1,0,0,'tyrande SAY_TYRANDE_REDEEMED'), +(-1000700,'For so long, I was lost... The Nightmare\'s corruption had consumed me... And now, you... all of you.. you have saved me. Released me from its grasp.',0,0,0,0,'eranikus SAY_REDEEMED_1'), +(-1000701,'But... Malfurion, Cenarius, Ysera... They still fight. They need me. I must return to the Dream at once.', 0,0,0,0,'eranikus SAY_REDEEMED_2'), +(-1000702,'My lady, I am unworthy of your prayer. Truly, you are an angel of light. Please, assist me in returning to the barrow den so that I may return to the Dream. I like Malfurion, also have a love awaiting me... I must return to her... to protect her...', 0,0,0,0,'eranikus SAY_REDEEMED_3'), +(-1000703,'And heroes... I hold that which you seek. May it once more see the evil dissolved. Remulos, see to it that our champion receives the shard of the Green Flight.',0,0,0,0,'eranikus SAY_REDEEMED_4'), +(-1000704,'It will be done, Eranikus. Be well, ancient one.',0,0,0,0,'remulos SAY_REMULOS_OUTRO_1'), +(-1000705,'Let us leave Nighthave, hero. Seek me out at the grove.',0,0,0,0,'remulos SAY_REMULOS_OUTRO_2'), +(-1000706,'Your world shall suffer an unmerciful end. The Nightmare comes for you!',0,0,0,0,'eranikus SAY_ERANIKUS_KILL'), + +(-1000707,'This blue light... It\'s strange. What do you think it means?',0,0,0,0,'Ranshalla SAY_ENTER_OWL_THICKET'), +(-1000708,'We\'ve found it!',0,0,0,0,'Ranshalla SAY_REACH_TORCH_1'), +(-1000709,'Please, light this while I am channeling',0,0,0,0,'Ranshalla SAY_REACH_TORCH_2'), +(-1000710,'This is the place. Let\'s light it.',0,0,0,0,'Ranshalla SAY_REACH_TORCH_3'), +(-1000711,'Let\'s find the next one.',0,0,0,0,'Ranshalla SAY_AFTER_TORCH_1'), +(-1000712,'We must continue on now.',0,0,0,0,'Ranshalla SAY_AFTER_TORCH_2'), +(-1000713,'It is time for the final step; we must activate the altar.',0,0,0,0,'Ranshalla SAY_REACH_ALTAR_1'), +(-1000714,'I will read the words carved into the stone, and you must find a way to light it.',0,0,0,0,'Ranshalla SAY_REACH_ALTAR_2'), +(-1000715,'The altar is glowing! We have done it!',0,0,0,0,'Ranshalla SAY_RANSHALLA_ALTAR_1'), +(-1000716,'What is happening? Look!',0,0,0,0,'Ranshalla SAY_RANSHALLA_ALTAR_2'), +(-1000717,'It has been many years...',0,0,0,0,'Priestess of Elune SAY_PRIESTESS_ALTAR_3'), +(-1000718,'Who has disturbed the altar of the goddess?',0,0,0,0,'Priestess of Elune SAY_PRIESTESS_ALTAR_4'), +(-1000719,'Please, priestesses, forgive us for our intrusion. We do not wish any harm here.',0,0,0,0,'Ranshalla SAY_RANSHALLA_ALTAR_5'), +(-1000720,'We only wish to know why the wildkin guard this area...',0,0,0,0,'Ranshalla SAY_RANSHALLA_ALTAR_6'), +(-1000721,'Enu thora\'serador. This is a sacred place.',0,0,0,0,'Priestess of Elune SAY_PRIESTESS_ALTAR_7'), +(-1000722,'We will show you...',0,0,0,0,'Priestess of Elune SAY_PRIESTESS_ALTAR_8'), +(-1000723,'Look above you; thara dormil dorah...',0,0,0,0,'Priestess of Elune SAY_PRIESTESS_ALTAR_9'), +(-1000724,'This gem once allowed direct communication with Elune, herself.',0,0,0,0,'Priestess of Elune SAY_PRIESTESS_ALTAR_10'), +(-1000725,'Through the gem, Elune channeled her infinite wisdom...',0,0,0,0,'Priestess of Elune SAY_PRIESTESS_ALTAR_11'), +(-1000726,'Realizing that the gem needed to be protected, we turned to the goddess herself.',0,0,0,0,'Priestess of Elune SAY_PRIESTESS_ALTAR_12'), +(-1000727,'Soon after, we began to have visions of a creature... A creature with the feathers of an owl, but the will and might of a bear...',0,0,0,0,'Priestess of Elune SAY_PRIESTESS_ALTAR_13'), +(-1000728,'It was on that day that the wildkin were given to us. Fierce guardians, the goddess assigned the wildkin to protect all of her sacred places.',0,0,0,0,'Priestess of Elune SAY_PRIESTESS_ALTAR_14'), +(-1000729,'Anu\'dorini Talah, Ru shallora enudoril.',0,0,0,0,'Voice of Elune SAY_VOICE_ALTAR_15'), +(-1000730,'But now, many years later, the wildkin have grown more feral, and without the guidance of the goddess, they are confused...',0,0,0,0,'Priestess of Elune SAY_PRIESTESS_ALTAR_16'), +(-1000731,'Without a purpose, they wander... But many find their way back to the sacred areas that they once were sworn to protect.',0,0,0,0,'Priestess of Elune SAY_PRIESTESS_ALTAR_17'), +(-1000732,'Wildkin are inherently magical; this power was bestowed upon them by the goddess.',0,0,0,0,'Priestess of Elune SAY_PRIESTESS_ALTAR_18'), +(-1000733,'Know that wherever you might find them in the world, they are protecting something of importance, as they were entrusted to do so long ago.',0,0,0,0,'Priestess of Elune SAY_PRIESTESS_ALTAR_19'), +(-1000734,'Please, remember what we have shown you...',0,0,0,0,'Priestess of Elune SAY_PRIESTESS_ALTAR_20'), +(-1000735,'Farewell.',0,0,0,0,'Priestess of Elune SAY_PRIESTESS_ALTAR_21'), +(-1000736,'Thank you for you help, $n. I wish you well in your adventures.',0,0,0,0,'Ranshalla SAY_QUEST_END_1'), +(-1000737,'I want to stay here and reflect on what we have seen. Please see Erelas and tell him what we have learned.',0,0,0,0,'Ranshalla SAY_QUEST_END_2'), +(-1000738,'%s begins chanting a strange spell...',0,2,0,0,'Ranshalla EMOTE_CHANT_SPELL'), +(-1000739,'Remember, I need your help to properly channel. I will ask you to aid me several times in our path, so please be ready.',0,0,0,0,'Ranshalla SAY_QUEST_START'), + +(-1000740,'We must act quickly or shall be lost!',0,0,0,1,'SAY_ANACHRONOS_INTRO_1'), +(-1000741,'My forces cannot overcome the Qiraji defenses. We will not be able to get close enough to place our precious barrier, dragon.',0,0,0,0,'SAY_FANDRAL_INTRO_2'), +(-1000742,'There is a way...',0,0,0,22,'SAY_MERITHRA_INTRO_3'), +(-1000743,'%s nods knowingly.',0,2,0,0,'EMOTE_ARYGOS_NOD'), +(-1000744,'Aye, Fandral, remember these words: Let not your grief guide your faith. These thoughts you hold... dark places you go, night elf.Absolution cannot be had through misguided vengeance.',0,0,0,1,'SAY_CAELESTRASZ_INTRO_4'), +(-1000745,'%s glances at her compatriots.',0,2,0,0,'EMOTE_MERITHRA_GLANCE'), +(-1000746,'We will push him back, Anachronos. This is wow. Uphold your end of this task. Let not your hands falter as you seal our fates behind the barrier.',0,0,0,1,'SAY_MERITHRA_INTRO_5'), +(-1000747,'Succumb to the endless dream, little ones. Let it comsume you!',0,1,0,22,'SAY_MERITHRA_ATTACK_1'), +(-1000748,'Anachronos, the diversion will give you an the young druid time enough to seal the gate. Do not falter. Now, let us see how they deal with chaotic magic.',0,0,0,1,'SAY_ARYGOS_ATTACK_2'), +(-1000749,'Let them feelt the wrath of the blue flight! May Malygos protect me!',0,1,0,22,'SAY_ARYGOS_ATTACK_3'), +(-1000750,'Do not forget sacrifices made on this day, night elf. We have all suffered immensely at the hands of these beasts.',0,0,0,1,'SAY_CAELESTRASZ_ATTACK_4'), +(-1000751,'Alexstrasza, give me the resolve to drive your enemies back.',0,1,0,22,'SAY_CAELESTRASZ_ATTACK_5'), +(-1000752,'NOW,STAGHELM! WE GO NOW! Prepare your magic!',0,0,0,22,'SAY_ANACHRONOS_SEAL_1'), +(-1000753,'It is done, dragon. Lead the way!',0,0,0,25,'SAY_FANDRAL_SEAL_2'), +(-1000754,'Stay close.',0,0,0,0,'SAY_ANACHRONOS_SEAL_3'), +(-1000755,'The sands of time will halt, but only for a moment! I will now conjure the barrier.',0,0,0,0,'SAY_ANACHRONOS_SEAL_4'), +(-1000756,'FINISH THE SPELL, STAGHELM! I CANNOT HOLD THE GLYPHS OF WARDING IN PLACE MUCH LONGER! CALL FORTH THE ROOTS!', 0,0,0,0,'SAY_ANACHRONOS_SEAL_5'), +(-1000757,'Ancient ones guide my hand... Wake from your slumber! WAKE AND SEAL THIS CURSED PLACE!',0,0,0,0, 'SAY_FANDRAL_SEAL_6'), +(-1000758,'%s falls to one knee - exhausted.',0,2,0,0,'EMOTE_FANDRAL_EXHAUSTED'), +(-1000759,'It... It is over, Lord Staghelm. We are victorious. Albeit the cost for this victory was great.',0,0,0,1,'SAY_ANACHRONOS_EPILOGUE_1'), +(-1000760,'There is but one duty that remains…',0,0,0,1,'SAY_ANACHRONOS_EPILOGUE_2'), +(-1000761,'Before I leave this place, I make one final offering for you, Lord Staghelm. Should a time arise in which you must gain entry to this accursed fortress, use the scepter of the shifting sands on the sacred gong. The magic holding the barrier together will dissipate an the horrors of the Ahn\'Qiraj will be unleashed upon the world once more.',0,0,0,1,'SAY_ANACHRONOS_EPILOGUE_3'), +(-1000762,'%s hands the Scepter of the Shifting Sands to $N.',0,2,0,0,'EMOTE_ANACHRONOS_SCEPTER'), +(-1000763,'After the savagery that my people have witnessed and felt, you expect me to accept another burden, dragon? Surely you are mad.',0,0,0,1,'SAY_FANDRAL_EPILOGUE_4'), +(-1000764,'I want nothing to do with Silithus, the Qiraji and least of all, any damed dragons!',0,0,0,1,'SAY_FANDRAL_EPILOGUE_5'), +(-1000765,'%s hurls the Scepter of the Shifting Sands into the barrier, shattering it.',0,2,0,0,'EMOTE_FANDRAL_SHATTER'), +(-1000766,'Lord Staghelm, where are you going? You would shatter our bond for the sake of pride?',0,0,0,1,'SAY_ANACHRONOS_EPILOGUE_6'), +(-1000767,'My son\'s soul will find no comfort in this hollow victory, dragon! I will have him back. Though it takes a millenia. I WILL have my son back!',0,0,0,1,'SAY_FANDRAL_EPILOGUE_7'), +(-1000768,'%s shakes his head in disappointment.',0,2,0,25,'EMOTE_ANACHRONOS_DISPPOINTED'), +(-1000769,'%s kneels down to pickup the fragments of the shattered scepter.',0,2,0,0,'EMOTE_ANACHRONOS_PICKUP'), +(-1000770,'And now you know all that there is to know, mortal…',0,0,0,0,'SAY_ANACHRONOS_EPILOGUE_8'), + +(-1000771,'Let\'s go $N!',0,0,0,0,'Feero Ironhand SAY_QUEST_START'), +(-1000772,'It looks like we\'re in trouble. Look lively, here they come!',0,0,0,0,'Feero Ironhand SAY_FIRST_AMBUSH_START'), +(-1000773,'Assassins from that cult you found... Let\'s get moving before someone else finds us out here.',0,0,0,0,'Feero Ironhand SAY_FIRST_AMBUSH_END'), +(-1000774,'Hold! I sense an evil presence... Undead!',0,0,0,0,'Feero Ironhand SAY_SECOND_AMBUSH_START'), +(-1000775,'A $C! Slaying him would please the master. Attack!',0,0,0,0,'Forsaken Scout SAY_SCOUT_SECOND_AMBUSH'), +(-1000776,'They\'re coming out of the woodwork today. Let\'s keep moving or we may find more things that want me dead.',0,0,0,0,'Feero Ironhand SAY_SECOND_AMBUSH_END'), +(-1000777,'These three again?',0,0,0,0,'Feero Ironhand SAY_FINAL_AMBUSH_START'), +(-1000778,'Not quite so sure of yourself without the Purifier, hm?',0,0,0,0,'Balizar the Umbrage SAY_BALIZAR_FINAL_AMBUSH'), +(-1000779,'I\'ll finish you off for good this time!',0,0,0,0,'Feero Ironhand SAY_FINAL_AMBUSH_ATTACK'), +(-1000780,'Well done! I should be fine on my own from here. Remember to talk to Delgren when you return to Maestra\'s Post in Ashenvale.',0,0,0,0,'Feero Ironhand SAY_QUEST_END'), + +(-1000781,'I knew Lurielle would send help! Thank you, friend, and give Lurielle my thanks as well!',0,0,0,0,'Chill Nymph SAY_FREE_1'), +(-1000782,'Where am I? What happend to me? You... you freed me?',0,0,0,0,'Chill Nymph SAY_FREE_2'), +(-1000783,'Thank you. I thought I would die without seeing my sisters again!',0,0,0,0,'Chill Nymph SAY_FREE_3'), + +(-1000784,'Thanks $N. Now let\'s get out of here!',0,0,0,0,'melizza SAY_MELIZZA_START'), +(-1000785,'We made it! Thanks again! I\'m going to run ahead!',0,0,0,0,'melizza SAY_MELIZZA_FINISH'), +(-1000786,'Hey Hornizz! I\'m back! And there are some people behind me who helped me out of a jam.',0,0,0,1,'melizza SAY_MELIZZA_1'), +(-1000787,'We\'re going to have to scratch the Maraudines off our list. Too hard to work with...',0,0,0,1,'melizza SAY_MELIZZA_2'), +(-1000788,'Well, I\'m off to the Gelkis. They\'re not as dumb as the Maraudines, but they\'re more reasonable.',0,0,0,3,'melizza SAY_MELIZZA_3'), + +(-1000789,'Well, now or never I suppose. Remember, once we get to the road safety, return to Terenthis to let him know we escaped.',0,0,0,0,'volcor SAY_START'), +(-1000790,'We made it, My friend. Remember to find Terenthis and let him know we\'re safe. Thank you again.',0,0,0,0,'volcor SAY_END'), +(-1000791,'Here they come.',0,0,0,0,'volcor SAY_FIRST_AMBUSH'), +(-1000792,'We can overcome these foul creatures.',0,0,0,0,'volcor SAY_AGGRO_1'), +(-1000793,'We shall earn our deaths at the very least!',0,0,0,0,'volcor SAY_AGGRO_2'), +(-1000794,'Don\'t give up! Fight, to the death!',0,0,0,0,'volcor SAY_AGGRO_3'), + +(-1000795,'OK boss, I get back to tree hitting.',0,0,0,0,'lazy peon SAY_AWAKE_1'), +(-1000796,'Sleepy... so sleepy...',0,0,0,0,'lazy peon SAY_AWAKE_2'), + +(-1000797,'%s squawks and heads toward Veil Shalas. Hurry and follow!',0,2,0,0,'skywing SAY_SKYWING_START'), +(-1000798,'%s pauses briefly before the tree and then heads inside.',0,2,0,0,'skywing SAY_SKYWING_TREE_DOWN'), +(-1000799,'%s seems to be looking for something. He wants you to follow.',0,2,0,0,'skywing SAY_SKYWING_TREE_UP'), +(-1000800,'%s flies to the platform below! You\'d better jump if you want to keep up. Hurry!',0,2,0,0,'skywing SAY_SKYWING_JUMP'), +(-1000801,'%s bellows a loud squawk!',0,2,0,0,'skywing SAY_SKYWING_SUMMON'), +(-1000802,'Free at last from that horrible curse! Thank you! Please send word to Rilak the Redeemed that I am okay. My mission lies in Skettis. Terokk must be defeated!',0,0,0,0,'skywing SAY_SKYWING_END'), + +(-1000803,'You do not fight alone, %n! Together, we will banish this spawn of hellfire!',0,1,0,0,'Oronok SAY_ORONOK_TOGETHER'), +(-1000804,'We will fight when you are ready.',0,0,0,0, 'Oronok SAY_ORONOK_READY'), +(-1000805,'We will set the elements free of your grasp by force!',0,1,0,0,'Oronok SAY_ORONOK_ELEMENTS'), +(-1000806,'What say the elements, Torlok? I only hear silence.',0,0,0,1,'Oronok SAY_ORONOK_EPILOGUE_1'), +(-1000807,'I hear what you hear, brother. Look behind you...',0,0,0,1,'Torlok SAY_TORLOK_EPILOGUE_2'), +(-1000808,'They are redeemed! Then we have won?',0,0,0,1,'Oronok SAY_ORONOK_EPILOGUE_3'), +(-1000809,'It is now as it should be, shaman. You have done well.',0,0,0,0,'Spirit of Earth SAY_EARTH_EPILOGUE_4'), +(-1000810,'Yes... Well enough for the elements that are here, but the cipher is known to another... The spirits of fire are in turmoil... If this force is not stopped, the world where these mortals came from will cease.',0,0,0,0,'Spirit of Fire SAY_FIRE_EPILOGUE_5'), +(-1000811,'Farewell, mortals... The earthmender knows what fire feels...',0,0,0,0, 'Spirit of Earth SAY_EARTH_EPILOGUE_6'), +(-1000812,'We leave, Torlok. I have only one request...',0,0,0,1,'Oronok SAY_ORONOK_EPILOGUE_7'), +(-1000813,'The Torn-heart men give their weapons to Earthmender Torlok.',0,2,0,0,'Torlok EMOTE_GIVE_WEAPONS'), +(-1000814,'Give these to the heroes that made this possible.',0,0,0,1,'Oronok SAY_ORONOK_EPILOGUE_8'), + +(-1000815,'Be healed!',0,1,0,0,'Eris Havenfire SAY_PHASE_HEAL'), +(-1000816,'We are saved! The peasants have escaped the Scourge!',0,1,0,0,'Eris Havenfire SAY_EVENT_END'), +(-1000817,'I have failed once more...',0,1,0,0,'Eris Havenfire SAY_EVENT_FAIL_1'), +(-1000818,'I now return to whence I came, only to find myself here once more to relive the same epic tragedy.',0,0,0,0,'Eris Havenfire SAY_EVENT_FAIL_2'), +(-1000819,'The Scourge are upon us! Run! Run for your lives!',0,1,0,0,'Peasant SAY_PEASANT_APPEAR_1'), +(-1000820,'Please help us! The Prince has gone mad!',0,1,0,0,'Peasant SAY_PEASANT_APPEAR_2'), +(-1000821,'Seek sanctuary in Hearthglen! It is our only hope!',0,1,0,0,'Peasant SAY_PEASANT_APPEAR_3'), + +(-1000822,'The signal has been sent. He should be arriving shortly.',0,0,0,1,'squire rowe SAY_SIGNAL_SENT'), +(-1000823,'Yawww!',0,0,0,35,'reginald windsor SAY_DISMOUNT'), +(-1000824,'I knew you would come, $N. It is good to see you again, friend.',0,0,0,1,'reginald windsor SAY_WELCOME'), + +(-1000825,'On guard, friend. The lady dragon will not give in without a fight.',0,0,0,1,'reginald windsor SAY_QUEST_ACCEPT'), +(-1000826,'As was fated a lifetime ago in Karazhan, monster - I come - and with me I bring justice.',0,6,0,22,'reginald windsor SAY_GET_READY'), +(-1000827,'Seize him! Seize the worthless criminal and his allies!',0,6,0,0,'prestor SAY_GONNA_DIE'), +(-1000828,'Reginald, you know that I cannot let you pass.',0,0,0,1,'jonathan SAY_DIALOG_1'), +(-1000829,'You must do what you think is right, Marcus. We served together under Turalyon. He made us both the men that we are today. Did he err with me? Do you truly believe my intent is to cause harm to our alliance? Would I shame our heroes?',0,0,0,1,'reginald windsor SAY_DIALOG_2'), +(-1000830,'Holding me here is not the right decision, Marcus.',0,0,0,1,'reginald windsor SAY_DIALOG_3'), +(-1000831,'%s appears lost in contemplation.',0,2,0,0,'jonathan EMOTE_CONTEMPLATION'), +(-1000832,'I am ashamed, old friend. I know not what I do anymore. It is not you that would dare bring shame to the heroes of legend - it is I. It is I and the rest of these corrupt politicians. They fill our lives with empty promises, unending lies.',0,0,0,1,'jonathan SAY_DIALOG_4'), +(-1000833,'We shame our ancestors. We shame those lost to us... forgive me, Reginald.',0,0,0,1,'jonathan SAY_DIALOG_5'), +(-1000834,'Dear friend, you honor them with your vigilant watch. You are steadfast in your allegiance. I do not doubt for a moment that you would not give as great a sacrifice for your people as any of the heroes you stand under.',0,0,0,1,'reginald windsor SAY_DIALOG_6'), +(-1000835,'Now, it is time to bring her reign to an end, Marcus. Stand down, friend.',0,0,0,1,'reginald windsor SAY_DIALOG_7'), +(-1000836,'Stand down! Can you not see that heroes walk among us?',0,0,0,5,'jonathan SAY_DIALOG_8'), +(-1000837,'Move aside! Let them pass!',0,0,0,5,'jonathan SAY_DIALOG_9'), +(-1000838,'Reginald Windsor is not to be harmed! He shall pass through untouched!',0,1,0,22,'jonathan SAY_DIALOG_10'), +(-1000839,'Go, Reginald. May the light guide your hand.',0,0,0,1,'jonathan SAY_DIALOG_11'), +(-1000840,'Thank you, old friend. You have done the right thing.',0,0,0,1,'reginald windsor SAY_DIALOG_12'), +(-1000841,'Follow me, friends. To Stormwind Keep!',0,0,0,0,'reginald windsor SAY_DIALOG_13'), +(-1000842,'Light be with you, sir.',0,0,0,66,'guard SAY_1'), +(-1000843,'We are but dirt beneath your feet, sir.',0,0,0,66,'guard SAY_2'), +(-1000844,'...nerves of thorium.',0,0,0,66,'guard SAY_3'), +(-1000845,'Make way!',0,0,0,66,'guard SAY_4'), +(-1000846,'A living legend...',0,0,0,66,'guard SAY_5'), +(-1000847,'A moment I shall remember for always.',0,0,0,66,'guard SAY_6'), +(-1000848,'You are an inspiration to us all, sir.',0,0,0,66,'guard SAY_7'), +(-1000849,'Be brave, friends. The reptile will thrash wildly. It is an act of desperation. When you are ready, give me the word.',0,0,0,25,'reginald windsor SAY_BEFORE_KEEP'), +(-1000850,'Onward!',0,0,0,5,'reginald windsor SAY_GO_TO_KEEP'), +(-1000851,'Majesty, run while you still can. She is not what you think her to be...',0,0,0,1,'reginald windsor SAY_IN_KEEP_1'), +(-1000852,'To the safe hall, your majesty.',0,0,0,1,'bolvar SAY_IN_KEEP_2'), +(-1000853,'The masquerade is over, Lady Prestor. Or should I call you by your true name... Onyxia...',0,0,0,25,'reginald windsor SAY_IN_KEEP_3'), +(-1000854,'%s laughs.',0,2,0,11,'prestor EMOTE_IN_KEEP_LAUGH'), +(-1000855,'You will be incarcerated and tried for treason, Windsor. I shall watch with glee as they hand down a guilty verdict and sentence you to death by hanging...',0,0,0,1,'prestor SAY_IN_KEEP_4'), +(-1000856,'And as your limp body dangles from the rafters, I shall take pleasure in knowing that a mad man has been put to death. After all, what proof do you have? Did you expect to come in here and point your fingers at royalty and leave unscathed?',0,0,0,6,'prestor SAY_IN_KEEP_5'), +(-1000857,'You will not escape your fate, Onyxia. It has been prophesied - a vision resonating from the great halls of Karazhan. It ends now...',0,0,0,1,'reginald windsor SAY_IN_KEEP_6'), +(-1000858,'%s reaches into his pack and pulls out the encoded tablets...',0,2,0,0,'reginald windsor EMOTE_IN_KEEP_REACH'), +(-1000859,'The Dark Irons thought these tablets to be encoded. This is not any form of coding, it is the tongue of ancient dragon.',0,0,0,1,'reginald windsor SAY_IN_KEEP_7'), +(-1000860,'Listen, dragon. Let the truth resonate throughout these halls.',0,0,0,1,'reginald windsor SAY_IN_KEEP_8'), +(-1000861,'%s reads from the tablets. Unknown, unheard sounds flow through your consciousness',0,2,0,0,'reginald windsor EMOTE_IN_KEEP_READ'), +(-1000862,'%s gasps.',0,2,0,0,'bolvar EMOTE_IN_KEEP_GASP'), +(-1000863,'Curious... Windsor, in this vision, did you survive? I only ask because one thing that I can and will assure is your death. Here and now.',0,0,0,1,'onyxia SAY_IN_KEEP_9'), +(-1000864,'Dragon filth! Guards! Guards! Seize this monster!',0,1,0,22,'bolvar SAY_IN_KEEP_1'), +(-1000865,'Yesss... Guards, come to your lord\'s aid!',0,0,0,1,'onyxia SAY_IN_KEEP_10'), +(-1000866,'DO NOT LET HER ESCAPE!',0,0,0,1,'reginald windsor SAY_IN_KEEP_11'), +(-1000867,'Was this fabled, Windsor? If it was death that you came for then the prophecy has been fulfilled. May your consciousness rot in the Twisting Nether. Finish the rest of these meddlesome insects, children. Bolvar, you have been a pleasureable puppet.',0,0,0,0,'onyxia SAY_IN_KEEP_12'), +(-1000868,'You have failed him, mortalsss... Farewell!',0,1,0,0,'onyxia SAY_IN_KEEP_12'), +(-1000869,'Reginald... I... I am sorry.',0,0,0,0,'bolvar SAY_IN_KEEP_13'), +(-1000870,'Bol... Bolvar... the medallion... use...',0,0,0,0,'reginald windsor SAY_IN_KEEP_14'), +(-1000871,'%s dies.',0,2,0,0,'reginald windsor EMOTE_IN_KEEP_DIE'), +(-1000872,'%s hisses',0,2,0,0,'reginald windsor EMOTE_GUARD_TRANSFORM'), + +(-1000873,'I know the way, insect. There is no need to prod me as if I were cattle.',0,0,0,1,'grark SAY_START'), +(-1000874,'Surely you do not think that you will get away with this incursion. They will come for me and you shall pay for your insolence.',0,0,0,1,'grark SAY_PAY'), +(-1000875,'RUN THEM THROUGH BROTHERS!',0,0,0,5,'grark SAY_FIRST_AMBUSH_START'), +(-1000876,'I doubt you will be so lucky the next time you encounter my brethren.',0,0,0,1,'grark SAY_FIRST_AMBUSH_END'), +(-1000877,'They come for you, fool!',0,0,0,5,'grark SAY_SEC_AMBUSH_START'), +(-1000878,'What do you think you accomplish from this, fool? Even now, the Blackrock armies make preparations to destroy your world.',0,0,0,1,'grark SAY_SEC_AMBUSH_END'), +(-1000879,'On darkest wing they fly. Prepare to meet your end!',0,0,0,5,'grark SAY_THIRD_AMBUSH_START'), +(-1000880,'The worst is yet to come!',0,0,0,1,'grark SAY_THIRD_AMBUSH_END'), +(-1000881,'%s laughs.',0,2,0,11,'grark EMOTE_LAUGH'), +(-1000882,'Time to make your final stand, Insect.',0,0,0,0,'grark SAY_LAST_STAND'), +(-1000883,'Kneel, Grark',0,0,0,1,'lexlort SAY_LEXLORT_1'), +(-1000884,'Grark Lorkrub, you have been charged and found guilty of treason against Horde. How you plead is unimportant. High Executioner Nuzrak, step forward.',0,0,0,1,'lexlort SAY_LEXLORT_2'), +(-1000885,'%s raises his massive axe over Grark.',0,2,0,27,'nuzark EMOTE_RAISE_AXE'), +(-1000886,'%s raises his hand and then lowers it.',0,2,0,0,'lexlort EMOTE_LOWER_HAND'), +(-1000887,'End him...',0,0,0,0,'lexlort SAY_LEXLORT_3'), +(-1000888,'You, soldier, report back to Kargath at once!',0,0,0,1,'lexlort SAY_LEXLORT_4'), +(-1000889,'%s submits.',0,2,0,0,'grark EMOTE_SUBMIT'), +(-1000890,'You have come to play? Then let us play!',0,0,0,0,'grark SAY_AGGRO'), + +(-1000891,'Let\'s do this... Just keep me covered and I\'ll deliver the package.',0,0,0,0,'demolitionist SAY_INTRO'), +(-1000892,'I\'m under attack! I repeat, I am under attack!',0,0,0,0,'demolitionist SAY_ATTACK_1'), +(-1000893,'I need to find a new line of work.',0,0,0,0,'demolitionist SAY_ATTACK_2'), +(-1000894,'By the second sun of K\'aresh, look at this place! I can only imagine what Salhadaar is planning. Come on, let\'s keep going.',0,0,0,1,'demolitionist SAY_STAGING_GROUNDS'), +(-1000895,'With this much void waste and run off, a toxic void horror can\'t be too far behind.',0,0,0,0,'demolitionist SAY_TOXIC_HORROR'), +(-1000896,'Look there, fleshling! Salhadaar\'s conduits! He\'s keeping well fed...',0,0,0,1,'demolitionist SAY_SALHADAAR'), +(-1000897,'Alright, keep me protected while I plant this disruptor. This shouldn\'t take very long...',0,0,0,0,'demolitionist SAY_DISRUPTOR'), +(-1000898,'Protect the conduit! Stop the intruders!',0,0,0,0,'nexus stalkers SAY_PROTECT'), +(-1000899,'Done! Back up! Back up!',0,0,0,0,'demolitionist SAY_FINISH_1'), +(-1000900,'Looks like my work here is done. Report to the holo-image of Ameer over at the transporter.',0,0,0,1,'demolitionist SAY_FINISH_2'), + +(-1000901,'Thanks, friend. Will you help me get out of here?',0,0,0,1,'vanguard SAY_VANGUARD_INTRO'), +(-1000902,'We\'re not too far from the Protectorate Watch Post, $N. This way!',0,0,0,1,'vanguard SAY_VANGUARD_START'), +(-1000903,'Commander! This fleshling rescued me!',0,0,0,0,'vanguard SAY_VANGUARD_FINISH'), +(-1000904,'%s salutes $N.',0,2,0,0,'vanguard EMOTE_VANGUARD_FINISH'), + +(-1000905,'Ok, let\'s go!!',0,0,0,1,'therylune SAY_THERYLUNE_START'), +(-1000906,'I can make it the rest of the way. $N. THANKS!',0,0,0,1,'therylune SAY_THERYLUNE_START'), + +(-1000907,'%s sniffs at the air. A tuber is near!',0,2,0,0,'domesticated felboar EMOTE_SNIFF_AIR'), +(-1000908,'%s starts to dig.',0,2,0,0,'domesticated felboar EMOTE_START_DIG'), +(-1000909,'%s squeals with glee at its discovery.',0,2,0,0,'domesticated felboar EMOTE_SQUEAL'), + +(-1000910,'Shall we begin, my friend?',0,0,0,0,'anchorite truuen SAY_BEGIN'), +(-1000911,'This area is known to be full of foul Scourge. You may want to take a moment to prepare any defenses at your disposal.',0,0,0,0,'anchorite truuen SAY_FIRST_STOP'), +(-1000912,'Very well, let us continue.',0,0,0,0,'anchorite truuen SAY_CONTINUE'), +(-1000913,'Beware! We are attacked!',0,0,0,0,'anchorite truuen SAY_FIRST_ATTACK'), +(-1000914,'It must be the purity of the Mark of the Lightbringer that is drawing forth the Scourge to us. We must proceed with caution lest we overwhelmed!',0,0,0,0,'anchorite truuen SAY_PURITY'), +(-1000915,'We are beset upon again! Defend yourself!',0,0,0,0,'anchorite truuen SAY_SECOND_ATTACK'), +(-1000916,'The land truly needs to be cleansed by the Light! Let us continue on the tomb. It isn\'t far now.',0,0,0,0,'anchorite truuen SAY_CLEANSE'), +(-1000917,'Be welcome, friends!',0,0,0,0,'high priest thel\'danis SAY_WELCOME'), +(-1000918,'Thank you for coming in remembrance of me. Your efforts in recovering that symbol, while unnecessary, are certainly touching to an old man\'s heart.',0,0,0,0,'ghost of uther SAY_EPILOGUE_1'), +(-1000919,'Please, rise my friend. Keep the Blessing as a symbol of the strength of the Light and how heroes long gone might once again rise in each of us to inspire.',0,0,0,0,'ghost of uther SAY_EPILOGUE_2'), + +(-1000920,'%s turns to face you.',0,2,0,0,'lich_king_wyrmskull EMOTE_LICH_KING_FACE'), +(-1000921,'Shamanism has brought you here... Its scent permeates the air. *The Lich King laughs* I was once a shaman.',14742,0,0,0,'lich_king_wyrmskull SAY_LICH_KING_1'), +(-1000922,'Shall we prepare it for you, my lord?',0,0,0,0,'valkyr_soulclaimer SAY_PREPARE'), +(-1000923,'No, minion. This one is not ready.',14743,0,0,0,'lich_king_wyrmskull SAY_LICH_KING_2'), +(-1000924,'Do you feel it, mortal? Death seeps through me, enveloping all that I touch. With just a snap of my finger your soul will languish in damnation for all eternity.',14744,0,0,0,'lich_king_wyrmskull SAY_LICH_KING_3'), +(-1000925,'But... It is not yet your time to serve the Lich King. Yes, a greater destiny awaits you. Power... You must become more powerful before you are to serve me.',14745,0,0,0,'lich_king_wyrmskull SAY_LICH_KING_4'), +(-1000926,'Now watch, val\'kyr. Observe as I apply pressure. Can you see that it is not yet ripe? Watch as it pops and falls lifeless to the floor.',14746,0,0,0,'lich_king_wyrmskull SAY_LICH_KING_5'), +(-1000927,'Persistence or stupidity? It matters not. Let this be a lesson learned, mortal!',14747,0,0,0,'lich_king_wyrmskull SAY_LICH_KING_6'), + +(-1000928,'%s motions for silence.',0,3,0,25,'king_ymiron EMOTE_KING_SILENCE'), +(-1000929,'Vrykul, your king implores you listen!',0,1,0,22,'king_ymiron SAY_KING_YMIRON_SPEECH_1'), +(-1000930,'The Gods have abandonned us!',0,1,0,22,'king_ymiron SAY_KING_YMIRON_SPEECH_2'), +(-1000931,'The crowd gasps in horror.',0,2,0,0,'king_ymiron EMOTE_YMIRON_CROWD_1'), +(-1000932,'Even now, in our darkest hour, they mock us!',0,1,0,22,'king_ymiron SAY_KING_YMIRON_SPEECH_3'), +(-1000933,'Where are the titans in out time of greatest need? Our women birth abberations - disfigured runts unable to even stand on their own! Weak and ugly... Useless...',0,1,0,22,'king_ymiron SAY_KING_YMIRON_SPEECH_4'), +(-1000934,'Ymiron has toiled. Long have I sat upon my throne and thought hard of our plight. There is only one answer... One reason...',0,1,0,22,'king_ymiron SAY_KING_YMIRON_SPEECH_5'), +(-1000935,'For who but the titans themselves could bestow such a curse? What could have such power?',0,1,0,22,'king_ymiron SAY_KING_YMIRON_SPEECH_6'), +(-1000936,'And the answer is nothing... For it is the titans who have cursed us!',0,1,0,22,'king_ymiron SAY_KING_YMIRON_SPEECH_7'), +(-1000937,'The crowd clamours.',0,2,0,0,'king_ymiron EMOTE_YMIRON_CROWD_2'), +(-1000938,'On this day all Vrykul will shed their old beliefs! We denounce our old gods! All Vrykul will pledge their allegiance to Ymiron! Ymiron will protect our noble race!',0,1,0,22,'king_ymiron SAY_KING_YMIRON_SPEECH_8'), +(-1000939,'The crowd cheers.',0,2,0,0,'king_ymiron EMOTE_YMIRON_CROWD_3'), +(-1000940,'And now my first decree upon the Vrykul! All malformed infants born of Vrykul mother and father are to be destroyed upon birth! Our blood must remain pure always! Those found in violation of Ymiron\'s decree will be taken to Gjalerbron for execution!',0,1,0,22,'king_ymiron SAY_KING_YMIRON_SPEECH_9'), +(-1000941,'Vrykul must remain pure!',0,0,0,0,'king_ymiron_crowd SAY_YMIRON_CROWD_1'), +(-1000942,'Show the aberrations no mercy, Ymiron!',0,0,0,0,'king_ymiron_crowd SAY_YMIRON_CROWD_2'), +(-1000943,'Show them mercy, my king! They are of our flesh and blood!',0,0,0,0,'king_ymiron_crowd SAY_YMIRON_CROWD_3'), +(-1000944,'They weaken us! Our strength is dilluted by their very existence! Destroy them all!',0,0,0,0,'king_ymiron_crowd SAY_YMIRON_CROWD_4'), +(-1000945,'All hail our glorious king, Ymiron!',0,0,0,0,'king_ymiron_crowd SAY_YMIRON_CROWD_5'), +(-1000946,'The King is going to speak!',0,0,0,0,'king_ymiron_crowd SAY_YMIRON_CROWD_6'), +(-1000947,'Let him speak! Be silent!',0,0,0,0,'king_ymiron_crowd SAY_YMIRON_CROWD_7'), + +(-1000948,'Well then, let\'s get this started. The longer we\'re here, the more damage the undead could be doing back in Hilsbrad.',0,0,0,0,'kinelory SAY_START'), +(-1000949,'All right, this is where we really have to be on our paws. Be ready!',0,0,0,0,'kinelory SAY_REACH_BOTTOM'), +(-1000950,'Attack me if you will, but you won\'t stop me from getting back to Quae.',0,0,0,0,'kinelory SAY_AGGRO_KINELORY'), +(-1000951,'You have my word that I shall find a use for your body after I\'ve killed you, Kinelory.',0,0,0,0,'jorell SAY_AGGRO_JORELL'), +(-1000952,'Watch my rear! I\'ll see what I can find in all this junk...',0,0,0,0,'kinelory SAY_WATCH_BACK'), +(-1000953,'%s begins rummaging through the apothecary\'s belongings.',0,2,0,0,'kinelory EMOTE_BELONGINGS'), +(-1000954,'I bet Quae\'ll think this is important. She\'s pretty knowledgeable about these things--no expert, but knowledgable.',0,0,0,0,'kinelory SAY_DATA_FOUND'), +(-1000955,'Okay, let\'s get out of here quick quick! Try and keep up. I\'m going to make a break for it.',0,0,0,0,'kinelory SAY_ESCAPE'), +(-1000956,'We made it! Quae, we made it!',0,0,0,0,'kinelory SAY_FINISH'), +(-1000957,'%s hands her pack to Quae.',0,2,0,0,'kinelory EMOTE_HAND_PACK'), + +(-1000958,'You must protect me from monsters, who are living in this forest!',0,0,0,0,'stinky ignatz SAY_STINKY_BEGIN'), +(-1000959,'This part of forest are very danger for us. We must be a careful!',0,0,0,0,'stinky ignatz SAY_STINKY_FIRST_STOP'), +(-1000960,'Kill two monsters, who stay near Bogbean plant and then I gather a bogbean.',0,0,0,0,'stinky ignatz SAY_STINKY_2_MONSTERS'), +(-1000961,'I am gathering a bogbean. It takes some time.',0,0,0,69,'stinky ignatz SAY_STINKY_GATHERING'), +(-1000962,'Thanks you for help.',0,0,0,0,'stinky ignatz SAY_STINKY_END'); + +-- -1 004 000 GENERAL MAPS - CATACLYSM (not instance maps) +INSERT INTO script_texts (entry,content_default,sound,type,language,emote,comment) VALUES +(-1004000,'Yipe! Help Hogger!',0,1,0,0,'hogger SAY_CALL_HELP'), +(-1004001,'Hogger is eating! Stop him!',0,5,0,0,'hogger WHISPER_EATING'), +(-1004002,'No hurt Hogger!',0,1,0,0,'hogger SAY_HOGGER_BEATEN'); + +-- -1 033 000 SHADOWFANG KEEP +INSERT INTO script_texts (entry,content_default,sound,type,language,emote,comment) VALUES +(-1033000,'Follow me and I\'ll open the courtyard door for you.',0,0,7,1,'prisoner ashcrombe SAY_FREE_AS'), +(-1033001,'I have just the spell to get this door open. Too bad the cell doors weren\'t locked so haphazardly.',0,0,7,1,'prisoner ashcrombe SAY_OPEN_DOOR_AS'), +(-1033002,'There it is! Wide open. Good luck to you conquering what lies beyond. I must report back to the Kirin Tor at once!',0,0,7,1,'prisoner ashcrombe SAY_POST_DOOR_AS'), + +(-1033003,'Free from this wretched cell at last! Let me show you to the courtyard....',0,0,1,1,'prisoner adamant SAY_FREE_AD'), +(-1033004,'You are indeed courageous for wanting to brave the horrors that lie beyond this door.',0,0,1,1,'prisoner adamant SAY_OPEN_DOOR_AD'), +(-1033005,'There we go!',0,0,1,1,'prisoner adamant SAY_POST1_DOOR_AD'), +(-1033006,'Good luck with Arugal. I must hurry back to Hadrec now.',0,0,1,1,'prisoner adamant SAY_POST2_DOOR_AD'), + +(-1033007,'About time someone killed the wretch.',0,0,1,1,'prisoner adamant SAY_BOSS_DIE_AD'), +(-1033008,'For once I agree with you... scum.',0,0,7,1,'prisoner ashcrombe SAY_BOSS_DIE_AS'), + +(-1033009,'I have changed my mind loyal servants, you do not need to bring the prisoner all the way to my study, I will deal with him here and now.',0,0,0,1,'arugal SAY_INTRO_1'), +(-1033010,'Vincent! You and your pathetic ilk will find no more success in routing my sons and I than those beggardly remnants of the Kirin Tor.',0,0,0,0,'arugal SAY_INTRO_2'), +(-1033011,'If you will not serve my Master with your sword and knowledge of his enemies...',0,0,0,1,'arugal SAY_INTRO_3'), +(-1033012,'Your moldering remains will serve ME as a testament to what happens when one is foolish enough to trespass in my domain!\n',0,0,0,0,'arugal SAY_INTRO_4'), + +(-1033013,'Who dares interfere with the Sons of Arugal?',0,1,0,0,'boss_arugal YELL_FENRUS'), +(-1033014,'%s vanishes.',0,2,0,0,'prisoner ashcrombe EMOTE_VANISH_AS'), +(-1033015,'%s fumbles with the rusty lock on the courtyard door.',0,2,0,432,'prisoner adamant EMOTE_UNLOCK_DOOR_AD'), +(-1033016,'Arrrgh!',0,0,0,0,'deathstalker vincent SAY_VINCENT_DIE'), +(-1033017,'You, too, shall serve!',5793,1,0,0,'boss_arugal YELL_AGGRO'), +(-1033018,'Another Falls!',5795,1,0,0,'boss_arugal YELL_KILLED_PLAYER'), +(-1033019,'Release your rage!',5797,1,0,0,'boss_arugal YELL_COMBAT'), + +(-1033020,'Did they bother to tell you who I am and why I am doing this?',0,0,0,0,'hummel SAY_INTRO_1'), +(-1033021,'...or are they just using you like they do everybody else?',0,0,0,0,'hummel SAY_INTRO_2'), +(-1033022,'But what does it matter. It is time for this to end.',0,0,0,0,'hummel SAY_INTRO_3'), +(-1033023,'Baxter! Get in there and help! NOW!',0,0,0,0,'hummel SAY_CALL_BAXTER'), +(-1033024,'It is time, Frye! Attack!',0,0,0,0,'hummel SAY_CALL_FRYE'), +(-1033025,'...please don\'t think less of me.',0,0,0,0,'hummel SAY_DEATH'); + +-- -1 034 000 STOCKADES + +-- -1 036 000 DEADMINES +INSERT INTO script_texts (entry,content_default,sound,type,language,emote,comment) VALUES +(-1036000,'You there! Check out that noise.',5775,6,7,0,'smite INST_SAY_ALARM1'), +(-1036001,'We\'re under attack! A vast, ye swabs! Repel the invaders!',5777,6,7,0,'smite INST_SAY_ALARM2'), +(-1036002,'You land lubbers are tougher than I thought! I\'ll have to improvise!',5778,0,0,21,'smite SAY_PHASE_2'), +(-1036003,'D\'ah! Now you\'re making me angry!',5779,0,0,15,'smite SAY_PHASE_3'); + +-- -1 043 000 WAILING CAVERNS +INSERT INTO script_texts (entry,content_default,sound,type,language,emote,comment) VALUES +(-1043000,'At last! Naralex can be awakened! Come aid me, brave adventurers!',0,6,0,0,'Disciple of Naralex - SAY_INTRO'), +(-1043001,'I must make the necessary preparations before the awakening ritual can begin. You must protect me!',0,0,0,0,'SAY_PREPARE'), +(-1043002,'These caverns were once a temple of promise for regrowth in the Barrens. Now, they are the halls of nightmares.',0,0,0,0,'Disciple of Naralex - SAY_FIRST_CORNER'), +(-1043003,'Come. We must continue. There is much to be done before we can pull Naralex from his nightmare.',0,0,0,0,'Disciple of Naralex - SAY_CONTINUE'), +(-1043004,'Within this circle of fire I must cast the spell to banish the spirits of the slain Fanglords.',0,0,0,0,'Disciple of Naralex - SAY_CIRCLE_BANISH'), +(-1043005,'The caverns have been purified. To Naralex\'s chamber we go!',0,0,0,0,'Disciple of Naralex - SAY_PURIFIED'), +(-1043006,'Beyond this corridor, Naralex lies in fitful sleep. Let us go awaken him before it is too late.',0,0,0,0,'Disciple of Naralex - SAY_NARALEX_CHAMBER'), +(-1043007,'Protect me brave souls as I delve into the Emerald Dream to rescue Naralex and put an end to this corruption!',0,1,0,0,'Disciple of Naralex - SAY_BEGIN_RITUAL'), +(-1043008,'%s begins to perform the awakening ritual on Naralex.',0,2,0,0,'Disciple of Naralex - EMOTE_RITUAL_BEGIN'), +(-1043009,'%s tosses fitfully in troubled sleep.',0,2,0,0,'Naralex - EMOTE_NARALEX_AWAKE'), +(-1043010,'%s writhes in agony. The Disciple seems to be breaking through.',0,2,0,0,'Naralex - EMOTE_BREAK_THROUGH'), +(-1043011,'%s dreams up a horrendous vision. Something stirs beneath the murky waters.',0,2,0,0,'Naralex - EMOTE_VISION'), +(-1043012,'This $N is a minion from Naralex\'s nightmare no doubt!.',0,0,0,0,'Disciple of Naralex - SAY_MUTANUS'), +(-1043013,'I AM AWAKE, AT LAST!',5789,1,0,0,'Naralex - SAY_NARALEX_AWAKE'), +(-1043014,'At last! Naralex awakes from the nightmare.',0,0,0,0,'Disciple of Naralex - SAY_AWAKE'), +(-1043015,'Ah, to be pulled from the dreaded nightmare! I thank you, my loyal Disciple, along with your brave companions.',0,0,0,0,'Naralex - SAY_NARALEX_THANKYOU'), +(-1043016,'We must go and gather with the other Disciplies. There is much work to be done before I can make another attempt to restore the Barrens. Farewell, brave souls!',0,0,0,0,'Naralex - SAY_FAREWELL'), +(-1043017,'Attacked! Help get this $N off of me!',0,0,0,0,'Disciple of Naralex - SAY_AGGRO_1'), +(-1043018,'Help!',0,0,0,0,'Disciple of Naralex - SAY_AGGRO_2'), +(-1043019,'Deal with this $N! I need to prepare to awake Naralex!',0,0,0,0,'Disciple of Naralex - SAY_AGGRO_3'); + +-- -1 047 000 RAZORFEN KRAUL +INSERT INTO script_texts (entry,content_default,sound,type,LANGUAGE,emote,comment) VALUES +(-1047000,'Woo hoo! Finally getting out of here. It\'s going to be rough though. Keep your eyes peeled for trouble.',0,0,0,0,'willix SAY_READY'), +(-1047001,'Up there is where Charlga Razorflank resides. Blasted old crone.',0,0,0,25,'willix SAY_1'), +(-1047002,'There\'s blueleaf tuber in this trench! It\'s like gold waiting to be mined I tell you!',0,0,0,0,'willix SAY_2'), +(-1047003,'There could be danger around every corner here.',0,0,0,0,'willix SAY_3'), +(-1047004,'I don\'t see how these foul animals live in this place... sheesh it smells!',0,0,0,0,'willix SAY_4'), +(-1047005,'I think I see a way for us to get out of this big twisted mess of a bramble.',0,0,0,0,'willix SAY_5'), +(-1047006,'Glad to be out of that wretched trench. Not much nicer up here though!',0,0,0,0,'willix SAY_6'), +(-1047007,'Finally! I\'ll be glad to get out of this place.',0,0,0,0,'willix SAY_7'), +(-1047008,'I think I\'ll rest a moment and catch my breath before heading back to Ratchet. Thanks for all the help!',0,0,0,0,'willix SAY_END'), +(-1047009,'$N heading this way fast! To arms!',0,0,0,0,'willix SAY_AGGRO_1'), +(-1047010,'Eek! $N coming right at us!',0,0,0,0,'willix SAY_AGGRO_2'), +(-1047011,'Egads! $N on me!',0,0,0,0,'willix SAY_AGGRO_3'), +(-1047012,'Help! Get this $N off of me!',0,0,0,0,'willix SAY_AGGRO_4'); + +-- -1 048 000 BLACKFATHOM DEEPS + +-- -1 070 000 ULDAMAN +INSERT INTO script_texts (entry,content_default,sound,type,language,emote,comment) VALUES +(-1070000,'REUSE ME',0,0,0,0,'REUSE ME'), +(-1070001,'Who dares awaken Archaedas? Who dares the wrath of the makers!',5855,1,0,0,'archaedas SAY_AGGRO'), +(-1070002,'Awake ye servants, defend the discs!',5856,1,0,0,'archaedas SAY_AWAKE_GUARDIANS'), +(-1070003,'To my side, brothers. For the makers!',5857,1,0,0,'archaedas SAY_AWAKE_WARDERS'), +(-1070004,'Reckless mortal.',5858,1,0,0,'archaedas SAY_UNIT_SLAIN'), +(-1070005,'%s breaks free from his stone slumber!',0,2,0,0,'archaedas EMOTE_BREAK_FREE'); + +-- -1 090 000 GNOMEREGAN +INSERT INTO script_texts (entry,content_default,sound,type,language,emote,comment) VALUES +(-1090000,'With your help, I can evaluate these tunnels.',0,0,0,1,'emi shortfuse SAY_START'), +(-1090001,'Let\'s see if we can find out where these Troggs are coming from.... and put a stop to the invasion!',0,0,0,1,'emi shortfuse SAY_INTRO_1'), +(-1090002,'Such devastation... what a horrible mess...',0,0,0,5,'emi shortfuse SAY_INTRO_2'), +(-1090003,'It\'s quiet here....',0,0,0,1,'emi shortfuse SAY_INTRO_3'), +(-1090004,'...too quiet.',0,0,0,1,'emi shortfuse SAY_INTRO_4'), +(-1090005,'Look! Over there at the tunnel wall!',0,0,0,25,'emi shortfuse SAY_LOOK_1'), +(-1090006,'Trogg incursion! Defend me while I blast the hole closed!',0,0,0,5,'emi shortfuse SAY_HEAR_1'), +(-1090007,'Get this, $n off of me!',0,0,0,0,'emi shortfuse SAY_AGGRO_1'), +(-1090008,'I don\'t think one charge is going to cut it. Keep fending them off!',0,0,0,0,'emi shortfuse SAY_CHARGE_1'), +(-1090009,'The charges are set. Get back before they blow!',0,0,0,5,'emi shortfuse SAY_CHARGE_2'), +(-1090010,'Incoming blast in 10 seconds!',0,1,0,5,'emi shortfuse SAY_BLOW_1_10'), +(-1090011,'Incoming blast in 5 seconds. Clear the tunnel! Stay back!',0,1,0,5,'emi shortfuse SAY_BLOW_1_5'), +(-1090012,'FIRE IN THE HOLE!',0,1,0,25,'emi shortfuse SAY_BLOW_1'), +(-1090013,'Well done! Without your help I would have never been able to thwart that wave of troggs.',0,0,0,4,'emi shortfuse SAY_FINISH_1'), +(-1090014,'Did you hear something?',0,0,0,6,'emi shortfuse SAY_LOOK_2'), +(-1090015,'I heard something over there.',0,0,0,25,'emi shortfuse SAY_HEAR_2'), +(-1090016,'More troggs! Ward them off as I prepare the explosives!',0,0,0,0,'emi shortfuse SAY_CHARGE_3'), +(-1090017,'The final charge is set. Stand back!',0,0,0,1,'emi shortfuse SAY_CHARGE_4'), +(-1090018,'10 seconds to blast! Stand back!!!',0,1,0,5,'emi shortfuse SAY_BLOW_2_10'), +(-1090019,'5 seconds until detonation!!!!!',0,1,0,5,'emi shortfuse SAY_BLOW_2_5'), +(-1090020,'Nice work! I\'ll set off the charges to prevent any more troggs from making it to the surface.',0,0,0,1,'emi shortfuse SAY_BLOW_SOON'), +(-1090021,'FIRE IN THE HOLE!',0,1,0,0,'emi shortfuse SAY_BLOW_2'), +(-1090022,'Superb! Because of your help, my people stand a chance of re-taking our beloved city. Three cheers to you!',0,0,0,0,'emi shortfuse SAY_FINISH_2'), + +(-1090023,'We come from below! You can never stop us!',0,1,0,1,'grubbis SAY_GRUBBIS_SPAWN'), + +(-1090024,'Usurpers! Gnomeregan is mine!',5807,1,0,0,'thermaplugg SAY_AGGRO'), +(-1090025,'My machines are the future! They\'ll destroy you all!',5808,1,0,0,'thermaplugg SAY_PHASE'), +(-1090026,'Explosions! MORE explosions! I\'ve got to have more explosions!',5809,1,0,0,'thermaplugg SAY_BOMB'), +(-1090027,'...and stay dead! He got served',5810,1,0,0,'thermaplugg SAY_SLAY'), + +(-1090028,'$n attacking! Help!',0,0,0,0,'emi shortfuse SAY_AGGRO_2'); + +-- -1 109 000 SUNKEN TEMPLE +INSERT INTO script_texts (entry,content_default,sound,type,language,emote,comment) VALUES +(-1109000,'The walls of the chamber tremble. Something is happening...',0,2,0,0,'malfurion stormrage EMOTE_MALFURION'), +(-1109001,'Be steadfast, champion. I know why it is that you are here and I know what it is that you seek. Eranikus will not give up the shard freely. He has been twisted... twisted by the same force that you seek to destroy.',0,0,0,0,'malfurion stormrge SAY_MALFURION1'), +(-1109002,'Are you really surprised? Is it hard to believe that the power of an Old God could reach even inside the Dream? It is true - Eranikus, Tyrant of the Dream, wages a battle against us all. The Nightmare follows in his wake of destruction.',0,0,0,0,'malfurion stormrge SAY_MALFURION2'), +(-1109003,'Understand this, Eranikus wants nothing more than to be brought to Azeroth from the Dream. Once he is out, he will stop at nothing to destroy my physical manifestation. This, however, is the only way in which you could recover the scepter shard.',0,0,0,0,'malfurion stormrge SAY_MAFLURION3'), +(-1109004,'You will bring him back into this world, champion.',0,0,0,0,'malfurion Stormrge SAY_MALFURION4'), + +(-1109005,'The shield be down! Rise up Atal\'ai! Rise up!',5861,6,0,0,'jammalan SAY_JAMMALAN_INTRO'), + +(-1109006,'HAKKAR LIVES!',5870,1,0,0,'avatar SAY_AVATAR_BRAZIER_1'), +(-1109007,'I TASTE THE BLOOD OF LIFE!',5868,1,0,0,'avatar SAY_AVATAR_BRAZIER_2'), +(-1109008,'I DRAW CLOSER TO YOUR WORLD!',5867,1,0,0,'avatar SAY_AVATAR_BRAZIER_3'), +(-1109009,'I AM NEAR!',5869,1,0,0,'avatar SAY_AVATAR_BRAZIER_4'), +(-1109010,'I AM HERE!',0,1,0,0,'avatar SAY_AVATAR_SPAWN'); + +-- -1 129 000 RAZORFEN DOWNS +INSERT INTO script_texts (entry,content_default,sound,type,language,emote,comment) VALUES +(-1129000,'REUSE_ME',0,0,0,0,'REUSE_ME'), +(-1129001,'REUSE_ME',0,0,0,0,'REUSE_ME'), +(-1129002,'REUSE_ME',0,0,0,0,'REUSE_ME'), +(-1129003,'REUSE_ME',0,0,0,0,'REUSE_ME'), +(-1129004,'REUSE_ME',0,0,0,0,'REUSE_ME'), + +(-1129005,'All right, stay close. These fiends will jump right out of the shadows at you if you let your guard down.',0,0,0,0,'belnistrasz SAY_READY'), +(-1129006,'Okay, here we go. It\'s going to take about five minutes to shut this thing down through the ritual. Once I start, keep the vermin off of me or it will be the end of us all!',0,0,0,0,'belnistrasz SAY_START_RIT'), +(-1129007,'You\'ll rue the day you crossed me, $N',0,0,0,0,'belnistrasz SAY_AGGRO_1'), +(-1129008,'Incoming $N - look sharp, friends!',0,0,0,0,'belnistrasz SAY_AGGRO_2'), +(-1129009,'Three minutes left -- I can feel the energy starting to build! Keep up the solid defense!',0,1,0,0,'belnistrasz SAY_3_MIN'), +(-1129010,'Just two minutes to go! We\'re half way there, but don\'t let your guard down!',0,1,0,0,'belnistrasz SAY_2_MIN'), +(-1129011,'One more minute! Hold on now, the ritual is about to take hold!',0,1,0,0,'belnistrasz SAY_1_MIN'), +(-1129012,'That\'s it -- we made it! The ritual is set in motion, and idol fires are about to go out for good! You truly are the heroes I thought you would be!',0,1,0,4,'belnistrasz SAY_FINISH'); + +-- -1 189 000 SCARLET MONASTERY +INSERT INTO script_texts (entry,content_default,sound,type,language,emote,comment) VALUES +(-1189000,'Ah, I have been waiting for a real challenge!',5830,1,0,0,'herod SAY_AGGRO'), +(-1189001,'Blades of Light!',5832,1,0,0,'herod SAY_WHIRLWIND'), +(-1189002,'Light, give me strength!',5833,1,0,0,'herod SAY_ENRAGE'), +(-1189003,'Hah, is that all?',5831,1,0,0,'herod SAY_KILL'), +(-1189004,'REUSE_ME',0,0,0,0,'REUSE_ME'), + +(-1189005,'Infidels! They must be purified!',5835,1,0,0,'mograine SAY_MO_AGGRO'), +(-1189006,'Unworthy!',5836,1,0,0,'mograine SAY_MO_KILL'), +(-1189007,'At your side, milady!',5837,1,0,0,'mograine SAY_MO_RESSURECTED'), + +(-1189008,'What, Mograine has fallen? You shall pay for this treachery!',5838,1,0,0,'whitemane SAY_WH_INTRO'), +(-1189009,'The Light has spoken!',5839,1,0,0,'whitemane SAY_WH_KILL'), +(-1189010,'Arise, my champion!',5840,1,0,0,'whitemane SAY_WH_RESSURECT'), + +(-1189011,'REUSE_ME',0,0,0,0,'REUSE_ME'), +(-1189012,'REUSE_ME',0,0,0,0,'REUSE_ME'), +(-1189013,'REUSE_ME',0,0,0,0,'REUSE_ME'), +(-1189014,'REUSE_ME',0,0,0,0,'REUSE_ME'), + +(-1189015,'The monster got what he deserved.',0,0,1,0,'vishas SAY_TRIGGER_VORREL'), + +(-1189016,'REUSE_ME',0,0,0,0,'REUSE_ME'), +(-1189017,'REUSE_ME',0,0,0,0,'REUSE_ME'), +(-1189018,'REUSE_ME',0,0,0,0,'REUSE_ME'), + +(-1189019,'You will not defile these mysteries!',5842,1,0,0,'doan SAY_AGGRO'), +(-1189020,'Burn in righteous fire!',5843,1,0,0,'doan SAY_SPECIALAE'), + +(-1189021,'REUSE_ME',0,0,0,0,'REUSE_ME'), + +(-1189022,'It is over, your search is done! Let fate choose now, the righteous one.',11961,1,0,0,'horseman SAY_ENTRANCE'), +(-1189023,'Here\'s my body, fit and pure! Now, your blackened souls I\'ll cure!',12567,1,0,0,'horseman SAY_REJOINED'), +(-1189024,'So eager you are for my blood to spill, yet to vanquish me this my head you must kill!',11969,1,0,0,'horseman SAY_BODY_DEFEAT'), +(-1189025,'Over here, you idiot!',12569,1,0,0,'horseman SAY_LOST_HEAD'), +(-1189026,'Harken, cur! Tis you I spurn! Now, $N, feel the burn!',12573,1,0,0,'horseman SAY_CONFLAGRATION'), +(-1189027,'Soldiers arise, stand and fight! Bring victory at last to this fallen knight!',11963,1,0,0,'horseman SAY_SPROUTING_PUMPKINS'), +(-1189028,'Your body lies beaten, battered and broken. Let my curse be your own, fate has spoken.',11962,1,0,0,'horseman SAY_SLAY'), +(-1189029,'This end have I reached before. What new adventure lies in store?',11964,1,0,0,'horseman SAY_DEATH'), +(-1189030,'%s laughs.',0,2,0,0,'horseman EMOTE_LAUGH'), +(-1189031,'Horseman rise...',0,0,0,0,'horseman SAY_PLAYER1'), +(-1189032,'Your time is night...',0,0,0,0,'horseman SAY_PLAYER2'), +(-1189033,'You felt death once...',0,0,0,0,'horseman SAY_PLAYER3'), +(-1189034,'Now, know demise!',0,0,0,0,'horseman SAY_PLAYER4'), + +(-1189035,'The master has fallen! Avenge him my brethren!',5834,1,0,0,'trainee SAY_TRAINEE_SPAWN'); + +-- -1 209 000 ZUL'FARRAK +INSERT INTO script_texts (entry,content_default,sound,type,language,emote,comment) VALUES +(-1209000,'How dare you enter my sanctum!',0,0,0,0,'zumrah SAY_INTRO'), +(-1209001,'Sands consume you!',5872,1,14,0,'zumrah SAY_AGGRO'), +(-1209002,'Fall!',5873,1,14,0,'zumrah SAY_KILL'), +(-1209003,'Come to me, my children!',0,0,8,0,'zumrah SAY_SUMMON'); + +-- -1 229 000 BLACKROCK SPIRE +INSERT INTO script_texts (entry,content_default,sound,type,language,emote,comment) VALUES +(-1229000,'%s begins to regain its strength!',0,2,0,0,'pyroguard EMOTE_BEGIN'), +(-1229001,'%s is nearly at full strength!',0,2,0,0,'pyroguard EMOTE_NEAR'), +(-1229002,'%s regains its power and breaks free of its bonds!',0,2,0,0,'pyroguard EMOTE_FULL'), +(-1229003,'Ha! Ha! Ha! Thank you for freeing me, fools. Now let me repay you by charring the flesh from your bones.',0,1,0,0,'pyroguard SAY_FREE'), + +(-1229004,'Excellent... it would appear as if the meddlesome insects have arrived just in time to feed my legion. Welcome, mortals!',0,1,0,1,'nefarius SAY_INTRO_1'), +(-1229005,'Let not even a drop of their blood remain upon the arena floor, my children. Feast on their souls!',0,1,0,1,'nefarius SAY_INTRO_2'), +(-1229006,'Foolsss...Kill the one in the dress!',0,1,0,0,'nefarius SAY_ATTACK_1'), +(-1229007,'Sire, let me join the fray! I shall tear their spines out with my bare hands!',0,1,0,1,'rend SAY_REND_JOIN'), +(-1229008,'Concentrate your attacks upon the healer!',0,1,0,0,'nefarius SAY_ATTACK_2'), +(-1229009,'Inconceivable!',0,1,0,0,'nefarius SAY_ATTACK_3'), +(-1229010,'Do not force my hand, children! I shall use your hides to line my boots.',0,1,0,0,'nefarius SAY_ATTACK_4'), +(-1229011,'Defilers!',0,1,0,0,'rend SAY_LOSE_1'), +(-1229012,'Impossible!',0,1,0,0,'rend SAY_LOSE_2'), +(-1229013,'Your efforts will prove fruitless. None shall stand in our way!',0,1,0,0,'nefarius SAY_LOSE_3'), +(-1229014,'THIS CANNOT BE!!! Rend, deal with these insects.',0,1,0,1,'nefarius SAY_LOSE_4'), +(-1229015,'With pleasure...',0,1,0,0,'rend SAY_REND_ATTACK'), +(-1229016,'The Warchief shall make quick work of you, mortals. Prepare yourselves!',0,1,0,25,'nefarius SAY_WARCHIEF'), +(-1229017,'Taste in my power!',0,1,0,0,'nefarius SAY_BUFF_GYTH'), +(-1229018,'Your victory shall be short lived. The days of both the Alliance and Horde are coming to an end. The next time we meet shall be the last.',0,1,0,1,'nefarius SAY_VICTORY'), + +(-1229019,'%s is knocked off his drake!',0,2,0,0,'rend EMOTE_KNOCKED_OFF'), + +(-1229020,'Intruders are destroying our eggs! Stop!!',0,1,0,0,'rookery hatcher - SAY_ROOKERY_EVENT_START'); + +-- -1 230 000 BLACKROCK DEPTHS +INSERT INTO script_texts (entry,content_default,sound,type,language,emote,comment) VALUES +(-1230000,'Ah, hits the spot!',0,0,0,0,'rocknot SAY_GOT_BEER'), +(-1230001,'Come to aid the Throne!',0,1,0,0,'dagran SAY_AGGRO'), +(-1230002,'Hail to the king, baby!',0,1,0,0,'dagran SAY_SLAY'), +(-1230003,'You have challenged the Seven, and now you will die!',0,0,0,0,'doomrel SAY_DOOMREL_START_EVENT'), + +(-1230004,'The Sons of Thaurissan shall watch you perish in the Ring of the Law!',0,1,0,0,'grimstone SAY_START_1'), +(-1230005,'You have been sentenced to death for crimes against the Dark Iron Nation!',0,1,0,0,'grimstone SAY_START_2'), +(-1230006,'Unleash the fury and let it be done!',0,1,0,0,'grimstone SAY_OPEN_EAST_GATE'), +(-1230007,'But your real punishment lies ahead.',0,1,0,0,'grimstone SAY_SUMMON_BOSS_1'), +(-1230008,'Haha! I bet you thought you were done!',0,1,0,0,'grimstone SAY_SUMMON_BOSS_2'), +(-1230009,'Good Riddance!',0,1,0,0,'grimstone SAY_OPEN_NORTH_GATE'), + +(-1230010,'Thank you, $N! I\'m free!!!',0,0,0,0,'dughal SAY_FREE'), +(-1230011,'You locked up the wrong Marshal, $N. Prepare to be destroyed!',0,0,0,0,'windsor SAY_AGGRO_1'), +(-1230012,'I bet you\'re sorry now, aren\'t you?',0,0,0,0,'windsor SAY_AGGRO_2'), +(-1230013,'You better hold me back or $N is going to feel some prison house beatings.',0,0,0,0,'windsor SAY_AGGRO_3'), +(-1230014,'Let\'s get a move on. My gear should be in the storage area up this way...',0,0,0,0,'windsor SAY_START'), +(-1230015,'Check that cell, $N. If someone is alive in there, we need to get them out.',0,0,0,25,'windsor SAY_CELL_DUGHAL_1'), +(-1230016,'Good work! We\'re almost there, $N. This way.',0,0,0,0,'windsor SAY_CELL_DUGHAL_3'), +(-1230017,'This is it, $N. My stuff should be in that room. Cover me, I\'m going in!',0,0,0,0,'windsor SAY_EQUIPMENT_1'), +(-1230018,'Ah, there it is!',0,0,0,0,'windsor SAY_EQUIPMENT_2'), +(-1230019,'Can you feel the power, $N??? It\'s time to ROCK!',0,0,0,0,'reginald_windsor SAY__EQUIPMENT_3'), +(-1230020,'Now we just have to free Tobias and we can get out of here. This way!',0,0,0,0,'reginald_windsor SAY__EQUIPMENT_4'), +(-1230021,'Open it.',0,0,0,25,'reginald_windsor SAY_CELL_JAZ_1'), +(-1230022,'I never did like those two. Let\'s get moving.',0,0,0,0,'reginald_windsor SAY_CELL_JAZ_2'), +(-1230023,'Open it and be careful this time!',0,0,0,25,'reginald_windsor SAY_CELL_SHILL_1'), +(-1230024,'That intolerant dirtbag finally got what was coming to him. Good riddance!',0,0,0,66,'reginald_windsor SAY_CELL_SHILL_2'), +(-1230025,'Alright, let\'s go.',0,0,0,0,'reginald_windsor SAY_CELL_SHILL_3'), +(-1230026,'Open it. We need to hurry up. I can smell those Dark Irons coming a mile away and I can tell you one thing, they\'re COMING!',0,0,0,25,'reginald_windsor SAY_CELL_CREST_1'), +(-1230027,'He has to be in the last cell. Unless... they killed him.',0,0,0,0,'reginald_windsor SAY_CELL_CREST_2'), +(-1230028,'Get him out of there!',0,0,0,25,'reginald_windsor SAY_CELL_TOBIAS_1'), +(-1230029,'Excellent work, $N. Let\'s find the exit. I think I know the way. Follow me!',0,0,0,0,'reginald_windsor SAY_CELL_TOBIAS_2'), +(-1230030,'We made it!',0,0,0,4,'reginald_windsor SAY_FREE_1'), +(-1230031,'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.',0,0,0,1,'reginald_windsor SAY_FREE_2'), +(-1230032,'Thank you! I will run for safety immediately!',0,0,0,0,'tobias SAY_TOBIAS_FREE_1'), +(-1230033,'Finally!! I can leave this dump.',0,0,0,0,'tobias SAY_TOBIAS_FREE_2'), + +(-1230034,'You\'ll pay for this insult, $c!',0,0,0,15,'coren direbrew SAY_AGGRO'); + +-- -1 249 000 ONYXIA'S LAIR +INSERT INTO script_texts (entry,content_default,sound,type,language,emote,comment) VALUES +(-1249000,'How fortuitous. Usually, I must leave my lair to feed.',0,1,0,0,'onyxia SAY_AGGRO'), +(-1249001,'Learn your place mortal!',0,1,0,0,'onyxia SAY_KILL'), +(-1249002,'This meaningless exertion bores me. I\'ll incinerate you all from above!',0,1,0,254,'onyxia SAY_PHASE_2_TRANS'), +(-1249003,'It seems you\'ll need another lesson, mortals!',0,1,0,293,'onyxia SAY_PHASE_3_TRANS'), +(-1249004,'%s takes in a deep breath...',0,3,0,0,'onyxia EMOTE_BREATH'); + +-- -1 269 000 OPENING OF THE DARK PORTAL (BLACK MORASS) +INSERT INTO script_texts (entry,content_default,sound,type,language,emote,comment) VALUES +(-1269000,'Why do you persist? Surely you can see the futility of it all. It is not too late! You may still leave with your lives ...',10442,1,0,0,'temporus SAY_ENTER'), +(-1269001,'So be it ... you have been warned.',10444,1,0,0,'temporus SAY_AGGRO'), +(-1269002,'Time... sands of time is run out for you.',10443,1,0,0,'temporus SAY_BANISH'), +(-1269003,'You should have left when you had the chance.',10445,1,0,0,'temporus SAY_SLAY1'), +(-1269004,'Your days are done.',10446,1,0,0,'temporus SAY_SLAY2'), +(-1269005,'My death means ... little.',10447,1,0,0,'temporus SAY_DEATH'), + +(-1269006,'Why do you aid the Magus? Just think of how many lives could be saved if the portal is never opened, if the resulting wars could be erased ...',10412,1,0,0,'chrono_lord_deja SAY_ENTER'), +(-1269007,'If you will not cease this foolish quest, then you will die!',10414,1,0,0,'chrono_lord_deja SAY_AGGRO'), +(-1269008,'You have outstayed your welcome, Timekeeper. Begone!',10413,1,0,0,'chrono_lord_deja SAY_BANISH'), +(-1269009,'I told you it was a fool\'s quest!',10415,1,0,0,'chrono_lord_deja SAY_SLAY1'), +(-1269010,'Leaving so soon?',10416,1,0,0,'chrono_lord_deja SAY_SLAY2'), +(-1269011,'Time ... is on our side.',10417,1,0,0,'chrono_lord_deja SAY_DEATH'), + +(-1269012,'The time has come to shatter this clockwork universe forever! Let us no longer be slaves of the hourglass! I warn you: those who do not embrace the greater path shall become victims of its passing!',10400,1,0,0,'aeonus SAY_ENTER'), +(-1269013,'Let us see what fate lays in store...',10402,1,0,0,'aeonus SAY_AGGRO'), +(-1269014,'Your time is up, slave of the past!',10401,1,0,0,'aeonus SAY_BANISH'), +(-1269015,'One less obstacle in our way!',10403,1,0,0,'aeonus SAY_SLAY1'), +(-1269016,'No one can stop us! No one!',10404,1,0,0,'aeonus SAY_SLAY2'), +(-1269017,'It is only a matter...of time.',10405,1,0,0,'aeonus SAY_DEATH'), +(-1269018,'REUSE ME',0,0,0,0,'REUSE ME'), + +(-1269019,'Stop! Do not go further, mortals. You are ill-prepared to face the forces of the Infinite Dragonflight. Come, let me help you.',0,0,0,0,'saat SAY_SAAT_WELCOME'), + +(-1269020,'The time has come! Gul\'dan, order your warlocks to double their efforts! Moments from now the gateway will open, and your Horde will be released upon this ripe, unsuspecting world!',10435,1,0,0,'medivh SAY_ENTER'), +(-1269021,'What is this? Champions, coming to my aid? I sense the hand of the dark one in this. Truly this sacred event bears his blessing?',10436,1,0,0,'medivh SAY_INTRO'), +(-1269022,'Champions, my shield grows weak!',10437,1,0,0,'medivh SAY_WEAK75'), +(-1269023,'My powers must be concentrated on the portal! I do not have time to hold the shield!',10438,1,0,0,'medivh SAY_WEAK50'), +(-1269024,'The shield is nearly gone! All that I have worked for is in danger!',10439,1,0,0,'medivh SAY_WEAK25'), +(-1269025,'No... damn this feeble mortal coil...',10441,1,0,0,'medivh SAY_DEATH'), +(-1269026,'I am grateful for your aid, champions. Now, Gul\'dan\'s Horde will sweep across this world, like a locust swarm, and all my designs, all my carefully laid plans will at last fall into place.',10440,1,0,0,'medivh SAY_WIN'), +(-1269027,'Orcs of the Horde! This portalis the gateway to your new destiny! Azeroth lies before you, ripe for the taking!',0,1,0,0,'medivh SAY_ORCS_ENTER'), +(-1269028,'Gul\'dan speaks the truth! We should return at once to tell our brothers of the news! Retreat back trought the portal!',0,1,0,0,'medivh SAY_ORCS_ANSWER'); + +-- -1 289 000 SCHOLOMANCE +INSERT INTO script_texts (entry,content_default,sound,type,language,emote,comment) VALUES +(-1289000,'School is in session!',0,1,0,0,'gandling SAY_GANDLING_SPAWN'); + +-- -1 309 000 ZUL'GURUB +INSERT INTO script_texts (entry,content_default,sound,type,language,emote,comment) VALUES +(-1309000,'Let the coils of hate unfurl!',8421,1,0,0,'venoxis SAY_TRANSFORM'), +(-1309001,'Ssserenity..at lassst!',0,1,0,0,'venoxis SAY_DEATH'), + +(-1309002,'Lord Hir\'eek, grant me wings of vengance!',8417,1,0,0,'jeklik SAY_AGGRO'), +(-1309003,'I command you to rain fire down upon these invaders!',0,1,0,0,'jeklik SAY_RAIN_FIRE'), +(-1309004,'Finally ...death. Curse you Hakkar! Curse you!',8422,1,0,0,'jeklik SAY_DEATH'), + +(-1309005,'Draw me to your web mistress Shadra. Unleash your venom!',8418,1,0,0,'marli SAY_AGGRO'), +(-1309006,'Shadra, make of me your avatar!',0,1,0,0,'marli SAY_TRANSFORM'), +(-1309007,'Aid me my brood!',0,1,0,0,'marli SAY_SPIDER_SPAWN'), +(-1309008,'Bless you mortal for this release. Hakkar controls me no longer...',8423,1,0,0,'marli SAY_DEATH'), + +(-1309009,'Shirvallah, fill me with your RAGE!',8419,1,0,0,'thekal SAY_AGGRO'), +(-1309010,'Hakkar binds me no more! Peace at last!',8424,1,0,0,'thekal SAY_DEATH'), + +(-1309011,'Bethekk, your priestess calls upon your might!',8416,1,0,0,'arlokk SAY_AGGRO'), +(-1309012,'Feast on $n, my pretties!',0,1,0,0,'arlokk SAY_FEAST_PANTHER'), +(-1309013,'At last, I am free of the Soulflayer!',8412,1,0,0,'arlokk SAY_DEATH'), + +(-1309014,'Welcome to da great show friends! Step right up to die!',8425,1,0,0,'jindo SAY_AGGRO'), + +(-1309015,'I\'ll feed your souls to Hakkar himself!',8413,1,0,0,'mandokir SAY_AGGRO'), +(-1309016,'DING!',0,1,0,0,'mandokir SAY_DING_KILL'), +(-1309017,'GRATS!',0,1,0,0,'mandokir SAY_GRATS_JINDO'), +(-1309018,'$N! I\'m watching you!',0,1,0,0,'mandokir SAY_WATCH'), +(-1309019,'Don\'t make me angry. You won\'t like it when I\'m angry.',0,4,0,0,'mandokir SAY_WATCH_WHISPER'), + +(-1309020,'PRIDE HERALDS THE END OF YOUR WORLD. COME, MORTALS! FACE THE WRATH OF THE SOULFLAYER!',8414,1,0,0,'hakkar SAY_AGGRO'), +(-1309021,'Fleeing will do you no good, mortals!',0,1,0,0,'hakkar SAY_FLEEING'), +(-1309022,'You dare set foot upon Hakkari holy ground? Minions of Hakkar, destroy the infidels!',0,6,0,0,'hakkar SAY_MINION_DESTROY'), +(-1309023,'Minions of Hakkar, hear your God. The sanctity of this temple has been compromised. Invaders encroach upon holy ground! The Altar of Blood must be protected. Kill them all!',0,6,0,0,'hakkar SAY_PROTECT_ALTAR'), + +(-1309024,'%s goes into a rage after seeing his raptor fall in battle!',0,2,0,0,'mandokir EMOTE_RAGE'), + +(-1309025,'The brood shall not fall!',0,1,0,0,'marli SAY_TRANSFORM_BACK'), + +(-1309026,'%s emits a deafening shriek!',0,2,0,0,'jeklik SAY_SHRIEK'), +(-1309027,'%s begins to cast a Great Heal!',0,2,0,0,'jeklik SAY_HEAL'); + +-- -1 329 000 STRATHOLME +INSERT INTO script_texts (entry,content_default,sound,type,language,emote,comment) VALUES +(-1329000,'Thanks to Egan',0,0,0,0,'freed_soul SAY_ZAPPED0'), +(-1329001,'Rivendare must die',0,0,0,0,'freed_soul SAY_ZAPPED1'), +(-1329002,'Who you gonna call?',0,0,0,0,'freed_soul SAY_ZAPPED2'), +(-1329003,'Don\'t cross those beams!',0,0,0,0,'freed_soul SAY_ZAPPED3'), + +(-1329004,'An Ash\'ari Crystal has fallen! Stay true to the Lich King, my brethren, and attempt to resummon it.',0,6,0,0,'thuzadin acolyte SAY_ANNOUNCE_ZIGGURAT_1'), +(-1329005,'One of the Ash\'ari Crystals has been destroyed! Slay the intruders!',0,6,0,0,'thuzadin acolyte SAY_ANNOUNCE_ZIGGURAT_2'), +(-1329006,'An Ash\'ari Crystal has been toppled! Restore the ziggurat before the Necropolis is vulnerable!',0,6,0,0,'thuzadin acolyte SAY_ANNOUNCE_ZIGGURAT_3'), +(-1329007,'The Ash\'ari Crystals have been destroyed! The Slaughterhouse is vulnerable!',0,6,0,0,'baron rivendare SAY_ANNOUNCE_RIVENDARE'), + +(-1329008,'Intruders at the Service Gate! Lord Rivendare must be warned!',0,6,0,0,'barthilas SAY_WARN_BARON'), +(-1329009,'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!',0,6,0,0,'baron rivendare SAY_ANNOUNCE_RUN_START'), +(-1329010,'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!',0,6,0,0,'baron rivendare SAY_ANNOUNCE_RUN_10_MIN'), +(-1329011,'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!',0,6,0,0,'baron rivendare SAY_ANNOUNCE_RUN_5_MIN'), +(-1329012,'May this prisoner\'s death serve as a warning. None shall defy the Scourge and live!',0,6,0,0,'baron rivendare SAY_ANNOUNCE_RUN_FAIL'), +(-1329013,'So you see fit to toy with the Lich King\'s creations? Ramstein, be sure to give the intruders a proper greeting.',0,6,0,0,'baron rivendare SAY_ANNOUNCE_RAMSTEIN'), +(-1329014,'Time to take matters into my own hands. Come. Enter my domain and challenge the might of the Scourge!',0,6,0,0,'baron rivendare SAY_UNDEAD_DEFEAT'), +(-1329015,'You did it... you\'ve slain Baron Rivendare! The Argent Dawn shall hear of your valiant deeds!',0,0,0,0,'ysida SAY_EPILOGUE'), + +(-1329016,'Today you have unmade what took me years to create! For this you shall all die by my hand!',0,1,0,0,'dathrohan SAY_AGGRO'), +(-1329017,'You fools think you can defeat me so easily? Face the true might of the Nathrezim!',0,1,0,0,'dathrohan SAY_TRANSFORM'), +(-1329018,'Damn you mortals! All my plans of revenge, all my hate... all burned to ash...',0,0,0,0,'dathrohan SAY_DEATH'); + +-- -1 349 000 MARAUDON + +-- -1 389 000 RAGEFIRE CHASM + +-- -1 409 000 MOLTEN CORE +INSERT INTO script_texts (entry,content_default,sound,type,language,emote,comment) VALUES +(-1409000,'%s performs one last service for Ragnaros.',0,2,0,0,'geddon EMOTE_SERVICE'), +(-1409001,'REUSE ME',0,0,0,0,'REUSE ME'), +(-1409002,'%s refuses to die while its master is in trouble.',0,2,0,0,'core rager EMOTE_LOWHP'), + +(-1409003,'Reckless mortals, none may challenge the sons of the living flame!',8035,1,0,0,'majordomo SAY_AGGRO'), +(-1409004,'The runes of warding have been destroyed! Hunt down the infedels my bretheren.',8039,6,0,0,'majordomo SAY_SPAWN'), +(-1409005,'Ashes to Ashes!',8037,1,0,0,'majordomo SAY_SLAY'), +(-1409006,'Burn mortals! Burn for this transgression!',8036,1,0,0,'majordomo SAY_SPECIAL'), +(-1409007,'Impossible! Stay your attack mortals! I submitt! I submitt!',8038,1,0,0,'majordomo SAY_DEFEAT_1'), + +(-1409008,'Behold Ragnaros, the Firelord! He who was ancient when this world was young! Bow before him, mortals! Bow before your ending!',8040,1,0,0,'ragnaros SAY_SUMMON_MAJ'), +(-1409009,'TOO SOON! YOU HAVE AWAKENED ME TOO SOON, EXECUTUS! WHAT IS THE MEANING OF THIS INTRUSION?',8043,1,0,0,'ragnaros SAY_ARRIVAL1_RAG'), +(-1409010,'These mortal infidels, my lord! They have invaded your sanctum, and seek to steal your secrets!',8041,1,0,0,'ragnaros SAY_ARRIVAL2_MAJ'), +(-1409011,'FOOL! YOU ALLOWED THESE INSECTS TO RUN RAMPANT THROUGH THE HALLOWED CORE, AND NOW YOU LEAD THEM TO MY VERY LAIR? YOU HAVE FAILED ME, EXECUTUS! JUSTICE SHALL BE MET, INDEED!',8044,1,0,0,'ragnaros SAY_ARRIVAL3_RAG'), +(-1409012,'NOW FOR YOU, INSECTS. BOLDLY YOU SAUGHT THE POWER OF RAGNAROS NOW YOU SHALL SEE IT FIRST HAND.',8045,1,0,0,'ragnaros SAY_ARRIVAL5_RAG'), + +(-1409013,'COME FORTH, MY SERVANTS! DEFEND YOUR MASTER!',8049,1,0,0,'ragnaros SAY_REINFORCEMENTS1'), +(-1409014,'YOU CANNOT DEFEAT THE LIVING FLAME! COME YOU MINIONS OF FIRE! COME FORTH YOU CREATURES OF HATE! YOUR MASTER CALLS!',8050,1,0,0,'ragnaros SAY_REINFORCEMENTS2'), +(-1409015,'BY FIRE BE PURGED!',8046,1,0,0,'ragnaros SAY_HAND'), +(-1409016,'TASTE THE FLAMES OF SULFURON!',8047,1,0,0,'ragnaros SAY_WRATH'), +(-1409017,'DIE INSECT!',8051,1,0,0,'ragnaros SAY_KILL'), +(-1409018,'MY PATIENCE IS DWINDILING! COME NATS TO YOUR DEATH!',8048,1,0,0,'ragnaros SAY_MAGMABURST'), + +(-1409019,'You think you\'ve won already? Perhaps you\'ll need another lesson in pain!',0,1,0,0,'majordomo SAY_LAST_ADD'), +(-1409020,'Brashly you have come to rest the secrets of the living flame. You will soon regret the recklessness of your quest.',0,1,0,0,'majordomo SAY_DEFEAT_2'), +(-1409021,'I go now to summon the lord whos house this is. Should you seek an audiance with him your paltry lives will surly be forfit. Nevertheless seek out his lair if you dare!',0,1,0,0,'majordomo SAY_DEFEAT_3'), +(-1409022,'My flame! Please don\'t take away my flame... ',8042,1,0,0,'ragnaros SAY_ARRIVAL4_MAJ'), +(-1409023,'Very well, $N.',0,0,0,0,'majordomo SAY_SUMMON_0'), +(-1409024,'Impudent whelps! You\'ve rushed headlong to your own deaths! See now, the master stirs!',0,1,0,0,'majordomo SAY_SUMMON_1'); + +-- -1 429 000 DIRE MAUL +INSERT INTO script_texts (entry,content_default,sound,type,language,emote,comment) VALUES +(-1429000,'The demon is loose! Quickly we must restrain him!',0,6,0,0,'highborne summoner SAY_FREE_IMMOLTHAR'), +(-1429001,'Who dares disrupt the sanctity of Eldre\'Thalas? Face me, cowards!',0,6,0,0,'prince tortheldrin SAY_KILL_IMMOLTHAR'), + +(-1429002,'At last... Freed from his cursed grasp!',0,6,0,0,'old ironbark SAY_IRONBARK_REDEEM'); + +-- -1 469 000 BLACKWING LAIR +INSERT INTO script_texts (entry,content_default,sound,type,language,emote,comment) VALUES +(-1469000,'None of your kind should be here! You\'ve doomed only yourselves!',8286,1,0,0,'broodlord SAY_AGGRO'), +(-1469001,'Clever Mortals but I am not so easily lured away from my sanctum!',8287,1,0,0,'broodlord SAY_LEASH'), + +(-1469002,'Run! They are coming!',0,1,0,0,'vaelastrasz blackwing tech SAY_INTRO_TECH'), +(-1469003,'%s flinches as its skin shimmers.',0,2,0,0,'chromaggus EMOTE_SHIMMER'), + +(-1469004,'In this world where time is your enemy, it is my greatest ally. This grand game of life that you think you play in fact plays you. To that I say...',0,1,0,1,'victor_nefarius SAY_GAMESBEGIN_1'), +(-1469005,'Let the games begin!',8280,1,0,22,'victor_nefarius SAY_GAMESBEGIN_2'), +(-1469006,'Ah...the heroes. You are persistent, aren\'t you? Your ally here attempted to match his power against mine - and paid the price. Now he shall serve me...by slaughtering you.',8279,1,0,23,'victor_nefarius SAY_NEFARIUS_CORRUPT'), + +(-1469007,'Well done, my minions. The mortals\' courage begins to wane! Now, let\'s see how they contend with the true Lord of Blackrock Spire!',8288,1,0,0,'nefarian SAY_AGGRO'), +(-1469008,'Enough! Now you vermin shall feel the force of my birthright, the fury of the earth itself.',8289,1,0,0,'nefarian SAY_XHEALTH'), +(-1469009,'BURN! You wretches! BURN!',8290,1,0,0,'nefarian SAY_SHADOWFLAME'), +(-1469010,'Impossible! Rise my minions! Serve your master once more!',8291,1,0,0,'nefarian SAY_RAISE_SKELETONS'), +(-1469011,'Worthless $N! Your friends will join you soon enough!',8293,1,0,0,'nefarian SAY_SLAY'), +(-1469012,'This cannot be! I am the master here! You mortals are nothing to my kind! Do you hear me? Nothing!',8292,1,0,0,'nefarian SAY_DEATH'), +(-1469013,'Mages too? You should be more careful when you play with magic...',0,1,0,0,'nefarian SAY_MAGE'), +(-1469014,'Warriors, I know you can hit harder than that! Let\'s see it!',0,1,0,0,'nefarian SAY_WARRIOR'), +(-1469015,'Druids and your silly shapeshifting. Let\'s see it in action!',0,1,0,0,'nefarian SAY_DRUID'), +(-1469016,'Priests! If you\'re going to keep healing like that, we might as well make it a little more interesting!',0,1,0,0,'nefarian SAY_PRIEST'), +(-1469017,'Paladins, I\'ve heard you have many lives. Show me.',0,1,0,0,'nefarian SAY_PALADIN'), +(-1469018,'Shamans, show me what your totems can do!',0,1,0,0,'nefarian SAY_SHAMAN'), +(-1469019,'Warlocks, you shouldn\'t be playing with magic you don\'t understand. See what happens?',0,1,0,0,'nefarian SAY_WARLOCK'), +(-1469020,'Hunters and your annoying pea-shooters!',0,1,0,0,'nefarian SAY_HUNTER'), +(-1469021,'Rogues? Stop hiding and face me!',0,1,0,0,'nefarian SAY_ROGUE'), + +(-1469022,'You\'ll pay for forcing me to do this.',8275,1,0,0,'razorgore SAY_EGGS_BROKEN1'), +(-1469023,'Fools! These eggs are more precious than you know!',8276,1,0,0,'razorgore SAY_EGGS_BROKEN2'), +(-1469024,'No - not another one! I\'ll have your heads for this atrocity!',8277,1,0,0,'razorgore SAY_EGGS_BROKEN3'), +(-1469025,'If I fall into the abyss I\'ll take all of you mortals with me...',8278,1,0,0,'razorgore SAY_DEATH'), + +(-1469026,'Too late, friends! Nefarius\' corruption has taken hold...I cannot...control myself.',8281,1,0,1,'vaelastrasz SAY_LINE1'), +(-1469027,'I beg you, mortals - FLEE! Flee before I lose all sense of control! The black fire rages within my heart! I MUST- release it!',8282,1,0,1,'vaelastrasz SAY_LINE2'), +(-1469028,'FLAME! DEATH! DESTRUCTION! Cower, mortals before the wrath of Lord...NO - I MUST fight this! Alexstrasza help me, I MUST fight it!',8283,1,0,1,'vaelastrasz SAY_LINE3'), +(-1469029,'Nefarius\' hate has made me stronger than ever before! You should have fled while you could, mortals! The fury of Blackrock courses through my veins!',8285,1,0,0,'vaelastrasz SAY_HALFLIFE'), +(-1469030,'Forgive me, $N! Your death only adds to my failure!',8284,1,0,0,'vaelastrasz SAY_KILLTARGET'), + +(-1469031,'Death Knights, get over here!',0,1,0,0,'nefarian SAY_DEATH_KNIGHT'), + +(-1469032,'Get up, little red wyrm...and destroy them!',0,1,0,1,'victor_nefarius SAY_NEFARIUS_CORRUPT_2'), + +(-1469033,'%s flee as the controlling power of the orb is drained.',0,2,0,0,'razorgore EMOTE_TROOPS_FLEE'), + +(-1469034,'Run! They are coming.',0,1,0,0,'blackwing technician SAY_TECHNICIAN_RUN'), + +(-1469035,'Orb of Domination loses power and shuts off!',0,2,0,0,'razorgore EMOTE_ORB_SHUT_OFF'); + +-- -1 509 000 RUINS OF AHN'QIRAJ +INSERT INTO script_texts (entry,content_default,sound,type,language,emote,comment) VALUES +(-1509000,'%s senses your fear.',0,2,0,0,'moam EMOTE_AGGRO'), +(-1509001,'%s bristles with energy!',0,2,0,0,'moan EMOTE_MANA_FULL'), +(-1509028,'%s drains your mana and turns to stone.',0,2,0,0,'moam EMOTE_ENERGIZING'), + +(-1509002,'%s sets eyes on $N!',0,2,0,0,'buru EMOTE_TARGET'), + +(-1509003,'They come now. Try not to get yourself killed, young blood.',0,1,0,22,'andorov SAY_ANDOROV_INTRO_3'), +(-1509004,'Remember, Rajaxx, when I said I\'d kill you last?',0,1,0,0,'andorov SAY_ANDOROV_INTRO_1'), + +(-1509005,'The time of our retribution is at hand! Let darkness reign in the hearts of our enemies!',8612,1,0,0,'rajaxx SAY_WAVE3'), +(-1509006,'No longer will we wait behind barred doors and walls of stone! No longer will our vengeance be denied! The dragons themselves will tremble before our wrath!',8610,1,0,0,'rajaxx SAY_WAVE4'), +(-1509007,'Fear is for the enemy! Fear and death!',8608,1,0,0,'rajaxx SAY_WAVE5'), +(-1509008,'Staghelm will whimper and beg for his life, just as his whelp of a son did! One thousand years of injustice will end this day!',8611,1,0,0,'rajaxx SAY_WAVE6'), +(-1509009,'Fandral! Your time has come! Go and hide in the Emerald Dream and pray we never find you!',8607,1,0,0,'rajaxx SAY_WAVE7'), +(-1509010,'Impudent fool! I will kill you myself!',8609,1,0,0,'rajaxx SAY_INTRO'), +(-1509011,'Attack and make them pay dearly!',8603,1,0,0,'rajaxx SAY_UNK1'), +(-1509012,'Crush them! Drive them out!',8605,1,0,0,'rajaxx SAY_UNK2'), +(-1509013,'Do not hesitate! Destroy them!',8606,1,0,0,'rajaxx SAY_UNK3'), +(-1509014,'Warriors! Captains! Continue the fight!',8613,1,0,0,'rajaxx SAY_UNK4'), +(-1509015,'You are not worth my time $N!',8614,1,0,0,'rajaxx SAY_DEAGGRO'), +(-1509016,'Breath your last!',8604,1,0,0,'rajaxx SAY_KILLS_ANDOROV'), +(-1509017,'Soon you will know the price of your meddling, mortals... The master is nearly whole... And when he rises, your world will be cease!',0,1,0,0,'rajaxx SAY_COMPLETE_QUEST'), + +(-1509018,'I am rejuvinated!',8593,1,0,0,'ossirian SAY_SURPREME1'), +(-1509019,'My powers are renewed!',8595,1,0,0,'ossirian SAY_SURPREME2'), +(-1509020,'My powers return!',8596,1,0,0,'ossirian SAY_SURPREME3'), +(-1509021,'Protect the city at all costs!',8597,1,0,0,'ossirian SAY_RAND_INTRO1'), +(-1509022,'The walls have been breached!',8599,6,0,0,'ossirian SAY_RAND_INTRO2'), +(-1509023,'To your posts. Defend the city.',8600,1,0,0,'ossirian SAY_RAND_INTRO3'), +(-1509024,'Tresspassers will be terminated.',8601,1,0,0,'ossirian SAY_RAND_INTRO4'), +(-1509025,'Sands of the desert rise and block out the sun!',8598,1,0,0,'ossirian SAY_AGGRO'), +(-1509026,'You are terminated.',8602,1,0,0,'ossirian SAY_SLAY'), +(-1509027,'I...have...failed.',8594,1,0,0,'ossirian SAY_DEATH'), +-- 28 (above) = EMOTE_ENERGIZING +(-1509029,'Come get some!',0,0,0,0,'andorov SAY_ANDOROV_INTRO_4'), +(-1509030,'Kill first, ask questions later... Incoming!',0,1,0,0,'andorov SAY_ANDOROV_ATTACK_START'), +(-1509031,'I lied...',0,1,0,0,'andorov SAY_ANDOROV_INTRO_2'); + +-- -1 531 000 TEMPLE OF AHN'QIRAJ +INSERT INTO script_texts (entry,content_default,sound,type,language,emote,comment) VALUES +(-1531000,'Are you so eager to die? I would be happy to accomodate you.',8615,1,0,0,'skeram SAY_AGGRO1'), +(-1531001,'Cower mortals! The age of darkness is at hand.',8616,1,0,0,'skeram SAY_AGGRO2'), +(-1531002,'Tremble! The end is upon you.',8621,1,0,0,'skeram SAY_AGGRO3'), +(-1531003,'Let your death serve as an example!',8617,1,0,0,'skeram SAY_SLAY1'), +(-1531004,'Spineless wretches! You will drown in rivers of blood!',8619,1,0,0,'skeram SAY_SLAY2'), +(-1531005,'The screams of the dying will fill the air. A symphony of terror is about to begin!',8620,1,0,0,'skeram SAY_SLAY3'), +(-1531006,'Prepare for the return of the ancient ones!',8618,1,0,0,'skeram SAY_SPLIT'), +(-1531007,'You only delay... the inevetable.',8622,1,0,0,'skeram SAY_DEATH'), + +(-1531008,'You will be judged for defiling these sacred grounds! The laws of the Ancients will not be challenged! Trespassers will be annihilated!',8646,1,0,0,'sartura SAY_AGGRO'), +(-1531009,'I sentence you to death!',8647,1,0,0,'sartura SAY_SLAY'), +(-1531010,'I serve to the last!',8648,1,0,0,'sartura SAY_DEATH'), + +(-1531011,'%s is weakened!',0,2,0,0,'cthun EMOTE_WEAKENED'), + +(-1531012,'The massive floating eyeball in the center of the chamber turns its gaze upon you. You stand before a god.',0,2,0,0,'eye cthun EMOTE_INTRO'), +(-1531013,'Only flesh and bone. Mortals are such easy prey...',0,1,0,0,'veklor SAY_INTRO_1'), +(-1531014,'Where are your manners, brother. Let us properly welcome our guests.',0,1,0,0,'veknilash SAY_INTRO_2'), +(-1531015,'There will be pain...',0,1,0,0,'veklor SAY_INTRO_3'), +(-1531016,'Oh so much pain...',0,1,0,0,'veknilash SAY_INTRO_4'), +(-1531017,'Come, little ones.',0,1,0,0,'veklor SAY_INTRO_5'), +(-1531018,'The feast of souls begin now...',0,1,0,0,'veknilash SAY_INTRO_6'), + +(-1531019,'It\'s too late to turn away.',8623,1,0,0,'veklor SAY_AGGRO_1'), +(-1531020,'Prepare to embrace oblivion!',8626,1,0,0,'veklor SAY_AGGRO_2'), +(-1531021,'Like a fly in a web.',8624,1,0,0,'veklor SAY_AGGRO_3'), +(-1531022,'Your brash arrogance!',8628,1,0,0,'veklor SAY_AGGRO_4'), +(-1531023,'You will not escape death!',8629,1,0,0,'veklor SAY_SLAY'), +(-1531024,'My brother...NO!',8625,1,0,0,'veklor SAY_DEATH'), +(-1531025,'To decorate our halls!',8627,1,0,0,'veklor SAY_SPECIAL'), + +(-1531026,'Ah, lambs to the slaughter!',8630,1,0,0,'veknilash SAY_AGGRO_1'), +(-1531027,'Let none survive!',8632,1,0,0,'veknilash SAY_AGGRO_2'), +(-1531028,'Join me brother, there is blood to be shed!',8631,1,0,0,'veknilash SAY_AGGRO_3'), +(-1531029,'Look brother, fresh blood!',8633,1,0,0,'veknilash SAY_AGGRO_4'), +(-1531030,'Your fate is sealed!',8635,1,0,0,'veknilash SAY_SLAY'), +(-1531031,'Vek\'lor, I feel your pain!',8636,1,0,0,'veknilash SAY_DEATH'), +(-1531032,'Shall be your undoing!',8634,1,0,0,'veknilash SAY_SPECIAL'), + +(-1531033,'Death is close...',8580,4,0,0,'cthun SAY_WHISPER_1'), +(-1531034,'You are already dead.',8581,4,0,0,'cthun SAY_WHISPER_2'), +(-1531035,'Your courage will fail.',8582,4,0,0,'cthun SAY_WHISPER_3'), +(-1531036,'Your friends will abandon you.',8583,4,0,0,'cthun SAY_WHISPER_4'), +(-1531037,'You will betray your friends.',8584,4,0,0,'cthun SAY_WHISPER_5'), +(-1531038,'You will die.',8585,4,0,0,'cthun SAY_WHISPER_6'), +(-1531039,'You are weak.',8586,4,0,0,'cthun SAY_WHISPER_7'), +(-1531040,'Your heart will explode.',8587,4,0,0,'cthun SAY_WHISPER_8'); + +-- -1 532 000 KARAZHAN +INSERT INTO script_texts (entry,content_default,sound,type,language,emote,comment) VALUES +(-1532000,'Well done Midnight!',9173,1,0,0,'attumen SAY_MIDNIGHT_KILL'), +(-1532001,'Cowards! Wretches!',9167,1,0,0,'attumen SAY_APPEAR1'), +(-1532002,'Who dares attack the steed of the Huntsman?',9298,1,0,0,'attumen SAY_APPEAR2'), +(-1532003,'Perhaps you would rather test yourselves against a more formidable opponent?!',9299,1,0,0,'attumen SAY_APPEAR3'), +(-1532004,'Come, Midnight, let\'s disperse this petty rabble!',9168,1,0,0,'attumen SAY_MOUNT'), +(-1532005,'It was... inevitable.',9169,1,0,0,'attumen SAY_KILL1'), +(-1532006,'Another trophy to add to my collection!',9300,1,0,0,'attumen SAY_KILL2'), +(-1532007,'Weapons are merely a convenience for a warrior of my skill!',9166,1,0,0,'attumen SAY_DISARMED'), +(-1532008,'I always knew... someday I would become... the hunted.',9165,1,0,0,'attumen SAY_DEATH'), +(-1532009,'Such easy sport.',9170,1,0,0,'attumen SAY_RANDOM1'), +(-1532010,'Amateurs! Do not think you can best me! I kill for a living.',9304,1,0,0,'attumen SAY_RANDOM2'), + +(-1532011,'Hmm, unannounced visitors? Preparations must be made.',9211,1,0,0,'moroes SAY_AGGRO'), +(-1532012,'Now, where was I? Oh yes...',9215,1,0,0,'moroes SAY_SPECIAL_1'), +(-1532013,'You rang?',9316,1,0,0,'moroes SAY_SPECIAL_2'), +(-1532014,'One more for dinner this evening.',9214,1,0,0,'moroes SAY_KILL_1'), +(-1532015,'Time... Never enough time.',9314,1,0,0,'moroes SAY_KILL_2'), +(-1532016,'I\'ve gone and made a mess.',9315,1,0,0,'moroes SAY_KILL_3'), +(-1532017,'How terribly clumsy of me...',9213,1,0,0,'moroes SAY_DEATH'), + +(-1532018,'Your behavior will not be tolerated!',9204,1,0,0,'maiden SAY_AGGRO'), +(-1532019,'Ah ah ah...',9207,1,0,0,'maiden SAY_SLAY1'), +(-1532020,'This is for the best.',9312,1,0,0,'maiden SAY_SLAY2'), +(-1532021,'Impure thoughts lead to profane actions.',9311,1,0,0,'maiden SAY_SLAY3'), +(-1532022,'Cast out your corrupt thoughts.',9313,1,0,0,'maiden SAY_REPENTANCE1'), +(-1532023,'Your impurity must be cleansed.',9208,1,0,0,'maiden SAY_REPENTANCE2'), +(-1532024,'Death comes. Will your conscience be clear?',9206,1,0,0,'maiden SAY_DEATH'), + +(-1532025,'Oh at last, at last. I can go home.',9190,1,0,0,'dorothee SAY_DOROTHEE_DEATH'), +(-1532026,'Don\'t let them hurt us, Tito! Oh, you won\'t, will you?',9191,1,0,0,'dorothee SAY_DOROTHEE_SUMMON'), +(-1532027,'Tito, oh Tito, no!',9192,1,0,0,'dorothee SAY_DOROTHEE_TITO_DEATH'), +(-1532028,'Oh dear, we simply must find a way home! The old wizard could be our only hope! Strawman, Roar, Tinhead, will you... wait! Oh golly, look! We have visitors!',9195,1,0,0,'dorothee SAY_DOROTHEE_AGGRO'), + +(-1532029,'Wanna fight? Huh? Do ya? C\'mon, I\'ll fight you with both claws behind my back!',9227,1,0,0,'roar SAY_ROAR_AGGRO'), +(-1532030,'You didn\'t have to go and do that.',9229,1,0,0,'roar SAY_ROAR_DEATH'), +(-1532031,'I think I\'m going to go take fourty winks.',9230,1,0,0,'roar SAY_ROAR_SLAY'), + +(-1532032,'Now what should I do with you? I simply can\'t make up my mind.',9254,1,0,0,'strawman SAY_STRAWMAN_AGGRO'), +(-1532033,'Don\'t let them make a mattress... out of me.',9256,1,0,0,'strawman SAY_STRAWMAN_DEATH'), +(-1532034,'I guess I\'m not a failure after all.',9257,1,0,0,'strawman SAY_STRAWMAN_SLAY'), + +(-1532035,'I could really use a heart. Say, can I have yours?',9268,1,0,0,'tinhead SAY_TINHEAD_AGGRO'), +(-1532036,'Back to being an old rustbucket.',9270,1,0,0,'tinhead SAY_TINHEAD_DEATH'), +(-1532037,'Guess I\'m not so rusty, after all.',9271,1,0,0,'tinhead SAY_TINHEAD_SLAY'), +(-1532038,'%s begins to rust.',0,2,0,0,'tinhead EMOTE_RUST'), + +(-1532039,'Woe to each and every one of you my pretties! ',9179,1,0,0,'crone SAY_CRONE_AGGRO'), +(-1532040,'It will all be over soon! ',9307,1,0,0,'crone SAY_CRONE_AGGRO2'), +(-1532041,'How could you? What a cruel, cruel world!',9178,1,0,0,'crone SAY_CRONE_DEATH'), +(-1532042,'Fixed you, didn\'t I? ',9180,1,0,0,'crone SAY_CRONE_SLAY'), + +(-1532043,'All the better to own you with!',9276,1,0,0,'wolf SAY_WOLF_AGGRO'), +(-1532044,'Mmmm... delicious.',9277,1,0,0,'wolf SAY_WOLF_SLAY'), +(-1532045,'Run away little girl, run away!',9278,1,0,0,'wolf SAY_WOLF_HOOD'), + +(-1532046,'What devil art thou, that dost torment me thus?',9196,1,0,0,'julianne SAY_JULIANNE_AGGRO'), +(-1532047,'Where is my lord? Where is my Romulo?',9199,1,0,0,'julianne SAY_JULIANNE_ENTER'), +(-1532048,'Romulo, I come! Oh... this do I drink to thee!',9198,1,0,0,'julianne SAY_JULIANNE_DEATH01'), +(-1532049,'Where is my Lord? Where is my Romulo? Ohh, happy dagger! This is thy sheath! There rust, and let me die!',9310,1,0,0,'julianne SAY_JULIANNE_DEATH02'), +(-1532050,'Come, gentle night; and give me back my Romulo!',9200,1,0,0,'julianne SAY_JULIANNE_RESURRECT'), +(-1532051,'Parting is such sweet sorrow.',9201,1,0,0,'julianne SAY_JULIANNE_SLAY'), + +(-1532052,'Wilt thou provoke me? Then have at thee, boy!',9233,1,0,0,'romulo SAY_ROMULO_AGGRO'), +(-1532053,'Thou smilest... upon the stroke that... murders me.',9235,1,0,0,'romulo SAY_ROMULO_DEATH'), +(-1532054,'This day\'s black fate on more days doth depend. This but begins the woe. Others must end.',9236,1,0,0,'romulo SAY_ROMULO_ENTER'), +(-1532055,'Thou detestable maw, thou womb of death; I enforce thy rotten jaws to open!',9237,1,0,0,'romulo SAY_ROMULO_RESURRECT'), +(-1532056,'How well my comfort is revived by this!',9238,1,0,0,'romulo SAY_ROMULO_SLAY'), + +(-1532057,'The Menagerie is for guests only.',9183,1,0,0,'curator SAY_AGGRO'), +(-1532058,'Gallery rules will be strictly enforced.',9188,1,0,0,'curator SAY_SUMMON1'), +(-1532059,'This curator is equipped for gallery protection.',9309,1,0,0,'curator SAY_SUMMON2'), +(-1532060,'Your request cannot be processed.',9186,1,0,0,'curator SAY_EVOCATE'), +(-1532061,'Failure to comply will result in offensive action.',9185,1,0,0,'curator SAY_ENRAGE'), +(-1532062,'Do not touch the displays.',9187,1,0,0,'curator SAY_KILL1'), +(-1532063,'You are not a guest.',9308,1,0,0,'curator SAY_KILL2'), +(-1532064,'This Curator is no longer op... er... ation... al.',9184,1,0,0,'curator SAY_DEATH'), + +(-1532065,'Your blood will anoint my circle.',9264,1,0,0,'terestian SAY_SLAY1'), +(-1532066,'The great one will be pleased.',9329,1,0,0,'terestian SAY_SLAY2'), +(-1532067,'My life, is yours. Oh great one.',9262,1,0,0,'terestian SAY_DEATH'), +(-1532068,'Ah, you\'re just in time. The rituals are about to begin.',9260,1,0,0,'terestian SAY_AGGRO'), +(-1532069,'Please, accept this humble offering, oh great one.',9263,1,0,0,'terestian SAY_SACRIFICE1'), +(-1532070,'Let the sacrifice serve his testament to my fealty.',9330,1,0,0,'terestian SAY_SACRIFICE2'), +(-1532071,'Come, you dwellers in the dark. Rally to my call!',9265,1,0,0,'terestian SAY_SUMMON1'), +(-1532072,'Gather, my pets. There is plenty for all.',9331,1,0,0,'terestian SAY_SUMMON2'), + +(-1532073,'Please, no more. My son... he\'s gone mad!',9241,1,0,0,'aran SAY_AGGRO1'), +(-1532074,'I\'ll not be tortured again!',9323,1,0,0,'aran SAY_AGGRO2'), +(-1532075,'Who are you? What do you want? Stay away from me!',9324,1,0,0,'aran SAY_AGGRO3'), +(-1532076,'I\'ll show you this beaten dog still has some teeth!',9245,1,0,0,'aran SAY_FLAMEWREATH1'), +(-1532077,'Burn you hellish fiends!',9326,1,0,0,'aran SAY_FLAMEWREATH2'), +(-1532078,'I\'ll freeze you all!',9246,1,0,0,'aran SAY_BLIZZARD1'), +(-1532079,'Back to the cold dark with you!',9327,1,0,0,'aran SAY_BLIZZARD2'), +(-1532080,'Yes, yes, my son is quite powerful... but I have powers of my own!',9242,1,0,0,'aran SAY_EXPLOSION1'), +(-1532081,'I am not some simple jester! I am Nielas Aran!',9325,1,0,0,'aran SAY_EXPLOSION2'), +(-1532082,'Surely you would not deny an old man a replenishing drink? No, no I thought not.',9248,1,0,0,'aran SAY_DRINK'), +(-1532083,'I\'m not finished yet! No, I have a few more tricks up me sleeve.',9251,1,0,0,'aran SAY_ELEMENTALS'), +(-1532084,'I want this nightmare to be over!',9250,1,0,0,'aran SAY_KILL1'), +(-1532085,'Torment me no more!',9328,1,0,0,'aran SAY_KILL2'), +(-1532086,'You\'ve wasted enough of my time. Let these games be finished!',9247,1,0,0,'aran SAY_TIMEOVER'), +(-1532087,'At last... The nightmare is.. over...',9244,1,0,0,'aran SAY_DEATH'), +(-1532088,'Where did you get that?! Did HE send you?!',9249,1,0,0,'aran SAY_ATIESH'), + +(-1532089,'%s cries out in withdrawal, opening gates to the warp.',0,3,0,0,'netherspite EMOTE_PHASE_PORTAL'), +(-1532090,'%s goes into a nether-fed rage!',0,3,0,0,'netherspite EMOTE_PHASE_BANISH'), + +(-1532091,'Madness has brought you here to me. I shall be your undoing!',9218,1,0,0,'malchezaar SAY_AGGRO'), +(-1532092,'Simple fools! Time is the fire in which you\'ll burn!',9220,1,0,0,'malchezaar SAY_AXE_TOSS1'), +(-1532093,'I see the subtlety of conception is beyond primitives such as you.',9317,1,0,0,'malchezaar SAY_AXE_TOSS2'), +(-1532094,'Who knows what secrets hide in the dark.',9223,1,0,0,'malchezaar SAY_SPECIAL1'), +(-1532095,'The cerestial forces are mine to manipulate.',9320,1,0,0,'malchezaar SAY_SPECIAL2'), +(-1532096,'How can you hope to withstand against such overwhelming power?',9321,1,0,0,'malchezaar SAY_SPECIAL3'), +(-1532097,'Surely you did not think you could win.',9222,1,0,0,'malchezaar SAY_SLAY1'), +(-1532098,'Your greed, your foolishness has brought you to this end.',9318,1,0,0,'malchezaar SAY_SLAY2'), +(-1532099,'You are, but a plaything, unfit even to amuse.',9319,1,0,0,'malchezaar SAY_SLAY3'), +(-1532100,'All realities, all dimensions are open to me!',9224,1,0,0,'malchezaar SAY_SUMMON1'), +(-1532101,'You face not Malchezaar alone, but the legions I command!',9322,1,0,0,'malchezaar SAY_SUMMON2'), +(-1532102,'I refuse to concede defeat. I am a prince of the Eredar! I am...',9221,1,0,0,'malchezaar SAY_DEATH'), + +(-1532103,'Welcome Ladies and Gentlemen, to this evening\'s presentation!',9174,1,0,0,'barnes OZ1'), +(-1532104,'Tonight we plumb the depths of the human soul as we join a lost, lonely girl trying desperately -- with the help of her loyal companions -- to find her way home!',9338,1,0,0,'barnes OZ2'), +(-1532105,'But she is pursued... by a wicked malevolent crone!',9339,1,0,0,'barnes OZ3'), +(-1532106,'Will she survive? Will she prevail? Only time will tell. And now ... on with the show!',9340,1,0,0,'barnes OZ4'), +(-1532107,'Good evening, Ladies and Gentlemen! Welcome to this evening\'s presentation!',9175,1,0,0,'barnes HOOD1'), +(-1532108,'Tonight, things are not what they seem. For tonight, your eyes may not be trusted',9335,1,0,0,'barnes HOOD2'), +(-1532109,'Take for instance, this quiet, elderly woman, waiting for a visit from her granddaughter. Surely there is nothing to fear from this sweet, grey-haired, old lady.',9336,1,0,0,'barnes HOOD3'), +(-1532110,'But don\'t let me pull the wool over your eyes. See for yourself what lies beneath those covers! And now... on with the show!',9337,1,0,0,'barnes HOOD4'), +(-1532111,'Welcome, Ladies and Gentlemen, to this evening\'s presentation!',9176,1,0,0,'barnes RAJ1'), +(-1532112,'Tonight, we explore a tale of forbidden love!',9341,1,0,0,'barnes RAJ2'), +(-1532113,'But beware, for not all love stories end happily, as you may find out. Sometimes, love pricks like a thorn.',9342,1,0,0,'barnes RAJ3'), +(-1532114,'But don\'t take it from me, see for yourself what tragedy lies ahead when the paths of star-crossed lovers meet. And now...on with the show!',9343,1,0,0,'barnes RAJ4'), +(-1532115,'Splendid, I\'m going to get the audience ready. Break a leg!',0,0,0,0,'barnes SAY_EVENT_START'), + +(-1532116,'You\'ve got my attention, dragon. You\'ll find I\'m not as easily scared as the villagers below.',0,1,0,0,'image of medivh SAY_MEDIVH_1'), +(-1532117,'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!',0,1,0,0,'arcanagos SAY_ARCANAGOS_2'), +(-1532118,'You dare challenge me at my own dwelling? Your arrogance is astounding, even for a dragon.',0,1,0,0,'image of medivh SAY_MEDIVH_3'), +(-1532119,'A dark power seeks to use you, Medivh! If you stay, dire days will follow. You must hurry, we don\'t have much time!',0,1,0,0,'arcanagos SAY_ARCANAGOS_4'), +(-1532120,'I do not know what you speak of, dragon... but I will not be bullied by this display of insolence. I\'ll leave Karazhan when it suits me!',0,1,0,0,'image of medivh SAY_MEDIVH_5'), +(-1532121,'You leave me no alternative. I will stop you by force if you wont listen to reason.',0,1,0,0,'arcanagos SAY_ARCANAGOS_6'), +(-1532122,'%s begins to cast a spell of great power, weaving his own essence into the magic.',0,2,0,0,'image of medivh EMOTE_CAST_SPELL'), +(-1532123,'What have you done, wizard? This cannot be! I\'m burning from... within!',0,1,0,0,'arcanagos SAY_ARCANAGOS_7'), +(-1532124,'He should not have angered me. I must go... recover my strength now...',0,0,0,0,'image of medivh SAY_MEDIVH_8'), + +(-1532125,'An ancient being awakens in the distance...',0,2,0,0,'nightbane EMOTE_AWAKEN'), +(-1532126,'What fools! I shall bring a quick end to your suffering!',0,1,0,0,'nightbane SAY_AGGRO'), +(-1532127,'Miserable vermin. I shall exterminate you from the air!',0,1,0,0,'nightbane SAY_AIR_PHASE'), +(-1532128,'Enough! I shall land and crush you myself!',0,1,0,0,'nightbane SAY_LAND_PHASE_1'), +(-1532129,'Insects! Let me show you my strength up close!',0,1,0,0,'nightbane SAY_LAND_PHASE_2'), +(-1532130,'%s takes a deep breath.',0,3,0,0,'nightbane EMOTE_DEEP_BREATH'), + +(-1532131,'The halls of Karazhan shake, as the curse binding the doors of the Gamemaster\'s Hall is lifted.',0,2,0,0,'echo_of_medivh EMOTE_LIFT_CURSE'), +(-1532132,'%s cheats!',0,3,0,0,'echo_of_medivh EMOTE_CHEAT'); + +-- -1 533 000 NAXXRAMAS +INSERT INTO script_texts (entry,content_default,sound,type,language,emote,comment) VALUES +(-1533000,'Ahh... welcome to my parlor.',8788,1,0,0,'anubrekhan SAY_GREET'), +(-1533001,'Just a little taste...',8785,1,0,0,'anubrekhan SAY_AGGRO1'), +(-1533002,'There is no way out.',8786,1,0,0,'anubrekhan SAY_AGGRO2'), +(-1533003,'Yes, Run! It makes the blood pump faster!',8787,1,0,0,'anubrekhan SAY_AGGRO3'), +(-1533004,'I hear little hearts beating. Yesss... beating faster now. Soon the beating will stop.',8790,1,0,0,'anubrekhan SAY_TAUNT1'), +(-1533005,'Where to go? What to do? So many choices that all end in pain, end in death.',8791,1,0,0,'anubrekhan SAY_TAUNT2'), +(-1533006,'Which one shall I eat first? So difficult to choose... the all smell so delicious.',8792,1,0,0,'anubrekhan SAY_TAUNT3'), +(-1533007,'Closer now... tasty morsels. I\'ve been too long without food. Without blood to drink.',8793,1,0,0,'anubrekhan SAY_TAUNT4'), +(-1533008,'Shh... it will all be over soon.',8789,1,0,0,'anubrekhan SAY_SLAY'), + +(-1533009,'Your old lives, your mortal desires, mean nothing. You are acolytes of the master now, and you will serve the cause without question! The greatest glory is to die in the master\'s service!',8799,1,0,0,'faerlina SAY_GREET'), +(-1533010,'Slay them in the master\'s name!',8794,1,0,0,'faerlina SAY_AGGRO1'), +(-1533011,'You cannot hide from me!',8795,1,0,0,'faerlina SAY_AGGRO2'), +(-1533012,'Kneel before me, worm!',8796,1,0,0,'faerlina SAY_AGGRO3'), +(-1533013,'Run while you still can!',8797,1,0,0,'faerlina SAY_AGGRO4'), +(-1533014,'You have failed!',8800,1,0,0,'faerlina SAY_SLAY1'), +(-1533015,'Pathetic wretch!',8801,1,0,0,'faerlina SAY_SLAY2'), +(-1533016,'The master... will avenge me!',8798,1,0,0,'faerlina SAY_DEATH'), + +(-1533017,'Patchwerk want to play!',8909,1,0,0,'patchwerk SAY_AGGRO1'), +(-1533018,'Kel\'Thuzad make Patchwerk his Avatar of War!',8910,1,0,0,'patchwerk SAY_AGGRO2'), +(-1533019,'No more play?',8912,1,0,0,'patchwerk SAY_SLAY'), +(-1533020,'What happened to... Patch...',8911,1,0,0,'patchwerk SAY_DEATH'), + +(-1533021,'%s sprays slime across the room!',0,3,0,0,'grobbulus EMOTE_SPRAY_SLIME'), + +(-1533022,'%s lifts off into the air!',0,3,0,0,'sapphiron EMOTE_FLY'), + +(-1533023,'Stalagg crush you!',8864,1,0,0,'stalagg SAY_STAL_AGGRO'), +(-1533024,'Stalagg kill!',8866,1,0,0,'stalagg SAY_STAL_SLAY'), +(-1533025,'Master save me...',8865,1,0,0,'stalagg SAY_STAL_DEATH'), +(-1533026,'Feed you to master!',8802,1,0,0,'feugen SAY_FEUG_AGGRO'), +(-1533027,'Feugen make master happy!',8804,1,0,0,'feugen SAY_FEUG_SLAY'), +(-1533028,'No... more... Feugen...',8803,1,0,0,'feugen SAY_FEUG_DEATH'), + +(-1533029,'You are too late... I... must... OBEY!',8872,1,0,0,'thaddius SAY_GREET'), +(-1533030,'KILL!',8867,1,0,0,'thaddius SAY_AGGRO1'), +(-1533031,'EAT YOUR BONES!',8868,1,0,0,'thaddius SAY_AGGRO2'), +(-1533032,'BREAK YOU!',8869,1,0,0,'thaddius SAY_AGGRO3'), +(-1533033,'You die now!',8877,1,0,0,'thaddius SAY_SLAY'), +(-1533034,'Now YOU feel pain!',8871,1,0,0,'thaddius SAY_ELECT'), +(-1533035,'Thank... you...',8870,1,0,0,'thaddius SAY_DEATH'), +(-1533036,'Pleeease!',8873,1,0,0,'thaddius SAY_SCREAM1'), +(-1533037,'Stop, make it stop!',8874,1,0,0,'thaddius SAY_SCREAM2'), +(-1533038,'Help me! Save me!',8875,1,0,0,'thaddius SAY_SCREAM3'), +(-1533039,'Please, nooo!',8876,1,0,0,'thaddius SAY_SCREAM4'), + +(-1533040,'Foolishly you have sought your own demise.',8807,1,0,0,'gothik SAY_SPEECH_1'), +(-1533041,'Death is the only escape.',8806,1,0,0,'gothik SAY_KILL'), +(-1533042,'I... am... undone!',8805,1,0,0,'gothik SAY_DEATH'), +(-1533043,'I have waited long enough! Now, you face the harvester of souls!',8808,1,0,0,'gothik SAY_TELEPORT'), + +(-1533044,'Defend youself!',8892,1,0,0,'blaumeux SAY_BLAU_AGGRO'), +(-1533045,'Come, Zeliek, do not drive them out. Not before we\'ve had our fun.',8896,6,0,0,'blaumeux SAY_BLAU_TAUNT1'), +(-1533046,'I do hope they stay alive long enough for me to... introduce myself.',8897,6,0,0,'blaumeux SAY_BLAU_TAUNT2'), +(-1533047,'The first kill goes to me! Anyone care to wager?',8898,6,0,0,'blaumeux SAY_BLAU_TAUNT3'), +(-1533048,'Your life is mine!',8895,1,0,0,'blaumeux SAY_BLAU_SPECIAL'), +(-1533049,'Who\'s next?',8894,1,0,0,'blaumeux SAY_BLAU_SLAY'), +(-1533050,'Tou... che!',8893,1,0,0,'blaumeux SAY_BLAU_DEATH'), + +(-1533051,'Come out and fight, ye wee ninny!',8899,1,0,0,'korthazz SAY_KORT_AGGRO'), +(-1533052,'To arms, ye roustabouts! We\'ve got company!',8903,6,0,0,'korthazz SAY_KORT_TAUNT1'), +(-1533053,'I heard about enough of yer sniveling. Shut yer fly trap \'afore I shut it for ye!',8904,6,0,0,'korthazz SAY_KORT_TAUNT2'), +(-1533054,'I\'m gonna enjoy killin\' these slack-jawed daffodils!',8905,6,0,0,'korthazz SAY_KORT_TAUNT3'), +(-1533055,'I like my meat extra crispy!',8902,1,0,0,'korthazz SAY_KORT_SPECIAl'), +(-1533056,'Next time, bring more friends!',8901,1,0,0,'korthazz SAY_KORT_SLAY'), +(-1533057,'What a bloody waste this is!',8900,1,0,0,'korthazz SAY_KORT_DEATH'), + +(-1533058,'Flee, before it\'s too late!',8913,1,0,0,'zeliek SAY_ZELI_AGGRO'), +(-1533059,'Invaders, cease this foolish venture at once! Turn away while you still can!',8917,6,0,0,'zeliek SAY_ZELI_TAUNT1'), +(-1533060,'Perhaps they will come to their senses, and run away as fast as they can!',8918,6,0,0,'zeliek SAY_ZELI_TAUNT2'), +(-1533061,'Do not continue! Turn back while there\'s still time!',8919,6,0,0,'zeliek SAY_ZELI_TAUNT3'), +(-1533062,'I- I have no choice but to obey!',8916,1,0,0,'zeliek SAY_ZELI_SPECIAL'), +(-1533063,'Forgive me!',8915,1,0,0,'zeliek SAY_ZELI_SLAY'), +(-1533064,'It is... as it should be.',8914,1,0,0,'zeliek SAY_ZELI_DEATH'), + +(-1533065,'You seek death?',14571,1,0,0,'rivendare_naxx SAY_RIVE_AGGRO1'), +(-1533066,'None shall pass!',14572,1,0,0,'rivendare_naxx SAY_RIVE_AGGRO2'), +(-1533067,'Be still!',14573,1,0,0,'rivendare_naxx SAY_RIVE_AGGRO3'), +(-1533068,'You will find no peace in death.',14574,1,0,0,'rivendare_naxx SAY_RIVE_SLAY1'), +(-1533069,'The master\'s will is done.',14575,1,0,0,'rivendare_naxx SAY_RIVE_SLAY2'), +(-1533070,'Bow to the might of the scourge!',14576,1,0,0,'rivendare_naxx SAY_RIVE_SPECIAL'), +(-1533071,'Enough prattling. Let them come! We shall grind their bones to dust.',14577,6,0,0,'rivendare_naxx SAY_RIVE_TAUNT1'), +(-1533072,'Conserve your anger! Harness your rage! You will all have outlets for your frustration soon enough.',14578,6,0,0,'rivendare_naxx SAY_RIVE_TAUNT2'), +(-1533073,'Life is meaningless. It is in death that we are truly tested.',14579,6,0,0,'rivendare_naxx SAY_RIVE_TAUNT3'), +(-1533074,'Death... will not stop me...',14580,1,0,0,'rivendare_naxx SAY_RIVE_DEATH'), + +(-1533075,'Glory to the master!',8845,1,0,0,'noth SAY_AGGRO1'), +(-1533076,'Your life is forfeit!',8846,1,0,0,'noth SAY_AGGRO2'), +(-1533077,'Die, trespasser!',8847,1,0,0,'noth SAY_AGGRO3'), +(-1533078,'Rise, my soldiers! Rise and fight once more!',8851,1,0,0,'noth SAY_SUMMON'), +(-1533079,'My task is done!',8849,1,0,0,'noth SAY_SLAY1'), +(-1533080,'Breathe no more!',8850,1,0,0,'noth SAY_SLAY2'), +(-1533081,'I will serve the master... in... death!',8848,1,0,0,'noth SAY_DEATH'), + +(-1533082,'%s takes in a deep breath...',0,3,0,0,'sapphiron EMOTE_BREATH'), +(-1533083,'%s resumes his attacks!',0,3,0,0,'sapphiron EMOTE_GROUND'), + +(-1533084,'Your forces are nearly marshalled to strike back against your enemies, my liege.',14467,6,0,0,'kelthuzad SAY_SAPP_DIALOG1'), +(-1533085,'Soon we will eradicate the Alliance and Horde, then the rest of Azeroth will fall before the might of my army.',14768,6,0,0,'lich_king SAY_SAPP_DIALOG2_LICH'), +(-1533086,'Yes, Master. The time of their ultimate demise grows close...What is this?',14468,6,0,0,'kelthuzad SAY_SAPP_DIALOG3'), +(-1533087,'Invaders...here?! DESTROY them, Kel\'Thuzad! Naxxramas must not fall!',14769,6,0,0,'lich_king SAY_SAPP_DIALOG4_LICH'), +(-1533088,'As you command, Master!',14469,6,0,0,'kelthuzad SAY_SAPP_DIALOG5'), +(-1533089,'No!!! A curse upon you, interlopers! The armies of the Lich King will hunt you down. You will not escape your fate...',14484,6,0,0,'kelthuzad SAY_CAT_DIED'), +(-1533090,'Who dares violate the sanctity of my domain? Be warned, all who trespass here are doomed.',14463,6,0,0,'kelthuzad SAY_TAUNT1'), +(-1533091,'Fools, you think yourselves triumphant? You have only taken one step closer to the abyss! ',14464,6,0,0,'kelthuzad SAY_TAUNT2'), +(-1533092,'I grow tired of these games. Proceed, and I will banish your souls to oblivion!',14465,6,0,0,'kelthuzad SAY_TAUNT3'), +(-1533093,'You have no idea what horrors lie ahead. You have seen nothing! The frozen heart of Naxxramas awaits you!',14466,6,0,0,'kelthuzad SAY_TAUNT4'), +(-1533094,'Pray for mercy!',14475,1,0,0,'kelthuzad SAY_AGGRO1'), +(-1533095,'Scream your dying breath!',14476,1,0,0,'kelthuzad SAY_AGGRO2'), +(-1533096,'The end is upon you!',14477,1,0,0,'kelthuzad SAY_AGGRO3'), +(-1533097,'The dark void awaits you!',14478,1,0,0,'kelthuzad SAY_SLAY1'), +(-1533098,'',14479,1,0,0,'kelthuzad SAY_SLAY2'), +(-1533099,'AAAAGHHH!... Do not rejoice... your victory is a hollow one... for I shall return with powers beyond your imagining!',14480,1,0,0,'kelthuzad SAY_DEATH'), +(-1533100,'Your soul, is bound to me now!',14472,1,0,0,'kelthuzad SAY_CHAIN1'), +(-1533101,'There will be no escape!',14473,1,0,0,'kelthuzad SAY_CHAIN2'), +(-1533102,'I will freeze the blood in your veins!',14474,1,0,0,'kelthuzad SAY_FROST_BLAST'), +(-1533103,'Master! I require aid! ',14470,1,0,0,'kelthuzad SAY_REQUEST_AID'), +(-1533104,'Very well... warriors of the frozen wastes, rise up! I command you to fight, kill, and die for your master. Let none survive...',0,1,0,0,'kelthuzad SAY_ANSWER_REQUEST'), +(-1533105,'Minions, servants, soldiers of the cold dark, obey the call of Kel\'Thuzad!',14471,1,0,0,'kelthuzad SAY_SUMMON_MINIONS'), +(-1533106,'Your petty magics are no challenge to the might of the Scourge! ',14481,1,0,0,'kelthuzad SAY_SPECIAL1_MANA_DET'), +(-1533107,'Enough! I grow tired of these distractions! ',14483,1,0,0,'kelthuzad SAY_SPECIAL3_MANA_DET'), +(-1533108,'Fools, you have spread your powers too thin. Be free, my minions!',14482,1,0,0,'kelthuzad SAY_SPECIAL2_DISPELL'), + +(-1533109,'You are mine now!',8825,1,0,0,'heigan SAY_AGGRO1'), +(-1533110,'I see you!',8826,1,0,0,'heigan SAY_AGGRO2'), +(-1533111,'You...are next!',8827,1,0,0,'heigan SAY_AGGRO3'), +(-1533112,'Close your eyes... sleep!',8829,1,0,0,'heigan SAY_SLAY'), +(-1533113,'The races of the world will perish. It is only a matter of time.',8830,1,0,0,'heigan SAY_TAUNT1'), +(-1533114,'I see endless suffering, I see torment, I see rage. I see... everything!',8831,1,0,0,'heigan SAY_TAUNT2'), +(-1533115,'Soon... the world will tremble!',8832,1,0,0,'heigan SAY_TAUNT3'), +(-1533116,'The end is upon you.',8833,1,0,0,'heigan SAY_CHANNELING'), +(-1533117,'Hungry worms will feast on your rotten flesh!',8834,1,0,0,'heigan SAY_TAUNT4'), +(-1533118,'Noo... o...',8828,1,0,0,'heigan SAY_DEATH'), + +(-1533119,'%s spots a nearby Zombie to devour!',0,3,0,0,'gluth EMOTE_ZOMBIE'), + +(-1533120,'Hah hah, I\'m just getting warmed up!',8852,1,0,0,'razuvious SAY_AGGRO1'), +(-1533121,'Stand and fight!',8853,1,0,0,'razuvious SAY_AGGRO2'), +(-1533122,'Show me what you\'ve got!',8854,1,0,0,'razuvious SAY_AGGRO3'), +(-1533123,'Sweep the leg! Do you have a problem with that?',8861,1,0,0,'razuvious SAY_SLAY1'), +(-1533124,'You should have stayed home!',8862,1,0,0,'razuvious SAY_SLAY2'), +(-1533125,'Do as I taught you!',8855,1,0,0,'razuvious SAY_COMMAND1'), +(-1533126,'Show them no mercy!',8856,1,0,0,'razuvious SAY_COMMAND2'), +(-1533127,'You disappoint me, students!',8858,1,0,0,'razuvious SAY_COMMAND3'), +(-1533128,'The time for practice is over! Show me what you\'ve learned!',8859,1,0,0,'razuvious SAY_COMMAND4'), +(-1533129,'An honorable... death...',8860,1,0,0,'razuvious SAY_DEATH'), + +(-1533130,'%s summons forth Skeletal Warriors!',0,3,0,0,'noth EMOTE_WARRIOR'), +(-1533131,'%s raises more skeletons!',0,3,0,0,'noth EMOTE_SKELETON'), +(-1533132,'%s teleports to the balcony above!',0,3,0,0,'noth EMOTE_TELEPORT'), +(-1533133,'%s teleports back into the battle!',0,3,0,0,'noth EMOTE_TELEPORT_RETURN'), + +(-1533134,'A Guardian of Icecrown enters the fight!',0,3,0,0,'kelthuzad EMOTE_GUARDIAN'), +(-1533135,'%s strikes!',0,3,0,0,'kelthuzad EMOTE_PHASE2'), + +(-1533136,'%s teleports and begins to channel a spell!',0,3,0,0,'heigan EMOTE_TELEPORT'), +(-1533137,'%s rushes to attack once more!',0,3,0,0,'heigan EMOTE_RETURN'), + +(-1533138,'%s teleports into the fray!',0,3,0,0,'gothik EMOTE_TO_FRAY'), +(-1533139,'The central gate opens!',0,3,0,0,'gothik EMOTE_GATE'), +(-1533140,'Brazenly you have disregarded powers beyond your understanding.',0,1,0,0,'gothik SAY_SPEECH_2'), +(-1533141,'You have fought hard to invade the realm of the harvester.',0,1,0,0,'gothik SAY_SPEECH_3'), +(-1533142,'Now there is only one way out - to walk the lonely path of the damned.',0,1,0,0,'gothik SAY_SPEECH_4'), + +(-1533143,'An aura of necrotic energy blocks all healing!',0,3,0,0,'Loatheb EMOTE_AURA_BLOCKING'), +(-1533144,'The power of Necrotic Aura begins to wane!',0,3,0,0,'Loatheb EMOTE_AURA_WANE'), +(-1533145,'The aura fades away, allowing healing once more!',0,3,0,0,'Loatheb EMOTE_AURA_FADING'), + +(-1533146,'%s spins her web into a cocoon!',0,3,0,0,'maexxna EMOTE_SPIN_WEB'), +(-1533147,'Spiderlings appear on the web!',0,3,0,0,'maexxna EMOTE_SPIDERLING'), +(-1533148,'%s sprays strands of web everywhere!',0,3,0,0,'maexxna EMOTE_SPRAY'), + +(-1533149,'%s loses its link!',0,3,0,0,'tesla_coil EMOTE_LOSING_LINK'), +(-1533150,'%s overloads!',0,3,0,0,'tesla_coil EMOTE_TESLA_OVERLOAD'), +(-1533151,'The polarity has shifted!',0,3,0,0,'thaddius EMOTE_POLARITY_SHIFT'), + +(-1533152,'%s decimates all nearby flesh!',0,3,0,0,'gluth EMOTE_DECIMATE'), + +(-1533153,'A %s joins the fight!',0,3,0,0,'crypt_guard EMOTE_CRYPT_GUARD'), +(-1533154,'%s begins to unleash an insect swarm!',0,3,0,0,'anubrekhan EMOTE_INSECT_SWARM'), +(-1533155,'Corpse Scarabs appear from a Crypt Guard\'s corpse!',0,3,0,0,'anubrekhan EMOTE_CORPSE_SCARABS'), + +(-1533156,'%s casts Unyielding Pain on everyone!',0,3,0,0,'lady_blaumeux EMOTE_UNYIELDING_PAIN'), +(-1533157,'%s casts Condemation on everyone!',0,3,0,0,'sir_zeliek EMOTE_CONDEMATION'), + +(-1533158,'%s injects you with a mutagen!',0,5,0,0,'grobbulus EMOTE_INJECTION'); + +-- -1 534 000 THE BATTLE OF MT. HYJAL +INSERT INTO script_texts (entry,content_default,sound,type,language,emote,comment) VALUES +(-1534000,'I\'m in jeopardy, help me if you can!',11007,1,0,0,'jaina hyjal ATTACKED 1'), +(-1534001,'They\'ve broken through!',11049,1,0,0,'jaina hyjal ATTACKED 2'), +(-1534002,'Stay alert! Another wave approaches.',11008,1,0,0,'jaina hyjal INCOMING'), +(-1534003,'Don\'t give up! We must prevail!',11006,1,0,0,'jaina hyjal BEGIN'), +(-1534004,'Hold them back as long as possible.',11050,1,0,0,'jaina hyjal RALLY 1'), +(-1534005,'We must hold strong!',11051,1,0,0,'jaina hyjal RALLY 2'), +(-1534006,'We are lost. Fall back!',11009,1,0,0,'jaina hyjal FAILURE'), +(-1534007,'We have won valuable time. Now we must pull back!',11011,1,0,0,'jaina hyjal SUCCESS'), +(-1534008,'I did... my best.',11010,1,0,0,'jaina hyjal DEATH'), + +(-1534009,'I will lie down for no one!',11031,1,0,0,'thrall hyjal ATTACKED 1'), +(-1534010,'Bring the fight to me and pay with your lives!',11061,1,0,0,'thrall hyjal ATTACKED 2'), +(-1534011,'Make ready for another wave! LOK-TAR OGAR!',11032,1,0,0,'thrall hyjal INCOMING'), +(-1534012,'Hold them back! Do not falter!',11030,1,0,0,'thrall hyjal BEGIN'), +(-1534013,'Victory or death!',11059,1,0,0,'thrall hyjal RALLY 1'), +(-1534014,'Do not give an inch of ground!',11060,1,0,0,'thrall hyjal RALLY 2'), +(-1534015,'It is over. Withdraw! We have failed.',11033,1,0,0,'thrall hyjal FAILURE'), +(-1534016,'We have played our part and done well. It is up to the others now.',11035,1,0,0,'thrall hyjal SUCCESS'), +(-1534017,'Uraaa...',11034,1,0,0,'thrall hyjal DEATH'), + +(-1534018,'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.',10986,6,0,0,'archimonde SAY_PRE_EVENTS_COMPLETE'), +(-1534019,'Your resistance is insignificant.',10987,1,0,0,'archimonde SAY_AGGRO'), +(-1534020,'This world will burn!',10990,1,0,0,'archimonde SAY_DOOMFIRE1'), +(-1534021,'Manach sheek-thrish!',11041,1,0,0,'archimonde SAY_DOOMFIRE2'), +(-1534022,'A-kreesh!',10989,1,0,0,'archimonde SAY_AIR_BURST1'), +(-1534023,'Away vermin!',11043,1,0,0,'archimonde SAY_AIR_BURST2'), +(-1534024,'All creation will be devoured!',11044,1,0,0,'archimonde SAY_SLAY1'), +(-1534025,'Your soul will languish for eternity.',10991,1,0,0,'archimonde SAY_SLAY2'), +(-1534026,'I am the coming of the end!',11045,1,0,0,'archimonde SAY_SLAY3'), +(-1534027,'At last it is here. Mourn and lament the passing of all you have ever known and all that would have been! Akmin-kurai!',10993,1,0,0,'archimonde SAY_ENRAGE'), +(-1534028,'No, it cannot be! Nooo!',10992,1,0,0,'archimonde SAY_DEATH'), +(-1534029,'You are mine now.',10988,1,0,0,'archimonde SAY_SOUL_CHARGE1'), +(-1534030,'Bow to my will.',11042,1,0,0,'archimonde SAY_SOUL_CHARGE2'); + +-- -1 540 000 SHATTERED HALLS +INSERT INTO script_texts (entry,content_default,sound,type,language,emote,comment) VALUES +(-1540000,'You wish to fight us all at once? This should be amusing!',10262,1,0,0,'nethekurse SAY_INTRO'), +(-1540001,'You can have that one. I no longer need him.',10263,1,0,0,'nethekurse PEON_ATTACK_1'), +(-1540002,'Yes, beat him mercilessly. His skull is a thick as an ogres.',10264,1,0,0,'nethekurse PEON_ATTACK_2'), +(-1540003,'Don\'t waste your time on that one. He\'s weak!',10265,1,0,0,'nethekurse PEON_ATTACK_3'), +(-1540004,'You want him? Very well, take him!',10266,1,0,0,'nethekurse PEON_ATTACK_4'), +(-1540005,'One pitiful wretch down. Go on, take another one.',10267,1,0,0,'nethekurse PEON_DIE_1'), +(-1540006,'Ahh, what a waste... Next!',10268,1,0,0,'nethekurse PEON_DIE_2'), +(-1540007,'I was going to kill him anyway!',10269,1,0,0,'nethekurse PEON_DIE_3'), +(-1540008,'Thank you for saving me the trouble! Now it\'s my turn to have some fun...',10270,1,0,0,'nethekurse PEON_DIE_4'), +(-1540009,'Beg for your pittyfull life!',10259,1,0,0,'nethekurse SAY_TAUNT_1'), +(-1540010,'Run covad, ruun!',10260,1,0,0,'nethekurse SAY_TAUNT_2'), +(-1540011,'Your pain amuses me.',10261,1,0,0,'nethekurse SAY_TAUNT_3'), +(-1540012,'I\'m already bored.',10271,1,0,0,'nethekurse SAY_AGGRO_1'), +(-1540013,'Come on! ... Show me a real fight.',10272,1,0,0,'nethekurse SAY_AGGRO_2'), +(-1540014,'I had more fun torturing the peons.',10273,1,0,0,'nethekurse SAY_AGGRO_3'), +(-1540015,'You Loose.',10274,1,0,0,'nethekurse SAY_SLAY_1'), +(-1540016,'Ohh! Just die.',10275,1,0,0,'nethekurse SAY_SLAY_2'), +(-1540017,'What a ... a shame.',10276,1,0,0,'nethekurse SAY_DIE'), + +(-1540018,'Smash!',10306,1,0,0,'omrogg GoCombat_1'), +(-1540019,'If you nice me let you live.',10308,1,0,0,'omrogg GoCombat_2'), +(-1540020,'Me hungry!',10309,1,0,0,'omrogg GoCombat_3'), +(-1540021,'Why don\'t you let me do the talking?',10317,1,0,0,'omrogg GoCombatDelay_1'), +(-1540022,'No, we will NOT let you live!',10318,1,0,0,'omrogg GoCombatDelay_2'), +(-1540023,'You always hungry. That why we so fat!',10319,1,0,0,'omrogg GoCombatDelay_3'), +(-1540024,'You stay here. Me go kill someone else!',10303,1,0,0,'omrogg Threat_1'), +(-1540025,'What are you doing!',10315,1,0,0,'omrogg Threat_2'), +(-1540026,'Me kill someone else...',10302,1,0,0,'omrogg Threat_3'), +(-1540027,'Me not like this one...',10300,1,0,0,'omrogg Threat_4'), +(-1540028,'That\'s not funny!',10314,1,0,0,'omrogg ThreatDelay1_1'), +(-1540029,'Me get bored...',10305,1,0,0,'omrogg ThreatDelay1_2'), +(-1540030,'I\'m not done yet, idiot!',10313,1,0,0,'omrogg ThreatDelay1_3'), +(-1540031,'Hey you numbskull!',10312,1,0,0,'omrogg ThreatDelay1_4'), +(-1540032,'Ha ha ha.',10304,1,0,0,'omrogg ThreatDelay2_1'), +(-1540033,'Whhy! He almost dead!',10316,1,0,0,'omrogg ThreatDelay2_2'), +(-1540034,'H\'ey...',10307,1,0,0,'omrogg ThreatDelay2_3'), +(-1540035,'We kill his friend!',10301,1,0,0,'omrogg ThreatDelay2_4'), +(-1540036,'This one die easy!',10310,1,0,0,'omrogg Killing_1'), +(-1540037,'I\'m tired. You kill next one!',10320,1,0,0,'omrogg Killing_2'), +(-1540038,'That\'s because I do all the hard work!',10321,1,0,0,'omrogg KillingDelay_1'), +(-1540039,'This all...your fault!',10311,1,0,0,'omrogg YELL_DIE_L'), +(-1540040,'I...hate...you...',10322,1,0,0,'omrogg YELL_DIE_R'), +(-1540041,'%s enrages!',0,2,0,0,'omrogg EMOTE_ENRAGE'), + +(-1540042,'Ours is the true Horde! The only Horde!',10323,1,0,0,'kargath SAY_AGGRO1'), +(-1540043,'I\'ll carve the meat from your bones!',10324,1,0,0,'kargath SAY_AGGRO2'), +(-1540044,'I am called Bladefist for a reason, as you will see!',10325,1,0,0,'kargath SAY_AGGRO3'), +(-1540045,'For the real Horde!',10326,1,0,0,'kargath SAY_SLAY1'), +(-1540046,'I am the only Warchief!',10327,1,0,0,'kargath SAY_SLAY2'), +(-1540047,'The true Horde... will.. prevail...',10328,1,0,0,'kargath SAY_DEATH'), +(-1540048,'Cowards! You\'ll never pull me into the shadows!',0,1,0,0,'kargath SAY_EVADE'), + +(-1540049,'The Alliance dares to intrude this far into my fortress? Bring out the Honor Hold prisoners and call for the executioner! They\'ll pay with their lives for this trespass!',0,6,0,0,'kargath SAY_EXECUTE_ALLY'), +(-1540050,'It looks like we have a ranking officer among our captives...how amusing. Execute the green-skinned dog at once!',0,6,0,0,'kargath SAY_EXECUTE_HORDE'); + +-- -1 542 000 BLOOD FURNACE +INSERT INTO script_texts (entry,content_default,sound,type,language,emote,comment) VALUES +(-1542000,'Who dares interrupt... What is this? What have you done? You ruin everything!',10164,1,0,0,'kelidan SAY_WAKE'), +(-1542001,'You mustn\'t let him loose!',10166,1,0,0,'kelidan SAY_ADD_AGGRO_1'), +(-1542002,'Ignorant whelps!',10167,1,0,0,'kelidan SAY_ADD_AGGRO_2'), +(-1542003,'You fools! He\'ll kill us all!',10168,1,0,0,'kelidan SAY_ADD_AGGRO_3'), +(-1542004,'Just as you deserve!',10169,1,0,0,'kelidan SAY_KILL_1'), +(-1542005,'Your friends will soon be joining you.',10170,1,0,0,'kelidan SAY_KILL_2'), +(-1542006,'Closer... Come closer.. and burn!',10165,1,0,0,'kelidan SAY_NOVA'), +(-1542007,'Good luck... you\'ll need it..',10171,1,0,0,'kelidan SAY_DIE'), + +(-1542008,'Come intruders....',0,1,0,0,'broggok SAY_AGGRO'), + +(-1542009,'My work must not be interrupted.',10286,1,0,0,'the_maker SAY_AGGRO_1'), +(-1542010,'Perhaps I can find a use for you.',10287,1,0,0,'the_maker SAY_AGGRO_2'), +(-1542011,'Anger... Hate... These are tools I can use.',10288,1,0,0,'the_maker SAY_AGGRO_3'), +(-1542012,'Let\'s see what I can make of you.',10289,1,0,0,'the_maker SAY_KILL_1'), +(-1542013,'It is pointless to resist.',10290,1,0,0,'the_maker SAY_KILL_2'), +(-1542014,'Stay away from... me.',10291,1,0,0,'the_maker SAY_DIE'), + +(-1542015,'Kill them!',0,1,0,0,'broggok SAY_BROGGOK_INTRO'), + +(-1542016,'How long do you beleive your pathetic sorcery can hold me?',0,6,0,0,'magtheridon SAY_MAGTHERIDON_WARN'); + +-- -1 543 000 HELLFIRE RAMPARTS +INSERT INTO script_texts (entry,content_default,sound,type,language,emote,comment) VALUES +(-1543000,'Do you smell that? Fresh meat has somehow breached our citadel. Be wary of any intruders.',0,1,0,0,'gargolmar SAY_TAUNT'), +(-1543001,'Heal me! QUICKLY!',10329,1,0,0,'gargolmar SAY_HEAL'), +(-1543002,'Back off, pup!',10330,1,0,0,'gargolmar SAY_SURGE'), +(-1543003,'What have we here...?',10331,1,0,0,'gargolmar SAY_AGGRO_1'), +(-1543004,'Heh... this may hurt a little.',10332,1,0,0,'gargolmar SAY_AGGRO_2'), +(-1543005,'I\'m gonna enjoy this.',10333,1,0,0,'gargolmar SAY_AGGRO_3'), +(-1543006,'Say farewell!',10334,1,0,0,'gargolmar SAY_KILL_1'), +(-1543007,'Much too easy...',10335,1,0,0,'gargolmar SAY_KILL_2'), +(-1543008,'Hahah.. ..argh!',10336,1,0,0,'gargolmar SAY_DIE'), + +(-1543009,'You dare stand against me?!',10280,1,0,0,'omor SAY_AGGRO_1'), +(-1543010,'I will not be defeated!',10279,1,0,0,'omor SAY_AGGRO_2'), +(-1543011,'Your insolence will be your death.',10281,1,0,0,'omor SAY_AGGRO_3'), +(-1543012,'Achor-she-ki! Feast my pet! Eat your fill!',10277,1,0,0,'omor SAY_SUMMON'), +(-1543013,'A-Kreesh!',10278,1,0,0,'omor SAY_CURSE'), +(-1543014,'Die, weakling!',10282,1,0,0,'omor SAY_KILL_1'), +(-1543015,'It is... not over.',10284,1,0,0,'omor SAY_DIE'), +(-1543016,'I am victorious!',10283,1,0,0,'omor SAY_WIPE'), + +(-1543017,'You have faced many challenges, pity they were all in vain. Soon your people will kneel to my lord!',10292,1,0,0,'vazruden SAY_INTRO'), +(-1543018,'Your time is running out!',10294,1,0,0,'vazruden SAY_AGGRO1'), +(-1543019,'You are nothing, I answer a higher call!',10295,1,0,0,'vazruden SAY_AGGRO2'), +(-1543020,'The Dark Lord laughs at you!',10296,1,0,0,'vazruden SAY_AGGRO3'), +(-1543021,'Is there no one left to test me?',10293,1,0,0,'vazruden SAY_TAUNT'), +(-1543022,'It is over. Finished!',10297,1,0,0,'vazruden SAY_KILL1'), +(-1543023,'Your days are done!',10298,1,0,0,'vazruden SAY_KILL2'), +(-1543024,'My lord will be the end you all...',10299,1,0,0,'vazruden SAY_DEATH'), +(-1543025,'%s descends from the sky.',0,3,0,0,'vazruden EMOTE_DESCEND'); + +-- -1 544 000 MAGTHERIDON'S LAIR +INSERT INTO script_texts (entry,content_default,sound,type,language,emote,comment) VALUES +(-1544000,'Wretched, meddling insects. Release me and perhaps i will grant you a merciful death!',10247,6,0,0,'magtheridon SAY_TAUNT1'), +(-1544001,'Vermin! Leeches! Take my blood and choke on it!',10248,6,0,0,'magtheridon SAY_TAUNT2'), +(-1544002,'Illidan is an arrogant fool. I will crush him and reclaim Outland as my own.',10249,6,0,0,'magtheridon SAY_TAUNT3'), +(-1544003,'Away, you mindless parasites. My blood is my own!',10250,6,0,0,'magtheridon SAY_TAUNT4'), +(-1544004,'How long do you believe your pathetic sorcery can hold me?',10251,6,0,0,'magtheridon SAY_TAUNT5'), +(-1544005,'My blood will be the end of you!',10252,6,0,0,'magtheridon SAY_TAUNT6'), +(-1544006,'I...am...UNLEASHED!!!',10253,1,0,0,'magtheridon SAY_FREED'), +(-1544007,'Thank you for releasing me. Now...die!',10254,1,0,0,'magtheridon SAY_AGGRO'), +(-1544008,'Not again...NOT AGAIN!',10256,1,0,0,'magtheridon SAY_BANISH'), +(-1544009,'I will not be taken so easily. Let the walls of this prison tremble...and FALL!!!',10257,1,0,0,'magtheridon SAY_CHAMBER_DESTROY'), +(-1544010,'Did you think me weak? Soft? Who is the weak one now?!',10255,1,0,0,'magtheridon SAY_PLAYER_KILLED'), +(-1544011,'The Legion...will consume you...all...',10258,1,0,0,'magtheridon SAY_DEATH'), +(-1544012,'REUSE_ME',0,0,0,0,'REUSE_ME'), +(-1544013,'%s begins to cast Blast Nova!',0,3,0,0,'magtheridon EMOTE_BLASTNOVA'), +(-1544014,'%s\'s bonds begin to weaken!',0,2,0,0,'magtheridon EMOTE_BEGIN'), +(-1544015,'%s breaks free!',0,2,0,0,'magtheridon EMOTE_FREED'), +(-1544016,'%s is nearly free of his bonds!',0,2,0,0,'magtheridon EMOTE_NEARLY_FREE'); + +-- -1 545 000 THE STEAMVAULT +INSERT INTO script_texts (entry,content_default,sound,type,language,emote,comment) VALUES +(-1545000,'Surge forth my pets!',10360,1,0,0,'thespia SAY_SUMMON'), +(-1545001,'The depths will consume you!',10361,1,0,0,'thespia SAY_AGGRO_1'), +(-1545002,'Meet your doom, surface dwellers!',10362,1,0,0,'thespia SAY_AGGRO_2'), +(-1545003,'You will drown in blood!',10363,1,0,0,'thespia SAY_AGGRO_3'), +(-1545004,'To the depths of oblivion with you!',10364,1,0,0,'thespia SAY_SLAY_1'), +(-1545005,'For my lady and master!',10365,1,0,0,'thespia SAY_SLAY_2'), +(-1545006,'Our matron will be.. the end of.. you..',10366,1,0,0,'thespia SAY_DEAD'), + +(-1545007,'I\'m bringin\' the pain!',10367,1,0,0,'mekgineer SAY_MECHANICS'), +(-1545008,'You\'re in for a world of hurt!',10368,1,0,0,'mekgineer SAY_AGGRO_1'), +(-1545009,'Eat hot metal, scumbag!',10369,1,0,0,'mekgineer SAY_AGGRO_2'), +(-1545010,'I\'ll come over there!',10370,1,0,0,'mekgineer SAY_AGGRO_3'), +(-1545011,'I\'m bringin\' the pain!',10371,1,0,0,'mekgineer SAY_AGGRO_4'), +(-1545012,'You just got served, punk!',10372,1,0,0,'mekgineer SOUND_SLAY_1'), +(-1545013,'I own you!',10373,1,0,0,'mekgineer SOUND_SLAY_2'), +(-1545014,'Have fun dyin\', cupcake!',10374,1,0,0,'mekgineer SOUND_SLAY_3'), +(-1545015,'Mommy!',10375,1,0,0,'mekgineer SAY_DEATH'), + +(-1545016,'You deem yourselves worthy simply because you bested my guards? Our work here will not be compromised!',10390,1,0,0,'kalithresh SAY_INTRO'), +(-1545017,'This is not nearly over...',10391,1,0,0,'kalithresh SAY_REGEN'), +(-1545018,'Your head will roll!',10392,1,0,0,'kalithresh SAY_AGGRO1'), +(-1545019,'I despise all of your kind!',10393,1,0,0,'kalithresh SAY_AGGRO2'), +(-1545020,'Ba\'ahntha sol\'dorei!',10394,1,0,0,'kalithresh SAY_AGGRO3'), +(-1545021,'Scram, surface filth!',10395,1,0,0,'kalithresh SAY_SLAY1'), +(-1545022,'Ah ha ha ha ha ha ha!',10396,1,0,0,'kalithresh SAY_SLAY2'), +(-1545023,'For her Excellency... for... Vashj!',10397,1,0,0,'kalithresh SAY_DEATH'), + +(-1545024,'Enjoy the storm, warm bloods!',0,1,0,0,'thespia SAY_CLOUD'); + +-- -1 546 000 THE UNDERBOG + +-- -1 547 000 THE SLAVE PENS + +-- -1 548 000 SERPENTSHRINE CAVERN +INSERT INTO script_texts (entry,content_default,sound,type,language,emote,comment) VALUES +(-1548000,'I cannot allow you to interfere!',11289,1,0,0,'hydross SAY_AGGRO'), +(-1548001,'Better, much better.',11290,1,0,0,'hydross SAY_SWITCH_TO_CLEAN'), +(-1548002,'They have forced me to this...',11291,1,0,0,'hydross SAY_CLEAN_SLAY1'), +(-1548003,'I have no choice.',11292,1,0,0,'hydross SAY_CLEAN_SLAY2'), +(-1548004,'I am... released...',11293,1,0,0,'hydross SAY_CLEAN_DEATH'), +(-1548005,'Aaghh, the poison...',11297,1,0,0,'hydross SAY_SWITCH_TO_CORRUPT'), +(-1548006,'I will purge you from this place.',11298,1,0,0,'hydross SAY_CORRUPT_SLAY1'), +(-1548007,'You are no better than they!',11299,1,0,0,'hydross SAY_CORRUPT_SLAY2'), +(-1548008,'You are the disease, not I',11300,1,0,0,'hydross SAY_CORRUPT_DEATH'), + +(-1548009,'Finally, my banishment ends!',11312,1,0,0,'leotheras SAY_AGGRO'), +(-1548010,'Be gone, trifling elf. I am in control now!',11304,1,0,0,'leotheras SAY_SWITCH_TO_DEMON'), +(-1548011,'We all have our demons...',11305,1,0,0,'leotheras SAY_INNER_DEMONS'), +(-1548012,'I have no equal.',11306,1,0,0,'leotheras SAY_DEMON_SLAY1'), +(-1548013,'Perish, mortal.',11307,1,0,0,'leotheras SAY_DEMON_SLAY2'), +(-1548014,'Yes, YES! Ahahah!',11308,1,0,0,'leotheras SAY_DEMON_SLAY3'), +(-1548015,'Kill! KILL!',11314,1,0,0,'leotheras SAY_NIGHTELF_SLAY1'), +(-1548016,'That\'s right! Yes!',11315,1,0,0,'leotheras SAY_NIGHTELF_SLAY2'), +(-1548017,'Who\'s the master now?',11316,1,0,0,'leotheras SAY_NIGHTELF_SLAY3'), +(-1548018,'No... no! What have you done? I am the master! Do you hear me? I am... aaggh! Can\'t... contain him...',11313,1,0,0,'leotheras SAY_FINAL_FORM'), +(-1548019,'At last I am liberated. It has been too long since I have tasted true freedom!',11309,1,0,0,'leotheras SAY_FREE'), +(-1548020,'You cannot kill me! Fools, I\'ll be back! I\'ll... aarghh...',11317,1,0,0,'leotheras SAY_DEATH'), + +(-1548021,'Guards, attention! We have visitors...',11277,1,0,0,'karathress SAY_AGGRO'), +(-1548022,'Your overconfidence will be your undoing! Guards, lend me your strength!',11278,1,0,0,'karathress SAY_GAIN_BLESSING'), +(-1548023,'Go on, kill them! I\'ll be the better for it!',11279,1,0,0,'karathress SAY_GAIN_ABILITY1'), +(-1548024,'I am more powerful than ever!',11280,1,0,0,'karathress SAY_GAIN_ABILITY2'), +(-1548025,'More knowledge, more power!',11281,1,0,0,'karathress SAY_GAIN_ABILITY3'), +(-1548026,'Land-dwelling scum!',11282,1,0,0,'karathress SAY_SLAY1'), +(-1548027,'Alana be\'lendor!',11283,1,0,0,'karathress SAY_SLAY2'), +(-1548028,'I am rid of you.',11284,1,0,0,'karathress SAY_SLAY3'), +(-1548029,'Her ... excellency ... awaits!',11285,1,0,0,'karathress SAY_DEATH'), + +(-1548030,'Flood of the deep, take you!',11321,1,0,0,'morogrim SAY_AGGRO'), +(-1548031,'By the Tides, kill them at once!',11322,1,0,0,'morogrim SAY_SUMMON1'), +(-1548032,'Destroy them my subjects!',11323,1,0,0,'morogrim SAY_SUMMON2'), +(-1548033,'There is nowhere to hide!',11324,1,0,0,'morogrim SAY_SUMMON_BUBL1'), +(-1548034,'Soon it will be finished!',11325,1,0,0,'morogrim SAY_SUMMON_BUBL2'), +(-1548035,'It is done!',11326,1,0,0,'morogrim SAY_SLAY1'), +(-1548036,'Strugging only makes it worse.',11327,1,0,0,'morogrim SAY_SLAY2'), +(-1548037,'Only the strong survive.',11328,1,0,0,'morogrim SAY_SLAY3'), +(-1548038,'Great... currents of... Ageon.',11329,1,0,0,'morogrim SAY_DEATH'), +(-1548039,'%s sends his enemies to their watery graves!',0,3,0,0,'morogrim EMOTE_WATERY_GRAVE'), +(-1548040,'The violent earthquake has alerted nearby murlocs!',0,3,0,0,'morogrim EMOTE_EARTHQUAKE'), +(-1548041,'%s summons Watery Globules!',0,3,0,0,'morogrim EMOTE_WATERY_GLOBULES'), + +(-1548042,'Water is life. It has become a rare commodity here in Outland. A commodity that we alone shall control. We are the Highborne, and the time has come at last for us to retake our rightful place in the world!',11531,1,0,0,'vashj SAY_INTRO'), +(-1548043,'I\'ll split you from stem to stern!',11532,1,0,0,'vashj SAY_AGGRO1'), +(-1548044,'Victory to Lord Illidan!',11533,1,0,0,'vashj SAY_AGGRO2'), +(-1548045,'I spit on you, surface filth!',11534,1,0,0,'vashj SAY_AGGRO3'), +(-1548046,'Death to the outsiders!',11535,1,0,0,'vashj SAY_AGGRO4'), +(-1548047,'I did not wish to lower myself by engaging your kind, but you leave me little choice!',11538,1,0,0,'vashj SAY_PHASE1'), +(-1548048,'The time is now! Leave none standing!',11539,1,0,0,'vashj SAY_PHASE2'), +(-1548049,'You may want to take cover.',11540,1,0,0,'vashj SAY_PHASE3'), +(-1548050,'Straight to the heart!',11536,1,0,0,'vashj SAY_BOWSHOT1'), +(-1548051,'Seek your mark!',11537,1,0,0,'vashj SAY_BOWSHOT2'), +(-1548052,'Your time ends now!',11541,1,0,0,'vashj SAY_SLAY1'), +(-1548053,'You have failed!',11542,1,0,0,'vashj SAY_SLAY2'), +(-1548054,'Be\'lamere an\'delay',11543,1,0,0,'vashj SAY_SLAY3'), +(-1548055,'Lord Illidan, I... I am... sorry.',11544,1,0,0,'vashj SAY_DEATH'), + +(-1548056,'%s takes a deep breath!',0,3,0,0,'lurker below EMOTE_DEEP_BREATH'); + +-- -1 550 000 THE EYE +INSERT INTO script_texts (entry,content_default,sound,type,language,emote,comment) VALUES +(-1550000,'Alert, you are marked for extermination!',11213,1,0,0,'voidreaver SAY_AGGRO'), +(-1550001,'Extermination, successful.',11215,1,0,0,'voidreaver SAY_SLAY1'), +(-1550002,'Imbecile life form, no longer functional.',11216,1,0,0,'voidreaver SAY_SLAY2'), +(-1550003,'Threat neutralized.',11217,1,0,0,'voidreaver SAY_SLAY3'), +(-1550004,'Systems... shutting... down...',11214,1,0,0,'voidreaver SAY_DEATH'), +(-1550005,'Alternative measure commencing...',11218,1,0,0,'voidreaver SAY_POUNDING1'), +(-1550006,'Calculating force parameters...',11219,1,0,0,'voidreaver SAY_POUNDING2'), + +(-1550007,'Tal anu\'men no Sin\'dorei!',11134,1,0,0,'solarian SAY_AGGRO'), +(-1550008,'Ha ha ha! You are hopelessly outmatched!',11139,1,0,0,'solarian SAY_SUMMON1'), +(-1550009,'I will crush your delusions of grandeur!',11140,1,0,0,'solarian SAY_SUMMON2'), +(-1550010,'Your soul belongs to the Abyss!',11136,1,0,0,'solarian SAY_KILL1'), +(-1550011,'By the blood of the Highborne!',11137,1,0,0,'solarian SAY_KILL2'), +(-1550012,'For the Sunwell!',11138,1,0,0,'solarian SAY_KILL3'), +(-1550013,'The warmth of the sun... awaits.',11135,1,0,0,'solarian SAY_DEATH'), +(-1550014,'Enough of this! Now I call upon the fury of the cosmos itself.',0,1,0,0,'solarian SAY_VOIDA'), +(-1550015,'I become ONE... with the VOID!',0,1,0,0,'solarian SAY_VOIDB'), + +(-1550016,'Energy. Power. My people are addicted to it... a dependence made manifest after the Sunwell was destroyed. Welcome... to the future. A pity you are too late to stop it. No one can stop me now! Selama ashal\'anore!',11256,1,0,0,'kaelthas SAY_INTRO'), +(-1550017,'Capernian will see to it that your stay here is a short one.',11257,1,0,0,'kaelthas SAY_INTRO_CAPERNIAN'), +(-1550018,'Well done, you have proven worthy to test your skills against my master engineer, Telonicus.',11258,1,0,0,'kaelthas SAY_INTRO_TELONICUS'), +(-1550019,'Let us see how your nerves hold up against the Darkener, Thaladred.',11259,1,0,0,'kaelthas SAY_INTRO_THALADRED'), +(-1550020,'You have persevered against some of my best advisors... but none can withstand the might of the Blood Hammer. Behold, Lord Sanguinar!',11260,1,0,0,'kaelthas SAY_INTRO_SANGUINAR'), +(-1550021,'As you see, I have many weapons in my arsenal...',11261,1,0,0,'kaelthas SAY_PHASE2_WEAPON'), +(-1550022,'Perhaps I underestimated you. It would be unfair to make you fight all four advisors at once, but... fair treatment was never shown to my people. I\'m just returning the favor.',11262,1,0,0,'kaelthas SAY_PHASE3_ADVANCE'), +(-1550023,'Alas, sometimes one must take matters into one\'s own hands. Balamore shanal!',11263,1,0,0,'kaelthas SAY_PHASE4_INTRO2'), +(-1550024,'I have not come this far to be stopped! The future I have planned will not be jeopardized! Now you will taste true power!!',11273,1,0,0,'kaelthas SAY_PHASE5_NUTS'), +(-1550025,'You will not prevail.',11270,1,0,0,'kaelthas SAY_SLAY1'), +(-1550026,'You gambled...and lost.',11271,1,0,0,'kaelthas SAY_SLAY2'), +(-1550027,'This was Child\'s play.',11272,1,0,0,'kaelthas SAY_SLAY3'), +(-1550028,'Obey me.',11268,1,0,0,'kaelthas SAY_MINDCONTROL1'), +(-1550029,'Bow to my will.',11269,1,0,0,'kaelthas SAY_MINDCONTROL2'), +(-1550030,'Let us see how you fare when your world is turned upside down.',11264,1,0,0,'kaelthas SAY_GRAVITYLAPSE1'), +(-1550031,'Having trouble staying grounded?',11265,1,0,0,'kaelthas SAY_GRAVITYLAPSE2'), +(-1550032,'Anara\'nel belore!',11267,1,0,0,'kaelthas SAY_SUMMON_PHOENIX1'), +(-1550033,'By the power of the sun!',11266,1,0,0,'kaelthas SAY_SUMMON_PHOENIX2'), +(-1550034,'For...Quel...thalas!',11274,1,0,0,'kaelthas SAY_DEATH'), + +(-1550035,'Prepare yourselves!',11203,1,0,0,'thaladred SAY_THALADRED_AGGRO'), +(-1550036,'Forgive me, my prince! I have... failed.',11204,1,0,0,'thaladred SAY_THALADRED_DEATH'), +(-1550037,'%s sets his gaze on $N!',0,2,0,0,'thaladred EMOTE_THALADRED_GAZE'), + +(-1550038,'Blood for blood!',11152,1,0,0,'sanguinar SAY_SANGUINAR_AGGRO'), +(-1550039,'NO! I ...will... not...',11153,1,0,0,'sanguinar SAY_SANGUINAR_DEATH'), + +(-1550040,'The sin\'dore reign supreme!',11117,1,0,0,'capernian SAY_CAPERNIAN_AGGRO'), +(-1550041,'This is not over!',11118,1,0,0,'capernian SAY_CAPERNIAN_DEATH'), + +(-1550042,'Anar\'alah belore!',11157,1,0,0,'telonicus SAY_TELONICUS_AGGRO'), +(-1550043,'More perils... await',11158,1,0,0,'telonicus SAY_TELONICUS_DEATH'), + +(-1550044,'%s begins to cast Pyroblast!',0,3,0,0,'kaelthas EMOTE_PYROBLAST'); + +-- -1 552 000 THE ARCATRAZ +INSERT INTO script_texts (entry,content_default,sound,type,language,emote,comment) VALUES +(-1552000,'It is a small matter to control the mind of the weak... for I bear allegiance to powers untouched by time, unmoved by fate. No force on this world or beyond harbors the strength to bend our knee... not even the mighty Legion!',11122,1,0,0,'skyriss SAY_INTRO'), +(-1552001,'Bear witness to the agent of your demise!',11123,1,0,0,'skyriss SAY_AGGRO'), +(-1552002,'Your fate is written!',11124,1,0,0,'skyriss SAY_KILL_1'), +(-1552003,'The chaos I have sown here is but a taste...',11125,1,0,0,'skyriss SAY_KILL_2'), +(-1552004,'You will do my bidding, weakling.',11127,1,0,0,'skyriss SAY_MIND_1'), +(-1552005,'Your will is no longer your own.',11128,1,0,0,'skyriss SAY_MIND_2'), +(-1552006,'Flee in terror!',11129,1,0,0,'skyriss SAY_FEAR_1'), +(-1552007,'I will show you horrors undreamed of!',11130,1,0,0,'skyriss SAY_FEAR_2'), +(-1552008,'We span the universe, as countless as the stars!',11131,1,0,0,'skyriss SAY_IMAGE'), +(-1552009,'I am merely one of... infinite multitudes.',11126,1,0,0,'skyriss SAY_DEATH'), + +(-1552010,'Where in Bonzo\'s brass buttons am I? And who are-- yaaghh, that\'s one mother of a headache!',11171,1,0,0,'millhouse SAY_INTRO_1'), +(-1552011,'\"Lowly\"? I don\'t care who you are friend, no one refers to the mighty Millhouse Manastorm as \"Lowly\"! I have no idea what goes on here, but I will gladly join your fight against this impudent imbecile! Prepare to defend yourself, cretin!',11172,1,0,0,'millhouse SAY_INTRO_2'), +(-1552012,'I just need to get some things ready first. You guys go ahead and get started. I need to summon up some water...',11173,1,0,0,'millhouse SAY_WATER'), +(-1552013,'Fantastic! Next, some protective spells. Yes! Now we\'re cookin\'',11174,1,0,0,'millhouse SAY_BUFFS'), +(-1552014,'And of course i\'ll need some mana. You guys are gonna love this, just wait.',11175,1,0,0,'millhouse SAY_DRINK'), +(-1552015,'Aaalllriiiight!! Who ordered up an extra large can of whoop-ass?',11176,1,0,0,'millhouse SAY_READY'), +(-1552016,'I didn\'t even break a sweat on that one.',11177,1,0,0,'millhouse SAY_KILL_1'), +(-1552017,'You guys, feel free to jump in anytime.',11178,1,0,0,'millhouse SAY_KILL_2'), +(-1552018,'I\'m gonna light you up, sweet cheeks!',11179,1,0,0,'millhouse SAY_PYRO'), +(-1552019,'Ice, ice, baby!',11180,1,0,0,'millhouse SAY_ICEBLOCK'), +(-1552020,'Heal me! Oh, for the love of all that is holy, HEAL me! I\'m dying!',11181,1,0,0,'millhouse SAY_LOWHP'), +(-1552021,'You\'ll be hearing from my lawyer...',11182,1,0,0,'millhouse SAY_DEATH'), +(-1552022,'Who\'s bad? Who\'s bad? That\'s right: we bad!',11183,1,0,0,'millhouse SAY_COMPLETE'), + +(-1552023,'I knew the prince would be angry but, I... I have not been myself. I had to let them out! The great one speaks to me, you see. Wait--outsiders. Kael\'thas did not send you! Good... I\'ll just tell the prince you released the prisoners!',11222,1,0,0,'mellichar YELL_INTRO1'), +(-1552024,'The naaru kept some of the most dangerous beings in existence here in these cells. Let me introduce you to another...',11223,1,0,0,'mellichar YELL_INTRO2'), +(-1552025,'Yes, yes... another! Your will is mine!',11224,1,0,0,'mellichar YELL_RELEASE1'), +(-1552026,'Behold another terrifying creature of incomprehensible power!',11225,1,0,0,'mellichar YELL_RELEASE2A'), +(-1552027,'What is this? A lowly gnome? I will do better, O\'great one.',11226,1,0,0,'mellichar YELL_RELEASE2B'), +(-1552028,'Anarchy! Bedlam! Oh, you are so wise! Yes, I see it now, of course!',11227,1,0,0,'mellichar YELL_RELEASE3'), +(-1552029,'One final cell remains. Yes, O\'great one, right away!',11228,1,0,0,'mellichar YELL_RELEASE4'), +(-1552030,'Welcome, O\'great one. I am your humble servant.',11229,1,0,0,'mellichar YELL_WELCOME'), + +(-1552031,'It is unwise to anger me.',11086,1,0,0,'dalliah SAY_AGGRO'), +(-1552032,'Ahh... That is much better.',11091,1,0,0,'dalliah SAY_HEAL_1'), +(-1552033,'Ahh... Just what I needed.',11092,1,0,0,'dalliah SAY_HEAL_2'), +(-1552034,'Completely ineffective. Just like someone else I know.',11087,1,0,0,'dalliah SAY_KILL_1'), +(-1552035,'You chose the wrong opponent.',11088,1,0,0,'dalliah SAY_KILL_2'), +(-1552036,'I\'ll cut you to pieces!',11090,1,0,0,'dalliah SAY_WHIRLWIND_1'), +(-1552037,'Reap the Whirlwind!',11089,1,0,0,'dalliah SAY_WHIRLWIND_2'), +(-1552038,'Now I\'m really... angry...',11093,1,0,0,'dalliah SAY_DEATH'), + +(-1552039,'Have you come to kill Dalliah? Can I watch?',11237,1,0,1,'soccothrates SAY_DALLIAH_AGGRO_1'), +(-1552040,'This may be the end for you, Dalliah. What a shame that would be.',11245,1,0,1,'soccothrates SAY_DALLIAH_TAUNT_1'), +(-1552041,'Facing difficulties, Dalliah? How nice.',11244,1,0,1,'soccothrates SAY_DALLIAH_TAUNT_2'), +(-1552042,'I suggest a new strategy, you draw the attackers while I gather reinforcements. Hahaha!',11246,1,0,1,'soccothrates SAY_DALLIAH_TAUNT_3'), +(-1552043,'Finally! Well done!',11247,1,0,66,'soccothrates SAY_DALLIAH_DEAD'), +(-1552044,'On guard!',11241,1,0,0,'soccothrates SAY_CHARGE_1'), +(-1552045,'Defend yourself, for all the good it will do...',11242,1,0,0,'soccothrates SAY_CHARGE_2'), +(-1552046,'Knew this was... the only way out',11243,1,0,0,'soccothrates SAY_DEATH'), +(-1552047,'Yes, that was quite satisfying',11239,1,0,0,'soccothrates SAY_KILL'), +(-1552048,'At last, a target for my frustrations!',11238,1,0,0,'soccothrates SAY_AGGRO'), + +(-1552049,'Did you call on me?',11236,1,0,397,'soccothrates SAY_INTRO_1'), +(-1552050,'Why would I call on you?',0,1,0,396,'dalliah SAY_INTRO_2'), +(-1552051,'To do your heavy lifting, most likely.',0,1,0,396,'soccothrates SAY_INTRO_3'), +(-1552052,'When I need someone to prance around like an overstuffed peacock, I''ll call on you.',0,1,0,396,'dalliah SAY_INTRO_4'), +(-1552053,'Then I\'ll commit myself to ignoring you.',0,1,0,396,'soccothrates SAY_INTRO_5'), +(-1552054,'What would you know about commitment, sheet-sah?',0,1,0,396,'dalliah SAY_INTRO_6'), +(-1552055,'You\'re the one who should be-- Wait, we have company...',0,1,0,396,'soccothrates SAY_INTRO_7'); + +-- -1 553 000 THE BOTANICA +INSERT INTO script_texts (entry,content_default,sound,type,language,emote,comment) VALUES +(-1553000,'What are you doing? These specimens are very delicate!',11144,1,0,0,'freywinn SAY_AGGRO'), +(-1553001,'Your life cycle is now concluded!',11145,1,0,0,'freywinn SAY_KILL_1'), +(-1553002,'You will feed the worms.',11146,1,0,0,'freywinn SAY_KILL_2'), +(-1553003,'Endorel aluminor!',11147,1,0,0,'freywinn SAY_TREE_1'), +(-1553004,'Nature bends to my will!',11148,1,0,0,'freywinn SAY_TREE_2'), +(-1553005,'The specimens...must be preserved.',11149,1,0,0,'freywinn SAY_DEATH'), + +(-1553006,'%s emits a strange noise.',0,2,0,0,'laj EMOTE_SUMMON'), + +(-1553007,'Who disturbs this sanctuary?',11230,1,0,0,'warp SAY_AGGRO'), +(-1553008,'You must die! But wait: this does not--No, no... you must die!',11231,1,0,0,'warp SAY_SLAY_1'), +(-1553009,'What am I doing? Why do I...',11232,1,0,0,'warp SAY_SLAY_2'), +(-1553010,'Children, come to me!',11233,1,0,0,'warp SAY_SUMMON_1'), +(-1553011,'Maybe this is not--No, we fight! Come to my aid.',11234,1,0,0,'warp SAY_SUMMON_2'), +(-1553012,'So... confused. Do not... belong here!',11235,1,0,0,'warp SAY_DEATH'); + +-- -1 554 000 THE MECHANAR +INSERT INTO script_texts (entry,content_default,sound,type,language,emote,comment) VALUES +(-1554000,'REUSE_ME',0,0,0,0,'REUSE_ME'), +(-1554001,'REUSE_ME',0,0,0,0,'REUSE_ME'), +(-1554002,'REUSE_ME',0,0,0,0,'REUSE_ME'), +(-1554003,'REUSE_ME',0,0,0,0,'REUSE_ME'), +(-1554004,'REUSE_ME',0,0,0,0,'REUSE_ME'), +(-1554005,'REUSE_ME',0,0,0,0,'REUSE_ME'), +(-1554006,'REUSE_ME',0,0,0,0,'REUSE_ME'), +(-1554007,'REUSE_ME',0,0,0,0,'REUSE_ME'), +(-1554008,'REUSE_ME',0,0,0,0,'REUSE_ME'), +(-1554009,'REUSE_ME',0,0,0,0,'REUSE_ME'), +(-1554010,'REUSE_ME',0,0,0,0,'REUSE_ME'), +(-1554011,'REUSE_ME',0,0,0,0,'REUSE_ME'), +(-1554012,'REUSE_ME',0,0,0,0,'REUSE_ME'), + +(-1554013,'Don\'t value your life very much, do you?',11186,1,0,0,'sepethrea SAY_AGGRO'), +(-1554014,'I am not alone.',11191,1,0,0,'sepethrea SAY_SUMMON'), +(-1554015,'Think you can take the heat?',11189,1,0,0,'sepethrea SAY_DRAGONS_BREATH_1'), +(-1554016,'Anar\'endal dracon!',11190,1,0,0,'sepethrea SAY_DRAGONS_BREATH_2'), +(-1554017,'And don\'t come back!',11187,1,0,0,'sepethrea SAY_SLAY1'), +(-1554018,'En\'dala finel el\'dal',11188,1,0,0,'sepethrea SAY_SLAY2'), +(-1554019,'Anu... bala belore...alon.',11192,1,0,0,'sepethrea SAY_DEATH'), + +(-1554020,'We are on a strict timetable. You will not interfere!',11193,1,0,0,'pathaleon SAY_AGGRO'), +(-1554021,'I\'m looking for a team player...',11197,1,0,0,'pathaleon SAY_DOMINATION_1'), +(-1554022,'You work for me now!',0,1,0,0,'pathaleon SAY_DOMINATION_2'), +(-1554023,'Time to supplement my work force.',11196,1,0,0,'pathaleon SAY_SUMMON'), +(-1554024,'I prefeer to be hands-on...',11199,1,0,0,'pathaleon SAY_ENRAGE'), +(-1554025,'A minor inconvenience.',11194,1,0,0,'pathaleon SAY_SLAY_1'), +(-1554026,'Looks like you lose.',11195,1,0,0,'pathaleon SAY_SLAY_2'), +(-1554027,'The project will... continue.',11200,1,0,0,'pathaleon SAY_DEATH'), +(-1554028,'I have been waiting for you!',0,1,0,53,'pathaleon SAY_INTRO'); + +-- -1 555 000 SHADOW LABYRINTH +INSERT INTO script_texts (entry,content_default,sound,type,language,emote,comment) VALUES +(-1555000,'Infidels have invaded the sanctuary! Sniveling pests...You have yet to learn the true meaning of agony!',10473,1,0,0,'hellmaw SAY_INTRO'), +(-1555001,'Pathetic mortals! You will pay dearly!',10475,1,0,0,'hellmaw SAY_AGGRO1'), +(-1555002,'I will break you!',10476,1,0,0,'hellmaw SAY_AGGRO2'), +(-1555003,'Finally! Something to relieve the tedium!',10477,1,0,0,'hellmaw SAY_AGGRO3'), +(-1555004,'Aid me, you fools, before it\'s too late!',10474,1,0,0,'hellmaw SAY_HELP'), +(-1555005,'Do you fear death?',10478,1,0,0,'hellmaw SAY_SLAY1'), +(-1555006,'This is the part I enjoy most.',10479,1,0,0,'hellmaw SAY_SLAY2'), +(-1555007,'Do not...grow...overconfident, mortal.',10480,1,0,0,'hellmaw SAY_DEATH'), + +(-1555008,'All flesh must burn.',10482,1,0,0,'blackhearth SAY_INTRO1'), +(-1555009,'All creation must be unmade!',10483,1,0,0,'blackhearth SAY_INTRO2'), +(-1555010,'Power will be yours!',10484,1,0,0,'blackhearth SAY_INTRO3'), +(-1555011,'You\'ll be sorry!',10486,1,0,0,'blackhearth SAY_AGGRO1'), +(-1555012,'Time for fun!',10487,1,0,0,'blackhearth SAY_AGGRO2'), +(-1555013,'I see dead people!',10488,1,0,0,'blackhearth SAY_AGGRO3'), +(-1555014,'No comin\' back for you!',10489,1,0,0,'blackhearth SAY_SLAY1'), +(-1555015,'Nice try!',10490,1,0,0,'blackhearth SAY_SLAY2'), +(-1555016,'Help us, hurry!',10485,1,0,0,'blackhearth SAY_HELP'), +(-1555017,'This... no... good...',10491,1,0,0,'blackhearth SAY_DEATH'), + +(-1555018,'Be ready for Dark One\'s return.',10492,1,0,0,'blackhearth SAY2_INTRO1'), +(-1555019,'So we have place in new universe.',10493,1,0,0,'blackhearth SAY2_INTRO2'), +(-1555020,'Dark one promise!',10494,1,0,0,'blackhearth SAY2_INTRO3'), +(-1555021,'You\'ll be sorry!',10496,1,0,0,'blackhearth SAY2_AGGRO1'), +(-1555022,'Time to kill!',10497,1,0,0,'blackhearth SAY2_AGGRO2'), +(-1555023,'You be dead people!',10498,1,0,0,'blackhearth SAY2_AGGRO3'), +(-1555024,'Now you gone for good.',10499,1,0,0,'blackhearth SAY2_SLAY1'), +(-1555025,'You failed, haha haha',10500,1,0,0,'blackhearth SAY2_SLAY2'), +(-1555026,'Help us, hurry!',10495,1,0,0,'blackhearth SAY2_HELP'), +(-1555027,'Arrgh, aah...ahhh',10501,1,0,0,'blackhearth SAY2_DEATH'), + +(-1555028,'Keep your minds focused for the days of reckoning are close at hand. Soon, the destroyer of worlds will return to make good on his promise. Soon the destruction of all that is will begin!',10522,1,0,0,'vorpil SAY_INTRO'), +(-1555029,'I\'ll make an offering of your blood!',10524,1,0,0,'vorpil SAY_AGGRO1'), +(-1555030,'You\'ll be a fine example, for the others.',10525,1,0,0,'vorpil SAY_AGGRO2'), +(-1555031,'Good, a worthy sacrifice.',10526,1,0,0,'vorpil SAY_AGGRO3'), +(-1555032,'Come to my aid, heed your master now!',10523,1,0,0,'vorpil SAY_HELP'), +(-1555033,'I serve with pride.',10527,1,0,0,'vorpil SAY_SLAY1'), +(-1555034,'Your death is for the greater cause!',10528,1,0,0,'vorpil SAY_SLAY2'), +(-1555035,'I give my life... Gladly.',10529,1,0,0,'vorpil SAY_DEATH'), + +(-1555036,'%s draws energy from the air.',0,2,0,0,'murmur EMOTE_SONIC_BOOM'); + +-- -1 556 000 SETHEKK HALLS +INSERT INTO script_texts (entry,content_default,sound,type,language,emote,comment) VALUES +(-1556000,'I have pets....of my own!',10502,1,0,0,'syth SAY_SUMMON'), +(-1556001,'Hrrmm.. Time to.. hrrm.. make my move.',10503,1,0,0,'syth SAY_AGGRO_1'), +(-1556002,'Nice pets..hrm.. Yes! ',10504,1,0,0,'syth SAY_AGGRO_2'), +(-1556003,'Nice pets have.. weapons. No so....nice.',10505,1,0,0,'syth SAY_AGGRO_3'), +(-1556004,'Death.. meeting life is.. ',10506,1,0,0,'syth SAY_SLAY_1'), +(-1556005,'Uhn.. Be free..',10507,1,0,0,'syth SAY_SLAY_2'), +(-1556006,'No more life..hrm. No more pain. ',10508,1,0,0,'syth SAY_DEATH'), + +(-1556007,'..Trinkets yes pretty Trinkets....power, great power...power in Trinkets..',10557,1,0,0,'ikiss SAY_INTRO'), +(-1556008,'You make war on Ikiss?..',10554,1,0,0,'ikiss SAY_AGGRO_1'), +(-1556009,'Ikiss cut you pretty....slice you. Yes!',10555,1,0,0,'ikiss SAY_AGGRO_2'), +(-1556010,'No escape for....for you',10556,1,0,0,'ikiss SAY_AGGRO_3'), +(-1556011,'You die....stay away from Trinkets',10558,1,0,0,'ikiss SAY_SLAY_1'), +(-1556012,'',10559,1,0,0,'ikiss SAY_SLAY_2'), +(-1556013,'Ikiss will not....die',10560,1,0,0,'ikiss SAY_DEATH'), +(-1556015,'%s begins to channel arcane energy...',0,3,0,0,'ikiss EMOTE_ARCANE_EXP'), + +(-1556016,'No! How can this be?',0,1,0,0,'anzu SAY_INTRO_1'), +(-1556017,'Pain will be the price for your insolence! You cannot stop me from claiming the Emerald Dream as my own!',0,1,0,0,'anzu SAY_INTRO_2'), +(-1556018,'Awaken, my children and assist your master!',0,1,0,0,'anzu SAY_BANISH'), +(-1556019,'Your magics shall be your undoing... ak-a-ak...',0,5,0,0,'anzu SAY_WHISPER_MAGIC_1'), +(-1556020,'%s returns to stone.',0,2,0,0,'anzu EMOTE_BIRD_STONE'), +(-1556021,'Your powers... ak-ak... turn against you...',0,5,0,0,'anzu SAY_WHISPER_MAGIC_2'), +(-1556022,'Your spells... ke-kaw... are weak magics... easy to turn against you...',0,5,0,0,'anzu SAY_WHISPER_MAGIC_3'); + +-- -1 557 000 MANA TOMBS +INSERT INTO script_texts (entry,content_default,sound,type,language,emote,comment) VALUES +(-1557000,'What is this? You must forgive me, but I was not expecting company. As you can see, we are somewhat preoccupied right now. But no matter. As I am a gracious host, I will tend to you... personally.',10539,1,0,0,'shaffar SAY_INTRO'), +(-1557001,'We have not yet been properly introduced.',10541,1,0,0,'shaffar SAY_AGGRO_1'), +(-1557002,'An epic battle. How exciting!',10542,1,0,0,'shaffar SAY_AGGRO_2'), +(-1557003,'I have longed for a good adventure.',10543,1,0,0,'shaffar SAY_AGGRO_3'), +(-1557004,'It has been... entertaining.',10544,1,0,0,'shaffar SAY_SLAY_1'), +(-1557005,'And now we part company.',10545,1,0,0,'shaffar SAY_SLAY_2'), +(-1557006,'I have such fascinating things to show you.',10540,1,0,0,'shaffar SAY_SUMMON'), +(-1557007,'I must bid you... farewell.',10546,1,0,0,'shaffar SAY_DEAD'), + +(-1557008,'I will feed on your soul.',10561,1,0,0,'pandemonius SAY_AGGRO_1'), +(-1557009,'So... full of life!',10562,1,0,0,'pandemonius SAY_AGGRO_2'), +(-1557010,'Do not... resist.',10563,1,0,0,'pandemonius SAY_AGGRO_3'), +(-1557011,'Yes! I am... empowered!',10564,1,0,0,'pandemonius SAY_KILL_1'), +(-1557012,'More... I must have more!',10565,1,0,0,'pandemonius SAY_KILL_2'), +(-1557013,'To the void... once... more..',10566,1,0,0,'pandemonius SAY_DEATH'), +(-1557014,'%s shifts into the void...',0,3,0,0,'pandemonius EMOTE_DARK_SHELL'); + +-- -1 558 000 AUCHENAI CRYPTS +INSERT INTO script_texts (entry,content_default,sound,type,language,emote,comment) VALUES +(-1558000,'You have defiled the resting place of our ancestors. For this offense, there can be but one punishment. It is fitting that you have come to a place of the dead... for you will soon be joining them.',10509,1,0,0,'maladaar SAY_INTRO'), +(-1558001,'Rise my fallen brothers. Take form and fight!',10512,1,0,0,'maladaar SAY_SUMMON'), +(-1558002,'You will pay with your life!',10513,1,0,0,'maladaar SAY_AGGRO_1'), +(-1558003,'There\'s no turning back now!',10514,1,0,0,'maladaar SAY_AGGRO_2'), +(-1558004,'Serve your penitence!',10515,1,0,0,'maladaar SAY_AGGRO_3'), +(-1558005,'Let your mind be clouded.',10510,1,0,0,'maladaar SAY_ROAR'), +(-1558006,'Stare into the darkness of your soul.',10511,1,0,0,'maladaar SAY_SOUL_CLEAVE'), +(-1558007,'These walls will be your doom.',10516,1,0,0,'maladaar SAY_SLAY_1'), +(-1558008,' Now, you\'ll stay for eternity!',10517,1,0,0,'maladaar SAY_SLAY_2'), +(-1558009,'This is... where.. I belong...',10518,1,0,0,'maladaar SAY_DEATH'), + +(-1558010,'%s focuses on $N',0,3,0,0,'shirrak EMOTE_FOCUS'); + +-- -1 560 000 ESCAPE FROM DURNHOLDE (OLD HILLSBRAD) +INSERT INTO script_texts (entry,content_default,sound,type,language,emote,comment) VALUES +(-1560000,'Thrall! You didn\'t really think you would escape did you? You and your allies shall answer to Blackmoore - after I\'ve had my fun!',10406,0,0,1,'skarloc SAY_ENTER'), + +(-1560001,'My magical power can turn back time to before Thrall\'s death, but be careful. My power to manipulate time is limited.',0,0,0,0,'image of eronzion SAY_RESET_THRALL'), +(-1560002,'I have set back the flow of time just once more. If you fail to prevent Thrall\'s death, then all is lost.',0,0,0,0,'image of eronzion SAY_RESET_THRALL_LAST'), + +(-1560003,'What\'s the meaning of this? GUARDS!',0,0,0,0,'armorer SAY_CALL_GUARDS'), +(-1560004,'All that you know... will be undone.',0,0,0,0,'infinite dragon SAY_INFINITE_AGGRO_1'), +(-1560005,'Let\'s go.',0,0,0,0,'thrall hillsbrad SAY_TH_ARMORY2'), +(-1560006,'%s startles the horse with a fierce yell!',0,2,0,0,'thrall hillsbrad EMOTE_TH_STARTLE_HORSE'), +(-1560007,'I thought I saw something go into the barn.',0,0,0,0,'tarren mill lookout SAY_LOOKOUT_BARN_1'), +(-1560008,'I didn\'t see anything.',0,0,0,0,'tarren mill lookout SAY_PROTECTOR_BARN_2'), +(-1560009,'%s tries to calm the horse down.',0,2,0,0,'thrall hillsbrad EMOTE_TH_CALM_HORSE'), +(-1560010,'Something riled that horse. Let\'s go!',0,0,0,0,'tarren mill lookout SAY_PROTECTOR_BARN_3'), +(-1560011,'Taretha isn\'t here. Let\'s head into town.',0,0,0,0,'thrall hillsbrad SAY_TH_HEAD_TOWN'), +(-1560012,'She\'s not here.',0,0,0,0,'thrall hillsbrad SAY_TH_CHURCH_ENTER'), + +(-1560013,'Thrall! Come outside and face your fate!',10418,1,0,0,'epoch SAY_ENTER1'), +(-1560014,'Taretha\'s life hangs in the balance. Surely you care for her. Surely you wish to save her...',10419,1,0,0,'epoch SAY_ENTER2'), +(-1560015,'Ah, there you are. I had hoped to accomplish this with a bit of subtlety, but I suppose direct confrontation was inevitable. Your future, Thrall, must not come to pass and so...you and your troublesome friends must die!',10420,1,0,0,'epoch SAY_ENTER3'), + +(-1560016,'Thrall\'s trapped himself in the chapel. He can\'t escape now.',0,0,0,0,'tarren mill lookout SAY_LOOKOUT_CHURCH'), +(-1560017,'He\'s here, stop him!',0,0,0,0,'tarren mill lookout SAY_LOOKOUT_INN'), +(-1560018,'We have all the time in the world....',0,0,0,0,'infinite dragon SAY_INFINITE_AGGRO_2'), +(-1560019,'You cannot escape us!',0,0,0,0,'infinite dragon SAY_INFINITE_AGGRO_3'), +(-1560020,'Do not think you can win!',0,0,0,0,'infinite dragon SAY_INFINITE_AGGRO_4'), + +(-1560021,'REUSE_ME',0,0,0,0,'REUSE_ME'), +(-1560022,'REUSE_ME',0,0,0,0,'REUSE_ME'), + +(-1560023,'Very well then. Let\'s go!',10465,0,0,0,'thrall hillsbrad SAY_TH_START_EVENT_PART1'), +(-1560024,'As long as we\'re going with a new plan, I may aswell pick up a weapon and some armor.',0,0,0,0,'thrall hillsbrad SAY_TH_ARMORY'), +(-1560025,'A rider approaches!',10466,0,0,0,'thrall hillsbrad SAY_TH_SKARLOC_MEET'), +(-1560026,'I\'ll never be chained again!',10467,1,0,0,'thrall hillsbrad SAY_TH_SKARLOC_TAUNT'), +(-1560027,'Very well. Tarren Mill lies just west of here. Since time is of the essence...',10468,0,0,0,'thrall hillsbrad SAY_TH_START_EVENT_PART2'), +(-1560028,'Let\'s ride!',10469,0,0,1,'thrall hillsbrad SAY_TH_MOUNTS_UP'), +(-1560029,'Taretha must be in the inn. Let\'s go.',0,0,0,0,'thrall hillsbrad SAY_TH_CHURCH_END'), +(-1560030,'Taretha! What foul magic is this?',0,0,0,0,'thrall hillsbrad SAY_TH_MEET_TARETHA'), +(-1560031,'Who or what was that?',10470,0,0,1,'thrall hillsbrad SAY_TH_EPOCH_WONDER'), +(-1560032,'No!',10471,0,0,5,'thrall hillsbrad SAY_TH_EPOCH_KILL_TARETHA'), +(-1560033,'Goodbye, Taretha. I will never forget your kindness.',10472,0,0,0,'thrall hillsbrad SAY_TH_EVENT_COMPLETE'), +(-1560034,'Things are looking grim...',10458,1,0,0,'thrall hillsbrad SAY_TH_RANDOM_LOW_HP1'), +(-1560035,'I will fight to the last!',10459,1,0,0,'thrall hillsbrad SAY_TH_RANDOM_LOW_HP2'), +(-1560036,'Taretha...',10460,1,0,0,'thrall hillsbrad SAY_TH_RANDOM_DIE1'), +(-1560037,'A good day...to die...',10461,1,0,0,'thrall hillsbrad SAY_TH_RANDOM_DIE2'), +(-1560038,'I have earned my freedom!',10448,0,0,0,'thrall hillsbrad SAY_TH_RANDOM_AGGRO1'), +(-1560039,'This day is long overdue. Out of my way!',10449,0,0,0,'thrall hillsbrad SAY_TH_RANDOM_AGGRO2'), +(-1560040,'I am a slave no longer!',10450,0,0,0,'thrall hillsbrad SAY_TH_RANDOM_AGGRO3'), +(-1560041,'Blackmoore has much to answer for!',10451,0,0,0,'thrall hillsbrad SAY_TH_RANDOM_AGGRO4'), +(-1560042,'You have forced my hand!',10452,0,0,0,'thrall hillsbrad SAY_TH_RANDOM_KILL1'), +(-1560043,'It should not have come to this!',10453,0,0,0,'thrall hillsbrad SAY_TH_RANDOM_KILL2'), +(-1560044,'I did not ask for this!',10454,0,0,0,'thrall hillsbrad SAY_TH_RANDOM_KILL3'), +(-1560045,'I am truly in your debt, strangers.',10455,0,0,0,'thrall hillsbrad SAY_TH_LEAVE_COMBAT1'), +(-1560046,'Thank you, strangers. You have given me hope.',10456,0,0,0,'thrall hillsbrad SAY_TH_LEAVE_COMBAT2'), +(-1560047,'I will not waste this chance. I will seek out my destiny.',10457,0,0,0,'thrall hillsbrad SAY_TH_LEAVE_COMBAT3'), + +(-1560048,'I\'m free! Thank you all!',0,0,0,0,'taretha SAY_TA_FREE'), +(-1560049,'Thrall, you escaped!',0,0,0,0,'taretha SAY_TA_ESCAPED'), + +(-1560050,'That\'s enough out of him.',0,0,0,0,'thrall hillsbrad SAY_TH_KILL_ARMORER'), +(-1560051,'That spell should wipe their memories of us and what just happened. All they should remember now is what reality would be like without the attempted temporal interference. Well done. Thrall will journey on to find his destiny, and Taretha...',0,0,0,0,'erozion SAY_WIPE_MEMORY'), +(-1560052,'Her fate is regrettably unavoidable.',0,0,0,0,'erozion SAY_ABOUT_TARETHA'), +(-1560053,'They call you a monster. But they\'re the monsters, not you. Farewell Thrall.',0,0,0,0,'taretha SAY_TA_FAREWELL'), + +(-1560054,'I\'m glad you\'re safe, Taretha. None of this would have been possible without your friends. They made all of this happen.',0,0,0,0,'thrall hillsbrad SAY_TR_GLAD_SAFE'), +(-1560055,'Thrall, I\'ve never met these people before in my life.',0,0,0,0,'taretha SAY_TA_NEVER_MET'), +(-1560056,'Then who are these people?',0,0,0,0,'thrall hillsbrad SAY_TR_THEN_WHO'), +(-1560057,'I believe I can explain everything to you two if you give me a moment of your time.',0,0,0,0,'erozion SAY_PRE_WIPE'), +(-1560058,'You have done a great thing. Alas, the young warchief\'s memory of these events must be as they originally were ... Andormu awaits you in the master\'s lair.',0,0,0,0,'erozion SAY_AFTER_WIPE'); + +-- -1 564 000 BLACK TEMPLE +INSERT INTO script_texts (entry,content_default,sound,type,language,emote,comment) VALUES +(-1564000,'You will die in the name of Lady Vashj!',11450,1,0,0,'SAY_AGGRO'), +(-1564001,'Stick around!',11451,1,0,0,'SAY_NEEDLE1'), +(-1564002,'I\'ll deal with you later!',11452,1,0,0,'SAY_NEEDLE2'), +(-1564003,'Your success was short lived!',11455,1,0,0,'SAY_SLAY1'), +(-1564004,'Time for you to go!',11456,1,0,0,'SAY_SLAY2'), +(-1564005,'Bel\'anen dal\'lorei!',11453,1,0,0,'SAY_SPECIAL1'), +(-1564006,'Blood will flow!',11454,1,0,0,'SAY_SPECIAL2'), +(-1564007,'Bal\'amer ch\'itah!',11457,1,0,0,'SAY_ENRAGE1'), +(-1564008,'My patience has ran out! Die, DIE!',11458,1,0,0,'SAY_ENRAGE2'), +(-1564009,'Lord Illidan will... crush you.',11459,1,0,0,'SAY_DEATH'), + +(-1564010,'%s acquires a new target!',0,3,0,0,'supremus EMOTE_NEW_TARGET'), +(-1564011,'%s punches the ground in anger!',0,3,0,0,'supremus EMOTE_PUNCH_GROUND'), +(-1564012,'The ground begins to crack open!',0,3,0,0,'supremus EMOTE_GROUND_CRACK'), + +(-1564013,'No! Not yet...',11386,1,0,0,'akama shade SAY_LOW_HEALTH'), +(-1564014,'I will not last much longer...',11385,1,0,0,'akama shade SAY_DEATH'), +(-1564015,'Come out from the shadows! I\'ve returned to lead you against our true enemy! Shed your chains and raise your weapons against your Illidari masters!',0,1,0,0,'akama shade SAY_FREE'), +(-1564016,'Hail our leader! Hail Akama!',0,1,0,0,'akama shade broken SAY_BROKEN_FREE_01'), +(-1564017,'Hail Akama!',0,1,0,0,'akama shade broken SAY_BROKEN_FREE_02'), + +(-1564018,'You play, you pay.',11501,1,0,0,'shahraz SAY_TAUNT1'), +(-1564019,'I\'m not impressed.',11502,1,0,0,'shahraz SAY_TAUNT2'), +(-1564020,'Enjoying yourselves?',11503,1,0,0,'shahraz SAY_TAUNT3'), +(-1564021,'So... business or pleasure?',11504,1,0,0,'shahraz SAY_AGGRO'), +(-1564022,'You seem a little tense.',11505,1,0,0,'shahraz SAY_SPELL1'), +(-1564023,'Don\'t be shy.',11506,1,0,0,'shahraz SAY_SPELL2'), +(-1564024,'I\'m all... yours.',11507,1,0,0,'shahraz SAY_SPELL3'), +(-1564025,'Easy come, easy go.',11508,1,0,0,'shahraz SAY_SLAY1'), +(-1564026,'So much for a happy ending.',11509,1,0,0,'shahraz SAY_SLAY2'), +(-1564027,'Stop toying with my emotions!',11510,1,0,0,'shahraz SAY_ENRAGE'), +(-1564028,'I wasn\'t... finished.',11511,1,0,0,'shahraz SAY_DEATH'), + +(-1564029,'Horde will... crush you.',11432,1,0,0,'bloodboil SOUND_AGGRO'), +(-1564030,'Time to feast!',11433,1,0,0,'bloodboil SAY_SLAY1'), +(-1564031,'More! I want more!',11434,1,0,0,'bloodboil SAY_SLAY2'), +(-1564032,'Drink your blood! Eat your flesh!',11435,1,0,0,'bloodboil SAY_SPECIAL1'), +(-1564033,'I hunger!',11436,1,0,0,'bloodboil SAY_SPECIAL2'), +(-1564034,'',11437,1,0,0,'bloodboil SAY_ENRAGE1'), +(-1564035,'I\'ll rip the meat from your bones!',11438,1,0,0,'bloodboil SAY_ENRAGE2'), +(-1564036,'Aaaahrg...',11439,1,0,0,'bloodboil SAY_DEATH'), + +(-1564037,'I was the first, you know. For me, the wheel of death has spun many times. So much time has passed. I have a lot of catching up to do...',11512,1,0,0,'teron SAY_INTRO'), +(-1564038,'Vengeance is mine!',11513,1,0,0,'teron SAY_AGGRO'), +(-1564039,'I have use for you!',11514,1,0,0,'teron SAY_SLAY1'), +(-1564040,'It gets worse...',11515,1,0,0,'teron SAY_SLAY2'), +(-1564041,'What are you afraid of?',11517,1,0,0,'teron SAY_SPELL1'), +(-1564042,'Death... really isn\'t so bad.',11516,1,0,0,'teron SAY_SPELL2'), +(-1564043,'Give in!',11518,1,0,0,'teron SAY_SPECIAL1'), +(-1564044,'I have something for you...',11519,1,0,0,'teron SAY_SPECIAL2'), +(-1564045,'YOU WILL SHOW THE PROPER RESPECT!',11520,1,0,0,'teron SAY_ENRAGE'), +(-1564046,'The wheel...spins...again....',11521,1,0,0,'teron SAY_DEATH'), + +(-1564047,'Pain and suffering are all that await you!',11415,1,0,0,'essence SUFF_SAY_FREED'), +(-1564048,'Don\'t leave me alone!',11416,1,0,0,'essence SUFF_SAY_AGGRO'), +(-1564049,'Look at what you make me do!',11417,1,0,0,'essence SUFF_SAY_SLAY1'), +(-1564050,'I didn\'t ask for this!',11418,1,0,0,'essence SUFF_SAY_SLAY2'), +(-1564051,'The pain is only beginning!',11419,1,0,0,'essence SUFF_SAY_SLAY3'), +(-1564052,'I don\'t want to go back!',11420,1,0,0,'essence SUFF_SAY_RECAP'), +(-1564053,'Now what do I do?',11421,1,0,0,'essence SUFF_SAY_AFTER'), +(-1564054,'REUSE_ME',0,0,0,0,'REUSE_ME'), + +(-1564055,'You can have anything you desire... for a price.',11408,1,0,0,'essence DESI_SAY_FREED'), +(-1564056,'Fulfilment is at hand!',11409,1,0,0,'essence DESI_SAY_SLAY1'), +(-1564057,'Yes... you\'ll stay with us now...',11410,1,0,0,'essence DESI_SAY_SLAY2'), +(-1564058,'Your reach exceeds your grasp.',11412,1,0,0,'essence DESI_SAY_SLAY3'), +(-1564059,'Be careful what you wish for...',11411,1,0,0,'essence DESI_SAY_SPEC'), +(-1564060,'I\'ll be waiting...',11413,1,0,0,'essence DESI_SAY_RECAP'), +(-1564061,'I won\'t be far...',11414,1,0,0,'essence DESI_SAY_AFTER'), + +(-1564062,'Beware: I live!',11399,1,0,0,'essence ANGER_SAY_FREED'), +(-1564063,'So... foolish.',11400,1,0,0,'essence ANGER_SAY_FREED2'), +(-1564064,'',11401,1,0,0,'essence ANGER_SAY_SLAY1'), +(-1564065,'Enough. No more.',11402,1,0,0,'essence ANGER_SAY_SLAY2'), +(-1564066,'On your knees!',11403,1,0,0,'essence ANGER_SAY_SPEC'), +(-1564067,'Beware, coward.',11405,1,0,0,'essence ANGER_SAY_BEFORE'), +(-1564068,'I won\'t... be... ignored.',11404,1,0,0,'essence ANGER_SAY_DEATH'), + +(-1564069,'You wish to test me?',11524,1,0,0,'council vera AGGRO'), +(-1564070,'I have better things to do...',11422,1,0,0,'council gath AGGRO'), +(-1564071,'Flee or die!',11482,1,0,0,'council mala AGGRO'), +(-1564072,'Common... such a crude language. Bandal!',11440,1,0,0,'council zere AGGRO'), + +(-1564073,'Enough games!',11428,1,0,0,'council gath ENRAGE'), +(-1564074,'You wish to kill me? Hahaha, you first!',11530,1,0,0,'council vera ENRAGE'), +(-1564075,'For Quel\'Thalas! For the Sunwell!',11488,1,0,0,'council mala ENRAGE'), +(-1564076,'Sha\'amoor sine menoor!',11446,1,0,0,'council zere ENRAGE'), + +(-1564077,'Enjoy your final moments!',11426,1,0,0,'council gath SPECIAL1'), +(-1564078,'You\'re not caught up for this!',11528,1,0,0,'council vera SPECIAL1'), +(-1564079,'No second chances!',11486,1,0,0,'council mala SPECIAL1'), +(-1564080,'Diel fin\'al',11444,1,0,0,'council zere SPECIAL1'), + +(-1564081,'You are mine!',11427,1,0,0,'council gath SPECIAL2'), +(-1564082,'Anar\'alah belore!',11529,1,0,0,'council vera SPECIAL2'), +(-1564083,'I\'m full of surprises!',11487,1,0,0,'council mala SPECIAL2'), +(-1564084,'Sha\'amoor ara mashal?',11445,1,0,0,'council zere SPECIAL2'), + +(-1564085,'Selama am\'oronor!',11423,1,0,0,'council gath SLAY'), +(-1564086,'Valiant effort!',11525,1,0,0,'council vera SLAY'), +(-1564087,'My work is done.',11483,1,0,0,'council mala SLAY'), +(-1564088,'Shorel\'aran.',11441,1,0,0,'council zere SLAY'), + +(-1564089,'Well done!',11424,1,0,0,'council gath SLAY_COMT'), +(-1564090,'A glorious kill!',11526,1,0,0,'council vera SLAY_COMT'), +(-1564091,'As it should be!',11484,1,0,0,'council mala SLAY_COMT'), +(-1564092,'Belesa menoor!',11442,1,0,0,'council zere SLAY_COMT'), + +(-1564093,'Lord Illidan... I...',11425,1,0,0,'council gath DEATH'), +(-1564094,'You got lucky!',11527,1,0,0,'council vera DEATH'), +(-1564095,'Destiny... awaits.',11485,1,0,0,'council mala DEATH'), +(-1564096,'Diel ma\'ahn... oreindel\'o',11443,1,0,0,'council zere DEATH'), + +(-1564097,'Akama. Your duplicity is hardly surprising. I should have slaughtered you and your malformed brethren long ago.',11463,1,0,0,'illidan SAY_ILLIDAN_SPEECH_1'), +(-1564098,'We\'ve come to end your reign, Illidan. My people and all of Outland shall be free!',11389,1,0,25,'akama(illidan) SAY_AKAMA_SPEECH_2'), +(-1564099,'Boldly said. But I remain unconvinced.',11464,1,0,6,'illidan SAY_ILLIDAN_SPEECH_3'), +(-1564100,'The time has come! The moment is at hand!',11380,1,0,22,'akama(illidan) SAY_AKAMA_SPEECH_4'), +(-1564101,'You are not prepared!',11466,1,0,406,'illidan SAY_ILLIDAN_SPEECH_5'), +(-1564102,'Is this it, mortals? Is this all the fury you can muster?',11476,1,0,0,'illidan SAY_ILLIDAN_SPEECH_6'), +(-1564103,'Their fury pales before mine, Illidan. We have some unsettled business between us.',11491,1,0,6,'maiev SAY_MAIEV_SPEECH_7'), +(-1564104,'Maiev... How is this even possible?',11477,1,0,1,'illidan SAY_ILLIDAN_SPEECH_8'), +(-1564105,'My long hunt is finally over. Today, Justice will be done!',11492,1,0,5,'maiev SAY_MAIEV_SPEECH_9'), +(-1564106,'Feel the hatred of ten thousand years!',11470,1,0,0,'illidan SAY_FRENZY'), +(-1564107,'It is finished. You are beaten.',11496,1,0,0,'maiev SAY_MAIEV_EPILOGUE_1'), +(-1564108,'You have won... Maiev. But the huntress... is nothing without the hunt. You... are nothing... without me.',11478,1,0,0,'illidan SAY_ILLIDAN_EPILOGUE_2'), +(-1564109,'He\'s right. I feel nothing... I am... nothing.',11497,1,0,0,'maiev SAY_MAIEV_EPILOGUE_3'), +(-1564110,'Farewell, champions.',11498,1,0,0,'maiev SAY_MAIEV_EPILOGUE_4'), +(-1564111,'The Light will fill these dismal halls once again. I swear it.',11387,1,0,0,'akama(illidan) SAY_AKAMA_EPILOGUE_5'), +(-1564112,'I can feel your hatred.',11467,1,0,0,'illidan SAY_TAUNT_1'), +(-1564113,'Give in to your fear!',11468,1,0,0,'illidan SAY_TAUNT_2'), +(-1564114,'You know nothing of power!',11469,1,0,0,'illidan SAY_TAUNT_3'), +(-1564115,'Such... arrogance!',11471,1,0,0,'illidan SAY_TAUNT_4'), +(-1564116,'That is for Naisha!',11493,1,0,0,'maiev SAY_MAIEV_TAUNT_1'), +(-1564117,'Bleed as I have bled!',11494,1,0,0,'maiev SAY_MAIEV_TAUNT_2'), +(-1564118,'There shall be no prison for you this time!',11495,1,0,0,'maiev SAY_MAIEV_TRAP'), +(-1564119,'Meet your end, demon!',11500,1,0,0,'maiev SAY_MAIEV_TAUNT_4'), +(-1564120,'Be wary friends, The Betrayer meditates in the court just beyond.',11388,1,0,0,'akama(illidan) SAY_AKAMA_BEWARE'), +(-1564121,'Come, my minions. Deal with this traitor as he deserves!',11465,1,0,0,'illidan SAY_AKAMA_MINION'), +(-1564122,'I\'ll deal with these mongrels. Strike now, friends! Strike at the betrayer!',11390,1,0,22,'akama(illidan) SAY_AKAMA_LEAVE'), +(-1564123,'Who shall be next to taste my blades?!',11473,1,0,0,'illidan SAY_KILL1'), +(-1564124,'This is too easy!',11472,1,0,0,'illidan SAY_KILL2'), +(-1564125,'I will not be touched by rabble such as you!',11479,1,0,254,'illidan SAY_TAKEOFF'), +(-1564126,'Behold the flames of Azzinoth!',11480,1,0,0,'illidan SAY_SUMMONFLAMES'), +(-1564127,'Stare into the eyes of the Betrayer!',11481,1,0,0,'illidan SAY_EYE_BLAST'), +(-1564128,'Behold the power... of the demon within!',11475,1,0,0,'illidan SAY_MORPH'), +(-1564129,'You\'ve wasted too much time mortals, now you shall fall!',11474,1,0,0,'illidan SAY_ENRAGE'), + +(-1564130,'Broken of the Ashtongue tribe, your leader speaks!',0,1,0,0,'akama(shade) SAY_FREE_1'), + +(-1564131,'This door is all that stands between us and the Betrayer. Stand aside, friends.',0,0,0,1,'akama(illidan) SAY_OPEN_DOOR_1'), +(-1564132,'I cannot do this alone...',0,0,0,0,'akama(illidan) SAY_OPEN_DOOR_2'), +(-1564133,'You are not alone, Akama.',0,0,0,0,'spirit_Udalo SAY_OPEN_DOOR_3'), +(-1564134,'Your people will always be with you!',0,0,0,0,'spirit_Olum SAY_OPEN_DOOR_4'); + +-- -1 565 000 GRUUL'S LAIR +INSERT INTO script_texts (entry,content_default,sound,type,language,emote,comment) VALUES +(-1565000,'Gronn are the real power in outland.',11367,1,0,0,'maulgar SAY_AGGRO'), +(-1565001,'You will not defeat the hand of Gruul!',11368,1,0,0,'maulgar SAY_ENRAGE'), +(-1565002,'You won\'t kill next one so easy!',11369,1,0,0,'maulgar SAY_OGRE_DEATH1'), +(-1565003,'Pah! Does not prove anything!',11370,1,0,0,'maulgar SAY_OGRE_DEATH2'), +(-1565004,'I\'m not afraid of you.',11371,1,0,0,'maulgar SAY_OGRE_DEATH3'), +(-1565005,'Good, now you fight me!',11372,1,0,0,'maulgar SAY_OGRE_DEATH4'), +(-1565006,'You not so tough afterall!',11373,1,0,0,'maulgar SAY_SLAY1'), +(-1565007,'Aha-ha ha ha!',11374,1,0,0,'maulgar SAY_SLAY2'), +(-1565008,'Mulgar is king!',11375,1,0,0,'maulgar SAY_SLAY3'), +(-1565009,'Gruul... will crush you...',11376,1,0,0,'maulgar SAY_DEATH'), + +(-1565010,'Come... and die.',11355,1,0,0,'gruul SAY_AGGRO'), +(-1565011,'Scurry',11356,1,0,0,'gruul SAY_SLAM1'), +(-1565012,'No escape',11357,1,0,0,'gruul SAY_SLAM2'), +(-1565013,'Stay',11358,1,0,0,'gruul SAY_SHATTER1'), +(-1565014,'Beg... for life',11359,1,0,0,'gruul SAY_SHATTER2'), +(-1565015,'No more',11360,1,0,0,'gruul SAY_SLAY1'), +(-1565016,'Unworthy',11361,1,0,0,'gruul SAY_SLAY2'), +(-1565017,'Die',11362,1,0,0,'gruul SAY_SLAY3'), +(-1565018,'Aaargh...',11363,1,0,0,'gruul SAY_DEATH'), +(-1565019,'%s grows in size!',0,2,0,0,'gruul EMOTE_GROW'); + +-- -1 568 000 ZUL'AMAN +INSERT INTO script_texts (entry,content_default,sound,type,language,emote,comment) VALUES +(-1568000,'Spirits of da wind be your doom!',12031,1,0,0,'janalai SAY_AGGRO'), +(-1568001,'I burn ya now!',12032,1,0,0,'janalai SAY_FIRE_BOMBS'), +(-1568002,'Where ma hatcha? Get to work on dem eggs!',12033,1,0,0,'janalai SAY_SUMMON_HATCHER'), +(-1568003,'I show you strength... in numbers.',12034,1,0,0,'janalai SAY_ALL_EGGS'), +(-1568004,'You done run outta time!',12035,1,0,0,'janalai SAY_BERSERK'), +(-1568005,'It all be over now, mon!',12036,1,0,0,'janalai SAY_SLAY_1'), +(-1568006,'Tazaga-choo!',12037,1,0,0,'janalai SAY_SLAY_2'), +(-1568007,'Zul\'jin... got a surprise for you...',12038,1,0,0,'janalai SAY_DEATH'), +(-1568008,'Come, strangers. The spirit of the dragonhawk hot be hungry for worthy souls.',12039,1,0,0,'janalai SAY_EVENT_STRANGERS'), +(-1568009,'Come, friends. Your bodies gonna feed ma hatchlings, and your souls are going to feed me with power!',12040,1,0,0,'janalai SAY_EVENT_FRIENDS'), + +(-1568010,'Get da move on, guards! It be killin\' time!',12066,1,0,0,'nalorakk SAY_WAVE1_AGGRO'), +(-1568011,'Guards, go already! Who you more afraid of, dem... or me?',12067,1,0,0,'nalorakk SAY_WAVE2_STAIR1'), +(-1568012,'Ride now! Ride out dere and bring me back some heads!',12068,1,0,0,'nalorakk SAY_WAVE3_STAIR2'), +(-1568013,'I be losin\' me patience! Go on: make dem wish dey was never born!',12069,1,0,0,'nalorakk SAY_WAVE4_PLATFORM'), +(-1568014,'What could be better than servin\' da bear spirit for eternity? Come closer now. Bring your souls to me!',12078,1,0,0,'nalorakk SAY_EVENT1_SACRIFICE'), +(-1568015,'Don\'t be delayin\' your fate. Come to me now. I make your sacrifice quick.',12079,1,0,0,'nalorakk SAY_EVENT2_SACRIFICE'), +(-1568016,'You be dead soon enough!',12070,1,0,0,'nalorakk SAY_AGGRO'), +(-1568017,'I bring da pain!',12071,1,0,0,'nalorakk SAY_SURGE'), +(-1568018,'You call on da beast, you gonna get more dan you bargain for!',12072,1,0,0,'nalorakk SAY_TOBEAR'), +(-1568019,'Make way for Nalorakk!',12073,1,0,0,'nalorakk SAY_TOTROLL'), +(-1568020,'You had your chance, now it be too late!',12074,1,0,0,'nalorakk SAY_BERSERK'), +(-1568021,'Mua-ha-ha! Now whatchoo got to say?',12075,1,0,0,'nalorakk SAY_SLAY1'), +(-1568022,'Da Amani gonna rule again!',12076,1,0,0,'nalorakk SAY_SLAY2'), +(-1568023,'I... be waitin\' on da udda side....',12077,1,0,0,'nalorakk SAY_DEATH'), + +(-1568024,'Da eagles gonna bear your spirits to me. Your sacrifice is not gonna be in vein!',12122,1,0,0,'akilzon SAY_EVENT1'), +(-1568025,'Your death gonna be quick, strangers. You shoulda never have come to this place...',12123,1,0,0,'akilzon SAY_EVENT2'), +(-1568026,'I be da predator! You da prey...',12013,1,0,0,'akilzon SAY_AGGRO'), +(-1568027,'Feed, me bruddahs!',12014,1,0,0,'akilzon SAY_SUMMON'), +(-1568028,'Come, and join me bruddahs!',12015,1,0,0,'akilzon SAY_SUMMON_ALT'), +(-1568029,'All you be doing is wasting my time!',12016,1,0,0,'akilzon SAY_ENRAGE'), +(-1568030,'Ya got nothin\'!',12017,1,0,0,'akilzon SAY_SLAY1'), +(-1568031,'Stop your cryin\'!',12018,1,0,0,'akilzon SAY_SLAY2'), +(-1568032,'You can\'t... kill... me spirit!',12019,1,0,0,'akilzon SAY_DEATH'), +(-1568033,'An Electrical Storm Appears!',0,2,0,0,'akilzon EMOTE_STORM'), + +(-1568034,'Get on ya knees and bow.... to da fang and claw!',12020,1,0,0,'halazzi SAY_AGGRO'), +(-1568035,'I fight wit\' untamed spirit....',12021,1,0,0,'halazzi SAY_SPLIT'), +(-1568036,'Spirit, come back to me!',12022,1,0,0,'halazzi SAY_MERGE'), +(-1568037,'Me gonna carve ya now!',12023,1,0,0,'halazzi SAY_SABERLASH1'), +(-1568038,'You gonna leave in pieces!',12024,1,0,0,'halazzi SAY_SABERLASH2'), +(-1568039,'Whatch you be doing? Pissin\' yourselves...',12025,1,0,0,'halazzi SAY_BERSERK'), +(-1568040,'You cant fight the power!',12026,1,0,0,'halazzi SAY_KILL1'), +(-1568041,'You gonna fail!',12027,1,0,0,'halazzi SAY_KILL2'), +(-1568042,'Chaga... choka\'jinn.',12028,1,0,0,'halazzi SAY_DEATH'), +(-1568043,'Come, fools. Fill ma empty cages...',12029,1,0,0,'halazzi SAY_EVENT1'), +(-1568044,'I be waitin, strangers. Your deaths gonna make me stronger!',12030,1,0,0,'halazzi SAY_EVENT2'), + +(-1568045,'Da shadow gonna fall on you...',12041,1,0,0,'malacrass SAY_AGGRO'), +(-1568046,'Ya don\'t kill me yet, ya don\'t get another chance!',12042,1,0,0,'malacrass SAY_ENRAGE'), +(-1568047,'Dis a nightmare ya don\' wake up from!',12043,1,0,0,'malacrass SAY_KILL1'), +(-1568048,'Azzaga choogo zinn!',12044,1,0,0,'malacrass SAY_KILL2'), +(-1568049,'Your will belong ta me now!',12045,1,0,0,'malacrass SAY_SOUL_SIPHON'), +(-1568050,'Darkness comin\' for you...',12046,1,0,0,'malacrass SAY_DRAIN_POWER'), +(-1568051,'Your soul gonna bleed!',12047,1,0,0,'malacrass SAY_SPIRIT_BOLTS'), +(-1568052,'It not gonna make no difference.',12048,1,0,0,'malacrass SAY_ADD_DIED1'), +(-1568053,'You gonna die worse dan him.',12049,1,0,0,'malacrass SAY_ADD_DIED2'), +(-1568054,'Dat no bodda me.',12050,1,0,0,'malacrass SAY_ADD_DIED3'), +(-1568055,'Dis not... da end of me...',12051,1,0,0,'malacrass SAY_DEATH'), + +(-1568056,'Everybody always wanna take from us. Now we gonna start takin\' back. Anybody who get in our way...gonna drown in dey own blood! Da Amani empire be back now...seekin\' vengeance. And we gonna start wit\' you.',12090,1,0,0,'zuljin SAY_INTRO'), +(-1568057,'Nobody badduh dan me!',12091,1,0,0,'zuljin SAY_AGGRO'), +(-1568058,'Got me some new tricks... like me brudda bear....',12092,1,0,0,'zuljin SAY_BEAR_TRANSFORM'), +(-1568059,'Dere be no hidin\' from da eagle!',12093,1,0,0,'zuljin SAY_EAGLE_TRANSFORM'), +(-1568060,'Let me introduce you to me new bruddas: fang and claw!',12094,1,0,0,'zuljin SAY_LYNX_TRANSFORM'), +(-1568061,'Ya don\' have to look to da sky to see da dragonhawk!',12095,1,0,0,'zuljin SAY_DRAGONHAWK_TRANSFORM'), +(-1568062,'Fire kill you just as quick!',12096,1,0,0,'zuljin SAY_FIRE_BREATH'), +(-1568063,'You too slow! Me too strong!',12097,1,0,0,'zuljin SAY_BERSERK'), +(-1568064,'Da Amani de chuka!',12098,1,0,0,'zuljin SAY_KILL1'), +(-1568065,'Lot more gonna fall like you!',12099,1,0,0,'zuljin SAY_KILL2'), +(-1568066,'Mebbe me fall...but da Amani empire...never gonna die...',12100,1,0,0,'zuljin SAY_DEATH'), + +(-1568067,'Zul\'jin got a surprise for ya...',12052,6,0,0,'zulaman SAY_INST_RELEASE'), +(-1568068,'Da spirits gonna feast today! Begin da ceremonies, sacrifice da prisoners... make room for our new guests!',12053,6,0,0,'zulaman SAY_INST_BEGIN'), +(-1568069,'Take your pick, trespassers! Any of ma priests be happy to accommodate ya.',12054,6,0,0,'zulaman SAY_INST_PROGRESS_1'), +(-1568070,'Don\'t be shy. Thousands have come before you. Ya not be alone in your service.',12055,6,0,0,'zulaman SAY_INST_PROGRESS_2'), +(-1568071,'Ya gonna fail, strangers. Many try before you, but dey only make us stronger!',12056,6,0,0,'zulaman SAY_INST_PROGRESS_3'), +(-1568072,'Your efforts was in vain, trespassers. The rituals nearly be complete.',12057,6,0,0,'zulaman SAY_INST_WARN_1'), +(-1568073,'Soon da cages gonna be empty, da sacrifices be complete, and you gonna take dere places.',12058,6,0,0,'zulaman SAY_INST_WARN_2'), +(-1568074,'Time be running low, strangers. Soon you gonna join da souls of dem ya failed to save.',12059,6,0,0,'zulaman SAY_INST_WARN_3'), +(-1568075,'Make haste, ma priests! Da rituals must not be interrupted!',12060,6,0,0,'zulaman SAY_INST_WARN_4'), +(-1568076,'Ya make a good try... but now you gonna join da ones who already fall.',12061,6,0,0,'zulaman SAY_INST_SACRIF1'), +(-1568077,'Ya not do too bad. Ya efforts [...] for a small time. Come to me now. Ya prove yourself worthy offerings.',12062,6,0,0,'zulaman SAY_INST_SACRIF2'), +(-1568078,'Watch now. Every offering gonna strengthen our ties to da spirit world. Soon, we gonna be unstoppable!',12065,6,0,0,'zulaman SAY_INST_COMPLETE'), + +(-1568079,'Suit yourself. At least five of you must assist me if we\'re to get inside. Follow me.',0,1,0,0,'harrison SAY_START'), +(-1568080,'According to my calculations, if enough of us bang the gong at once the seal on these doors will break and we can enter.',0,1,0,0,'harrison SAY_AT_GONG'), +(-1568081,'I\'ve researched this site extensively and I won\'t allow any dim-witted treasure hunters to swoop in and steal what belongs to in a museum. I\'ll lead this charge.',0,1,0,0,'harrison SAY_OPEN_ENTRANCE'), + +(-1568082,'%s absorbs the essence of the bear spirit!',0,2,0,0,'zuljin EMOTE_BEAR_SPIRIT'), +(-1568083,'%s absorbs the essence of the eagle spirit!',0,2,0,0,'zuljin EMOTE_EAGLE_SPIRIT'), +(-1568084,'%s absorbs the essence of the lynx spirit!',0,2,0,0,'zuljin EMOTE_LYNX_SPIRIT'), +(-1568085,'%s absorbs the essence of the dragonhawk spirit!',0,2,0,0,'zuljin EMOTE_DRAGONHAWK_SPIRIT'); + +-- -1 574 000 UTGARDE KEEP +INSERT INTO script_texts (entry,content_default,sound,type,language,emote,comment) VALUES +(-1574000,'Your blood is mine!',13221,1,0,0,'keleseth SAY_AGGRO'), +(-1574001,'Not so fast.',13222,1,0,0,'keleseth SAY_FROSTTOMB'), +(-1574002,'Aranal, lidel! Their fate shall be yours!',13224,1,0,0,'keleseth SAY_SKELETONS'), +(-1574003,'Darkness waits!',13223,1,0,0,'keleseth SAY_KILL'), +(-1574004,'I join... the night.',13225,1,0,0,'keleseth SAY_DEATH'), + +(-1574005,'I\'ll paint my face with your blood!',13207,1,0,0,'ingvar SAY_AGGRO_FIRST'), +(-1574006,'I return! A second chance to carve out your skull!',13209,1,0,0,'ingvar SAY_AGGRO_SECOND'), +(-1574007,'My life for the... death god!',13213,1,0,0,'ingvar SAY_DEATH_FIRST'), +(-1574008,'No! I can do... better! I can...',13211,1,0,0,'ingvar SAY_DEATH_SECOND'), +(-1574009,'Mjul orm agn gjor!',13212,1,0,0,'ingvar SAY_KILL_FIRST'), +(-1574010,'I am a warrior born!',13214,1,0,0,'ingvar SAY_KILL_SECOND'), + +(-1574011,'Dalronn! See if you can muster the nerve to join my attack!',13229,1,0,0,'skarvald SAY_SKA_AGGRO'), +(-1574012,'Not... over... yet.',13230,1,0,0,'skarvald SAY_SKA_DEATH'), +(-1574013,'A warrior\'s death.',13231,1,0,0,'skarvald SAY_SKA_DEATH_REAL'), +(-1574014,'???',13232,1,0,0,'skarvald SAY_SKA_KILL'), +(-1574015,'Pagh! What sort of necromancer lets death stop him? I knew you were worthless!',13233,1,0,0,'skarvald SAY_SKA_DAL_DIES_REPLY'), + +(-1574016,'By all means, don\'t assess the situation, you halfwit! Just jump into the fray!',13199,1,0,0,'dalronn SAY_DAL_AGGRO_REPLY'), +(-1574017,'See... you... soon.',13200,1,0,0,'dalronn SAY_DAL_DEATH'), +(-1574018,'There\'s no... greater... glory.',13201,1,0,0,'dalronn SAY_DAL_DEATH_REAL'), +(-1574019,'You may serve me yet.',13202,1,0,0,'dalronn SAY_DAL_KILL'), +(-1574020,'Skarvald, you incompetent slug! Return and make yourself useful!',13203,1,0,0,'dalronn SAY_DAL_SKA_DIES_REPLY'), + +(-1574021,'%s casts Frost Tomb on $N',0,3,0,0,'keleseth EMOTE_FROST_TOMB'), + +(-1574022,'%s roars!',0,3,0,0,'ingvar EMOTE_ROAR'), +(-1574023,'Ingvar! Your pathetic failure will serve as a warning to all... you are damned! Arise and carry out the masters will!',13754,1,0,0,'annhylde REZZ'); + +-- -1 575 000 UTGARDE PINNACLE +INSERT INTO script_texts (entry,content_default,sound,type,language,emote,comment) VALUES +(-1575000,'My liege! I have done as you asked, and now beseech you for your blessing!',13856,1,0,0,'svala SAY_INTRO_1'), +(-1575001,'Your sacrifice is a testament to your obedience. Indeed you are worthy of this charge. Arise, and forever be known as Svala Sorrowgrave!',14732,1,0,0,'svala SAY_INTRO_2_ARTHAS'), +(-1575002,'The sensation is... beyond my imagining. I am yours to command, my king.',13857,1,0,0,'svala SAY_INTRO_3'), +(-1575003,'Your first test awaits you. Destroy our uninvited guests.',14733,1,0,0,'svala SAY_INTRO_4_ARTHAS'), +(-1575004,'I will be happy to slaughter them in your name! Come, enemies of the Scourge! I will show you the might of the Lich King!',13858,1,0,0,'svala SAY_INTRO_5'), +(-1575005,'I will vanquish your soul!',13842,1,0,0,'svala SAY_AGGRO'), +(-1575006,'You were a fool to challenge the power of the Lich King!',13845,1,0,0,'svala SAY_SLAY_1'), +(-1575007,'Your will is done, my king.',13847,1,0,0,'svala SAY_SLAY_2'), +(-1575008,'Another soul for my master.',13848,1,0,0,'svala SAY_SLAY_3'), +(-1575009,'Your death approaches.',13850,1,0,0,'svala SAY_SACRIFICE_1'), +(-1575010,'Go now to my master.',13851,1,0,0,'svala SAY_SACRIFICE_2'), +(-1575011,'Your end is inevitable.',13852,1,0,0,'svala SAY_SACRIFICE_3'), +(-1575012,'Yor-guul mak!',13853,1,0,0,'svala SAY_SACRIFICE_4'), +(-1575013,'Any last words?',13854,1,0,0,'svala SAY_SACRIFICE_5'), +(-1575014,'Nooo! I did not come this far... to...',13855,1,0,0,'svala SAY_DEATH'), + +(-1575015,'What this place? I will destroy you!',13464,1,0,0,'gortok SAY_AGGRO'), +(-1575016,'You die! That what master wants!',13465,1,0,0,'gortok SAY_SLAY_1'), +(-1575017,'An easy task!',13466,1,0,0,'gortok SAY_SLAY_2'), +(-1575018,' ',13467,1,0,0,'gortok SAY_DEATH'), + +(-1575019,'What mongrels dare intrude here? Look alive, my brothers! A feast for the one that brings me their heads!',13497,1,0,22,'skadi SAY_AGGRO'), +(-1575020,'Sear them to the bone!',13498,1,0,0,'skadi SAY_DRAKEBREATH_1'), +(-1575021,'Go now! Leave nothing but ash in your wake!',13499,1,0,0,'skadi SAY_DRAKEBREATH_2'), +(-1575022,'Cleanse our sacred halls with flame!',13500,1,0,0,'skadi SAY_DRAKEBREATH_3'), +(-1575023,'I ask for ... to kill them, yet all I get is feeble whelps! By Ye.. SLAUGHTER THEM!',13501,1,0,0,'skadi SAY_DRAKE_HARPOON_1'), +(-1575024,'If one more harpoon touches my drake I\'ll flae my miserable heins.',13502,1,0,0,'skadi SAY_DRAKE_HARPOON_2'), +(-1575025,'Mjor Na Ul Kaval!',13503,1,0,0,'skadi SAY_KILL_1'), +(-1575026,'Not so brash now, are you?',13504,1,0,0,'skadi SAY_KILL_2'), +(-1575027,'I\'ll mount your skull from the highest tower!',13505,1,0,0,'skadi SAY_KILL_3'), +(-1575028,'ARGH! You call that... an attack? I\'ll... show... aghhhh...',13506,1,0,0,'skadi SAY_DEATH'), +(-1575029,'You motherless knaves! Your corpses will make fine morsels for my new drake!',13507,1,0,0,'skadi SAY_DRAKE_DEATH'), +(-1575030,'%s is within range of the harpoon launchers!',0,3,0,0,'skadi EMOTE_HARPOON_RANGE'), + +(-1575031,'You invade my home and then dare to challenge me? I will tear the hearts from your chests and offer them as gifts to the death god! Rualg nja gaborr!',13609,1,0,0,'ymiron SAY_AGGRO'), +(-1575032,'Bjorn of the Black Storm! Honor me now with your presence!',13610,1,0,0,'ymiron SAY_SUMMON_BJORN'), +(-1575033,'Haldor of the rocky cliffs, grant me your strength!',13611,1,0,0,'ymiron SAY_SUMMON_HALDOR'), +(-1575034,'Ranulf of the screaming abyss, snuff these maggots with darkest night!',13612,1,0,0,'ymiron SAY_SUMMON_RANULF'), +(-1575035,'Tor of the Brutal Siege! Bestow your might upon me!',13613,1,0,0,'ymiron SAY_SUMMON_TORGYN'), +(-1575036,'Your death is only the beginning!',13614,1,0,0,'ymiron SAY_SLAY_1'), +(-1575037,'You have failed your people!',13615,1,0,0,'ymiron SAY_SLAY_2'), +(-1575038,'There is a reason I am king!',13616,1,0,0,'ymiron SAY_SLAY_3'), +(-1575039,'Bleed no more!',13617,1,0,0,'ymiron SAY_SLAY_4'), +(-1575040,'What... awaits me... now?',13618,1,0,0,'ymiron SAY_DEATH'), + +(-1575041,'%s takes a deep breath.',0,3,0,0,'grauf EMOTE_DEEP_BREATH'); + +-- -1 576 000 NEXUS +INSERT INTO script_texts (entry,content_default,sound,type,language,emote,comment) VALUES +(-1576000,'You know what they say about curiosity.',13319,1,0,0,'telestra SAY_AGGRO'), +(-1576001,'I\'ll give you more than you can handle.',13321,1,0,0,'telestra SAY_SPLIT_1'), +(-1576002,'There\'s plenty of me to go around.',13322,1,0,0,'telestra SAY_SPLIT_2'), +(-1576003,'Now to finish the job!',13323,1,0,0,'telestra SAY_MERGE'), +(-1576004,'Death becomes you!',13324,1,0,0,'telestra SAY_KILL'), +(-1576005,'Damn the... luck.',13320,1,0,0,'telestra SAY_DEATH'), + +(-1576006,'Chaos beckons.',13186,1,0,0,'anomalus SAY_AGGRO'), +(-1576007,'Reality... unwoven.',13188,1,0,0,'anomalus SAY_RIFT'), +(-1576008,'Indestructible.',13189,1,0,0,'anomalus SAY_SHIELD'), +(-1576009,'Expiration... is necesarry.',13274,1,0,0,'anomalus SAY_KILL'), +(-1576010,'Of course.',13187,1,0,0,'anomalus SAY_DEATH'), + +(-1576011,'Noo!',13328,1,0,0,'ormorok SAY_AGGRO'), +(-1576012,'???',13329,1,0,0,'ormorok SAY_KILL'), +(-1576013,'Baaack!',13331,1,0,0,'ormorok SAY_REFLECT'), +(-1576014,'Bleeeed!',13332,1,0,0,'ormorok SAY_ICESPIKE'), +(-1576015,'Aaggh!',13330,1,0,0,'ormorok SAY_DEATH'), + +(-1576016,'Preserve? Why? There\'s no truth in it. No no no... only in the taking! I see that now!',13450,1,0,0,'keristrasza SAY_AGGRO'), +(-1576017,'Stay. Enjoy your final moments.',13451,1,0,0,'keristrasza SAY_CRYSTAL_NOVA'), +(-1576018,'Finish it! Finish it! Kill me, or I swear by the Dragonqueen you\'ll never see daylight again!',13452,1,0,0,'keristrasza SAY_ENRAGE'), +(-1576019,'Now we\'ve come to the truth!',13453,1,0,0,'keristrasza SAY_KILL'), +(-1576020,'Dragonqueen... Life-Binder... preserve... me.',13454,1,0,0,'keristrasza SAY_DEATH'), + +(-1576021,'%s opens a Chaotic Rift!',0,3,0,0,'anomalus EMOTE_OPEN_RIFT'), +(-1576022,'%s shields himself and divert his power to the rifts!',0,3,0,0,'anomalus EMOTE_SHIELD'); + +-- -1 578 000 OCULUS +INSERT INTO script_texts (entry,content_default,sound,type,language,emote,comment) VALUES +(-1578000,'What do we have here... those would defy the Spell-Weaver? Those without foresight or understanding. How could I make you see? Malygos is saving the world from itself! Bah! You are hardly worth my time!',13635,1,0,0,'urom SAY_SUMMON_1'), +(-1578001,'Clearly my pets failed. Perhaps another demonstration is in order.',13636,1,0,0,'urom SAY_SUMMON_2'), +(-1578002,'Still you fight. Still you cling to misguided principles. If you survive, you\'ll find me in the center ring.',13637,1,0,0,'urom SAY_SUMMON_3'), +(-1578003,'Poor blind fools!',13638,1,0,0,'urom SAY_AGGRO'), +(-1578004,'A taste... just a small taste... of the Spell-Weaver\'s power!',13639,1,0,0,'urom SAY_EXPLOSION_1'), +(-1578005,'So much unstable energy... but worth the risk to destroy you!',13640,1,0,0,'urom SAY_EXPLOSION_2'), +(-1578006,'If only you understood!',13641,1,0,0,'urom SAY_KILL_1'), +(-1578007,'Now do you see? Do you?!',13642,1,0,0,'urom SAY_KILL_2'), +(-1578008,'Unfortunate, but necessary.',13643,1,0,0,'urom SAY_KILL_3'), +(-1578009,'Everything I\'ve done... has been for Azeroth...',13644,1,0,0,'urom SAY_DEATH'), + +(-1578010,'Simpletons! You cannot comprehend the forces you have set in motion. The ley line conduit will not be disrupted! Your defeat shall be absolute!',13622,6,0,0,'eregos SAY_SPAWN'), +(-1578011,'You brash interlopers are out of your element! I will ground you!',13623,1,0,0,'eregos SAY_AGGRO'), +(-1578012,'We command the arcane! It shall not be used against us.',13626,1,0,0,'eregos SAY_ARCANE_SHIELD'), +(-1578013,'It is trivial to extinguish your fire!',13627,1,0,0,'eregos SAY_FIRE_SHIELD'), +(-1578014,'No magic of nature will help you now!',13625,1,0,0,'eregos SAY_NATURE_SHIELD'), +(-1578015,'Such insolence... such arrogance... must be PUNISHED!',13624,1,0,0,'eregos SAY_FRENZY'), +(-1578016,'It\'s a long way down...',13628,1,0,0,'eregos SAY_KILL_1'), +(-1578017,'Back to the earth with you!',13629,1,0,0,'eregos SAY_KILL_2'), +(-1578018,'Enjoy the fall!',13630,1,0,0,'eregos SAY_KILL_3'), +(-1578019,'Savor this small victory, foolish little creatures. You and your dragon allies have won this battle. But we will win... the Nexus War.',13631,1,0,0,'eregos SAY_DEATH'), + +(-1578020,'There will be no mercy!',13649,1,0,0,'varos SAY_AGGRO'), +(-1578021,'Blast them! Destroy them!',13650,1,0,0,'varos SAY_CALL_CAPTAIN_1'), +(-1578022,'Take no prisoners! Attack!',13651,1,0,0,'varos SAY_CALL_CAPTAIN_2'), +(-1578023,'Strike now! Obliterate them!',13652,1,0,0,'varos SAY_CALL_CAPTAIN_3'), + +(-1578024,'Anomalies form as %s shifts into the Astral Plane!',0,3,0,0,'eregos EMOTE_ASTRAL_PLANE'), +(-1578025,'%s begins to cast Empowered Arcane Explosion!',0,3,0,0,'urom EMOTE_EXPLOSION'), + +(-1578026,'You were warned!',13653,1,0,0,'varos SAY_KILL_1'), +(-1578027,'The Oculus is ours!',13654,1,0,0,'varos SAY_KILL_2'), +(-1578028,'They are... too strong! Underestimated their... fortitude.',13655,1,0,0,'varos SAY_DEATH'), +(-1578029,'%s calls an Azure Ring Captain!',0,3,0,0,'varos EMOTE_CAPTAIN'), + +(-1578030,'%s flies away.',0,2,0,0,'drakes EMOTE_FLY_AWAY'); + +-- -1 580 000 SUNWELL PLATEAU +INSERT INTO script_texts (entry,content_default,sound,type,language,emote,comment) VALUES +(-1580000,'Aggh! No longer will I be a slave to Malygos! Challenge me and you will be destroyed!',12422,1,0,0,'kalecgos SAY_EVIL_AGGRO'), +(-1580001,'I will purge you!',12423,1,0,0,'kalecgos SAY_EVIL_SPELL1'), +(-1580002,'Your pain has only begun!',12424,1,0,0,'kalecgos SAY_EVIL_SPELL2'), +(-1580003,'In the name of Kil\'jaeden!',12425,1,0,0,'kalecgos SAY_EVIL_SLAY1'), +(-1580004,'You were warned!',12426,1,0,0,'kalecgos SAY_EVIL_SLAY2'), +(-1580005,'My awakening is complete! You shall all perish!',12427,1,0,0,'kalecgos SAY_EVIL_ENRAGE'), +(-1580006,'I need... your help... Cannot... resist him... much longer...',12428,1,0,0,'kalecgos humanoid SAY_GOOD_AGGRO'), +(-1580007,'Aaahhh! Help me, before I lose my mind!',12429,1,0,0,'kalecgos humanoid SAY_GOOD_NEAR_DEATH'), +(-1580008,'Hurry! There is not much of me left!',12430,1,0,0,'kalecgos humanoid SAY_GOOD_NEAR_DEATH2'), +(-1580009,'I am forever in your debt. Once we have triumphed over Kil\'jaeden, this entire world will be in your debt as well.',12431,1,0,0,'kalecgos humanoid SAY_GOOD_PLRWIN'), +(-1580010,'There will be no reprieve. My work here is nearly finished.',12451,1,0,0,'sathrovarr SAY_SATH_AGGRO'), +(-1580011,'I\'m... never on... the losing... side...',12452,1,0,0,'sathrovarr SAY_SATH_DEATH'), +(-1580012,'Your misery is my delight!',12453,1,0,0,'sathrovarr SAY_SATH_SPELL1'), +(-1580013,'I will watch you bleed!',12454,1,0,0,'sathrovarr SAY_SATH_SPELL2'), +(-1580014,'Pitious mortal!',12455,1,0,0,'sathrovarr SAY_SATH_SLAY1'), +(-1580015,'Haven\'t you heard? I always win!',12456,1,0,0,'sathrovarr SAY_SATH_SLAY2'), +(-1580016,'I have toyed with you long enough!',12457,1,0,0,'sathrovarr SAY_SATH_ENRAGE'), + +(-1580017,'Puny lizard! Death is the only answer you\'ll find here!',12458,1,0,0,'brutallus YELL_INTRO'), +(-1580018,'Grah! Your magic is weak!',12459,1,0,0,'brutallus YELL_INTRO_BREAK_ICE'), +(-1580019,'I will crush you!',12460,1,0,0,'brutallus YELL_INTRO_CHARGE'), +(-1580020,'That was fun, but I still await a true challenge!',12461,1,0,0,'brutallus YELL_INTRO_KILL_MADRIGOSA'), +(-1580021,'Come, try your luck!',12462,1,0,0,'brutallus YELL_INTRO_TAUNT'), +(-1580022,'Ahh! More lambs to the slaughter!',12463,1,0,0,'brutallus YELL_AGGRO'), +(-1580023,'Perish, insect!',12464,1,0,0,'brutallus YELL_KILL1'), +(-1580024,'You are meat!',12465,1,0,0,'brutallus YELL_KILL2'), +(-1580025,'Too easy!',12466,1,0,0,'brutallus YELL_KILL3'), +(-1580026,'Bring the fight to me!',12467,1,0,0,'brutallus YELL_LOVE1'), +(-1580027,'Another day, another glorious battle!',12468,1,0,0,'brutallus YELL_LOVE2'), +(-1580028,'I live for this!',12469,1,0,0,'brutallus YELL_LOVE3'), +(-1580029,'So much for a real challenge... Die!',12470,1,0,0,'brutallus YELL_BERSERK'), +(-1580030,'Gah! Well done... Now... this gets... interesting...',12471,1,0,0,'brutallus YELL_DEATH'), + +(-1580031,'Hold, friends! There is information to be had before this devil meets his fate!',12472,1,0,0,'madrigosa YELL_MADR_ICE_BARRIER'), +(-1580032,'Where is Anveena, demon? What has become of Kalec?',12473,1,0,293,'madrigosa YELL_MADR_INTRO'), +(-1580033,'You will tell me where they are!',12474,1,0,0,'madrigosa YELL_MADR_ICE_BLOCK'), +(-1580034,'Speak, I grow weary of asking!',12475,1,0,0,'madrigosa YELL_MADR_TRAP'), +(-1580035,'Malygos, my lord! I did my best!',12476,1,0,0,'madrigosa YELL_MADR_DEATH'), + +(-1580036,'Glory to Kil\'jaeden! Death to all who oppose!',12477,1,0,0,'felmyst SAY_INTRO'), +(-1580037,'I kill for the master!',12480,1,0,0,'felmyst SAY_KILL_1'), +(-1580038,'The end has come! ',12481,1,0,0,'felmyst SAY_KILL_2'), +(-1580039,'Choke on your final breath! ',12478,1,0,0,'felmyst SAY_BREATH'), +(-1580040,'I am stronger than ever before! ',12479,1,0,0,'felmyst SAY_TAKEOFF'), +(-1580041,'No more hesitation! Your fates are written! ',12482,1,0,0,'felmyst SAY_BERSERK'), +(-1580042,'Kil\'jaeden... will... prevail... ',12483,1,0,0,'felmyst SAY_DEATH'), +(-1580043,'Madrigosa deserved a far better fate. You did what had to be done, but this battle is far from over.',12439,1,0,0,'kalecgos SAY_KALECGOS_OUTRO'), + +(-1580044,'Misery...',12484,1,0,0,'sacrolash SAY_INTRO_1'), +(-1580045,'Depravity...',0,1,0,0,'alythess SAY_INTRO_2'), +(-1580046,'Confusion...',0,1,0,0,'sacrolash SAY_INTRO_3'), +(-1580047,'Hatred...',0,1,0,0,'alythess SAY_INTRO_4'), +(-1580048,'Mistrust...',0,1,0,0,'sacrolash SAY_INTRO_5'), +(-1580049,'Chaos...',0,1,0,0,'alythess SAY_INTRO_6'), +(-1580050,'These are the hallmarks...',0,1,0,0,'sacrolash SAY_INTRO_7'), +(-1580051,'These are the pillars...',0,1,0,0,'alythess SAY_INTRO_8'), + +(-1580052,'Shadow to the aid of fire!',12485,1,0,0,'sacrolash SAY_SACROLASH_SHADOW_NOVA'), +(-1580053,'Alythess! Your fire burns within me!',12488,1,0,0,'sacrolash SAY_SACROLASH_EMPOWER'), +(-1580054,'Shadows, engulf!',12486,1,0,0,'sacrolash SAY_SACROLASH_KILL_1'), +(-1580055,'Ee-nok Kryul!',12487,1,0,0,'sacrolash SAY_SACROLASH_KILL_2'), +(-1580056,'I... fade.',12399,1,0,0,'sacrolash SAY_SACROLASH_DEAD'), +(-1580057,'Time is a luxury you no longer possess!',0,1,0,0,'sacrolash SAY_SACROLASH_BERSERK'), +(-1580058,'Fire to the aid of shadow!',12489,1,0,0,'alythess SAY_ALYTHESS_CANFLAGRATION'), +(-1580059,'Sacrolash!',12492,1,0,0,'alythess SAY_ALYTHESS_EMPOWER'), +(-1580060,'Fire, consume!',12490,1,0,0,'alythess SAY_ALYTHESS_KILL_1'), +(-1580061,'Ed-ir Halach!',12491,1,0,0,'alythess SAY_ALYTHESS_KILL_2'), +(-1580062,'De-ek Anur!',12494,1,0,0,'alythess SAY_ALYTHESS_DEAD'), +(-1580063,'Your luck has run its course!',12493,1,0,0,'alythess SAY_ALYTHESS_BERSERK'), + +(-1580064,'All my plans have led to this!',12495,6,0,0,'kiljaeden SAY_ORDER_1'), +(-1580065,'Stay on task! Do not waste time!',12496,6,0,0,'kiljaeden SAY_ORDER_2'), +(-1580066,'I have waited long enough!',12497,6,0,0,'kiljaeden SAY_ORDER_3'), +(-1580067,'Fail me and suffer for eternity!',12498,6,0,0,'kiljaeden SAY_ORDER_4'), +(-1580068,'Drain the girl! Drain her power until there is nothing but a vacant shell!',12499,6,0,0,'kiljaeden SAY_ORDER_5'), +(-1580069,'The expendible have perished... So be it! Now I shall succeed where Sargeras could not! I will bleed this wretched world and secure my place as the true master of the Burning Legion. The end has come! Let the unraveling of this world commence!',12500,1,0,0,'kiljaeden SAY_EMERGE'), +(-1580070,'Another step towards destruction!',12501,1,0,0,'kiljaeden SAY_SLAY_1'), +(-1580071,'Anukh-Kyrie!',12502,1,0,0,'kiljaeden SAY_SLAY_2'), +(-1580072,'Who can you trust?',12503,1,0,0,'kiljaeden SAY_REFLECTION_1'), +(-1580073,'The enemy is among you.',12504,1,0,0,'kiljaeden SAY_REFLECTION_2'), +(-1580074,'Chaos!',12505,1,0,0,'kiljaeden SAY_DARKNESS_1'), +(-1580075,'Destruction!',12506,1,0,0,'kiljaeden SAY_DARKNESS_2'), +(-1580076,'Oblivion!',12507,1,0,0,'kiljaeden SAY_DARKNESS_3'), +(-1580077,'I will not be denied! This world shall fall!',12508,1,0,0,'kiljaeden SAY_PHASE_3'), +(-1580078,'Do not harbor false hope. You cannot win!',12509,1,0,0,'kiljaeden SAY_PHASE_4'), +(-1580079,'Aggghh! The powers of the Sunwell... turn... against me! What have you done? What have you done???',12510,1,0,0,'kiljaeden SAY_PHASE_5'), +(-1580080,'You are not alone. The Blue Dragonflight shall help you vanquish the Deceiver.',12438,1,0,0,'kalecgos SAY_KALECGOS_INTRO'), +(-1580081,'Anveena, you must awaken, this world needs you!',12445,1,0,0,'kalecgos SAY_KALECGOS_AWAKE_1'), +(-1580082,'I serve only the Master now.',12511,0,0,0,'anveena SAY_ANVEENA_IMPRISONED'), +(-1580083,'You must let go! You must become what you were always meant to be! The time is now, Anveena!',12446,1,0,0,'kalecgos SAY_KALECGOS_AWAKE_2'), +(-1580084,'But I\'m... lost. I cannot find my way back.',12512,0,0,0,'anveena SAY_ANVEENA_LOST'), +(-1580085,'Anveena, I love you! Focus on my voice, come back for me now! Only you can cleanse the Sunwell!',12447,1,0,0,'kalecgos SAY_KALECGOS_AWAKE_4'), +(-1580086,'Kalec... Kalec?',12513,0,0,0,'anveena SAY_ANVEENA_AWAKE'), +(-1580087,'Yes, Anveena! Let fate embrace you now!',12448,1,0,0,'kalecgos SAY_KALECGOS_AWAKE_5'), +(-1580088,'The nightmare is over, the spell is broken! Goodbye, Kalec, my love!',12514,0,0,0,'anveena SAY_ANVEENA_SACRIFICE'), +(-1580089,'Goodbye, Anveena, my love. Few will remember your name, yet this day you change the course of destiny. What was once corrupt is now pure. Heroes, do not let her sacrifice be in vain.',12450,0,0,0,'kalecgos SAY_KALECGOS_GOODBYE'), +(-1580090,'Strike now, heroes, while he is weakened! Vanquish the Deceiver!',12449,1,0,0,'kalecgos SAY_KALECGOS_ENCOURAGE'), +(-1580091,'I will channel my power into the orbs, be ready!',12440,1,0,0,'kalecgos SAY_KALECGOS_ORB_1'), +(-1580092,'I have empowered another orb! Use it quickly!',12441,1,0,0,'kalecgos SAY_KALECGOS_ORB_2'), +(-1580093,'Another orb is ready! Make haste!',12442,1,0,0,'kalecgos SAY_KALECGOS_ORB_3'), +(-1580094,'I have channeled all I can! The power is in your hands!',12443,1,0,0,'kalecgos SAY_KALECGOS_ORB_4'), + +(-1580095,'Mortal heroes - your victory here today was foretold long ago. My brother\'s anguished cry of defeat will echo across the universe - bringing renewed hope to all those who still stand against the Burning Crusade.',12515,0,0,1,'velen SAY_OUTRO_1'), +(-1580096,'As the Legion\'s final defeat draws ever-nearer, stand proud in the knowledge that you have saved worlds without number from the flame.',12516,0,0,1,'velen SAY_OUTRO_2'), +(-1580097,'Just as this day marks an ending, so too does it herald a new beginning...',12517,0,0,1,'velen SAY_OUTRO_3'), +(-1580098,'The creature Entropius, whom you were forced to destroy, was once the noble naaru, M\'uru. In life, M\'uru channeled vast energies of LIGHT and HOPE. For a time, a misguided few sought to steal those energies...',12518,0,0,1,'velen SAY_OUTRO_4'), +(-1580099,'Our arrogance was unpardonable. We damned one of the most noble beings of all. We may never atone for this sin.',12524,0,0,1,'liadrin SAY_OUTRO_5'), +(-1580100,'Than fortunate it is, that I have reclaimed the noble naaru\'s spark from where it fell! Where faith dwells, hope is never lost, young blood elf.',12519,0,0,1,'velen SAY_OUTRO_6'), +(-1580101,'Can it be ?',12525,0,0,1,'liadrin SAY_OUTRO_7'), +(-1580102,'Gaz now, mortals - upon the HEART OF M\'URU! Umblemished. Bathed by the light of Creation - just as it was at the Dawn.',12520,0,0,1,'velen SAY_OUTRO_8'), +(-1580103,'In time, the light and hope held within - will rebirth more than this mere fount of power... Mayhap, they will rebirth the soul of a nation.',12521,0,0,1,'velen SAY_OUTRO_9'), +(-1580104,'Blessed ancestors! I feel it... so much love... so much grace... there are... no words... impossible to describe...',12526,0,0,1,'liadrin SAY_OUTRO_10'), +(-1580105,'Salvation, young one. It waits for us all.',12522,0,0,1,'velen SAY_OUTRO_11'), +(-1580106,'Farewell...!',12523,0,0,1,'velen SAY_OUTRO_12'), + +(-1580107,'%s takes a deep breath.',0,3,0,0,'felmyst EMOTE_DEEP_BREATH'); + +-- -1 585 000 MAGISTER'S TERRACE +INSERT INTO script_texts (entry,content_default,sound,type,language,emote,comment) VALUES +(-1585000,'You only waste my time!',12378,1,0,0,'selin SAY_AGGRO'), +(-1585001,'My hunger knows no bounds!',12381,1,0,0,'selin SAY_ENERGY'), +(-1585002,'Yes! I am a god!',12382,1,0,0,'selin SAY_EMPOWERED'), +(-1585003,'Enough distractions!',12388,1,0,0,'selin SAY_KILL_1'), +(-1585004,'I am invincible!',12385,1,0,0,'selin SAY_KILL_2'), +(-1585005,'No! More... I must have more!',12383,1,0,0,'selin SAY_DEATH'), +(-1585006,'%s begins to channel from the nearby Fel Crystal...',0,3,0,0,'selin EMOTE_CRYSTAL'), + +(-1585007,'Drain...life!',12389,1,0,0,'vexallus SAY_AGGRO'), +(-1585008,'Un...con...tainable.',12392,1,0,0,'vexallus SAY_ENERGY'), +(-1585009,'Un...leash...',12390,1,0,0,'vexallus SAY_OVERLOAD'), +(-1585010,'Con...sume.',12393,1,0,0,'vexallus SAY_KILL'), +(-1585011,'%s discharges pure energy!',0,3,0,0,'vexallus EMOTE_DISCHARGE_ENERGY'), + +(-1585012,'Annihilate them!',12395,1,0,0,'delrissa SAY_AGGRO'), +(-1585013,'Oh, the horror.',12398,1,0,0,'delrissa LackeyDeath1'), +(-1585014,'Well, aren\'t you lucky?',12400,1,0,0,'delrissa LackeyDeath2'), +(-1585015,'Now I\'m getting annoyed.',12401,1,0,0,'delrissa LackeyDeath3'), +(-1585016,'Lackies be damned! I\'ll finish you myself!',12403,1,0,0,'delrissa LackeyDeath4'), +(-1585017,'I call that a good start.',12405,1,0,0,'delrissa PlayerDeath1'), +(-1585018,'I could have sworn there were more of you.',12407,1,0,0,'delrissa PlayerDeath2'), +(-1585019,'Not really much of a group, anymore, is it?',12409,1,0,0,'delrissa PlayerDeath3'), +(-1585020,'One is such a lonely number.',12410,1,0,0,'delrissa PlayerDeath4'), +(-1585021,'It\'s been a kick, really.',12411,1,0,0,'delrissa PlayerDeath5'), +(-1585022,'Not what I had... planned...',12397,1,0,0,'delrissa SAY_DEATH'), + +(-1585023,'Don\'t look so smug! I know what you\'re thinking, but Tempest Keep was merely a set back. Did you honestly believe I would trust the future to some blind, half-night elf mongrel?',12413,1,0,0,'kaelthas MT SAY_INTRO_1'), +(-1585024,'Vengeance burns!',12415,1,0,0,'kaelthas MT SAY_PHOENIX'), +(-1585025,'Felomin ashal!',12417,1,0,0,'kaelthas MT SAY_FLAMESTRIKE'), +(-1585026,'I\'ll turn your world... upside... down...',12418,1,0,0,'kaelthas MT SAY_GRAVITY_LAPSE'), +(-1585027,'Master... grant me strength.',12419,1,0,0,'kaelthas MT SAY_TIRED'), +(-1585028,'Do not... get too comfortable.',12420,1,0,0,'kaelthas MT SAY_RECAST_GRAVITY'), +(-1585029,'My demise accomplishes nothing! The Master will have you! You will drown in your own blood! This world shall burn! Aaaghh!',12421,1,0,0,'kaelthas MT SAY_DEATH'), +(-1585030,'Oh no, he was merely an instrument, a stepping stone to a much larger plan! It has all led to this, and this time, you will not interfere!',0,1,0,0,'kaelthas MT SAY_INTRO_2'); + +-- -1 595 000 CULLING OF STRATHOLME + +-- -1 599 000 HALLS OF STONE +INSERT INTO script_texts (entry,content_default,sound,type,language,emote,comment) VALUES +(-1599000,'Soft, vulnerable shells. Brief, fragile lives. You can not escape the curse of flesh!',14180,1,0,0,'sjonnir SAY_AGGRO'), +(-1599001,'???',14181,1,0,0,'sjonnir SAY_SLAY_1'), +(-1599002,'Flesh is no match for iron!',14182,1,0,0,'sjonnir SAY_SLAY_2'), +(-1599003,'Armies of iron will smother the world!',14183,1,0,0,'sjonnir SAY_SLAY_3'), +(-1599004,'Loken will not rest, until the forge is retaken. You changed nothing!',14184,1,0,0,'sjonnir SAY_DEATH'), + +(-1599005,'You shouldn\'t have come...now you will die!',13487,1,0,0,'maiden SAY_AGGRO'), +(-1599006,'Why must it be this way?',13488,1,0,0,'maiden SAY_SLAY_1'), +(-1599007,'You had it coming!',13489,1,0,0,'maiden SAY_SLAY_2'), +(-1599008,'My burden grows heavier.',13490,1,0,0,'maiden SAY_SLAY_3'), +(-1599009,'This is your own fault!',13491,1,0,0,'maiden SAY_SLAY_4'), +(-1599010,'So much lost time... that you\'ll never get back!',13492,1,0,0,'maiden SAY_STUN'), +(-1599011,'I hope you all rot! I never...wanted...this.',13493,1,0,0,'maiden SAY_DEATH'), + +(-1599012,'Now that\'s owning your supper!',14244,1,0,0,'brann SAY_KILL_1'), +(-1599013,'Press on, that\'s the way!',14245,1,0,0,'brann SAY_KILL_2'), +(-1599014,'Keep it up now. Plenty of death-dealing for everyone!',14246,1,0,0,'brann SAY_KILL_3'), +(-1599015,'I\'m all kinds of busted up. Might not... make it...',14257,1,0,0,'brann SAY_LOW_HEALTH'), +(-1599016,'Not yet, not... yet-',14258,1,0,0,'brann SAY_DEATH'), +(-1599017,'I\'m doing everything I can!',14260,1,0,0,'brann SAY_PLAYER_DEATH_1'), +(-1599018,'Light preserve you!',14261,1,0,0,'brann SAY_PLAYER_DEATH_2'), +(-1599019,'I hope this is all worth it!',14262,1,0,0,'brann SAY_PLAYER_DEATH_3'), +(-1599020,'Time to get some answers! Let\'s get this show on the road!',14259,1,0,0,'brann SAY_ESCORT_START'), + +(-1599021,'Don\'t worry. Old Brann has got your back. Keep that metal monstrosity busy and I\'ll see if I can sweet talk this machine into helping you.',14274,1,0,0,'brann SAY_SPAWN_DWARF'), +(-1599022,'This is a wee bit trickier that before... Oh, bloody--incomin\'!',14275,1,0,0,'brann SAY_SPAWN_TROGG'), +(-1599023,'What in the name o\' Madoran did THAT do? Oh! Wait: I just about got it...',14276,1,0,0,'brann SAY_SPAWN_OOZE'), +(-1599024,'Ha, that did it. Help\'s a-coming. Take this you glow-eying brute!',14277,1,0,0,'brann SAY_SPAWN_EARTHEN'), + +(-1599025,'Take a moment and relish this with me! Soon all will be revealed! Okay then, let\'s do this!',14247,1,0,0,'brann SAY_EVENT_INTRO_1'), +(-1599026,'Now keep an eye out! I\'ll have this licked in two shakes of a--',14248,1,0,0,'brann SAY_EVENT_INTRO_2'), +(-1599027,'Warning! Life form pattern not recognized. Archival processing terminated. Continued interference will result in targeted response.',13765,1,0,0,'brann SAY_EVENT_INTRO_3_ABED'), + +(-1599028,'Oh, that doesn\'t sound good. We might have a complication or two...',14249,1,0,0,'brann SAY_EVENT_A_1'), +(-1599029,'Security breach in progress. Analysis of historical archives transferred to lower priority queue. Countermeasures engaged.',13756,1,0,0,'brann SAY_EVENT_A_2_KADD'), +(-1599030,'Ah, you want to play hardball, eh? That\'s just my game!',14250,1,0,0,'brann SAY_EVENT_A_3'), + +(-1599031,'Couple more minutes and I\'ll--',14251,1,0,0,'brann SAY_EVENT_B_1'), +(-1599032,'Threat index threshold exceeded. Celestial archive aborted. Security level heightened.',13761,1,0,0,'brann SAY_EVENT_B_2_MARN'), +(-1599033,'Heightened? What\'s the good news?',14252,1,0,0,'brann SAY_EVENT_B_3'), + +(-1599034,'So that was the problem? Now I\'m makin\' progress...',14253,1,0,0,'brann SAY_EVENT_C_1'), +(-1599035,'Critical threat index. Void analysis diverted. Initiating sanitization protocol.',13767,1,0,0,'brann SAY_EVENT_C_2_ABED'), +(-1599036,'Hang on! Nobody\'s gonna\' be sanitized as long as I have a say in it!',14254,1,0,0,'brann SAY_EVENT_C_3'), + +(-1599037,'Ha! The old magic fingers finally won through! Now let\'s get down to-',14255,1,0,0,'brann SAY_EVENT_D_1'), +(-1599038,'Alert! Security fail safes deactivated. Beginning memory purge...',13768,1,0,0,'brann SAY_EVENT_D_2_ABED'), +(-1599039,'Purge? No no no no no! Where did I-- Aha, this should do the trick...',14256,1,0,0,'brann SAY_EVENT_D_3'), +(-1599040,'System online. Life form pattern recognized. Welcome Branbronzan. Query?',13769,1,0,0,'brann SAY_EVENT_D_4_ABED'), + +(-1599041,'Query? What do you think I\'m here for? Tea and biscuits? Spill the beans already!',14263,1,0,0,'brann SAY_EVENT_END_01'), +(-1599042,'Tell me how that dwarfs came to be! And start at the beginning!',14264,1,0,0,'brann SAY_EVENT_END_02'), +(-1599043,'Accessing prehistoric data. Retrieved. In the beginning Earthen were created to-',13770,1,0,0,'brann SAY_EVENT_END_03_ABED'), +(-1599044,'Right, right! I know that the Earthen were made of stone to shape the deep reaches of the world but what about the anomalies? Matrix non-stabilizing and whatnot.',14265,1,0,0,'brann SAY_EVENT_END_04'), +(-1599045,'Accessing. In the early stages of its development cycle Azeroth suffered infection by parasitic, necrophotic symbiotes.',13771,1,0,0,'brann SAY_EVENT_END_05_ABED'), +(-1599046,'Necro-what? Speak bloody common will ya?',14266,1,0,0,'brann SAY_EVENT_END_06'), +(-1599047,'Designation: Old Gods. Old Gods rendered all systems, including Earthen defenseless in order to facilitate assimilation. This matrix destabilization has been termed the Curse of Flesh. Effects of destabilization increased over time.',13772,1,0,0,'brann SAY_EVENT_END_07_ABED'), +(-1599048,'Old Gods eh? So they zapped the Earthen with this Curse of Flesh. And then what?',14267,1,0,0,'brann SAY_EVENT_END_08'), +(-1599049,'Accessing. Creators arrived to extirpate symbiotic infection. Assessment revealed that Old God infestation had grown malignant. Excising parasites would result in loss of host.',13757,1,0,0,'brann SAY_EVENT_END_09_KADD'), +(-1599050,'If they killed the Old Gods Azeroth would have been destroyed.',14268,1,0,0,'brann SAY_EVENT_END_10'), +(-1599051,'Correct. Creators neutralized parasitic threat and contained it within the host. Forge of Wills and other systems were instituted to create new Earthen. Safeguards were implemented and protectors were appointed.',13758,1,0,0,'brann SAY_EVENT_END_11_KADD'), +(-1599052,'What protectors?',14269,1,0,0,'brann SAY_EVENT_END_12'), +(-1599053,'Designations: Aesir and Vanir or in common nomenclator Storm and Earth Giants. Sentinel Loken designated supreme. Dragon Aspects appointed to monitor evolution of Azeroth.',13759,1,0,0,'brann SAY_EVENT_END_13_KADD'), +(-1599054,'Aesir and Vanir. Okay. So the Forge of Wills started to make new Earthen. But what happened to the old ones?',14270,1,0,0,'brann SAY_EVENT_END_14'), +(-1599055,'Additional background is relevant to your query. Following global combat between-',13762,1,0,0,'brann SAY_EVENT_END_15_MARN'), +(-1599056,'Hold everything! The Aesir and Vanir went to war? Why?',14271,1,0,0,'brann SAY_EVENT_END_16'), +(-1599057,'Unknown. Data suggests that impetus for global combat originated with prime designate Loken who neutralized all remaining Aesir and Vanir affecting termination of conflict. Prime designate Loken then initiated stasis of several seed races including Earthen, Giant and Vrykul at designated holding facilities.',13763,1,0,0,'brann SAY_EVENT_END_17_MARN'), +(-1599058,'This Loken sounds like a nasty character. Glad we don\'t have to worry about the likes of him anymore. So if I\'m understanding you lads the original Earthen eventually woke up from this statis. And by that time this destabily-whatever had turned them into our brother dwarfs. Or at least dwarf ancestors. Hm?',14272,1,0,0,'brann SAY_EVENT_END_18'), +(-1599059,'Essentially that is correct.',13764,1,0,0,'brann SAY_EVENT_END_19_MARN'), +(-1599060,'Well now. That\'s a lot to digest. I\'m gonna need some time to take all of this in. Thank you!',14273,1,0,0,'brann SAY_EVENT_END_20'), +(-1599061,'Acknowledged Branbronzan. Session terminated.',13773,1,0,0,'brann SAY_EVENT_END_21_ABED'), + +(-1599062,'Loken?! That\'s downright bothersome... We might\'ve neutralized the iron dwarves, but I\'d lay odds there\'s another machine somewhere else churnin\' out a whole mess o\' these iron vrykul!',14278,1,0,0,'brann SAY_VICTORY_SJONNIR_1'), +(-1599063,'I\'ll use the forge to make badtches o\' earthen to stand guard... But our greatest challenge still remains: find and stop Loken!',14279,1,0,0,'brann SAY_VICTORY_SJONNIR_2'), + +(-1599064,'I think it\'s time to see what\'s behind the door near the entrance. I\'m going to sneak over there, nice and quiet. Meet me at the door and I\'ll get us in.',0,1,0,0,'brann SAY_ENTRANCE_MEET'); + +-- -1 600 000 DRAK'THARON KEEP +INSERT INTO script_texts (entry,content_default,sound,type,language,emote,comment) VALUES +(-1600000,'More grunts, more glands, more FOOD!',13181,1,0,0,'trollgore SAY_AGGRO'), +(-1600001,'So hungry! Must feed!',13182,1,0,0,'trollgore SAY_CONSUME'), +(-1600002,'Aaaargh...',13183,1,0,0,'trollgore SAY_DEATH'), +(-1600003,'Corpse go boom!',13184,1,0,0,'trollgore SAY_EXPLODE'), +(-1600004,'You have gone, me gonna eat you!',13185,1,0,0,'trollgore SAY_KILL'), + +(-1600005,'The chill that you feel is the herald of your doom!',13173,1,0,0,'novos SAY_AGGRO'), +(-1600006,'Your efforts... are in vain.',13174,1,0,0,'novos SAY_DEATH'), +(-1600007,'Such is the fate of all who oppose the Lich King.',13175,1,0,0,'novos SAY_KILL'), +(-1600008,'Bolster my defenses! Hurry, curse you!',13176,1,0,0,'novos SAY_ADDS'), +(-1600009,'Surely you can see the futility of it all!',13177,1,0,0,'novos SAY_BUBBLE_1'), +(-1600010,'Just give up and die already!',13178,1,0,0,'novos SAY_BUBBLE_2'), +(-1600011,'%s calls for assistance.',0,3,0,0,'novos EMOTE_ASSISTANCE'), + +(-1600012,'Tharon\'ja sees all! The work of mortals shall not end the eternal dynasty!',13862,1,0,0,'tharonja SAY_AGGRO'), +(-1600013,'As Tharon\'ja predicted.',13863,1,0,0,'tharonja SAY_KILL_1'), +(-1600014,'As it was written.',13864,1,0,0,'tharonja SAY_KILL_2'), +(-1600015,'Your flesh serves Tharon\'ja now!',13865,1,0,0,'tharonja SAY_FLESH_1'), +(-1600016,'Tharon\'ja has a use for your mortal shell!',13866,1,0,0,'tharonja SAY_FLESH_2'), +(-1600017,'No! A taste... all too brief!',13867,1,0,0,'tharonja SAY_SKELETON_1'), +(-1600018,'Tharon\'ja will have more!',13868,1,0,0,'tharonja SAY_SKELETON_2'), +(-1600019,'Im... impossible! Tharon\'ja is eternal! Tharon\'ja... is...',13869,1,0,0,'tharonja SAY_DEATH'); + +-- -1 601 000 AZJOL-NERUB +INSERT INTO script_texts (entry,content_default,sound,type,language,emote,comment) VALUES +(-1601000,'This kingdom belongs to the Scourge! Only the dead may enter.',14075,1,0,0,'krikthir SAY_AGGRO'), +(-1601001,'???',14076,1,0,0,'krikthir SAY_KILL_1'), +(-1601002,'You were foolish to come.',14077,1,0,0,'krikthir SAY_KILL_2'), +(-1601003,'As Anub\'Arak commands!',14078,1,0,0,'krikthir SAY_KILL_3'), +(-1601004,'We are besieged. Strike out and bring back their corpses!',14079,1,0,0,'krikthir SAY_SEND_GROUP_1'), +(-1601005,'We must hold the gate. Attack! Tear them limb from limb!',14080,1,0,0,'krikthir SAY_SEND_GROUP_2'), +(-1601006,'The gate must be protected at all costs. Rip them to shreds!',14081,1,0,0,'krikthir SAY_SEND_GROUP_3'), +(-1601007,'Keep an eye on the tunnel. We must not let anyone through!',14082,1,0,0,'krikthir SAY_PREFIGHT_1'), +(-1601008,'I hear footsteps. Be on your guard.',14083,1,0,0,'krikthir SAY_PREFIGHT_2'), +(-1601009,'I sense the living. Be ready.',14084,1,0,0,'krikthir SAY_PREFIGHT_3'), +(-1601010,'They hunger.',14085,1,0,0,'krikthir SAY_SWARM_1'), +(-1601011,'Dinner time, my pets.',14086,1,0,0,'krikthir SAY_SWARM_2'), +(-1601012,'I should be grateful. But I long ago lost the capacity.',14087,1,0,0,'krikthir SAY_DEATH'), + +(-1601013,'%s moves up the tunnel!',0,3,0,0,'hadronox EMOTE_MOVE_TUNNEL'), + +(-1601014,'I was king of this empire once, long ago. In life I stood as champion. In death I returned as conqueror. Now I protect the kingdom once more. Ironic, yes?',14053,1,0,0,'anubarak SAY_INTRO'), +(-1601015,'Eternal agony awaits you!',14054,1,0,0,'anubarak SAY_AGGRO'), +(-1601016,'You shall experience my torment, first-hand!',14055,1,0,0,'anubarak SAY_KILL_1'), +(-1601017,'You have made your choice.',14056,1,0,0,'anubarak SAY_KILL_2'), +(-1601018,'Soon, the Master\'s voice will call to you.',14057,1,0,0,'anubarak SAY_KILL_3'), +(-1601019,'Come forth, my brethren. Feast on their flesh!',14059,1,0,0,'anubarak SAY_SUBMERGE_1'), +(-1601020,'Auum na-l ak-k-k-k, isshhh.',14058,1,0,0,'anubarak SAY_SUBMERGE_2'), +(-1601021,'Your armor is useless against my locusts!',14060,1,0,0,'anubarak SAY_LOCUST_1'), +(-1601022,'The pestilence upon you!',14068,1,0,0,'anubarak SAY_LOCUST_2'), +(-1601023,'Uunak-hissss tik-k-k-k-k!',14067,1,0,0,'anubarak SAY_LOCUST_3'), +(-1601024,'Ahhh... RAAAAAGH! Never thought... I would be free of him...',14069,1,0,0,'anubarak SAY_DEATH'), + +(-1601025,'The gate has been breached! Quickly, divert forces to deal with these invaders!',0,1,0,0,'anub\'ar crusher SAY_AGGRO'), +(-1601026,'There\'s no time left! All remaining forces, attack the invaders!',0,1,0,0,'anub\'ar crusher SAY_SPECIAL'); + +-- -1 602 000 HALLS OF LIGHTNING +INSERT INTO script_texts (entry,content_default,sound,type,language,emote,comment) VALUES +(-1602000,'I am the greatest of my father\'s sons! Your end has come!',14149,1,0,0,'bjarngrim SAY_AGGRO'), +(-1602001,'So ends your curse!',14153,1,0,0,'bjarngrim SAY_SLAY_1'), +(-1602002,'Flesh... is... weak!',14154,1,0,0,'bjarngrim SAY_SLAY_2'), +(-1602003,'...',14155,1,0,0,'bjarngrim SAY_SLAY_3'), +(-1602004,'How can it be...? Flesh is not... stronger!',14156,1,0,0,'bjarngrim SAY_DEATH'), +(-1602005,'Defend yourself, for all the good it will do!',14151,1,0,0,'bjarngrim SAY_BATTLE_STANCE'), +(-1602006,'%s switches to Battle Stance!',0,3,0,0,'bjarngrim EMOTE_BATTLE_STANCE'), +(-1602007,'GRAAAAAH! Behold the fury of iron and steel!',14152,1,0,0,'bjarngrim SAY_BERSEKER_STANCE'), +(-1602008,'%s switches to Berserker Stance!',0,3,0,0,'bjarngrim EMOTE_BERSEKER_STANCE'), +(-1602009,'Give me your worst!',14150,1,0,0,'bjarngrim SAY_DEFENSIVE_STANCE'), +(-1602010,'%s switches to Defensive Stance!',0,3,0,0,'bjarngrim EMOTE_DEFENSIVE_STANCE'), + +(-1602011,'You wish to confront the master? You must weather the storm!',14453,1,0,0,'ionar SAY_AGGRO'), +(-1602012,'Shocking ... I know!',14456,1,0,0,'ionar SAY_SLAY_1'), +(-1602013,'You atempt the unpossible.',14457,1,0,0,'ionar SAY_SLAY_2'), +(-1602014,'Your spark of light is ... extinguish.',14458,1,0,0,'ionar SAY_SLAY_3'), +(-1602015,'Master... you have guests.',14459,1,0,0,'ionar SAY_DEATH'), +(-1602016,'The slightest spark shall be your undoing.',14454,1,0,0,'ionar SAY_SPLIT_1'), +(-1602017,'No one is safe!',14455,1,0,0,'ionar SAY_SPLIT_2'), + +(-1602018,'What hope is there for you? None!',14162,1,0,0,'loken SAY_AGGRO0'), +(-1602019,'I have witnessed the rise and fall of empires. The birth and extinction of entire species. Over countless millennia the foolishness of mortals has remained beyond a constant. Your presence here confirms this.',14160,1,0,0,'loken SAY_INTRO_1'), +(-1602020,'My master has shown me the future, and you have no place in it. Azeroth will be reborn in darkness. Yogg-Saron shall be released! The Pantheon shall fall!',14161,1,0,0,'loken SAY_INTRO_2'), +(-1602021,'Only mortal...',14166,1,0,0,'loken SAY_SLAY_1'), +(-1602022,'I... am... FOREVER!',14167,1,0,0,'loken SAY_SLAY_2'), +(-1602023,'What little time you had, you wasted!',14168,1,0,0,'loken SAY_SLAY_3'), +(-1602024,'My death... heralds the end of this world.',14172,1,0,0,'loken SAY_DEATH'), +(-1602025,'You cannot hide from fate!',14163,1,0,0,'loken SAY_NOVA_1'), +(-1602026,'Come closer. I will make it quick.',14164,1,0,0,'loken SAY_NOVA_2'), +(-1602027,'Your flesh cannot hold out for long.',14165,1,0,0,'loken SAY_NOVA_3'), +(-1602028,'You stare blindly into the abyss!',14169,1,0,0,'loken SAY_75HEALTH'), +(-1602029,'Your ignorance is profound. Can you not see where this path leads?',14170,1,0,0,'loken SAY_50HEALTH'), +(-1602030,'You cross the precipice of oblivion!',14171,1,0,0,'loken SAY_25HEALTH'), +(-1602031,'%s begins to cast Lightning Nova!',0,3,0,0,'loken EMOTE_NOVA'), + +(-1602032,'It is you who have destroyed my children? You... shall... pay!',13960,1,0,0,'volkhan SAY_AGGRO'), +(-1602033,'The armies of iron will conquer all!',13965, 1,0,0,'volkhan SAY_SLAY_1'), +(-1602034,'Ha, pathetic!',13966,1,0,0,'volkhan SAY_SLAY_2'), +(-1602035,'You have cost me too much work!',13967,1,0,0,'volkhan SAY_SLAY_3'), +(-1602036,'The master was right... to be concerned.',13968,1,0,0,'volkhan SAY_DEATH'), +(-1602037,'I will crush you beneath my boots!',13963,1,0,0,'volkhan SAY_STOMP_1'), +(-1602038,'All my work... undone!',13964,1,0,0,'volkhan SAY_STOMP_2'), +(-1602039,'Life from the lifelessness... death for you.',13961,1,0,0,'volkhan SAY_FORGE_1'), +(-1602040,'Nothing is wasted in the process. You will see....',13962,1,0,0,'volkhan SAY_FORGE_2'), +(-1602041,'%s runs to his anvil!',0,3,0,0,'volkhan EMOTE_TO_ANVIL'), +(-1602042,'%s prepares to shatter his Brittle Golems!',0,3,0,0,'volkhan EMOTE_SHATTER'); + +-- -1 603 000 ULDUAR +INSERT INTO script_texts (entry,content_default,sound,type,language,emote,comment) VALUES +(-1603000,'The Conservatory must be protected!',15526,1,0,0,'freya SAY_AGGRO'), +(-1603001,'Elders, grant me your strength!',15527,1,0,0,'freya SAY_AGGRO_HARD'), +(-1603002,'Eonar, your servant requires aid!',15528,1,0,0,'freya SAY_ADDS_CONSERVATOR'), +(-1603003,'Children, assist me!',15533,1,0,0,'freya SAY_ADDS_TRIO'), +(-1603004,'The swarm of the elements shall overtake you!',15534,1,0,0,'freya SAY_ADDS_LASHER'), +(-1603005,'Forgive me.',15529,1,0,0,'freya SAY_SLAY_1'), +(-1603006,'From your death springs life anew!',15530,1,0,0,'freya SAY_SLAY_2'), +(-1603007,'His hold on me dissipates. I can see clearly once more. Thank you, heroes.',15531,1,0,0,'freya SAY_DEATH'), +(-1603008,'You have strayed too far, wasted too much time!',15532,1,0,0,'freya SAY_BERSERK'), +(-1603009,'Eonar, your servant calls for your blessing!',15535,1,0,0,'freya SAY_HELP_YOGG'), + +(-1603010,'Allies of Nature have appeared!',0,3,0,0,'freya EMOTE_ALLIES_NATURE'), +(-1603011,'A Lifebinder\'s Gift begins to grow!',0,3,0,0,'freya EMOTE_LIFEBINDER'), +(-1603012,'Freya begins to cast Ground Tremor!',0,3,0,0,'freya EMOTE_TREMOR'), +(-1603013,'Freya casts Strenghtened Iron Roots!',0,3,0,0,'freya EMOTE_IRON_ROOTS'), + +(-1603014,'Matron, the Conservatory has been breached!',15483,1,0,0,'brightleaf SAY_AGGRO_BRIGHT'), +(-1603015,'Fertilizer.',15485,1,0,0,'brightleaf SAY_SLAY_1_BRIGHT'), +(-1603016,'Your corpse will nourish the soil!',15486,1,0,0,'brightleaf SAY_SLAY_2_BRIGHT'), +(-1603017,'Matron, one has fallen!',15487,1,0,0,'brightleaf SAY_DEATH_BRIGHT'), + +(-1603018,'Mortals have no place here!',15493,1,0,0,'ironbranch SAY_AGGRO_IRON'), +(-1603019,'I return you whence you came!',15494,1,0,0,'ironbranch SAY_SLAY_1_IRON'), +(-1603020,'BEGONE!',15495,1,0,0,'ironbranch SAY_SLAY_2_IRON'), +(-1603021,'Freya! They come for you.',15496,1,0,0,'ironbranch SAY_DEATH_IRON'), + +(-1603022,'This place will serve as your graveyard.',15500,1,0,0,'stonebark SAY_AGGRO_STONE'), +(-1603023,'',15501,1,0,0,'stonebark SAY_SLAY_1_STONE'), +(-1603024,'Such a waste.',15502,1,0,0,'stonebark SAY_SLAY_2_STONE'), +(-1603025,'Matron, flee! They are ruthless....',15503,1,0,0,'stonebark SAY_DEATH_STONE'), + +(-1603026,'Insolent whelps! Your blood will temper the weapons used to reclaim this world!',15564,1,0,0,'ignis SAY_AGGRO'), +(-1603027,'Let the inferno consume you!',15567,1,0,0,'ignis SAY_SCORCH_1'), +(-1603028,'BURN! Burn in the makers fire!',15568,1,0,0,'ignis SAY_SCORCH_2'), +(-1603029,'I will burn away your impurities!',15566,1,0,0,'ignis SAY_SLAGPOT'), +(-1603030,'Arise, soldiers of the Iron Crucible! The Makers\' will be done!',15565,1,0,0,'ignis SAY_ADDS'), +(-1603031,'More scraps for the scrapheap!',15569,1,0,0,'ignis SAY_SLAY_1'), +(-1603032,'Your bones will serve as kindling!',15570,1,0,0,'ignis SAY_SLAY_2'), +(-1603033,'Let it be finished!',15571,1,0,0,'ignis SAY_BERSERK'), +(-1603034,'I. Have. Failed.',15572,1,0,0,'ignis SAY_DEATH'), +(-1603035,'Ignis the Furnace Master begins to cast Flame Jets!',0,3,0,0,'ignis EMOTE_FLAME_JETS'), + +(-1603036,'Welcome, champions! All of our attempts at grounding her have failed. We could use a hand in bring her down with these harpoon guns.',15647,0,0,0,'razorscale SAY_INTRO_WELCOME'), +(-1603037,'Give us a moment to prepare to build the turrets.',0,1,0,0,'razorscale SAY_INTRO_1'), +(-1603038,'Be on the lookout! Mole machines will be surfacing soon with those nasty Iron dwarves aboard!',0,1,0,0,'razorscale SAY_INTRO_2'), +(-1603039,'Ready to move out, keep those dwarves off of our backs!',0,1,0,0,'razorscale SAY_INTRO_3'), +(-1603040,'Move! Quickly! She won\'t remain grounded for long.',0,1,0,0,'razorscale SAY_GROUNDED'), +(-1603041,'Razorscale takes a deep breath...',0,3,0,0,'razorscale EMOTE_BREATH'), +(-1603042,'Fires out! Let\'s rebuild those turrets!',0,1,0,0,'razorscale SAY_EXTINGUISH_FIRE'), +(-1603043,'Harpoon Turret is ready for use!',0,3,0,0,'razorscale EMOTE_HARPOON_READY'), +(-1603044,'Razorscale grounded permanently!',0,3,0,0,'razorscale EMOTE_GROUNDED'), + +(-1603045,'New toys? For me? I promise I won\'t break them this time!',15724,1,0,0,'xt-002 SAY_AGGRO'), +(-1603046,'I... I think I broke it.',15728,1,0,0,'xt-002 SAY_SLAY_1'), +(-1603047,'I guess it doesn\'t bend that way.',15729,1,0,0,'xt-002 SAY_SLAY_2'), +(-1603048,'I\'m tired of these toys. I don\'t want to play anymore!',15730,1,0,0,'xt-002 SAY_BERSERK'), +(-1603049,'Time for a new game! My old toys will fight my new toys!',15732,1,0,0,'xt-002 SAY_ADDS'), +(-1603050,'You are bad... Toys... Very... Baaaaad!',15731,1,0,0,'xt-002 SAY_DEATH'), +(-1603051,'So tired. I will rest for just a moment!',15725,1,0,0,'xt-002 SAY_HEART_OPEN'), +(-1603052,'I\'m ready to play!',15726,1,0,0,'xt-002 SAY_HEART_CLOSE'), +(-1603053,'NO! NO! NO! NO! NO!',15727,1,0,0,'xt-002 SAY_TANCTRUM'), +(-1603054,'XT-002 Deconstructor\'s heart is exposed and leaking energy!',0,3,0,0,'xt-002 EMOTE_EXPOSE_HEART'), +(-1603055,'XT-002 Deconstructor consumes a scrapbot to repair himself.',0,3,0,0,'xt-002 EMOTE_REPAIR'), + +(-1603056,'Whether the world\'s greatest gnats or the world\'s greatest heroes, you\'re still only mortal.',15684,1,0,0,'brundir SAY_BRUNDIR_AGGRO'), +(-1603057,'Stand still and stare into the light!',15687,1,0,0,'brundir SAY_BRUNDIR_WHIRL'), +(-1603058,'The power of the storm lives on...',15689,1,0,0,'brundir SAY_BRUNDIR_DEATH_1'), +(-1603059,'You rush headlong into the maw of madness!',15690,1,0,0,'brundir SAY_BRUNDIR_DEATH_2'), +(-1603060,'A merciful kill!',15685,1,0,0,'brundir SAY_BRUNDIR_SLAY_1'), +(-1603061,'HAH!',15686,1,0,0,'brundir SAY_BRUNDIR_SLAY_2'), +(-1603062,'This meeting of the Assembly of Iron is adjourned!',15691,1,0,0,'brundir SAY_BRUNDIR_BERSERK'), +(-1603063,'Let the storm clouds rise and rain down death from above!',15688,1,0,0,'brundir SAY_BRUNDIR_FLY'), + +(-1603064,'Nothing short of total decimation will suffice!',15657,1,0,0,'molgeim SAY_MOLGEIM_AGGRO'), +(-1603065,'The legacy of storms shall not be undone...',15662,1,0,0,'molgeim SAY_MOLGEIM_DEATH_1'), +(-1603066,'What have you gained from my defeat? You are no less doomed, mortals...',15663,1,0,0,'molgeim SAY_MOLGEIM_DEATH_2'), +(-1603067,'Decipher this!',15660,1,0,0,'molgeim SAY_MOLGEIM_DEATH_RUNE'), +(-1603068,'Face the lightning surge!',15661,1,0,0,'molgeim SAY_MOLGEIM_SURGE'), +(-1603069,'The world on suffers yet another insignificant loss!',15658,1,0,0,'molgeim SAY_MOLGEIM_SLAY_1'), +(-1603070,'Death is the price of your arrogance.',15659,1,0,0,'molgeim SAY_MOLGEIM_SLAY_2'), +(-1603071,'This meeting of the Assembly of Iron is adjourned!',15664,1,0,0,'molgeim SAY_MOLGEIM_BERSERK'), + +(-1603072,'You will not defeat the Assembly of Iron so easily, invaders!',15674,1,0,0,'steelbreaker SAY_STEEL_AGGRO'), +(-1603073,'My death only serves to hasten your demise.',15678,1,0,0,'steelbreaker SAY_STEEL_DEATH_1'), +(-1603074,'Impossible!',15679,1,0,0,'steelbreaker SAY_STEEL_DEATH_2'), +(-1603075,'So fragile and weak!',15675,1,0,0,'steelbreaker SAY_STEEL_SLAY_1'), +(-1603076,'Flesh... such a hindrance.',15676,1,0,0,'steelbreaker SAY_STEEL_SLAY_2'), +(-1603077,'You seek the secrets of Ulduar? Then take them!',15677,1,0,0,'steelbreaker SAY_STEEL_OVERWHELM'), +(-1603078,'This meeting of the Assembly of Iron is adjourned!',15680,1,0,0,'steelbreaker SAY_STEEL_BERSERK'), + +(-1603079,'Some things are better left alone!',15473,1,0,0,'auriaya SAY_AGGRO'), +(-1603080,'There is no escape!',15475,1,0,0,'auriaya SAY_SLAY_1'), +(-1603081,'The secret dies with you!',15474,1,0,0,'auriaya SAY_SLAY_2'), +(-1603082,'You waste my time!',15477,1,0,0,'auriaya SAY_BERSERK'), +(-1603083,'Auriaya screams in agony.',15476,2,0,0,'auriaya SAY_DEATH'), +(-1603084,'Auriaya begins to cast Terrifying Screech.',0,3,0,0,'auriaya EMOTE_SCREECH'), +(-1603085,'Auriaya begins to activate the Feral Defender!',0,3,0,0,'auriaya EMOTE_DEFENDER'), + +(-1603086,'You will suffer for this trespass!',15552,1,0,0,'hodir SAY_AGGRO'), +(-1603087,'Tragic. To come so far, only to fail.',15553,1,0,0,'hodir SAY_SLAY_1'), +(-1603088,'Welcome to the endless winter.',15554,1,0,0,'hodir SAY_SLAY_2'), +(-1603089,'Winds of the north consume you!',15555,1,0,0,'hodir SAY_FLASH_FREEZE'), +(-1603090,'',15556,2,0,0,'hodir SAY_FROZEN_BLOWS'), +(-1603091,'I... I am released from his grasp... at last.',15557,1,0,0,'hodir SAY_DEATH'), +(-1603092,'Enough! This ends now!',15558,1,0,0,'hodir SAY_BERSERK'), +(-1603093,'The veil of winter will protect you, champions!',15559,1,0,0,'hodir SAY_HELP_YOGG'), +(-1603094,'Hodir begins to cast Flash Freeze!',0,3,0,0,'hodir EMOTE_FLASH_FREEZE'), +(-1603095,'Hodir gains Frozen Blows!',0,3,0,0,'hodir EMOTE_FROZEN_BLOWS'), + +(-1603096,'Your destruction will herald a new age of suffering!',15542,1,0,0,'vezax SAY_AGGRO'), +(-1603097,'You thought to stand before the legions of death... and survive?',15543,1,0,0,'vezax SAY_SLAY_1'), +(-1603098,'Defiance... a flaw of mortality.',15544,1,0,0,'vezax SAY_SLAY_2'), +(-1603099,'The black blood of Yogg-Saron courses through me! I. AM. UNSTOPPABLE!',15545,1,0,0,'vezaz SAY_SURGE'), +(-1603100,'Oh, what horrors await....',15546,1,0,0,'vezax SAY_DEATH'), +(-1603101,'Your defeat was inevitable!',15547,1,0,0,'vezax SAY_ENRAGE'), +(-1603102,'Behold, now! Terror, absolute!',15548,1,0,0,'vezax SAY_HARD_MODE'), +(-1603103,'A cloud of saronite vapors coalesces nearby!',0,3,0,0,'vezax EMOTE_VAPOR'), +(-1603104,'General Vezax roars and surges with dark might!',0,3,0,0,'vezax EMOTE_SURGE'), +(-1603105,'The saronite vapors mass and swirl violently, merging into a monstrous form!',0,3,0,0,'vezax EMOTE_ANIMUS'), + +(-1603106,'Trans-location complete. Commencing planetary analysis of Azeroth.',15405,1,0,0,'algalon SAY_INTRO_1'), +(-1603107,'Stand back, mortals. I am not here to fight you.',15406,1,0,0,'algalon SAY_INTRO_2'), +(-1603108,'It is in the universe\'s best interest to re-originate this planet should my analysis find systemic corruption. Do not interfere.',15407,1,0,0,'algalon SAY_INTRO_3'), + +(-1603109,'See your world through my eyes. A universe so vast as to be immeasurable. Incomprehensible even to your greatest minds.',15390,1,0,0,'algalon SAY_ENGAGE'), +(-1603110,'Your actions are illogical. All possible results for this encounter have been calculated. The pantheon will receive the observer\'s message regardless outcome.',15386,1,0,0,'algalon SAY_AGGRO'), +(-1603111,'Loss of life, unavoidable.',15387,1,0,0,'algalon SAY_SLAY_1'), +(-1603112,'I do what I must.',15388,1,0,0,'algalon SAY_SLAY_2'), +(-1603113,'The stars come to my aid.',15392,1,0,0,'algalon SAY_SUMMON_STAR'), +(-1603114,'Witness the fury of cosmos!',15396,1,0,0,'algalon SAY_BIG_BANG_1'), +(-1603115,'Behold the tools of creation!',15397,1,0,0,'algalon SAY_BIG_BANG_2'), +(-1603116,'Beware!',15391,1,0,0,'algalon SAY_PHASE_2'), +(-1603117,'You are... out of time.',15394,1,0,0,'algalon SAY_BERSERK'), + +(-1603118,'Analysis complete. There is partial corruption in the plane\'s life-support systems as well as complete corruption in most of the planet\'s defense mechanisms.',15398,1,0,0,'algalon SAY_DESPAWN_1'), +(-1603119,'Begin uplink: Reply Code: \'Omega\'. Planetary re-origination requested.',15399,1,0,0,'algalon SAY_DESPAWN_2'), +(-1603120,'Farewell, mortals. Your bravery is admirable, for such flawed creatures.',15400,1,0,0,'algalon SAY_DESPAWN_3'), + +(-1603121,'I have seen worlds bathed in the Makers\' flames. Their denizens fading without so much as a whimper. Entire planetary systems born and raised in the time that it takes your mortal hearts to beat once. Yet all throughout, my own heart, devoid of emotion... of empathy. I... have... felt... NOTHING! A million, million lives wasted. Had they all held within them your tenacity? Had they all loved life as you do?',15393,1,0,0,'algalon SAY_OUTRO_1'), +(-1603122,'Perhaps it is your imperfection that which grants you free will. That allows you to persevere against cosmically calculated odds. You prevailed where the Titans\' own perfect creations have failed.',15401,1,0,0,'algalon SAY_OUTRO_2'), +(-1603123,'I\'ve rearranged the reply code. Your planet will be spared. I cannot be certain of my own calculations anymore.',15402,1,0,0,'algalon SAY_OUTRO_3'), +(-1603124,'I lack the strength to transmit this signal. You must hurry. Find a place of power, close to the skies.',15403,1,0,0,'algalon SAY_OUTRO_4'), +(-1603125,'Do not worry about my fate $n. If the signal is not transmitted in time re-origination will proceed regardless. Save. Your. World.',15404,1,0,0,'algalon SAY_OUTRO_5'), + +(-1603126,'None shall pass!',15586,1,0,0,'kologarn SAY_AGGRO'), +(-1603127,'OBLIVION!',15591,1,0,0,'kologarn SAY_SHOCKWAVE'), +(-1603128,'I will squeeze the life from you!',15592,1,0,0,'kologarn SAY_GRAB'), +(-1603129,'Just a scratch!',15589,1,0,0,'kologarn SAY_ARM_LOST_LEFT'), +(-1603130,'Only a flesh wound!',15590,1,0,0,'kologarn SAY_ARM_LOST_RIGHT'), +(-1603131,'KOL-THARISH!',15587,1,0,0,'kologarn SAY_SLAY_1'), +(-1603132,'YOU FAIL!',15588,1,0,0,'kologarn SAY_SLAY_2'), +(-1603133,'I am invincible!',15594,1,0,0,'kologarn SAY_BERSERK'), +(-1603134,'Master, they come...',15593,1,0,0,'kologarn SAY_DEATH'), +(-1603135,'The Right Arm has regrown!',0,3,0,0,'kologarn EMOTE_ARM_RIGHT'), +(-1603136,'The Left Arm has regrown!',0,3,0,0,'kologarn EMOTE_ARM_LEFT'), +(-1603137,'Kologarn casts Stone Grip!',0,3,0,0,'kologarn EMOTE_STONE_GRIP'), + +(-1603138,'Interlopers! You mortals who dare to interfere with my sport will pay... Wait--you...',15733,1,0,0,'thorim SAY_AGGRO_1'), +(-1603139,'I remember you... In the mountains... But you... what is this? Where am--',15734,1,0,0,'thorim SAY_AGGRO_2'), + +(-1603140,'Behold the power of the storms and despair!',15735,1,0,0,'thorim SAY_SPECIAL_1'), +(-1603141,'Do not hold back! Destroy them!',15736,1,0,0,'thorim SAY_SPECIAL_2'), +(-1603142,'Have you begun to regret your intrusion?',15737,1,0,0,'thorim SAY_SPECIAL_3'), + +(-1603143,'Impertinent whelps! You dare challenge me atop my pedestal! I will crush you myself!',15738,1,0,0,'thorim SAY_JUMP'), +(-1603144,'Can\'t you at least put up a fight!?',15739,1,0,0,'thorim SAY_SLAY_1'), +(-1603145,'Pathetic!',15740,1,0,0,'thorim SAY_SLAY_2'), +(-1603146,'My patience has reached its limit!',15741,1,0,0,'thorim SAY_BERSERK'), + +(-1603147,'Failures! Weaklings!',15742,1,0,0,'thorim SAY_ARENA_WIPE'), +(-1603148,'Stay your arms! I yield!',15743,1,0,0,'thorim SAY_DEFEATED'), + +(-1603149,'I feel as though I am awakening from a nightmare, but the shadows in this place yet linger.',15744,1,0,0,'thorim SAY_OUTRO_1'), +(-1603150,'Sif... was Sif here? Impossible--she died by my brother\'s hand. A dark nightmare indeed....',15745,1,0,0,'thorim SAY_OUTRO_2'), +(-1603151,'I need time to reflect.... I will aid your cause if you should require it. I owe you at least that much. Farewell.',15746,1,0,0,'thorim SAY_OUTRO_3'), + +(-1603152,'You! Fiend! You are not my beloved! Be gone!',15747,1,0,0,'thorim SAY_OUTRO_HARD_1'), +(-1603153,'Behold the hand behind all the evil that has befallen Ulduar! Left my kingdom in ruins, corrupted my brother and slain my wife!',15748,1,0,0,'thorim SAY_OUTRO_HARD_2'), +(-1603154,'And now it falls to you, champions, to avenge us all! The task before you is great, but I will lend you my aid as I am able. You must prevail!',15749,1,0,0,'thorim SAY_OUTRO_HARD_3'), + +(-1603155,'Golganneth, lend me your strengh! Grant my mortal allies the power of thunder!',15750,1,0,0,'thorim SAY_HELP_YOGG'), + +(-1603156,'Thorim, my lord, why else would these invaders have come into your sanctum but to slay you? They must be stopped!',15668,1,0,0,'thorim SAY_SIF_BEGIN'), +(-1603157,'Impossible! Lord Thorim, I will bring your foes a frigid death!',15670,1,0,0,'thorim SAY_SIF_EVENT'), +(-1603158,'These pathetic mortals are harmless, beneath my station. Dispose of them!',15669,1,0,0,'thorim SAY_SIF_DESPAWN'), + +(-1603159,'Hostile entities detected. Threat assessment protocol active. Primary target engaged. Time minus thirty seconds to re-evaluation.',15506,1,0,0,'leviathan SAY_AGGRO'), +(-1603160,'Threat assessment routine modified. Current target threat level: zero. Acquiring new target.',15521,1,0,0,'leviathan SAY_SLAY'), +(-1603161,'Total systems failure. Defense protocols breached. Leviathan Unit shutting down.',15520,1,0,0,'leviathan SAY_DEATH'), +(-1603162,'Threat re-evaluated. Target assessment complete. Changing course.',15507,1,0,0,'leviathan SAY_CHANGE_1'), +(-1603163,'Pursuit objective modified. Changing course.',15508,1,0,0,'leviathan SAY_CHANGE_2'), +(-1603164,'Hostile entity stratagem predicted. Rerouting battle function. Changing course.',15509,1,0,0,'leviathan SAY_CHANGE_3'), +(-1603165,'Unauthorized entity attempting circuit overload. Activating anti-personnel countermeasures.',15516,1,0,0,'leviathan SAY_PLAYER_RIDE'), +(-1603166,'System malfunction. Diverting power to support systems.',15517,1,0,0,'leviathan SAY_OVERLOAD_1'), +(-1603167,'Combat matrix overload. Powering do-o-o-own...',15518,1,0,0,'leviathan SAY_OVERLOAD_2'), +(-1603168,'System restart required. Deactivating weapon systems.',15519,1,0,0,'leviathan SAY_OVERLOAD_3'), +(-1603169,'Orbital countermeasures enabled.',15510,1,0,0,'leviathan SAY_HARD_MODE'), +(-1603170,'\'Hodir\'s Fury\' online. Acquiring target.',15512,1,0,0,'leviathan SAY_TOWER_FROST'), +(-1603171,'\'Mimiron\'s Inferno\' online. Acquiring target.',15513,1,0,0,'leviathan SAY_TOWER_FIRE'), +(-1603172,'\'Thorim\'s Hammer\' online. Acquiring target.',15515,1,0,0,'leviathan SAY_TOWER_ENERGY'), +(-1603173,'\'Freya\'s Ward\' online. Acquiring target.',15514,1,0,0,'leviathan SAY_TOWER_NATURE'), +(-1603174,'Alert! Static defense system failure. Orbital countermeasures disabled.',15511,1,0,0,'leviathan SAY_TOWER_DOWN'), +(-1603175,'%s pursues $N',0,3,0,0,'leviathan EMOTE_PURSUE'), + +(-1603176,'Oh, my! I wasn\'t expecting company! The workshop is such a mess! How embarrassing!',15611,1,0,0,'mimiron SAY_AGGRO'), +(-1603177,'Now why would you go and do something like that? Didn\'t you see the sign that said \'DO NOT PUSH THIS BUTTON!\'? How will we finish testing with the self-destruct mechanism active?',15629,1,0,0,'mimiron SAY_HARD_MODE'), +(-1603178,'Oh, my! It would seem that we are out of time, my friends!',15628,1,0,0,'mimiron SAY_BERSERK'), + +(-1603179,'We haven\'t much time, friends! You\'re going to help me test out my latest and greatest creation. Now, before you change your minds, remember, that you kind of owe it to me after the mess you made with the XT-002.',15612,1,0,0,'mimiron SAY_TANK_ACTIVE'), +(-1603180,'MEDIC!',15613,1,0,0,'mimiron SAY_TANK_SLAY_1'), +(-1603181,'I can fix that... or, maybe not! Sheesh, what a mess...',15614,1,0,0,'mimiron SAY_TANK_SLAY_2'), +(-1603182,'WONDERFUL! Positively marvelous results! Hull integrity at 98.9 percent! Barely a dent! Moving right along.',15615,1,0,0,'mimiron SAY_TANK_DEATH'), + +(-1603183,'Behold the VX-001 Anti-personnel Assault Cannon! You might want to take cover.',15616,1,0,0,'mimiron SAY_TORSO_ACTIVE'), +(-1603184,'Fascinating. I think they call that a \'clean kill\'.',15617,1,0,0,'mimiron SAY_TORSO_SLAY_1'), +(-1603185,'Note to self: Cannon highly effective against flesh.',15618,1,0,0,'mimiron SAY_TORSO_SLAY_2'), +(-1603186,'Thank you, friends! Your efforts have yielded some fantastic data! Now, where did I put-- oh, there it is!',15619,1,0,0,'mimiron SAY_TORSO_DEATH'), + +(-1603187,'Isn\'t it beautiful? I call it the magnificent aerial command unit!',15620,1,0,0,'mimiron SAY_HEAD_ACTIVE'), +(-1603188,'Outplayed!',15621,1,0,0,'mimiron SAY_HEAD_SLAY_1'), +(-1603189,'You can do better than that!',15622,1,0,0,'mimiron SAY_HEAD_SLAY_2'), +(-1603190,'Preliminary testing phase complete. Now comes the true test!!',15623,1,0,0,'mimiron SAY_HEAD_DEATH'), + +(-1603191,'Gaze upon its magnificence! Bask in its glorious, um, glory! I present you... V-07-TR-0N!',15624,1,0,0,'mimiron SAY_ROBOT_ACTIVE'), +(-1603192,'Prognosis: Negative!',15625,1,0,0,'mimiron SAY_ROBOT_SLAY_1'), +(-1603193,'You\'re not going to get up from that one, friend.',15626,1,0,0,'mimiron SAY_ROBOT_SLAY_2'), +(-1603194,'It would appear that I\'ve made a slight miscalculation. I allowed my mind to be corrupted by the fiend in the prison, overriding my primary directive. All systems seem to be functional now. Clear.',15627,1,0,0,'mimiron SAY_ROBOT_DEATH'), + +(-1603195,'Combat matrix enhanced. Behold wonderous rapidity!',15630,1,0,0,'mimiron SAY_HELP_YOGG'), +(-1603196,'Leviathan Mk II begins to cast Plasma Blast!',0,3,0,0,'mimiron EMOTE_PLASMA_BLAST'), + +(-1603197,'Aaaaaaaaaaaaaaaaa... Help me!!! Please got to help me!',15771,1,0,0,'yogg SAY_SARA_INTRO_1'), +(-1603198,'What do you want from me? Leave me alone!',15772,1,0,0,'yogg SAY_SARA_INTRO_2'), +(-1603199,'The time to strike at the head of the beast will soon be upon us! Focus your anger and hatred on his minions!',15775,1,0,0,'yogg SAY_SARA_AGGRO'), +(-1603201,'Yes! YES! Show them no mercy! Give no pause to your attacks!',15773,1,0,0,'yogg SAY_SARA_HELP_1'), +(-1603202,'Let hatred and rage guide your blows!',15774,1,0,0,'yogg SAY_SARA_HELP_2'), +(-1603203,'Could they have been saved?',15779,1,0,0,'yogg SAY_SARA_SLAY_1'), +(-1603204,'Powerless to act...',15778,1,0,0,'yogg SAY_SARA_SLAY_2'), + +(-1603205,'Weak-minded fools!',15780,4,0,0,'yogg SAY_WIPE_PHASE_1'), + +(-1603206,'I am the lucid dream. The monster in your nightmares. The fiend of a thousand faces. Cower before my true form. BOW DOWN BEFORE THE GOD OF DEATH!',15754,1,0,0,'yogg SAY_PHASE_2_INTRO'), +(-1603207,'Tremble, mortals, before the coming of the end!',15777,1,0,0,'yogg SAY_SARA_PHASE_2_INTRO_A'), +(-1603208,'Suffocate upon your own hate!',15776,1,0,0,'yogg SAY_SARA_PHASE_2_INTRO_B'), + +(-1603209,'MADNESS WILL CONSUME YOU!',15756,1,0,0,'yogg SAY_MADNESS'), +(-1603210,'Look upon the true face of death and know that your end comes soon!',15755,1,0,0,'yogg SAY_PHASE_3'), +(-1603211,'Hoohehehahahaha... AHAHAHAHAHAHA!',15757,1,0,0,'yogg SAY_SLAY_1'), +(-1603212,'Eternal suffering awaits!',15758,1,0,0,'yogg SAY_SLAY_2'), +(-1603213,'Your fate is sealed. The end of days is finally upon you and ALL who inhabit this miserable little seedling. Uulwi ifis halahs gag erh\'ongg w\'ssh.',15761,1,0,0,'yogg SAY_DEATH'), +(-1603214,'Your will is no longer you own...',15759,4,0,0,'yogg SAY_TO_INSANE_1'), +(-1603215,'Destroy them minion, your master commands it!',15760,4,0,0,'yogg SAY_TO_INSANE_2'), + +(-1603216,'Your resilience is admirable.',15598,0,0,0,'yogg SAY_LICH_KING_1'), +(-1603217,'Arrrrrrgh!',15470,1,0,0,'yogg SAY_CHAMPION_1'), +(-1603218,'I\'m not afraid of you!',15471,0,0,0,'yogg SAY_CHAMPION_2'), +(-1603219,'I will break you as I broke him.',15599,0,0,0,'yogg SAY_LICH_KING_2'), +(-1603220,'Yrr n\'lyeth... shuul anagg!',15766,0,0,0,'yogg SAY_YOGG_V3_1'), +(-1603221,'He will learn... no king rules forever; only death is eternal!',15767,0,0,0,'yogg SAY_YOGG_V3_2'), + +(-1603222,'It is done... All have been given that which must be given. I now seal the Dragon Soul forever...',15631,0,0,0,'yogg SAY_NELTHARION_1'), +(-1603223,'That terrible glow... should that be?',15784,0,0,0,'yogg SAY_YSERA'), +(-1603224,'For it to be as it must, yes.',15632,0,0,0,'yogg SAY_NELTHARION_2'), +(-1603225,'It is a weapon like no other. It must be like no other.',15610,0,0,0,'yogg SAY_MALYGOS'), +(-1603226,'His brood learned their lesson before too long, you shall soon learn yours!',15765,0,0,0,'yogg SAY_YOGG_V2'), + +(-1603227,'Bad news sire. The clans are united under Blackhand in this assault. They will stand together until Stormwind has fallen.',15538,0,0,0,'yogg SAY_GARONA_1'), +(-1603228,'Gul\'dan is bringing up his warlocks by nightfall. Until then, the Blackrock clan will be trying to take the Eastern Wall.',15539,0,0,0,'yogg SAY_GARONA_2'), +(-1603229,'A thousand deaths... ',15762,0,0,0,'yogg SAY_YOGG_V1_1'), +(-1603230,'or one murder.',15763,0,0,0,'yogg SAY_YOGG_V1_2'), +(-1603231,'We will hold until the reinforcements come. As long as men with stout hearts are manning the walls and throne Stormwind will hold.',15540,0,0,0,'yogg SAY_GARONA_3'), +(-1603232,'The orc leaders agree with your assessment.',15541,0,0,0,'yogg SAY_GARONA_4'), +(-1603233,'Your petty quarrels only make me stronger!',15764,0,0,0,'yogg SAY_YOGG_V1_3'), + +(-1603234,'Portals open into Yogg-Saron\'s mind!',0,3,0,0,'yogg EMOTE_VISION_BLAST'), +(-1603235,'The illusion shatters and a path to the central chamber opens!',0,3,0,0,'yogg EMOTE_SHATTER_BLAST'); + +-- -1 604 000 GUNDRAK +INSERT INTO script_texts (entry,content_default,sound,type,language,emote,comment) VALUES +(-1604000,'Drakkari gonna kill anybody who trespass on these lands!',14443,1,0,0,'sladran SAY_AGGRO'), +(-1604001,'Minions of the scale, heed my call!',14444,1,0,0,'sladran SAY_SUMMON_SNAKE'), +(-1604002,'Our thousand fangs gonna rend your flesh! ',14445,1,0,0,'sladran SAY_SUMMON_CONSTRICTOR'), +(-1604003,'Ye not breathin\'! Good.',14446,1,0,0,'sladran SAY_SLAY_1'), +(-1604004,'You scared now?',14447,1,0,0,'sladran SAY_SLAY_2'), +(-1604005,'I\'ll eat you next, mon!',14448,1,0,0,'sladran SAY_SLAY_3'), +(-1604006,'I sssee now... Ssscourge wasss not... our greatessst enemy...',14449,1,0,0,'sladran SAY_DEATH'), +(-1604007,'%s begins to cast Poison Nova!',0,3,0,0,'sladran EMOTE_NOVA'), + +(-1604008,'%s surges forward!',0,2,0,0,'colossus EMOTE_SURGE'), +(-1604009,'%s seep into the ground.',0,2,0,0,'colossus EMOTE_SEEP'), +(-1604010,'%s begins to glow faintly.',0,2,0,0,'colossus EMOTE_GLOW'), + +(-1604011,'We fought back da Scourge. What chance joo be thinkin\' JOO got?',14721,1,0,0,'moorabi SAY_AGGRO'), +(-1604012,'Da ground gonna swallow you up! ',14723,1,0,0,'moorabi SAY_QUAKE'), +(-1604013,'Get ready for somethin\'... much... BIGGAH!',14722,1,0,0,'moorabi SAY_TRANSFORM'), +(-1604014,'I crush you, cockroaches!',14725,1,0,0,'moorabi SAY_SLAY_1'), +(-1604015,'Who gonna stop me; you?',14726,1,0,0,'moorabi SAY_SLAY_2'), +(-1604016,'Not so tough now.',14727,1,0,0,'moorabi SAY_SLAY_3'), +(-1604017,'If our gods can die... den so can we...',14728,1,0,0,'moorabi SAY_DEATH'), +(-1604018,'%s begins to transform!',0,3,0,0,'moorabi EMOTE_TRANSFORM'), + +(-1604019,'I\'m gonna spill your guts, mon!',14430,1,0,0,'galdarah SAY_AGGRO'), +(-1604020,'Ain\'t gonna be nottin\' left after this!',14431,1,0,0,'galdarah SAY_TRANSFORM_1'), +(-1604021,'You wanna see power? I\'m gonna show you power!',14432,1,0,0,'galdarah SAY_TRANSFORM_2'), +(-1604022,'Gut them! Impale them!',14433,1,0,0,'galdarah SAY_SUMMON_1'), +(-1604023,'Kill them all!',14434,1,0,0,'galdarah SAY_SUMMON_2'), +(-1604024,'Say hello to my BIG friend!',14435,1,0,0,'galdarah SAY_SUMMON_3'), +(-1604025,'What a rush!',14436,1,0,0,'galdarah SAY_SLAY_1'), +(-1604026,'Who needs gods, when WE ARE GODS!',14437,1,0,0,'galdarah SAY_SLAY_2'), +(-1604027,'I told ya so!',14438,1,0,0,'galdarah SAY_SLAY_3'), +(-1604028,'Even the mighty... can fall.',14439,1,0,0,'galdarah SAY_DEATH'), + +(-1604029,'%s transforms into a Mammoth!',14724,2,0,0,'moorabi EMOTE_TRANSFORMED'), +(-1604030,'$N is impaled!',0,3,0,0,'EMOTE_IMPALED'); + + +-- -1 608 000 VIOLET HOLD +INSERT INTO script_texts (entry,content_default,sound,type,language,emote,comment) VALUES +(-1608000,'Prison guards, we are leaving! These adventurers are taking over! Go, go, go!',0,1,0,0,'sinclari SAY_BEGIN'), +(-1608001,'I\'m locking the door. Good luck, and thank you for doing this.',0,0,0,1,'sinclari SAY_LOCK_DOOR'), + +(-1608002,'Adventurers, the door is beinning to weaken!',0,1,0,0,'sinclair SAY_SEAL_75'), +(-1608003,'Only half of the door seal\'s strength remains! You must fight on!',0,1,0,0,'sinclair SAY_SEAL_50'), +(-1608004,'The door seal is about to collapse! All is lost if the Blue Dragonflight breaks through the door!',0,1,0,0,'sinclair SAY_SEAL_5'), + +(-1608005,'A Portal Guardian defends the new portal!',0,3,0,0,'EMOTE_GUARDIAN_PORTAL'), +(-1608006,'An elite Blue Dragonflight squad appears from the portal!',0,3,0,0,'EMOTE_DRAGONFLIGHT_PORTAL'), +(-1608007,'A Guardian Keeper emerges from the portal!',0,3,0,0,'EMOTE_KEEPER_PORTAL'), + +(-1608008,'Free to--mm--fly now. Ra-aak... Not find us--ekh-ekh! Escape!',14218,1,0,0,'erekem SAY_RELEASE_EREKEM'), +(-1608009,'I... am fury... unrestrained!',14229,1,0,0,'ichoron SAY_RELEASE_ICHORON'), +(-1608010,'Back in business! Now to execute an exit strategy.',14498,1,0,0,'xevozz SAY_RELEASE_XEVOZZ'), +(-1608011,'I am... renewed.',13995,1,0,0,'zuramat SAY_RELEASE_ZURAMAT'), + +(-1608012,'Not--caww--get in way of--rrak-rrak--flee!',14219,1,0,0,'erekem SAY_AGGRO'), +(-1608013,'My---raaak--favorite! Awk awk awk! Raa-kaa!',14220,1,0,0,'erekem SAY_ADD_DIE_1'), +(-1608014,'Nasty little...A-ak,kaw! Kill! Yes,kill you!',14221,1,0,0,'erekem SAY_ADD_DIE_2'), +(-1608018,'No--kaw,kaw--flee...',14225,1,0,0,'erekem SAY_DEATH'), + +(-1608019,'Stand aside, mortals!',14230,1,0,0,'ichoron SAY_AGGRO'), +(-1608020,'I will not be contained! Ngyah!!',14233,1,0,0,'ichoron SAY_SHATTERING'), +(-1608021,'Water can hold any form, take any shape... overcome any obstacle.',14232,1,0,0,'ichoron SAY_SHIELD'), +(-1608022,'I am a force of nature!',14234,1,0,0,'ichoron SAY_SLAY_1'), +(-1608023,'I shall pass!',14235,1,0,0,'ichoron SAY_SLAY_2'), +(-1608024,'You can not stop the tide!',14236,1,0,0,'ichoron SAY_SLAY_3'), +(-1608025,'I shall consume,decimate, devastate,and destroy! Yield now to the wrath of the pounding sea!',14231,1,0,0,'ichoron SAY_ENRAGE'), +(-1608026,'I... recede.',14237,1,0,0,'ichoron SAY_DEATH'), + +(-1608027,'You did it! You held the Blue Dragonflight back and defeated their commander. Amazing work!',0,0,0,1,'sinclari SAY_VICTORY'), + +(-1608028,'%s\'s Protective Bubble shatters!',0,3,0,0,'ichoron EMOTE_BUBBLE'); + +-- -1 609 000 EBON HOLD (DK START) +INSERT INTO script_texts (entry,content_default,sound,type,language,emote,comment) VALUES +(-1609000,'You have made a grave error, fiend!',0,0,0,0,'unworthy SAY_START_1'), +(-1609001,'I was a soldier of the Light once... Look at what I have become... ',0,0,0,0,'unworthy SAY_START_2'), +(-1609002,'You are hopelessly outmatched, $R.',0,0,0,0,'unworthy SAY_START_3'), +(-1609003,'They brand me unworthy? I will show them unmorthy!',0,0,0,0,'unworthy SAY_START_4'), +(-1609004,'You will allow me a weapon and armor, yes?',0,0,0,0,'unworthy SAY_START_5'), +(-1609005,'I will win my freedom and leave this cursed place!',0,0,0,0,'unworthy SAY_START_6'), +(-1609006,'I will dismantle this festering hellhole!',0,0,0,0,'unworthy SAY_START_7'), +(-1609007,'There can be only one survivor!',0,0,0,0,'unworthy SAY_START_8'), + +(-1609008,'Let your fears consume you!',0,0,0,0,'unworthy SAY_AGGRO_1'), +(-1609009,'HAH! You can barely hold a blade! Yours will be a quick death.',0,0,0,0,'unworthy SAY_AGGRO_2'), +(-1609010,'And now you die',0,0,0,0,'unworthy SAY_AGGRO_3'), +(-1609011,'To battle!',0,0,0,0,'unworthy SAY_AGGRO_4'), +(-1609012,'There is no hope for our future...',0,0,0,0,'unworthy SAY_AGGRO_5'), +(-1609013,'Sate your hunger on cold steel, $R',0,0,0,0,'unworthy SAY_AGGRO_6'), +(-1609014,'It ends here!',0,0,0,0,'unworthy SAY_AGGRO_7'), +(-1609015,'Death is the only cure!',0,0,0,0,'unworthy SAY_AGGRO_8'), + +(-1609016,'No potions!',0,0,0,0,'dk_initiate SAY_DUEL_A'), +(-1609017,'Remember this day, $n, for it is the day that you will be thoroughly owned.',0,0,0,0,'dk_initiate SAY_DUEL_B'), +(-1609018,'I\'m going to tear your heart out, cupcake!',0,0,0,0,'dk_initiate SAY_DUEL_C'), +(-1609019,'Don\'t make me laugh.',0,0,0,0,'dk_initiate SAY_DUEL_D'), +(-1609020,'Here come the tears...',0,0,0,0,'dk_initiate SAY_DUEL_E'), +(-1609021,'You have challenged death itself!',0,0,0,0,'dk_initiate SAY_DUEL_F'), +(-1609022,'The Lich King will see his true champion on this day!',0,0,0,0,'dk_initiate SAY_DUEL_G'), +(-1609023,'You\'re going down!',0,0,0,0,'dk_initiate SAY_DUEL_H'), +(-1609024,'You don\'t stand a chance, $n',0,0,0,0,'dk_initiate SAY_DUEL_I'), + +(-1609025,'Come to finish the job, have you?',0,0,0,1,'special_surprise SAY_EXEC_START_1'), +(-1609026,'Come to finish the job, have ye?',0,0,0,1,'special_surprise SAY_EXEC_START_2'), +(-1609027,'Come ta finish da job, mon?',0,0,0,1,'special_surprise SAY_EXEC_START_3'), + +(-1609028,'You\'ll look me in the eyes when...',0,0,0,25,'special_surprise SAY_EXEC_PROG_1'), +(-1609029,'Well this son o\' Ironforge would like...',0,0,0,25,'special_surprise SAY_EXEC_PROG_2'), +(-1609030,'Ironic, isn\'t it? To be killed...',0,0,0,25,'special_surprise SAY_EXEC_PROG_3'), +(-1609031,'If you\'d allow me just one...',0,0,0,25,'special_surprise SAY_EXEC_PROG_4'), +(-1609032,'I\'d like to stand for...',0,0,0,25,'special_surprise SAY_EXEC_PROG_5'), +(-1609033,'I want to die like an orc...',0,0,0,25,'special_surprise SAY_EXEC_PROG_6'), +(-1609034,'Dis troll gonna stand for da...',0,0,0,25,'special_surprise SAY_EXEC_PROG_7'), + +(-1609035,'$N?',0,0,0,1,'special_surprise SAY_EXEC_NAME_1'), +(-1609036,'$N? Mon?',0,0,0,1,'special_surprise SAY_EXEC_NAME_2'), + +(-1609037,'$N, I\'d recognize that face anywhere... What... What have they done to you, $N?',0,0,0,1,'special_surprise SAY_EXEC_RECOG_1'), +(-1609038,'$N, I\'d recognize those face tentacles anywhere... What... What have they done to you, $N?',0,0,0,1,'special_surprise SAY_EXEC_RECOG_2'), +(-1609039,'$N, I\'d recognize that face anywhere... What... What have they done to ye, $Glad:lass;?',0,0,0,1,'special_surprise SAY_EXEC_RECOG_3'), +(-1609040,'$N, I\'d recognize that decay anywhere... What... What have they done to you, $N?',0,0,0,1,'special_surprise SAY_EXEC_RECOG_4'), +(-1609041,'$N, I\'d recognize those horns anywhere... What have they done to you, $N?',0,0,0,1,'special_surprise SAY_EXEC_RECOG_5'), +(-1609042,'$N, I\'d recognize dem tusks anywhere... What... What have dey done ta you, mon?',0,0,0,1,'special_surprise SAY_EXEC_RECOG_6'), + +(-1609043,'You don\'t remember me, do you? Blasted Scourge... They\'ve tried to drain you of everything that made you a righteous force of reckoning. Every last ounce of good... Everything that made you a draenei!',0,0,0,1,'special_surprise SAY_EXEC_NOREM_1'), +(-1609044,'Ye don\'t remember me, do ye? Blasted Scourge... They\'ve tried to drain ye o\' everything that made ye a righteous force o\' reckoning. Every last ounce o\' good... Everything that made you a $Gson:daughter; of Ironforge!',0,0,0,1,'special_surprise SAY_EXEC_NOREM_2'), +(-1609045,'You don\'t remember me, do you? We were humans once - long, long ago - until Lordaeron fell to the Scourge. Your transformation to a Scourge zombie came shortly after my own. Not long after that, our minds were freed by the Dark Lady.',0,0,0,1,'special_surprise SAY_EXEC_NOREM_3'), +(-1609046,'You don\'t remember me, do you? Blasted Scourge... They\'ve tried to drain you of everything that made you a pint-sized force of reckoning. Every last ounce of good... Everything that made you a gnome!',0,0,0,1,'special_surprise SAY_EXEC_NOREM_4'), +(-1609047,'You don\'t remember me, do you? Blasted Scourge...They\'ve tried to drain of everything that made you a righteous force of reckoning. Every last ounce of good...Everything that made you a human!',0,0,0,1,'special_surprise SAY_EXEC_NOREM_5'), +(-1609048,'You don\'t remember me? When you were a child your mother would leave you in my care while she served at the Temple of the Moon. I held you in my arms and fed you with honey and sheep\'s milk to calm you until she would return. You were my little angel. Blasted Scourge... What have they done to you, $N?',0,0,0,1,'special_surprise SAY_EXEC_NOREM_6'), +(-1609049,'You don\'t recognize me, do you? Blasted Scourge... They\'ve tried to drain you of everything that made you a righteous force of reckoning. Every last ounce of good... Everything that made you an orc!',0,0,0,1,'special_surprise SAY_EXEC_NOREM_7'), +(-1609050,'You don\'t remember me, do you? Blasted Scourge... They\'ve tried to drain you of everything that made you a righteous force of reckoning. Every last ounce of good... Everything that made you a tauren!',0,0,0,1,'special_surprise SAY_EXEC_NOREM_8'), +(-1609051,'You don\'t remember me, mon? Damn da Scourge! Dey gone ta drain you of everytin dat made ya a mojo masta. Every last ounce of good... Everytin\' dat made ya a troll hero, mon!',0,0,0,1,'special_surprise SAY_EXEC_NOREM_9'), + +(-1609052,'A pact was made, $Gbrother:sister;! We vowed vengeance against the Lich King! For what he had done to us! We battled the Scourge as Forsaken, pushing them back into the plaguelands and freeing Tirisfal! You and I were champions of the Forsaken!',0,0,0,1,'special_surprise SAY_EXEC_THINK_1'), +(-1609053,'You must remember the splendor of life, $Gbrother:sister;. You were a champion of the Kaldorei once! This isn\'t you!',0,0,0,1,'special_surprise SAY_EXEC_THINK_2'), +(-1609054,'Think, $N. Think back. Try and remember the majestic halls of Silvermoon City, where you were born. Remember the splendor of life, $Gbrother:sister;. You were a champion of the sin\'dorei once! This isn\'t you.',0,0,0,6,'special_surprise SAY_EXEC_THINK_3'), +(-1609055,'Think, $N. Think back. Try and remember the proud mountains of Argus, where you were born. Remember the splendor of life, $Gbrother:sister;. You were a champion of the draenei once! This isn\'t you.',0,0,0,6,'special_surprise SAY_EXEC_THINK_4'), +(-1609056,'Think, $N. Think back. Try and remember the snow capped mountains o\' Dun Morogh! Ye were born there, $Glad:lass;. Remember the splendor o\' life, $N! Ye were a champion o\' the dwarves once! This isn\'t ye!',0,0,0,6,'special_surprise SAY_EXEC_THINK_5'), +(-1609057,'Think, $N. Think back. Try and remember Gnomeregan before those damned troggs! Remember the feel of an [arclight spanner] $Gbrother:sister;. You were a champion of gnome-kind once! This isn\'t you.',0,0,0,6,'special_surprise SAY_EXEC_THINK_6'), +(-1609058,'Think, $N. Think back. Try and remember the hills and valleys of Elwynn, where you were born. Remember the splendor of life, $Gbrother:sister;. You were a champion of the Alliance once! This isn\'t you.',0,0,0,6,'special_surprise SAY_EXEC_THINK_7'), +(-1609059,'Think, $N. Think back. Try and remember Durotar, $Gbrother:sister;! Remember the sacrifices our heroes made so that we could be free of the blood curse. Harken back to the Valley of Trials, where we were reborn into a world without demonic influence. We found the splendor of life, $N. Together! This isn\'t you. You were a champion of the Horde once!',0,0,0,6,'special_surprise SAY_EXEC_THINK_8'), +(-1609060,'Think, $N. Think back. Try and remember the rolling plains of Mulgore, where you were born. Remember the splendor of life, $Gbrother:sister;. You were a champion of the tauren once! This isn\'t you.',0,0,0,6,'special_surprise SAY_EXEC_THINK_9'), +(-1609061,'TINK $N. Tink back, mon! We be Darkspear, mon! Bruddas and sistas! Remember when we fought the Zalazane and done took he head and freed da Echo Isles? MON! TINK! You was a champion of da Darkspear trolls!',0,0,0,6,'special_surprise SAY_EXEC_THINK_10'), + +(-1609062,'Listen to me, $N. You must fight against the Lich King\'s control. He is a monster that wants to see this world - our world - in ruin. Don\'t let him use you to accomplish his goals. You were once a hero and you can be again. Fight, damn you! Fight his control!',0,0,0,5,'special_surprise SAY_EXEC_LISTEN_1'), +(-1609063,'Listen to me, $N Ye must fight against the Lich King\'s control. He\'s a monster that wants to see this world - our world - in ruin. Don\'t let him use ye to accomplish his goals. Ye were once a hero and ye can be again. Fight, damn ye! Fight his control!',0,0,0,5,'special_surprise SAY_EXEC_LISTEN_2'), +(-1609064,'Listen to me, $N. You must fight against the Lich King\'s control. He is a monster that wants to see this world - our world - in ruin. Don\'t let him use you to accomplish his goals AGAIN. You were once a hero and you can be again. Fight, damn you! Fight his control!',0,0,0,5,'special_surprise SAY_EXEC_LISTEN_3'), +(-1609065,'Listen ta me, $Gbrudda:sista;. You must fight against da Lich King\'s control. He be a monstar dat want ta see dis world - our world - be ruined. Don\'t let he use you ta accomplish he goals. You be a hero once and you be a hero again! Fight it, mon! Fight he control!',0,0,0,5,'special_surprise SAY_EXEC_LISTEN_4'), + +(-1609066,'What\'s going on in there? What\'s taking so long, $N?',0,1,0,0,'special_surprise SAY_PLAGUEFIST'), + +(-1609067,'There... There\'s no more time for me. I\'m done for. Finish me off, $N. Do it or they\'ll kill us both. $N... Remember Silvermoon. This world is worth saving!',0,0,0,18,'special_surprise SAY_EXEC_TIME_1'), +(-1609068,'There... There\'s no more time for me. I\'m done for. Finish me off, $N. Do it or they\'ll kill us both. $N... Remember Argus. Don\'t let that happen to this world.',0,0,0,18,'special_surprise SAY_EXEC_TIME_2'), +(-1609069,'There... There\'s no more time for me. I\'m done for. Finish me off, $N. Do it or they\'ll kill us both $N... For KHAAAAAAAAZZZ MODAAAAAANNNNNN!!!',0,0,0,18,'special_surprise SAY_EXEC_TIME_3'), +(-1609070,'There... There\'s no more time for me. I\'m done for. Finish me off, $N. Do it or they\'ll kill us both. $N... Remember Tirisfal! This world is worth saving!',0,0,0,18,'special_surprise SAY_EXEC_TIME_4'), +(-1609071,'There... There\'s no more time for me. I\'m done for. Finish me off, $N. Do it or they\'ll kill us both. $N... Remember Gnomeregan! This world is worth saving.',0,0,0,18,'special_surprise SAY_EXEC_TIME_5'), +(-1609072,'There... There\'s no more time for me. I\'m done for. FInish me off, $N. Do it or they\'ll kill us both. $N...Remember Elwynn. This world is worth saving.',0,0,0,18,'special_surprise SAY_EXEC_TIME_6'), +(-1609073,'There... There\'s no more time for me. I\'m done for. Finish me off, $N. Do it or they\'ll kill us both. $N... Remember Teldrassil, our beloved home. This world is worth saving.',0,0,0,18,'special_surprise SAY_EXEC_TIME_7'), +(-1609074,'There... There\'s no more time for me. I\'m done for. Finish me off, $N. Do it or they\'ll kill us both. $N... For the Horde! This world is worth saving.',0,0,0,18,'special_surprise SAY_EXEC_TIME_8'), +(-1609075,'There... There\'s no more time for me. I\'m done for. Finish me off, $N. Do it or they\'ll kill us both. $N... Remember Mulgore. This world is worth saving.',0,0,0,18,'special_surprise SAY_EXEC_TIME_9'), +(-1609076,'Der... Der\'s no more time for me. I be done for. Finish me off $N. Do it or they\'ll kill us both. $N... Remember Sen\'jin Village, mon! Dis world be worth saving!',0,0,0,18,'special_surprise SAY_EXEC_TIME_10'), + +(-1609077,'Do it, $N! Put me out of my misery!',0,0,0,1,'special_surprise SAY_EXEC_WAITING'), +(-1609078,'%s dies from his wounds.',0,2,0,0,'special_surprise EMOTE_DIES'), + +(-1609079,'I\'ll need to get my runeblade and armor... Just need a little more time.',0,0,0,399,'koltira SAY_BREAKOUT1'), +(-1609080,'I\'m still weak, but I think I can get an anti-magic barrier up. Stay inside it or you\'ll be destroyed by their spells.',0,0,0,0,'koltira SAY_BREAKOUT2'), +(-1609081,'Maintaining this barrier will require all of my concentration. Kill them all!',0,0,0,16,'koltira SAY_BREAKOUT3'), +(-1609082,'There are more coming. Defend yourself! Don\'t fall out of the anti-magic field! They\'ll tear you apart without its protection!',0,0,0,0,'koltira SAY_BREAKOUT4'), +(-1609083,'I can\'t keep barrier up much longer... Where is that coward?',0,0,0,0,'koltira SAY_BREAKOUT5'), +(-1609084,'The High Inquisitor comes! Be ready, death knight! Do not let him draw you out of the protective bounds of my anti-magic field! Kill him and take his head!',0,0,0,0,'koltira SAY_BREAKOUT6'), +(-1609085,'Stay in the anti-magic field! Make them come to you!',0,0,0,0,'koltira SAY_BREAKOUT7'), +(-1609086,'The death of the High Inquisitor of New Avalon will not go unnoticed. You need to get out of here at once! Go, before more of them show up. I\'ll be fine on my own.',0,0,0,0,'koltira SAY_BREAKOUT8'), +(-1609087,'I\'ll draw their fire, you make your escape behind me.',0,0,0,0,'koltira SAY_BREAKOUT9'), +(-1609088,'Your High Inquisitor is nothing more than a pile of meat, Crusaders! There are none beyond the grasp of the Scourge!',0,1,0,0,'koltira SAY_BREAKOUT10'), + +(-1609089,'The Eye of Acherus launches towards its destination',0,3,0,0,'eye of acherus EMOTE_DESTIANTION'), +(-1609090,'The Eye of Acherus is in your control',0,3,0,0,'eye of acherus EMOTE_CONTROL'), + +(-1609091,'Mommy?',0,0,0,434,'scarlet ghoul SAY_GHUL_SPAWN_1'), +(-1609092,'GIVE ME BRAINS!',0,0,0,434,'scarlet ghoul SAY_GHUL_SPAWN_2'), +(-1609093,'Must feed...',0,0,0,434,'scarlet ghoul SAY_GHUL_SPAWN_3'), +(-1609094,'So hungry...',0,0,0,434,'scarlet ghoul SAY_GHUL_SPAWN_4'), +(-1609095,'$gPoppy:Mama;!',0,0,0,434,'scarlet ghoul SAY_GHUL_SPAWN_5'), +(-1609096,'It puts the ghoul in the pit or else it gets the lash!',0,0,0,25,'gothik the harvester SAY_GOTHIK_THROW_IN_PIT'), + +(-1609097,'%s rears up, beckoning you to ride it.',0,2,0,0,'Acherus Deathcharger EMOTE_HORSE_READY'), +(-1609098,'Impressive, death knight. Return to me in the world of the living for your reward.',0,0,0,2,'Salanar the Horseman SAY_RACE_FINISHED'), + +(-1609201,'Soldiers of the Scourge, stand ready! Prepare to unleash your fury upon the Argent Dawn!',14677,1,0,0,'Highlord Darion Mograine'), +(-1609202,'The sky weeps at the devastation of these lands! Soon, Azeroth\'s futile tears will rain down upon us!',14678,1,0,0,'Highlord Darion Mograine'), +(-1609203,'Death knights of Acherus, the death march begins!',14681,1,0,0,'Highlord Darion Mograine'), +(-1609204,'Soldiers of the Scourge, death knights of Acherus, minions of the darkness: hear the call of the Highlord!',14679,1,0,22,'Highlord Darion Mograine'), +(-1609205,'RISE!',14680,1,0,15,'Highlord Darion Mograine'), +(-1609206,'The skies turn red with the blood of the fallen! The Lich King watches over us, minions! Leave only ashes and misery in your destructive wake!',14682,1,0,25,'Highlord Darion Mograine'), +(-1609207,'Scourge armies approach!',0,1,0,0,'Korfax, Champion of the Light'), +(-1609208,'Stand fast, brothers and sisters! The Light will prevail!',14487,1,0,0,'Lord Maxwell Tyrosus'), +(-1609209,'Kneel before the Highlord!',14683,0,0,0,'Highlord Darion Mograine'), +(-1609210,'You stand no chance!',14684,0,0,0,'Highlord Darion Mograine'), +(-1609211,'The Scourge will destroy this place!',14685,0,0,0,'Highlord Darion Mograine'), +(-1609212,'Your life is forfeit.',14686,0,0,0,'Highlord Darion Mograine'), +(-1609213,'Life is meaningless without suffering.',14687,0,0,0,'Highlord Darion Mograine'), +(-1609214,'How much longer will your forces hold out?',14688,0,0,0,'Highlord Darion Mograine'), +(-1609215,'The Argent Dawn is finished!"',14689,0,0,0,'Highlord Darion Mograine'), +(-1609216,'Spare no one!',14690,0,0,0,'Highlord Darion Mograine'), +(-1609217,'What is this?! My... I cannot strike...',14691,0,0,0,'Highlord Darion Mograine'), +(-1609218,'Obey me, blade!',14692,1,0,0,'Highlord Darion Mograine'), +(-1609219,'You will do as I command! I am in control here!',14693,0,0,0,'Highlord Darion Mograine'), +(-1609220,'I can not... the blade fights me.',14694,0,0,0,'Highlord Darion Mograine'), +(-1609221,'What is happening to me?',14695,0,0,0,'Highlord Darion Mograine'), +(-1609222,'Power...wanes...',14696,0,0,0,'Highlord Darion Mograine'), +(-1609223,'Ashbringer defies me...',14697,0,0,0,'Highlord Darion Mograine'), +(-1609224,'Minions, come to my aid!',14698,0,0,0,'Highlord Darion Mograine'), +(-1609225,'You cannot win, Darion!',14584,1,0,0,'Highlord Tirion Fordring'), +(-1609226,'Bring them before the chapel!',14585,1,0,0,'Highlord Tirion Fordring'), +(-1609227,'Stand down, death knights. We have lost... The Light... This place... No hope...',14699,0,0,68,'Highlord Darion Mograine'), +(-1609228,'Have you learned nothing, boy? You have become all that your father fought against! Like that coward, Arthas, you allowed yourself to be consumed by the darkness...the hate... Feeding upon the misery of those you tortured and killed!',14586,0,0,1,'Highlord Tirion Fordring'), +(-1609229,'Your master knows what lies beneath the chapel. It is why he dares not show his face! He\'s sent you and your death knights to meet their doom, Darion.',14587,0,0,25,'Highlord Tirion Fordring'), +(-1609230,'What you are feeling right now is the anguish of a thousand lost souls! Souls that you and your master brought here! The Light will tear you apart, Darion!',14588,0,0,1,'Highlord Tirion Fordring'), +(-1609231,'Save your breath, old man. It might be the last you ever draw.',14700,0,0,25,'Highlord Darion Mograine'), +(-1609232,'My son! My dear, beautiful boy!',14493,0,0,0,'Highlord Alexandros Mograine'), +(-1609233,'Father!',14701,0,0,5,'Highlord Darion Mograine'), +(-1609234,'Argh...what...is...',14702,0,0,68,'Highlord Darion Mograine'), +(-1609235,'Father, you have returned!',14703,0,0,0,'Darion Mograine'), +(-1609236,'You have been gone a long time, father. I thought...',14704,0,0,0,'Darion Mograine'), +(-1609237,'Nothing could have kept me away from here, Darion. Not from my home and family.',14494,0,0,1,'Highlord Alexandros Mograine'), +(-1609238,'Father, I wish to join you in the war against the undead. I want to fight! I can sit idle no longer!',14705,0,0,6,'Darion Mograine'), +(-1609239,'Darion Mograine, you are barely of age to hold a sword, let alone battle the undead hordes of Lordaeron! I couldn\'t bear losing you. Even the thought...',14495,0,0,1,'Highlord Alexandros Mograine'), +(-1609240,'If I die, father, I would rather it be on my feet, standing in defiance against the undead legions! If I die, father, I die with you!',14706,0,0,6,'Darion Mograine'), +(-1609241,'My son, there will come a day when you will command the Ashbringer and, with it, mete justice across this land. I have no doubt that when that day finally comes, you will bring pride to our people and that Lordaeron will be a better place because of you. But, my son, that day is not today.',14496,0,0,1,'Highlord Alexandros Mograine'), +(-1609242,'Do not forget...',14497,0,0,6,'Highlord Alexandros Mograine'), +(-1609243,'Touching...',14803,1,0,0,'The Lich King'), +(-1609244,'You have\'ve betrayed me! You betrayed us all you monster! Face the might of Mograine!',14707,1,0,0,'Highlord Darion Mograine'), +(-1609245,'He\'s mine now...',14805,0,0,0,'The Lich King'), +(-1609246,'Pathetic...',14804,0,0,0,'The Lich King'), +(-1609247,'You\'re a damned monster, Arthas!',14589,0,0,25,'Highlord Tirion Fordring'), +(-1609248,'You were right, Fordring. I did send them in to die. Their lives are meaningless, but yours...',14806,0,0,1,'The Lich King'), +(-1609249,'How simple it was to draw the great Tirion Fordring out of hiding. You\'ve left yourself exposed, paladin. Nothing will save you...',14807,0,0,1,'The Lich King'), +(-1609250,'ATTACK!!!',14488,1,0,0,'Lord Maxwell Tyrosus'), +(-1609251,'APOCALYPSE!',14808,1,0,0,'The Lich King'), +(-1609252,'That day is not today...',14708,0,0,0,'Highlord Darion Mograine'), +(-1609253,'Tirion!',14709,1,0,0,'Highlord Darion Mograine'), +(-1609254,'ARTHAS!!!!',14591,1,0,0,'Highlord Tirion Fordring'), +(-1609255,'What is this?',14809,1,0,0,'The Lich King'), +(-1609256,'Your end.',14592,1,0,0,'Highlord Tirion Fordring'), +(-1609257,'Impossible...',14810,1,0,0,'The Lich King'), +(-1609258,'This... isn\'t... over...',14811,1,0,25,'The Lich King'), +(-1609259,'When next we meet it won\'t be on holy ground, paladin.',14812,1,0,1,'The Lich King'), +(-1609260,'Rise, Darion, and listen...',14593,0,0,0,'Highlord Tirion Fordring'), +(-1609261,'We have all been witness to a terrible tragedy. The blood of good men has been shed upon this soil! Honorable knights, slain defending their lives - our lives!',14594,0,0,0,'Highlord Tirion Fordring'), +(-1609262,'And while such things can never be forgotten, we must remain vigilant in our cause!',14595,0,0,0,'Highlord Tirion Fordring'), +(-1609263,'The Lich King must answer for what he has done and must not be allowed to cause further destruction to our world.',14596,0,0,0,'Highlord Tirion Fordring'), +(-1609264,'I make a promise to you now, brothers and sisters: The Lich King will be defeated! On this day, I call for a union.',14597,0,0,0,'Highlord Tirion Fordring'), +(-1609265,'The Argent Dawn and the Order of the Silver Hand will come together as one! We will succeed where so many before us have failed!',14598,0,0,0,'Highlord Tirion Fordring'), +(-1609266,'We will take the fight to Arthas and tear down the walls of Icecrown!',14599,0,0,15,'Highlord Tirion Fordring'), +(-1609267,'The Argent Crusade comes for you, Arthas!',14600,1,0,15,'Highlord Tirion Fordring'), +(-1609268,'So too do the Knights of the Ebon Blade... While our kind has no place in your world, we will fight to bring an end to the Lich King. This I vow!',14710,0,0,1,'Highlord Darion Mograine'), +(-1609269,'Thousands of Scourge rise up at the Highlord\'s command.',0,3,0,0,''), +(-1609270,'The army marches towards Light\'s Hope Chapel.',0,3,0,0,''), +(-1609271,'After over a hundred Defenders of the Light fall, Highlord Tirion Fordring arrives.',0,3,0,0,''), +(-1609272,'%s flee',0,2,0,0,'Orbaz'), +(-1609273,'%s kneels in defeat before Tirion Fordring.',0,3,0,0,'Highlord Darion Mograine'), +(-1609274,'%s arrives.',0,2,0,0,'Highlord Alexandros Mograine'), +(-1609275,'%s becomes a shade of his past, and walks up to his father.',0,2,0,0,'Highlord Darion Mograine'), +(-1609276,'%s hugs his father.',0,2,0,0,'Darion Mograine'), +(-1609277,'%s disappears, and the Lich King appears.',0,2,0,0,'Alexandros'), +(-1609278,'%s becomes himself again...and is now angry.',0,2,0,0,'Highlord Darion Mograine'), +(-1609279,'%s casts a spell on Tirion.',0,2,0,0,'The Lich King'), +(-1609280,'%s gasps for air.',0,2,0,0,'Highlord Tirion Fordring'), +(-1609281,'%s casts a powerful spell, killing the Defenders and knocking back the others.',0,2,0,0,'The Lich King'), +(-1609282,'%s throws the Corrupted Ashbringer to Tirion, who catches it. Tirion becomes awash with Light, and the Ashbringer is cleansed.',0,2,0,0,'Highlord Darion Mograine'), +(-1609283,'%s collapses.',0,2,0,0,'Highlord Darion Mograine'), +(-1609284,'%s charges towards the Lich King, Ashbringer in hand and strikes the Lich King.',0,2,0,0,'Highlord Tirion Fordring'), +(-1609285,'%s disappears. Tirion walks over to where Darion lay',0,2,0,0,'The Lich King'), +(-1609286,'Light washes over the chapel -- the Light of Dawn is uncovered.',0,2,0,0,''); + +-- -1 615 000 OBSIDIAN SANCTUM +INSERT INTO script_texts (entry,content_default,sound,type,language,emote,comment) VALUES +(-1615000,'I fear nothing! Least of all you!',14111,1,0,0,'shadron SAY_SHADRON_AGGRO'), +(-1615001,'You are insignificant!',14112,1,0,0,'shadron SAY_SHADRON_SLAY_1'), +(-1615002,'Such mediocre resistance!',14113,1,0,0,'shadron SAY_SHADRON_SLAY_2'), +(-1615003,'We...are superior! How could this...be...',14118,1,0,0,'shadron SAY_SHADRON_DEATH'), +(-1615004,'You are easily bested! ',14114,1,0,0,'shadron SAY_SHADRON_BREATH'), +(-1615005,'I will take pity on you Sartharion, just this once.',14117,1,0,0,'shadron SAY_SHADRON_RESPOND'), +(-1615006,'Father tought me well!',14115,1,0,0,'shadron SAY_SHADRON_SPECIAL_1'), +(-1615007,'On your knees!',14116,1,0,0,'shadron SAY_SHADRON_SPECIAL_2'), +(-1615008,'A Shadron Disciple appears in the Twilight!',0,3,0,0,'shadron WHISPER_SHADRON_DICIPLE'), + +(-1615009,'You have no place here. Your place is among the departed.',14122,1,0,0,'tenebron SAY_TENEBRON_AGGRO'), +(-1615010,'No contest.',14123,1,0,0,'tenebron SAY_TENEBRON_SLAY_1'), +(-1615011,'Typical... Just as I was having fun.',14124,1,0,0,'tenebron SAY_TENEBRON_SLAY_2'), +(-1615012,'I should not... have held back...', 14129,1,0,0,'tenebron SAY_TENEBRON_DEATH'), +(-1615013,'To darkness I condemn you...',14125,1,0,0,'tenebron SAY_TENEBRON_BREATH'), +(-1615014,'It is amusing to watch you struggle. Very well, witness how it is done.',14128,1,0,0,'tenebron SAY_TENEBRON_RESPOND'), +(-1615015,'Arrogant little creatures! To challenge powers you do not yet understand...',14126,1,0,0,'tenebron SAY_TENEBRON_SPECIAL_1'), +(-1615016,'I am no mere dragon! You will find I am much, much, more...',14127,1,0,0,'tenebron SAY_TENEBRON_SPECIAL_2'), +(-1615017,'%s begins to hatch eggs in the twilight!',0,3,0,0,'tenebron WHISPER_HATCH_EGGS'), + +(-1615018,'It is my charge to watch over these eggs. I will see you burn before any harm comes to them!',14093,1,0,0,'sartharion SAY_SARTHARION_AGGRO'), +(-1615019,'This pathetic siege ends NOW!',14103,1,0,0,'sartharion SAY_SARTHARION_BERSERK'), +(-1615020,'Burn, you miserable wretches!',14098, 1,0,0,'sartharion SAY_SARTHARION_BREATH'), +(-1615021,'Shadron! Come to me, all is at risk!',14105,1,0,0,'sartharion SARTHARION_CALL_SHADRON'), +(-1615022,'Tenebron! The eggs are yours to protect as well!',14106,1,0,0,'sartharion SAY_SARTHARION_CALL_TENEBRON'), +(-1615023,'Vesperon! The clutch is in danger! Assist me!',14104,1,0,0,'sartharion SAY_SARTHARION_CALL_VESPERON'), +(-1615024,'Such is the price... of failure...',14107,1,0,0,'sartharion SAY_SARTHARION_DEATH'), +(-1615025,'Such flammable little insects....',14099,1,0,0,'sartharion SAY_SARTHARION_SPECIAL_1'), +(-1615026,'Your charred bones will litter the floor!',14100,1,0,0,'sartharion SAY_SARTHARION_SPECIAL_2'), +(-1615027,'How much heat can you take?',14101,1,0,0,'sartharion SAY_SARTHARION_SPECIAL_3'), +(-1615028,'All will be reduced to ash!',14102,1,0,0,'sartharion SAY_SARTHARION_SPECIAL_4'), +(-1615029,'You will make a fine meal for the hatchlings.',14094,1,0,0,'sartharion SAY_SARTHARION_SLAY_1'), +(-1615030,'You are the grave disadvantage.',14096,1,0,0,'sartharion SAY_SARTHARION_SLAY_2'), +(-1615031,'This is why we call you lesser beeings.',14097,1,0,0,'sartharion SAY_SARTHARION_SLAY_3'), +(-1615032,'The lava surrounding %s churns!',0,3,0,0,'sartharion WHISPER_LAVA_CHURN'), + +(-1615033,'You pose no threat, lesser beings...give me your worst!',14133,1,0,0,'vesperon SAY_VESPERON_AGGRO'), +(-1615034,'The least you could do is put up a fight...',14134,1,0,0,'vesperon SAY_VESPERON_SLAY_1'), +(-1615035,'Was that the best you can do?',14135,1,0,0,'vesperon SAY_VESPERON_SLAY_2'), +(-1615036,'I still have some...fight..in...me...', 14140,1,0,0,'vesperon SAY_VESPERON_DEATH'), +(-1615037,'I will pick my teeth with your bones!',14136,1,0,0,'vesperon SAY_VESPERON_BREATH'), +(-1615038,'Father was right about you, Sartharion...You are a weakling!',14139,1,0,0,'vesperon SAY_VESPERON_RESPOND'), +(-1615039,'Aren\'t you tricky...I have a few tricks of my own...',14137,1,0,0,'vesperon SAY_VESPERON_SPECIAL_1'), +(-1615040,'Unlike, I have many talents.',14138,1,0,0,'vesperon SAY_VESPERON_SPECIAL_2'), +(-1615041,'A Vesperon Disciple appears in the Twilight!',0,3,0,0,'shadron WHISPER_VESPERON_DICIPLE'), + +(-1615042,'%s begins to open a Twilight Portal!',0,3,0,0,'sartharion drake WHISPER_OPEN_PORTAL'); + +-- -1 616 000 EYE OF ETERNITY +INSERT INTO script_texts (entry,content_default,sound,type,LANGUAGE,emote,comment) VALUES +(-1616000,'Lesser beings, intruding here! A shame that your excess courage does not compensate for your stupidity!',14512,1,0,0,'malygos SAY_INTRO_1'), +(-1616001,'None but the blue dragonflight are welcome here! Perhaps this is the work of Alexstrasza? Well then, she has sent you to your deaths.',14513,1,0,0,'malygos SAY_INTRO_2'), +(-1616002,'What could you hope to accomplish, to storm brazenly into my domain? To employ MAGIC? Against ME? ',14514,1,0,0,'malygos SAY_INTRO_3'), +(-1616003,'I am without limits here... the rules of your cherished reality do not apply... In this realm, I am in control...',14515,1,0,0,'malygos SAY_INTRO_4'), +(-1616004,'I give you one chance. Pledge fealty to me, and perhaps I won\'t slaughter you for your insolence!',14516,1,0,0,'malygos SAY_INTRO_5'), +(-1616005,'My patience has reached its limit, I WILL BE RID OF YOU!',14517,1,0,0,'malygos SAY_AGGRO'), +(-1616006,'Watch helplessly as your hopes are swept away...',14525,1,0,0,'malygos SAY_VORTEX'), +(-1616007,'I AM UNSTOPPABLE!',14533,1,0,0,'malygos SAY_SPARK_BUFF'), +(-1616008,'Your stupidity has finally caught up to you!',14519,1,0,0,'malygos SAY_SLAY_1_A'), +(-1616009,'More artifacts to confiscate...',14520,1,0,0,'malygos SAY_SLAY_1_B'), +(-1616010,' How very... naive...',14521,1,0,0,'malygos SAY_SLAY_1_C'), +(-1616011,'I had hoped to end your lives quickly, but you have proven more...resilient then I had anticipated. Nonetheless, your efforts are in vain, it is you reckless, careless mortals who are to blame for this war! I do what I must...And if it means your...extinction...THEN SO BE IT!',14522,1,0,0,'malygos SAY_END_PHASE_1'), +(-1616012,'Few have experienced the pain I will now inflict upon you!',14523,1,0,0,'malygos SAY_START_PHASE_2'), +(-1616013,'You will not succeed while I draw breath!',14518,1,0,0,'malygos SAY_DEEP_BREATH'), +(-1616014,'I will teach you IGNORANT children just how little you know of magic...',14524,1,0,0,'malygos SAY_SHELL'), +(-1616015,'Your energy will be put to good use!',14526,1,0,0,'malygos SAY_SLAY_2_A'), +(-1616016,'I am the spell-weaver! My power is infinite!',14527,1,0,0,'malygos SAY_SLAY_2_B'), +(-1616017,'Your spirit will linger here forever!',14528,1,0,0, 'malygos SAY_SLAY_2_C'), +(-1616018,'ENOUGH! If you intend to reclaim Azeroth\'s magic, then you shall have it...',14529,1,0,0,'malygos SAY_END_PHASE_2'), +(-1616019,'Now your benefactors make their appearance...But they are too late. The powers contained here are sufficient to destroy the world ten times over! What do you think they will do to you?',14530,1,0,0,'malygos SAY_INTRO_PHASE_3'), +(-1616020,'SUBMIT!',14531,1,0,0,'malygos SAY_START_PHASE_3'), +(-1616021,'Alexstrasza! Another of your brood falls!',14534,1,0,0,'malygos SAY_SLAY_3_A'), +(-1616022,'Little more then gnats!',14535,1,0,0,'malygos SAY_SLAY_3_B'), +(-1616023,'Your red allies will share your fate...',14536,1,0,1,'malygos SAY_SLAY_3_C'), +(-1616024,'The powers at work here exceed anything you could possibly imagine!',14532,1,0,0,'malygos SAY_SURGE'), +(-1616025,'Still standing? Not for long...',14537,1,0,0,'malygos SAY_SPELL_1'), +(-1616026,'Your cause is lost!',14538,1,0,0,'malygos SAY_SPELL_2'), +(-1616027,'Your fragile mind will be shattered!',14539,1,0,0,'malygos SAY_SPELL_3'), +(-1616028,'UNTHINKABLE! The mortals will destroy... e-everything... my sister... what have you-',14540,1,0,0,'malygos SAY_DEATH'), +(-1616029,'I did what I had to, brother. You gave me no alternative.',14406,1,0,1,'alextrasza SAY_OUTRO_1'), +(-1616030,'And so ends the Nexus War.',14407,1,0,1,'alextrasza SAY_OUTRO_2'), +(-1616031,'This resolution pains me deeply, but the destruction, the monumental loss of life had to end. Regardless of Malygos\' recent transgressions, I will mourn his loss. He was once a guardian, a protector. This day, one of the world\'s mightiest has fallen.',14408,1,0,1,'alextrasza SAY_OUTRO_3'), +(-1616032,'The red dragonflight will take on the burden of mending the devastation wrought on Azeroth. Return home to your people and rest. Tomorrow will bring you new challenges, and you must be ready to face them. Life...goes on.',14409,1,0,1,'alextrasza SAY_OUTRO_4'), +(-1616033,'A Power Spark forms from a nearby rift!',0,3,0,0,'malygos SAY_EMOTE_SPARK'), +(-1616034,'%s takes a deep breath.',0,3,0,0,'malygos SAY_EMOTE_BREATH'); + +-- -1 619 000 AHN'KAHET +INSERT INTO script_texts (entry,content_default,sound,type,language,emote,comment) VALUES +(-1619000,'The secrets of the deep shall remain hidden.',14033,1,0,0,'nadox SAY_AGGRO'), +(-1619001,'The young must not grow hungry...',14034,1,0,0,'nadox SAY_SUMMON_EGG_1'), +(-1619002,'Shhhad ak kereeesshh chak-k-k!',14035,1,0,0,'nadox SAY_SUMMON_EGG_2'), +(-1619003,'Sleep now, in the cold dark.',14036,1,0,0,'nadox SAY_SLAY_1'), +(-1619004,'For the Lich King!',14037,1,0,0,'nadox SAY_SLAY_2'), +(-1619005,'Perhaps we will be allies soon.',14038,1,0,0,'nadox SAY_SLAY_3'), +(-1619006,'Master, is my service complete?',14039,1,0,0,'nadox SAY_DEATH'), +(-1619007,'An Ahn\'kahar Guardian hatches!',0,3,0,0,'nadox EMOTE_HATCH'), + +(-1619008,'I will feast on your remains.',14360,1,0,0,'taldaram SAY_AGGRO'), +(-1619009,'Your heartbeat is music to my ears.',14361,1,0,0,'taldaram SAY_VANISH_1'), +(-1619010,'I am nowhere. I am everywhere. I am the watcher, unseen.',14362,1,0,0,'taldaram SAY_VANISH_2'), +(-1619011,'So appetizing.',14363,1,0,0,'taldaram SAY_FEED_1'), +(-1619012,'Fresh, warm blood. It has been too long.',14364,1,0,0,'taldaram SAY_FEED_2'), +(-1619013,'Bin-dor\'el',14365,1,0,0,'taldaram SAY_SLAY_1'), +(-1619014,'I will drink no blood before it\'s time.',14366,1,0,0,'taldaram SAY_SLAY_2'), +(-1619015,'One final embrace.',14367,1,0,0,'taldaram SAY_SLAY_3'), +(-1619016,'Still I hunger, still I thirst.',14368,1,0,0,'taldaram SAY_DEATH'), + +(-1619017,'These are sacred halls! Your intrusion will be met with death.',14343,1,0,0,'jedoga SAY_AGGRO'), +(-1619018,'Who among you is devoted?',14344,1,0,0,'jedoga SAY_CALL_SACRIFICE_1'), +(-1619019,'You there! Step forward!',14345,1,0,0,'jedoga SAY_CALL_SACRIFICE_2'), +(-1619020,'Yogg-Saron, grant me your power!',14346,1,0,0,'jedoga SAY_SACRIFICE_1'), +(-1619021,'Master, a gift for you!',14347,1,0,0,'jedoga SAY_SACRIFICE_2'), +(-1619022,'Glory to Yogg-Saron!',14348,1,0,0,'jedoga SAY_SLAY_1'), +(-1619023,'You are unworthy!',14349,1,0,0,'jedoga SAY_SLAY_2'), +(-1619024,'Get up! You haven\'t suffered enough.',14350,1,0,0,'jedoga SAY_SLAY_3'), +(-1619025,'Do not expect your sacrilege... to go unpunished.',14351,1,0,0,'jedoga SAY_DEATH'), +(-1619026,'The elements themselves will rise up against the civilized world! Only the faithful will be spared!',14352,1,0,0,'jedoga SAY_PREACHING_1'), +(-1619027,'Immortality can be yours. But only if you pledge yourself fully to Yogg-Saron!',14353,1,0,0,'jedoga SAY_PREACHING_2'), +(-1619028,'Here on the very borders of his domain. You will experience powers you would never have imagined! ',14354,1,0,0,'jedoga SAY_PREACHING_3'), +(-1619029,'You have traveled long and risked much to be here. Your devotion shall be rewarded.',14355,1,0,0,'jedoga SAY_PREACHING_4'), +(-1619030,'The faithful shall be exalted! But there is more work to be done. We will press on until all of Azeroth lies beneath his shadow!',14356,1,0,0,'jedoga SAY_PREACHING_5'), +(-1619031,'I have been chosen!',0,1,0,0,'jedoga SAY_VOLUNTEER_1'), +(-1619032,'I give myself to the master!',0,1,0,0,'jedoga SAY_VOLUNTEER_2'), + +(-1619033,'Shgla\'yos plahf mh\'naus.',14043,1,0,0,'volazj SAY_AGGRO'), +(-1619034,'Gul\'kafh an\'shel. Yoq\'al shn ky ywaq nuul.',14044,1,0,0,'volazj SAY_INSANITY'), +(-1619035,'Ywaq puul skshgn: on\'ma yeh\'glu zuq.',14045,1,0,0,'volazj SAY_SLAY_1'), +(-1619036,'Ywaq ma phgwa\'cul hnakf.',14046,1,0,0,'volazj SAY_SLAY_2'), +(-1619037,'Ywaq maq oou; ywaq maq ssaggh. Ywaq ma shg\'fhn.',14047,1,0,0,'volazj SAY_SLAY_3'), +(-1619038,' ',14048,1,0,0,'volazj SAY_DEATH_1'), +(-1619039,'Iilth vwah, uhn\'agth fhssh za.',14049,1,0,0,'volazj SAY_DEATH_2'); + +-- -1 631 000 ICC: ICECROWN CITADEL +INSERT INTO script_texts (entry,content_default,sound,type,language,emote,comment) VALUES +(-1631001,'This is the beginning AND the end, mortals. None may enter the master\'s sanctum!',16950,1,0,0,'marrowgar SAY_INTRO'), +(-1631002,'The Scourge will wash over this world as a swarm of death and destruction!',16941,1,0,0,'marrowgar SAY_AGGRO'), +(-1631003,'BONE STORM!',16946,1,0,0,'marrowgar SAY_BONE_STORM'), +(-1631004,'Bound by bone!',16947,1,0,0,'marrowgar SAY_BONE_SPIKE_1'), +(-1631005,'Stick Around!',16948,1,0,0,'marrowgar SAY_BONE_SPIKE_2'), +(-1631006,'The only escape is death!',16949,1,0,0,'marrowgar SAY_BONE_SPIKE_3'), +(-1631007,'More bones for the offering!',16942,1,0,0,'marrowgar SAY_SLAY_1'), +(-1631008,'Languish in damnation!',16943,1,0,0,'marrowgar SAY_SLAY_2'), +(-1631009,'I see... only darkness...',16944,1,0,0,'marrowgar SAY_DEATH'), +(-1631010,'THE MASTER\'S RAGE COURSES THROUGH ME!',16945,1,0,0,'marrowgar SAY_BERSERK'), + +(-1631011,'You have found your way here, because you are among the few gifted with true vision in a world cursed with blindness.',17272,1,0,0,'deathwhisper SAY_SPEECH_1'), +(-1631012,'You can see through the fog that hangs over this world like a shroud, and grasp where true power lies.',17273,1,0,0,'deathwhisper SAY_SPEECH_2'), +(-1631013,'Fix your eyes upon your crude hands: the sinew, the soft meat, the dark blood coursing within.',16878,1,0,0,'deathwhisper SAY_SPEECH_3'), +(-1631014,'It is a weakness; a crippling flaw.... A joke played by the Creators upon their own creations.',17268,1,0,0,'deathwhisper SAY_SPEECH_4'), +(-1631015,'The sooner you come to accept your condition as a defect, the sooner you will find yourselves in a position to transcend it.',17269,1,0,0,'deathwhisper SAY_SPEECH_5'), +(-1631016,'Through our Master, all things are possible. His power is without limit, and his will unbending.',17270,1,0,0,'deathwhisper SAY_SPEECH_6'), +(-1631017,'Those who oppose him will be destroyed utterly, and those who serve -- who serve wholly, unquestioningly, with utter devotion of mind and soul -- elevated to heights beyond your ken.',17271,1,0,0,'deathwhisper SAY_SPEECH_7'), +(-1631018,'What is this disturbance?! You dare trespass upon this hallowed ground? This shall be your final resting place.',16868,1,0,0,'deathwhisper SAY_AGGRO'), +(-1631019,'Enough! I see I must take matters into my own hands!',16877,1,0,0,'deathwhisper SAY_PHASE_TWO'), +(-1631020,'Take this blessing and show these intruders a taste of our master\'s power.',16873,1,0,0,'deathwhisper SAY_DARK_EMPOWERMENT'), +(-1631021,'I release you from the curse of flesh!',16874,1,0,0,'deathwhisper SAY_DARK_TRANSFORMATION'), +(-1631022,'Arise and exalt in your pure form!',16875,1,0,0,'deathwhisper SAY_ANIMATE_DEAD'), +(-1631023,'You are weak, powerless to resist my will!',16876,1,0,0,'deathwhisper SAY_DOMINATE_MIND'), +(-1631024,'This charade has gone on long enough.',16872,1,0,0,'deathwhisper SAY_BERSERK'), +(-1631025,'All part of the masters plan! Your end is... inevitable!',16871,1,0,0,'deathwhisper SAY_DEATH'), +(-1631026,'Do you yet grasp of the futility of your actions?',16869,1,0,0,'deathwhisper SAY_SLAY_1'), +(-1631027,'Embrace the darkness... Darkness eternal!',16870,1,0,0,'deathwhisper SAY_SLAY_2'), + +(-1631028,'BY THE MIGHT OF THE LICH KING!',16694,1,0,0,'saurfang SAY_AGGRO'), +(-1631029,'The ground runs red with your blood!',16699,1,0,0,'saurfang SAY_FALLENCHAMPION'), +(-1631030,'Feast, my minions!',16700,1,0,0,'saurfang SAY_BLOODBEASTS'), +(-1631031,'You are nothing!',16695,1,0,0,'saurfang SAY_SLAY_1'), +(-1631032,'Your soul will find no redemption here!',16696,1,0,0,'saurfang SAY_SLAY_2'), +(-1631033,'I have become...DEATH!',16698,1,0,0,'saurfang SAY_BERSERK'), +(-1631034,'I... Am... Released.',16697,1,0,0,'saurfang SAY_DEATH'), +(-1631035,'Let\'s get a move on then! Move ou...',16974,1,0,0,'bronzebeard SAY_INTRO_ALLY_0'), +(-1631036,'For every Horde soldier that you killed, for every Alliance dog that fell, the Lich King\'s armies grew. Even now the Val\'kyr work to raise your fallen... As Scourge.',16701,1,0,0,'saurfang SAY_INTRO_ALLY_1'), +(-1631037,'Things are about to get much worse. Come, taste the power that the Lich King has bestowed upon me!',16702,1,0,0,'saurfang SAY_INTRO_ALLY_2'), +(-1631038,'A lone orc, against the might of the Alliance?',16970,1,0,0,'bronzebeard SAY_INTRO_ALLY_3'), +(-1631039,'Charge!',16971,1,0,0,'bronzebeard SAY_INTRO_ALLY_4'), +(-1631040,'Hahahaha! Dwarves...',16703,1,0,0,'saurfang SAY_INTRO_ALLY_5'), +(-1631041,'Kor\'kron, move out! Champions, watch your backs. The Scourge have been..',17103,1,0,0,'overlord SAY_INTRO_HORDE_1'), +(-1631042,'Join me, father. Join me and we will crush this world in the name of the Scourge -- for the glory of the Lich King!',16704,1,0,0,'saurfang SAY_INTRO_HORDE_2'), +(-1631043,'My boy died at the Wrath Gate. I am here only to collect his body.',17097,0,0,0,'overlord SAY_INTRO_HORDE_3'), +(-1631044,'Stubborn and old. What chance do you have? I am stronger, and more powerful than you ever were.',16705,1,0,0,'saurfang SAY_INTRO_HORDE_4'), +(-1631045,'We named him Dranosh. It means "Heart of Draenor" in orcish. I would not let the warlocks take him. My boy would be safe, hidden away by the elders of Garadar.',17098,0,0,0,'overlord SAY_INTRO_HORDE_5'), +(-1631046,'I made a promise to his mother before she died; that I would cross the Dark Portal alone - whether I lived or died, my son would be safe. Untainted...',17099,0,0,0,'overlord SAY_INTRO_HORDE_6'), +(-1631047,'Today, I fulfill that promise.',17100,0,0,0,'overlord SAY_INTRO_HORDE_7'), +(-1631048,'High Overlord Saurfang charges!',17104,2,0,0,'overlord SAY_INTRO_HORDE_8'), +(-1631049,'Pathetic old orc. Come then heroes. Come and face the might of the Scourge!',16706,1,0,0,'saurfang SAY_INTRO_HORDE_9'), +(-1631050,'%s gasps for air',16975,2,0,0,'bronzebeard SAY_OUTRO_ALLY_1'), +(-1631051,'That was Saurfang\'s boy - the Horde commander at the Wrath Gate. Such a tragic end...',16976,0,0,0,'bronzebeard SAY_OUTRO_ALLY_2'), +(-1631052,'What in the... There, in the distance!',16977,0,0,0,'bronzebeard SAY_OUTRO_ALLY_3'), +(-1631053,'Soldiers, fall in! Looks like the Horde are comin\' in to take another shot!',16978,1,0,0,'bronzebeard SAY_OUTRO_ALLY_4'), +(-1631054,'Don\'t force my hand, orc. We can\'t let you pass.',16972,0,0,0,'bronzebeard SAY_OUTRO_ALLY_5'), +(-1631055,'Behind you lies the body of my only son. Nothing will keep me from him.',17094,0,0,0,'overlord SAY_OUTRO_ALLY_6'), +(-1631056,'He... I can\'t do it. Get back on your ship and we\'ll spare your life.',16973,0,0,0,'bronzebeard SAY_OUTRO_ALLY_7'), +(-1631057,'Stand down, Muradin. Let a grieving father pass.',16690,0,0,0,'varian SAY_OUTRO_ALLY_8'), +(-1631058,'No\'ku kil zil\'nok ha tar.',17096,0,1,0,'overlord SAY_OUTRO_ALLY_9'), +(-1631059,'I will not forget this kindess. I thank you, highness.',17095,0,0,0,'overlord SAY_OUTRO_ALLY_10'), +(-1631060,'I... I was not at the Wrathgate. But the soldiers who survived told me much of what happened. Your son fought with honor. He died a hero\'s death. He deserves a hero\'s burial.',16691,0,0,0,'varian SAY_OUTRO_ALLY_11'), +(-1631061,'%s cries.',16651,2,0,0,'proudmore SAY_OUTRO_ALLY_12'), +(-1631062,'Jaina, why are you crying?',16692,0,0,0,'varian SAY_OUTRO_ALLY_13'), +(-1631063,'It was nothing, your majesty. Just... I\'m proud of my king.',16652,0,0,0,'proudmore SAY_OUTRO_ALLY_14'), +(-1631064,'Bah! Muradin, secure the deck and prepare our soldiers for an assault on the upper citadel. I\'ll send out another regiment from Stormwind.',16693,0,0,0,'varian SAY_OUTRO_ALLY_15'), +(-1631065,'Right away, yer majesty!',16979,0,0,0,'bronzebeard SAY_OUTRO_ALLY_16'), +(-1631066,'%s coughs.',17105,2,0,0,'overlord SAY_OUTRO_HORDE_1'), +(-1631067,'%s weeps over the corpse of his son.',17106,2,0,0,'overlord SAY_OUTRO_HORDE_2'), +(-1631068,'You will have a proper ceremony in Nagrand next to the pyres of your mother and ancestors.',17101,0,0,0,'overlord SAY_OUTRO_HORDE_3'), +(-1631069,'Honor, young heroes... no matter how dire the battle... Never forsake it!',17102,0,0,0,'overlord SAY_OUTRO_HORDE_4'), + +(-1631070,'What? Precious? Noooooooooo!!!',16993,6,0,0,'rotface SAY_PRECIOUS_DIES'), +(-1631071,'WEEEEEE!',16986,1,0,0,'rotface SAY_AGGRO'), +(-1631072,'Icky sticky.',16991,1,0,0,'rotface SAY_SLIME_SPRAY'), +(-1631073,'I think I made an angry poo-poo. It gonna blow!',16992,1,0,0,'rotface SAY_OOZE_EXPLODE'), +(-1631074,'Great news, everyone! The slime is flowing again!',17126,1,0,0,'putricide SAY_SLIME_FLOW_1'), +(-1631075,'Good news, everyone! I\'ve fixed the poison slime pipes!',17123,1,0,0,'putricide SAY_SLIME_FLOW_2'), +(-1631076,'Daddy make toys out of you!',16987,1,0,0,'rotface SAY_SLAY_1'), +(-1631077,'I brokes-ded it...',16988,1,0,0,'rotface SAY_SLAY_2'), +(-1631078,'Sleepy Time!',16990,1,0,0,'rotface SAY_BERSERK'), +(-1631079,'Bad news daddy.',16989,1,0,0,'rotface SAY_DEATH'), +(-1631080,'Terrible news, everyone, Rotface is dead! But great news everyone, he left behind plenty of ooze for me to use! Whaa...? I\'m a poet, and I didn\'t know it? Astounding!',17146,6,0,0,'putricide SAY_ROTFACE_DEATH'), + +(-1631081,'NOOOO! You kill Stinky! You pay!',16907,6,0,0,'festergut SAY_STINKY_DIES'), +(-1631082,'Fun time!',16901,1,0,0,'festergut SAY_AGGRO'), +(-1631083,'Just an ordinary gas cloud. But watch out, because that\'s no ordinary gas cloud! ',17119,1,0,0,'putricide SAY_BLIGHT'), +(-1631084,'%s farts.',16911,2,0,0,'festergut SAY_SPORE'), -- TODO Can be wrong +(-1631085,'Gyah! Uhhh, I not feel so good...',16906,1,0,0,'festergut SAY_PUNGUENT_BLIGHT'), +(-1631086,'%s vomits',0,2,0,0,'festergut SAY_PUNGUENT_BLIGHT_EMOTE'), -- TODO Can be wrong +(-1631087,'Daddy, I did it',16902,1,0,0,'festergut SAY_SLAY_1'), +(-1631088,'Dead, dead, dead!',16903,1,0,0,'festergut SAY_SLAY_2'), +(-1631089,'Fun time over!',16905,1,0,0,'festergut SAY_BERSERK'), +(-1631090,'Da ... Ddy...',16904,1,0,0,'festergut SAY_DEATH'), +(-1631091,'Oh, Festergut. You were always my favorite. Next to Rotface. The good news is you left behind so much gas, I can practically taste it!',17124,6,0,0,'putricide SAY_FESTERGUT_DEATH'), + +(-1631092,'Good news, everyone! I think I perfected a plague that will destroy all life on Azeroth!',17114,1,0,0,'putricide SAY_AGGRO'), +(-1631093,'You can\'t come in here all dirty like that! You need that nasty flesh scrubbed off first!',17125,1,0,0,'putricide SAY_AIRLOCK'), +(-1631094,'Two oozes, one room! So many delightful possibilities...',17122,1,0,0,'putricide SAY_PHASE_CHANGE'), +(-1631095,'Hmm. I don\'t feel a thing. Whaa...? Where\'d those come from?',17120,1,0,0,'putricide SAY_TRANSFORM_1'), +(-1631096,'Tastes like... Cherry! Oh! Excuse me!',17121,1,0,0,'putricide SAY_TRANSFORM_2'), +(-1631097,'Hmm... Interesting...',17115,1,0,0,'putricide SAY_SLAY_1'), +(-1631098,'That was unexpected!',17116,1,0,0,'putricide SAY_SLAY_2'), +(-1631099,'Great news, everyone!',17118,1,0,0,'putricide SAY_BERSERK'), +(-1631100,'Bad news, everyone! I don\'t think I\'m going to make it',17117,1,0,0,'putricide SAY_DEATH'), + +(-1631101,'Foolish mortals. You thought us defeated so easily? The San\'layn are the Lich King\'s immortal soldiers! Now you shall face their might combined!',16795,6,0,1,'lanathel SAY_COUNCIL_INTRO_1'), +(-1631102,'Rise up, brothers, and destroy our enemies',16796,6,0,0,'lanathel SAY_COUNCIL_INTRO_2'), + +(-1631103,'Such wondrous power! The Darkfallen Orb has made me INVINCIBLE!',16727,1,0,0,'keleseth SAY_KELESETH_INVOCATION'), +(-1631104,'Blood will flow!',16728,1,0,0,'keleseth SAY_KELESETH_SPECIAL'), +(-1631105,'Were you ever a threat?',16723,1,0,0,'keleseth SAY_KELESETH_SLAY_1'), +(-1631106,'Truth is found in death.',16724,1,0,0,'keleseth SAY_KELESETH_SLAY_2'), +(-1631107,'%s cackles maniacally!',16726,2,0,0,'keleseth SAY_KELESETH_BERSERK'), -- TODO Can be wrong +(-1631108,'My queen... they come...',16725,1,0,0,'keleseth SAY_KELESETH_DEATH'), + +(-1631109,'Tremble before Taldaram, mortals, for the power of the orb flows through me!',16857,1,0,0,'taldaram SAY_TALDARAM_INVOCATION'), +(-1631110,'Delight in the pain!',16858,1,0,0,'taldaram SAY_TALDARAM_SPECIAL'), +(-1631111,'Worm food.',16853,1,0,0,'taldaram SAY_TALDARAM_SLAY_1'), +(-1631112,'Beg for mercy!',16854,1,0,0,'taldaram SAY_TALDARAM_SLAY_2'), +(-1631113,'%s laughs.',16856,2,0,0,'taldaram SAY_TALDARAM_BERSERK'), -- TODO Can be wrong +(-1631114,'%s gurgles and dies.',16855,2,0,0,'taldaram SAY_TALDARAM_DEATH'), -- TODO Can be wrong + +(-1631115,'Naxxanar was merely a setback! With the power of the orb, Valanar will have his vengeance!',16685,1,0,0,'valanar SAY_VALANAR_INVOCATION'), +(-1631116,'My cup runneth over.',16686,1,0,0,'valanar SAY_VALANAR_SPECIAL'), +(-1631117,'Dinner... is served.',16681,1,0,0,'valanar SAY_VALANAR_SLAY_1'), +(-1631118,'Do you see NOW the power of the Darkfallen?',16682,1,0,0,'valanar SAY_VALANAR_SLAY_2'), +(-1631119,'BOW DOWN BEFORE THE SAN\'LAYN!',16684,1,0,0,'valanar SAY_VALANAR_BERSERK'), +(-1631120,'...why...?',16683,1,0,0,'valanar SAY_VALANAR_DEATH'), + +(-1631121,'You have made an... unwise... decision.',16782,1,0,0,'blood_queen SAY_AGGRO'), +(-1631122,'Just a taste...',16783,1,0,0,'blood_queen SAY_BITE_1'), +(-1631123,'Know my hunger!',16784,1,0,0,'blood_queen SAY_BITE_2'), +(-1631124,'SUFFER!',16786,1,0,0,'blood_queen SAY_SHADOWS'), +(-1631125,'Can you handle this?',16787,1,0,0,'blood_queen SAY_PACT'), +(-1631126,'Yes... feed my precious one! You\'re mine now!',16790,1,0,0,'blood_queen SAY_MC'), +(-1631127,'Here it comes.',16788,1,0,0,'blood_queen SAY_AIR_PHASE'), +(-1631128,'THIS ENDS NOW!',16793,1,0,0,'blood_queen SAY_BERSERK'), +(-1631129,'But... we were getting along... so well...',16794,1,0,0,'blood_queen SAY_DEATH'), + +(-1631130,'Ready your arms, my Argent Brothers. The Vrykul will protect the Frost Queen with their lives.',16819,1,0,0,'scourgebane SAY_SVALNA_EVENT_1'), +(-1631131,'Even dying here beats spending another day collecting reagents for that madman, Finklestein.',16585,1,0,0,'arnath SAY_SVALNA_EVENT_2'), +(-1631132,'Enough idle banter! Our champions have arrived - support them as we push our way through the hall!',16820,1,0,0,'scourgebane SAY_SVALNA_EVENT_3'), +(-1631133,'You may have once fought beside me, Crok, but now you are nothing more than a traitor. Come, your second death approaches!',17017,1,0,0,'svalna SAY_SVALNA_EVENT_4'), +(-1631134,'Miserable creatures, Die!',17018,1,0,0,'svalna SAY_KILLING_CRUSADERS'), +(-1631135,'Foolish Crok, you brought my reinforcements with you! Arise Argent Champions and serve the Lich King in death!',17019,1,0,0,'svalna SAY_RESSURECT'), +(-1631136,'Come Scourgebane, I\'ll show the Lich King which one of us is truly worthy of the title, champion!',17020,1,0,0,'svalna SAY_SVALNA_AGGRO'), +(-1631137,'What? They died so easily? No matter.',17022,1,0,0,'svalna SAY_KILL_CAPTAIN'), +(-1631138,'What a pitiful choice of an ally Crok.',17021,1,0,0,'svalna SAY_KILL_PLAYER'), +(-1631139,'Perhaps... you were right... Crok.',17023,1,0,0,'svalna SAY_DEATH'), + +(-1631140,'Heroes, lend me your aid! I... I cannot hold them off much longer! You must heal my wounds!',17064,1,0,0,'dreamwalker SAY_AGGRO'), +(-1631141,'I have opened a portal into the Dream. Your salvation lies within, heroes.',17068,1,0,0,'dreamwalker SAY_PORTAL'), +(-1631142,'My strength is returning! Press on, heroes!',17070,1,0,0,'dreamwalker SAY_75_HEALTH'), +(-1631143,'I will not last much longer!',17069,1,0,0,'dreamwalker SAY_25_HEALTH'), +(-1631144,'Forgive me for what I do! I... cannot... stop... ONLY NIGHTMARES REMAIN!',17072,1,0,0,'dreamwalker SAY_0_HEALTH'), +(-1631145,'A tragic loss...',17066,1,0,0,'dreamwalker SAY_PLAYER_DIES'), +(-1631146,'FAILURES!',17067,1,0,0,'dreamwalker SAY_BERSERK'), +(-1631147,'I am renewed! Ysera grants me the favor to lay these foul creatures to rest!',17071,1,0,0,'dreamwalker SAY_VICTORY'), + +(-1631148,'You are fools who have come to this place! The icy winds of Northrend will consume your souls!',17007,1,0,0,'sindragosa SAY_AGGRO'), +(-1631149,'Suffer, mortals, as your pathetic magic betrays you!',17014,1,0,0,'sindragosa SAY_UNCHAINED_MAGIC'), +(-1631150,'Can you feel the cold hand of death upon your heart?',17013,1,0,0,'sindragosa SAY_BLISTERING_COLD'), +(-1631151,'Aaah! It burns! What sorcery is this?!',17015,1,0,0,'sindragosa SAY_RESPIRE'), +(-1631152,'Your incursion ends here! None shall survive!',17012,1,0,0,'sindragosa SAY_TAKEOFF'), +(-1631153,'Now feel my master\'s limitless power and despair!',17016,1,0,0,'sindragosa SAY_PHASE_3'), +(-1631154,'Perish!',17008,1,0,0,'sindragosa SAY_SLAY_1'), +(-1631155,'A flaw of mortality...',17009,1,0,0,'sindragosa SAY_SLAY_2'), +(-1631156,'Enough! I tire of these games!',17011,1,0,0,'sindragosa SAY_BERSERK'), +(-1631157,'Free...at last...',17010,1,0,0,'sindragosa SAY_DEATH'), + +(-1631158,'So...the Light\'s vaunted justice has finally arrived. Shall I lay down Frostmourne and throw myself at your mercy, Fordring?',17349,1,0,0,'lich_king SAY_INTRO_1'), +(-1631159,'We will grant you a swift death, Arthas. More than can be said for the thousands you\'ve tortured and slain.',17390,1,0,0,'tirion SAY_INTRO_2'), +(-1631160,'You will learn of that first hand. When my work is complete, you will beg for mercy -- and I will deny you. Your anguished cries will be testament to my unbridled power.',17350,1,0,0,'lich_king SAY_INTRO_3'), +(-1631161,'So be it. Champions, attack!',17391,1,0,0,'tirion SAY_INTRO_4'), +(-1631162,'I\'ll keep you alive to witness the end, Fordring. I would not want the Light\'s greatest champion to miss seeing this wretched world remade in my image.',17351,1,0,0,'lich_king SAY_INTRO_5'), +(-1631163,'Come then champions, feed me your rage!',17352,1,0,0,'lich_king SAY_AGGRO'), +(-1631164,'I will freeze you from within until all that remains is an icy husk!',17369,1,0,0,'lich_king SAY_REMORSELESS_WINTER'), +(-1631165,'Watch as the world around you collapses!',17370,1,0,0,'lich_king SAY_SHATTER_ARENA'), +(-1631166,'Val\'kyr, your master calls!',17373,1,0,0,'lich_king SAY_SUMMON_VALKYR'), +(-1631167,'Frostmourne hungers...',17366,1,0,0,'lich_king SAY_HARVEST_SOUL'), +(-1631168,'You have come to bring Arthas to justice? To see the Lich King destroyed?',17394,1,0,0,'terenas SAY_FM_TERENAS_AID_1'), +(-1631169,'First, you must escape Frostmourne\'s hold, or be damned as I am; trapped within this cursed blade for all eternity.',17395,1,0,0,'terenas SAY_FM_TERENAS_AID_2'), +(-1631170,'Aid me in destroying these tortured souls! Together we will loosen Frostmourne\'s hold and weaken the Lich King from within!',17396,1,0,0,'terenas SAY_FM_TERENAS_AID_3'), +(-1631171,'Argh... Frostmourne, obey me!',17367,1,0,0,'lich_king SAY_FM_PLAYER_ESCAPE'), +(-1631172,'Frostmourne feeds on the soul of your fallen ally!',17368,1,0,0,'lich_king SAY_FM_PLAYER_DEATH'), +(-1631173,'Apocalypse!',17371,1,0,0,'lich_king SAY_SPECIAL_1'), +(-1631174,'Bow down before your lord and master!',17372,1,0,0,'lich_king SAY_SPECIAL_2'), +(-1631175,'You gnats actually hurt me! Perhaps I\'ve toyed with you long enough, now taste the vengeance of the grave!',17359,1,0,0,'lich_king SAY_LAST_PHASE'), +(-1631176,'Hope wanes!',17363,1,0,0,'lich_king SAY_SLAY_1'), +(-1631177,'The end has come!',17364,1,0,0,'lich_king SAY_SLAY_2'), +(-1631178,'Face now your tragic end!',17365,1,0,0,'lich_king SAY_ENRAGE'), +(-1631179,'No question remains unanswered. No doubts linger. You are Azeroth\'s greatest champions! You overcame every challenge I laid before you. My mightiest servants have fallen before your relentless onslaught, your unbridled fury...',17353,1,0,0,'lich_king SAY_OUTRO_1'), +(-1631180,'Is it truly righteousness that drives you? I wonder',17354,1,0,0,'lich_king SAY_OUTRO_2'), +(-1631181,'You trained them well, Fordring. You delivered the greatest fighting force this world has ever known... right into my hands -- exactly as I intended. You shall be rewarded for your unwitting sacrifice.',17355,1,0,0,'lich_king SAY_OUTRO_3'), +(-1631182,'Watch now as I raise them from the dead to become masters of the Scourge. They will shroud this world in chaos and destruction. Azeroth\'s fall will come at their hands -- and you will be the first to die.',17356,1,0,0,'lich_king SAY_OUTRO_4'), +(-1631183,'I delight in the irony.',17357,1,0,0,'lich_king SAY_OUTRO_5'), +(-1631184,'LIGHT, GRANT ME ONE FINAL BLESSING. GIVE ME THE STRENGTH... TO SHATTER THESE BONDS!',17392,1,0,0,'tirion SAY_OUTRO_6'), +(-1631185,'Impossible...',17358,1,0,0,'lich_king SAY_OUTRO_7'), +(-1631186,'No more, Arthas! No more lives will be consumed by your hatred!',17393,1,0,0,'tirion SAY_OUTRO_8'), +(-1631187,'Free at last! It is over, my son. This is the moment of reckoning.',17397,1,0,0,'terenas SAY_OUTRO_9'), +(-1631188,'Rise up, champions of the Light!',17398,1,0,0,'terenas SAY_OUTRO_10'), +(-1631189,'THE LICH KING...MUST...FALL!',17389,1,0,0,'tirion SAY_OUTRO_11'), +(-1631190,'Now I stand, the lion before the lambs... and they do not fear.',17361,1,0,0,'lich_king SAY_OUTRO_12'), +(-1631191,'They cannot fear.',17362,1,0,0,'lich_king SAY_OUTRO_13'), +(-1631192,'%s dies',17374,2,0,0,'lich_king SAY_OUTRO_14'), -- TODO Can be wrong + +(-1631193,'%s goes into a frenzy!',0,3,0,0,'saurfang EMOTE_FRENZY'), +(-1631194,'%s\'s Blood Beasts gain the scent of blood!',0,3,0,0,'saurfang EMOTE_SCENT'), +(-1631195,'Really... Is that all you got?',16791,1,0,0,'blood_queen SAY_SLAY_1'), +(-1631196,'Such a pity...',16792,1,0,0,'blood_queen SAY_SLAY_2'), + +(-1631197,'Invocation of Blood jumps to %s!',0,3,0,0,'blood_princes EMOTE_INVOCATION'), +(-1631198,'%s begins casting Empowered Shock Vortex!',0,3,0,0,'valanar EMOTE_SHOCK_VORTEX'), +(-1631199,'%s speed toward $N!',0,3,0,0,'taldaram EMOTE_FLAMES'); + +-- -1 632 000 ICC: FORGE OF SOULS +INSERT INTO script_texts (entry,content_default,sound,type,language,emote,comment) VALUES +(-1632000,'More souls to power the engine!',0,1,0,0,'boss_bronjahm SAY_AGGRO_1'), +(-1632001,'Finally...a captive audience!',16595,1,0,0,'boss_bronjahm SAY_AGGRO_2'), +(-1632002,'Fodder for the engine!',16596,1,0,0,'boss_bronjahm SAY_SLAY_1'), +(-1632003,'Another soul to strengthen the host!',16597,1,0,0,'boss_bronjahm SAY_SLAY_2'), +(-1632004,'My soul for you, master.',16598,1,0,0,'boss_bronjahm SAY_DEATH'), +(-1632005,'The vortex of the harvested calls to you!',16599,1,0,0,'boss_bronjahm SAY_SOULSTORM'), +(-1632006,'I will sever the soul from your body!',16600,1,0,0,'boss_bronjahm SAY_CORRUPT_SOUL'), + +(-1632007,'You dare look upon the host of souls?! I SHALL DEVOUR YOU WHOLE!',16884,1,0,0,'boss_devourer SAY_MALE_1_AGGRO'), +(-1632008,'You dare look upon the host of souls?! I SHALL DEVOUR YOU WHOLE!',16890,1,0,0,'boss_devourer SAY_FEMALE_AGGRO'), +(-1632009,'Damnation!',16885,1,0,0,'boss_devourer SAY_MALE_1_SLAY_1'), +(-1632010,'Damnation!',16891,1,0,0,'boss_devourer SAY_FEMALE_SLAY_1'), +(-1632011,'Damnation!',16896,1,0,0,'boss_devourer SAY_MALE_2_SLAY_1'), +(-1632012,'Doomed for eternity!',16886,1,0,0,'boss_devourer SAY_MALE_1_SLAY_2'), +(-1632013,'Doomed for eternity!',16892,1,0,0,'boss_devourer SAY_FEMALE_SLAY_2'), +(-1632014,'Doomed for eternity!',16897,1,0,0,'boss_devourer SAY_MALE_2_SLAY_2'), +(-1632015,'The swell of souls will not be abated! You only delay the inevitable!',16887,1,0,0,'boss_devourer SAY_MALE_1_DEATH'), +(-1632016,'The swell of souls will not be abated! You only delay the inevitable!',16893,1,0,0,'boss_devourer SAY_FEMALE_DEATH'), +(-1632017,'The swell of souls will not be abated! You only delay the inevitable!',16898,1,0,0,'boss_devourer SAY_MALE_2_DEATH'), +(-1632018,'SUFFERING! ANGUISH! CHAOS! RISE AND FEED!',16888,1,0,0,'boss_devourer SAY_MALE_1_SOUL_ATTACK'), +(-1632019,'SUFFERING! ANGUISH! CHAOS! RISE AND FEED!',16894,1,0,0,'boss_devourer SAY_FEMALE_SOUL_ATTACK'), +(-1632020,'SUFFERING! ANGUISH! CHAOS! RISE AND FEED!',16899,1,0,0,'boss_devourer SAY_MALE_2_SOUL_ATTACK'), +(-1632021,'Stare into the abyss, and see your end!',16889,1,0,0,'boss_devourer SAY_MALE_1_DARK_GLARE'), +(-1632022,'Stare into the abyss, and see your end!',16895,1,0,0,'boss_devourer SAY_FEMALE_DARK_GLARE'), +(-1632023,'%s begins to cast Mirrored Soul!',0,3,0,0,'boss_devourer EMOTE_MIRRORED_SOUL'), +(-1632024,'%s begins to Unleash Souls!',0,3,0,0,'boss_devourer EMOTE_UNLEASH_SOULS'), +(-1632025,'%s begins to cast Wailing Souls!',0,3,0,0,'boss_devourer EMOTE_WAILING_SOULS'); + +-- -1 649 000 TRIAL OF THE CRUSADER +INSERT INTO script_texts (entry,content_default,sound,type,language,emote,comment) VALUES +(-1649000,'Welcome champions, you have heard the call of the argent crusade and you have boldly answered. It is here in the crusaders coliseum that you will face your greatest challenges. Those of you who survive the rigors of the coliseum will join the Argent Crusade on it\'s marsh to ice crown citadel.',16036,1,0,0,'tirion SAY_TIRION_RAID_INTRO_LONG'), +(-1649001,'Welcome to the trials of the crusader. Only the most powerful combatants of azeroth are allowed to undergo these trials. You are among the worthy few.',16053,1,0,0,'tirion SAY_RAID_TRIALS_INTRO'), + +(-1649002,'Hailing from the deepest, darkest carverns of the storm peaks, Gormok the Impaler! Battle on, heroes!',16038,1,0,0,'tirion SAY_TIRION_BEAST_1'), +(-1649003,'Your beast will be no match for my champions Tirion.',16069,1,0,0,'varian SAY_VARIAN_BEAST_1'), +(-1649004,'I have seen more worthy challenges in the ring of blood, you waste our time paladin.',16026,1,0,0,'garrosh SAY_GARROSH_BEAST_1'), +(-1649005,'Steel yourselves, heroes, for the twin terrors Acidmaw and Dreadscale. Enter the arena!',16039,1,0,0,'tirion SAY_TIRION_BEAST_2'), +(-1649006,'The air freezes with the introduction of our next combatant, Icehowl! Kill or be killed, champions!',16040,1,0,0,'tirion SAY_TIRION_BEAST_3'), +(-1649007,'The monstrous menagerie has been vanquished!',16041,1,0,0,'tirion SAY_TIRION_BEAST_SLAY'), +(-1649008,'Tragic... They fought valiantly, but the beasts of Northrend triumphed. Let us observe a moment of silence for our fallen heroes.',16042,1,0,0,'tirion SAY_TIRION_BEAST_WIPE'), + +(-1649009,'Grand Warlock Wilfred Fizzlebang will summon forth your next challenge. Stand by for his entry!',16043,1,0,0,'tirion SAY_TIRION_JARAXXUS_INTRO_1'), +(-1649010,'Thank you, Highlord. Now, challengers, I will begin the ritual of summoning. When I am done a fearsome doomguard will appear!',16268,1,0,2,'wilfred SAY_WILFRED_JARAXXUS_INTRO_1'), +(-1649011,'Prepare for oblivion!',16269,1,0,0,'wilfred SAY_WILFRED_JARAXXUS_INTRO_2'), +(-1649012,'Ah ha! Behold the absolute power of Wilfred Fizzlebang, master summoner! You are bound to ME, demon!',16270,1,0,0,'wilfred SAY_WILFRED_JARAXXUS_INTRO_3'), +(-1649013,'Trifling gnome, your arrogance will be your undoing!',16143,1,0,0,'jaraxxus SAY_JARAXXUS_JARAXXAS_INTRO_1'), +(-1649014,'But I\'m in charge her-',16271,1,0,0,'wilfred SAY_WILFRED_DEATH'), +(-1649015,'Quickly, heroes! Destroy the demon lord before it can open a portal to its twisted demonic realm!',16044,1,0,0,'tirion SAY_TIRION_JARAXXUS_INTRO_2'), +(-1649016,'The loss of Wilfred Fizzlebang, while unfortunate, should be a lesson to those that dare dabble in dark magic. Alas, you are victorious and must now face the next challenge.',16045,1,0,0,'tirion SAY_TIRION_JARAXXUS_EXIT_1'), +(-1649017,'Treacherous Alliance dogs! You summon a demon lord against warriors of the Horde!? Your deaths will be swift!',16021,1,0,0,'garrosh SAY_GARROSH_JARAXXUS_EXIT_1'), +(-1649018,'The Alliance doesn\'t need the help of a demon lord to deal with Horde filth. Come, pig!',16064,1,0,0,'varian SAY_VARIAN_JARAXXUS_SLAY'), +(-1649019,'Everyone, calm down! Compose yourselves! There is no conspiracy at play here. The warlock acted on his own volition - outside of influences from the Alliance. The tournament must go on!',16046,1,0,0,'tirion SAY_TIRION_JARAXXUS_EXIT_2'), + +(-1649020,'The next battle will be against the Argent Crusade\'s most powerful knights! Only by defeating them will you be deemed worthy...',16047,1,0,0,'tirion SAY_TIRION_PVP_INTRO_1'), +(-1649021,'The Horde demands justice! We challenge the Alliance. Allow us to battle in place of your knights, paladin. We will show these dogs what it means to insult the Horde!',16023,1,0,0,'garrosh SAY_GARROSH_PVP_A_INTRO_1'), +(-1649022,'Our honor has been besmirched! They make wild claims and false accusations against us. I demand justice! Allow my champions to fight in place of your knights, Tirion. We challenge the Horde!',16066,1,0,0,'varian SAY_VARIAN_PVP_H_INTRO_1'), +(-1649023,'Very well, I will allow it. Fight with honor!',16048,1,0,0,'tirion SAY_TIRION_PVP_INTRO_2'), +(-1649024,'Fight for the glory of the Alliance, heroes! Honor your king and your people!',16065,1,0,0,'varian SAY_VARIAN_PVP_H_INTRO_2'), +(-1649025,'Show them no mercy, Horde champions! LOK\'TAR OGAR!',16022,1,0,0,'garrosh SAY_GARROSH_PVP_A_INTRO_2'), +(-1649026,'GLORY TO THE ALLIANCE!',16067,1,0,0,'varian SAY_VARIAN_PVP_A_WIN'), +(-1649027,'That was just a taste of what the future brings. FOR THE HORDE!',16024,1,0,0,'garrosh SAY_GARROSH_PVP_H_WIN'), +(-1649028,'A shallow and tragic victory. We are weaker as a whole from the losses suffered today. Who but the Lich King could benefit from such foolishness? Great warriors have lost their lives. And for what? The true threat looms ahead - the Lich King awaits us all in death.',16049,1,0,0,'tirion SAY_TIRION_PVP_WIN'), + +(-1649029,'Only by working together will you overcome the final challenge. From the depths of Icecrown come two of the Scourge\'s most powerful lieutenants: fearsome val\'kyr, winged harbingers of the Lich King!',16050,1,0,0,'tirion SAY_TIRION_TWINS_INTRO'), +(-1649030,'Let the games begin!',16037,1,0,0,'tirion SAY_RAID_INTRO_SHORT'), +(-1649031,'Not even the lich king\'s most powerful minions could stand against the alliance. All hail our victors.',16068,1,0,0,'varian SAY_VARIAN_TWINS_A_WIN'), +(-1649032,'Do you still question the might of the Horde, paladin? We will take on all comers!',16025,1,0,0,'garrosh SAY_GARROSH_TWINS_H_WIN'), +(-1649033,'A mighty blow has been dealt to the Lich King! You have proven yourselves able bodied champions of the Argent Crusade. Together we will strike at Icecrown Citadel and destroy what remains of the Scourge! There is no challenge that we cannot face united!',16051,1,0,0,'tirion SAY_TIRION_TWINS_WIN'), + +(-1649034,'You will have your challenge, Fordring.',16321,1,0,0,'lich_king SAY_LKING_ANUB_INTRO_1'), +(-1649035,'Arthas! You are hopelessly outnumbered! Lay down Frostmourne and I will grant you a just death.',16052,1,0,0,'tirion SAY_TIRION_ABUN_INTRO_1'), +(-1649036,'The Nerubians built an empire beneath the frozen wastes of Northrend. An empire that you so foolishly built your structures upon. MY EMPIRE.',16322,1,0,11,'lich_king SAY_LKING_ANUB_INTRO_2'), +(-1649037,'The souls of your fallen champions will be mine, Fordring.',16323,1,0,0,'lich_king SAY_LKING_ANUB_INTRO_3'), +(-1649038,'Ahhh, our guests have arrived, just as the master promised.',16235,1,0,0,'anubarak SAY_ANUB_ANUB_INTRO_1'), + +(-1649039,'%s glares at $N and lets out a bellowing roar!',0,3,0,0,'icehowl EMOTE_MASSIVE_CRASH'), + +(-1649040,'You face Jaraxxus, eredar lord of the Burning Legion!',16144,1,0,0,'jaraxxus SAY_AGGRO'), +(-1649041,'Insignificant gnat!',16145,1,0,0,'jaraxxus SAY_SLAY_1'), +(-1649042,'Banished to the Nether!',16146,1,0,0,'jaraxxus SAY_SLAY_2'), +(-1649043,'Another will take my place. Your world is doomed.',16147,1,0,0,'jaraxxus SAY_DEATH'), +(-1649044,'',16148,1,0,0,'jaraxxus SAY_BERSERK'), -- TODO, just some laughing +(-1649045,'Flesh from bone!',16149,1,0,0,'jaraxxus SAY_INCINERATE'), +(-1649046,'Come forth, sister! Your master calls!',16150,1,0,0,'jaraxxus SAY_MISTRESS'), +(-1649047,'Inferno!',16151,1,0,0,'jaraxxus SAY_INFERNO'), + +(-1649048,'Weakling!',16017,1,0,0,'garrosh SAY_GARROSH_PVP_A_SLAY_1'), +(-1649049,'Pathetic!',16018,1,0,0,'garrosh SAY_GARROSH_PVP_A_SLAY_2'), +(-1649050,'Overpowered.',16019,1,0,0,'garrosh SAY_GARROSH_PVP_A_SLAY_3'), +(-1649051,'Lok\'tar!',16020,1,0,0,'garrosh SAY_GARROSH_PVP_A_SLAY_4'), +(-1649052,'Hah!',16060,1,0,0,'varian SAY_VARIAN_PVP_H_SLAY_1'), +(-1649053,'Hardly a challenge!',16061,1,0,0,'varian SAY_VARIAN_PVP_H_SLAY_2'), +(-1649054,'Worthless scrub.',16062,1,0,0,'varian SAY_VARIAN_PVP_H_SLAY_3'), +(-1649055,'Is this the best the Horde has to offer?',16063,1,0,0,'varian SAY_VARIAN_PVP_H_SLAY_4'), + +(-1649056,'In the name of our dark master. For the Lich King. You. Will. Die.',16272,1,0,0,'twin_valkyr SAY_AGGRO'), +(-1649057,'You are finished!',16273,1,0,0,'twin_valkyr SAY_BERSERK'), +(-1649058,'Chaos!',16274,1,0,0,'twin_valkyr SAY_COLORSWITCH'), +(-1649059,'The Scourge cannot be stopped...',16275,1,0,0,'twin_valkyr SAY_DEATH'), +(-1649060,'You have been measured, and found wanting!',16276,1,0,0,'twin_valkyr SAY_SLAY_1'), +(-1649061,'Unworthy!',16277,1,0,0,'twin_valkyr SAY_SLAY_2'), +(-1649062,'Let the dark consume you!',16278,1,0,0,'twin_valkyr SAY_TO_BLACK'), +(-1649063,'Let the light consume you!',16279,1,0,0,'twin_valkyr SAY_TO_WHITE'), + +(-1649064,'This place will serve as your tomb!',16234,1,0,0,'anubarak SAY_AGGRO'), +(-1649065,'F-lakkh shir!',16236,1,0,0,'anubarak SAY_SLAY_1'), +(-1649066,'Another soul to sate the host.',16237,1,0,0,'anubarak SAY_SLAY_2'), +(-1649067,'I have failed you, master...',16238,1,0,0,'anubarak SAY_DEATH'), +(-1649068,'',16239,1,0,0,'anubarak SAY_BERSERK'), +(-1649069,'Auum na-l ak-k-k-k, isshhh. Rise, minions. Devour...',16240,1,0,0,'anubarak SAY_SUBMERGE'), +(-1649070,'The swarm shall overtake you!',16241,1,0,0,'anubarak SAY_LEECHING_SWARM'), + +(-1649071,'%s burrows into the ground!',0,3,0,0,'anubarak EMOTE_BURROW'), +(-1649072,'%s spikes pursue $N!',0,3,0,0,'anubarak EMOTE_PURSUE'), +(-1649073,'%s emerges from the ground!',0,3,0,0,'anubarak EMOTE_EMERGE'), +(-1649074,'%s unleashes a Leeching Swarm to heal himself!',0,3,0,0,'anubarak EMOTE_SWARM'), + +(-1649075,'Champions, you\'re alive! Not only have you defeated every challenge of the Trial of the Crusader, but also thwarted Arthas\' plans! Your skill and cunning will prove to be a powerful weapon against the Scourge. Well done! Allow one of the Crusade\'s mages to transport you to the surface!',0,0,0,1,'tirion SAY_EPILOGUE'); + +-- -1 650 000 TRIAL OF THE CHAMPION + +INSERT INTO script_texts (entry, content_default, sound, type, language, emote, comment) VALUES +(-1654001, 'I want the perimeter secured and the gates manned by two guards at all times. No one gets in, no one gets out.', 0, 0, 7, 424, 'npc_prince_liam_greymane_phase1'), +(-1654002, 'We protected Gilneas from the Scourge. We protected Gilneas during the Northgate rebellion. We will protect Gilneas from whatever this new threat may be.', 0, 0, 7, 424, 'npc_prince_liam_greymane_phase1'), +(-1654003, 'Stand ready, guards! We don\'t know how many intruders we\'re dealing with, but the Headlands are overrun and we\'re cut off from the harbor towns. Expect to be outnumbered.', 0, 0, 7, 424, 'npc_prince_liam_greymane_phase1'), +(-1654004, 'Stand your ground men!', 0, 1, 7, 424, 'npc_prince_liam_greymane_phase2'), +(-1654005, 'Defeat these foul beasts!', 0, 1, 7, 424, 'npc_prince_liam_greymane_phase2'), +(-1654006, 'Protect the Civilians!', 0, 1, 7, 424, 'npc_prince_liam_greymane_phase2'), +(-1654007, 'Push them back!', 0, 1, 7, 424, 'npc_prince_liam_greymane_phase2'), +(-1654008, 'Take heart men, we must protect our city!', 0, 1, 7, 424, 'npc_prince_liam_greymane_phase2'), +(-1654009, 'Protect me, please!', 0, 0, 7, 424, 'frightened_citizen_quest'), +(-1654010, 'It\'s coming right for me!', 0, 0, 7, 424, 'frightened_citizen_quest'), +(-1654011, 'What in the world? Let\'s get out of here!', 0, 0, 7, 424, 'frightened_citizen_quest'), +(-1654012, 'Help!', 0, 0, 7, 424, 'frightened_citizen_quest'), +(-1654013, 'Help me, please!', 0, 0, 7, 424, 'frightened_citizen_quest'), +(-1654014, 'Worgen! Worgen everywhere!', 0, 0, 7, 424, 'frightened_citizen_quest'), +(-1654015, 'Flee! They\'re everywhere!', 0, 0, 7, 424, 'frightened_citizen_quest'); +-- -1 658 000 ICC: PIT OF SARON +INSERT INTO script_texts (entry,content_default,sound,type,language,emote,comment) VALUES +(-1658001,'Intruders have entered the masters domain. Signal the alarms!',16747,1,0,0,'tyrannus SAY_TYRANNUS_INTRO_1'), +(-1658002,'Heroes of the Alliance, attack!',16626,1,0,0,'jaina SAY_JAINA_INTRO_1'), +(-1658003,'Soldiers of the Horde, attack!',17045,1,0,0,'sylvanas SAY_SYLVANAS_INTRO_1'), +(-1658004,'Hrmph, fodder. Not even fit to labor in the quarry. Relish these final moments for soon you will be nothing more than mindless undead.',16748,1,0,0,'tyrannus SAY_TYRANNUS_INTRO_2'), +(-1658005,'Your last waking memory will be of agonizing pain.',16749,1,0,0,'tyrannus SAY_TYRANNUS_INTRO_3'), +(-1658006,'No! You monster!',16627,1,0,0,'jaina SAY_JAINA_INTRO_2'), +(-1658007,'Pathetic weaklings!',17046,1,0,1,'sylvanas SAY_SYLVANAS_INTRO_2'), +(-1658008,'Minions, destroy these interlopers!',16751,1,0,0,'tyrannus SAY_TYRANNUS_INTRO_4'), +(-1658009,'I do what I must. Please forgive me, noble soldiers.',16628,1,0,0,'jaina SAY_JAINA_INTRO_3'), +(-1658010,'You will have to make your way across this quarry on your own.',16629,0,0,1,'jaina SAY_JAINA_INTRO_4'), +(-1658011,'You will have to battle your way through this cesspit on your own.',17047,0,0,1,'sylvanas SAY_SYLVANAS_INTRO_3'), +(-1658012,'Free any Alliance slaves that you come across. We will most certainly need their assistance in battling Tyrannus. I will gather reinforcements and join you on the other side of the quarry.',16630,0,0,0,'jaina SAY_JAINA_INTRO_5'), +(-1658013,'Free any Horde slaves that you come across. We will most certainly need their assistance in battling Tyrannus. I will gather reinforcements and join you on the other side of the quarry.',17048,0,0,0,'sylvanas SAY_SYLVANAS_INTRO_4'), + +(-1658014,'Tiny creatures under feet, you bring Garfrost something good to eat!',16912,1,0,0,'garfrost SAY_AGGRO'), +(-1658015,'Will save for snack. For later.',16913,1,0,0,'garfrost SAY_SLAY_1'), +(-1658016,'That one maybe not so good to eat now. Stupid Garfrost! BAD! BAD!',16914,1,0,0,'garfrost SAY_BOULDER_HIT'), +(-1658017,'Garfrost hope giant underpants clean. Save boss great shame. For later.',16915,1,0,0,'garfrost SAY_DEATH'), +(-1658018,'Axe too weak. Garfrost make better and CRUSH YOU!',16916,1,0,0,'garfrost SAY_FORGE_1'), +(-1658019,'That one maybe not so good to eat now. Stupid Garfrost! BAD! BAD!',16917,1,0,0,'garfrost SAY_FORGE_2'), +(-1658020,'Another shall take his place. You waste your time.',16752,6,0,0,'tyrannus SAY_TYRANNUS_GARFROST'), +(-1658021,'The forgemaster is dead! Get geared up men, we have a Scourgelord to kill.',0,1,0,0,'victus_or_ironskull SAY_GENERAL_GARFROST'), +(-1658022,'%s hurls a massive saronite boulder at you!',0,5,0,0,'garfrost EMOTE_THROW_SARONITE'), -- TODO emote only displayed to target +(-1658023,'%s casts Deep Freeze at $N.',0,3,0,0,'garfrost EMOTE_DEEP_FREEZE'), + +(-1658024,'Our work must not be interrupted! Ick! Take care of them!',16926,1,0,0,'krick SAY_AGGRO'), +(-1658025,'Ooh...We could probably use these parts!',16927,1,0,0,'krick SAY_SLAY_1'), +(-1658026,'Arms and legs are in short supply...Thanks for your contribution!',16928,1,0,0,'krick SAY_SLAY_2'), +(-1658027,'Enough moving around! Hold still while I blow them all up!',16929,1,0,0,'krick SAY_ORDER_STOP'), +(-1658028,'Quickly! Poison them all while they\'re still close!',16930,1,0,0,'krick SAY_ORDER_BLOW'), +(-1658029,'No! That one! That one! Get that one!',16931,1,0,0,'krick SAY_TARGET_1'), +(-1658030,'I\'ve changed my mind...go get that one instead!',16932,1,0,0,'krick SAY_TARGET_2'), +(-1658031,'What are you attacking him for? The dangerous one is over there,fool!',16933,1,0,0,'krick SAY_TARGET_3'), +(-1658032,'%s begins rapidly conjuring explosive mines!',0,3,0,0,'krick EMOTE_KRICK_MINES'), +(-1658033,'%s begins to unleash a toxic poison cloud!',0,3,0,0,'ick EMOTE_ICK_POISON'), +(-1658034,'%s is chasing you!',0,5,0,0,'ick EMOTE_ICK_CHASING'), -- TODO emote type? + +(-1658035,'Wait! Stop! Don\'t kill me, please! I\'ll tell you everything!',16934,1,0,431,'krick SAY_OUTRO_1'), +(-1658036,'I\'m not so naive as to believe your appeal for clemency, but I will listen.',16611,1,0,0,'jaina SAY_JAINA_KRICK_1'), +(-1658037,'Why should the Banshee Queen spare your miserable life?',17033,1,0,396,'sylvanas SAY_SYLVANAS_KRICK_1'), +(-1658038,'What you seek is in the master\'s lair, but you must destroy Tyrannus to gain entry. Within the Halls of Reflection you will find Frostmourne. It... it holds the truth.',16935,1,0,0,'krick SAY_OUTRO_2'), +(-1658039,'Frostmourne lies unguarded? Impossible!',16612,1,0,0,'jaina SAY_JAINA_KRICK_2'), +(-1658040,'Frostmourne? The Lich King is never without his blade! If you are lying to me...',17034,1,0,15,'sylvanas SAY_SYLVANAS_KRICK_2'), +(-1658041,'I swear it is true! Please, don\'t kill me!!',16936,1,0,0,'krick SAY_OUTRO_3'), +(-1658042,'Worthless gnat! Death is all that awaits you!',16753,1,0,0,'tyrannus SAY_TYRANNUS_KRICK_1'), +(-1658043,'Urg... no!!',16937,1,0,0,'krick SAY_OUTRO_4'), +(-1658044,'Do not think that I shall permit you entry into my master\'s sanctum so easily. Pursue me if you dare.',16754,1,0,0,'tyrannus SAY_TYRANNUS_KRICK_2'), +(-1658045,'What a cruel end. Come, heroes. We must see if the gnome\'s story is true. If we can separate Arthas from Frostmourne, we might have a chance at stopping him.',16613,1,0,0,'jaina SAY_JAINA_KRICK_3'), +(-1658046,'A fitting end for a traitor. Come, we must free the slaves and see what is within the Lich King\'s chamber for ourselves.',17035,1,0,396,'sylvanas SAY_SYLVANAS_KRICK_3'), + +(-1658047,'Your pursuit shall be in vain, adventurers, for the Lich King has placed an army of undead at my command! Behold!',16755,6,0,0,'tyrannus SAY_TYRANNUS_AMBUSH_1'), +(-1658048,'Persistent whelps! You will not reach the entrance of my lord\'s lair! Soldiers, destroy them!',16756,6,0,0,'tyrannus SAY_TYRANNUS_AMBUSH_2'), +(-1658049,'Rimefang! Trap them within the tunnel! Bury them alive!',16757,6,0,0,'tyrannus SAY_GAUNTLET'), + +(-1658050,'Alas, brave, brave adventurers, your meddling has reached its end. Do you hear the clatter of bone and steel coming up the tunnel behind you? That is the sound of your impending demise.',16758,1,0,0,'tyrannus SAY_PREFIGHT_1'), +(-1658051,'Heroes! We will hold off the undead as long as we can, even to our dying breath. Deal with the Scourgelord!',17148,1,0,0,'victus SAY_VICTUS_TRASH'), +(-1658052,'Ha, such an amusing gesture from the rabble. When I have finished with you, my master\'s blade will feast upon your souls. Die!',16759,1,0,0,'tyrannus SAY_PREFIGHT_2'), +(-1658053,'I shall not fail The Lich King! Come and meet your end!',16760,1,0,0,'tyrannus SAY_AGGRO'), +(-1658054,'Such a shameful display...',16761,1,0,0,'tyrannus SAY_SLAY_1'), +(-1658055,'Perhaps you should have stayed in the mountains!',16762,1,0,0,'tyrannus SAY_SLAY_2'), +(-1658056,'Impossible! Rimefang...Warn...',16763,1,0,0,'tyrannus SAY_DEATH'), +(-1658057,'Rimefang, destroy this fool!',16764,1,0,0,'tyrannus SAY_MARK'), +(-1658058,'Power... overwhelming!',16765,1,0,0,'tyrannus SAY_SMASH'), +(-1658059,'The frostwyrm %s gazes at $N and readies an icy attack!',0,3,0,0,'rimefang EMOTE_RIMEFANG_ICEBOLT'), +(-1658060,'%s roars and swells with dark might!',0,3,0,0,'tyrannus EMOTE_SMASH'), + +(-1658061,'Brave champions, we owe you our lives, our freedom... Though it be a tiny gesture in the face of this enormous debt, I pledge that from this day forth, all will know of your deeds, and the blazing path of light you cut through the shadow of this dark citadel.',17149,1,0,0,'victus SAY_VICTUS_OUTRO_1'), +(-1658062,'This day will stand as a testament not only to your valor, but to the fact that no foe, not even the Lich King himself, can stand when Alliance and Horde set aside their differences and ---',0,1,0,0,'victus_or_ironskull SAY_GENERAL_OUTRO_2'), +(-1658063,'Heroes, to me!',16614,0,0,5,'jaina SAY_JAINA_OUTRO_1'), +(-1658064,'Take cover behind me! Quickly!',17037,0,0,5,'sylvanas SAY_SYLVANAS_OUTRO_1'), +(-1658065,'The Frost Queen is gone. We must keep moving - our objective is near.',16615,0,0,0,'jaina SAY_JAINA_OUTRO_2'), +(-1658066,'I... I could not save them... Damn you, Arthas! DAMN YOU!',16616,0,0,0,'jaina SAY_JAINA_OUTRO_3'), +(-1658067,'I thought he\'d never shut up. At last, Sindragosa silenced that long-winded fool. To the Halls of Reflection, champions! Our objective is near... I can sense it.',17036,0,0,396,'sylvanas SAY_SYLVANAS_OUTRO_2'), + +(-1658068,'Heroes! We will hold off the undead as long as we can, even to our dying breath. Deal with the Scourgelord!',17150,1,0,0,'ironskull SAY_IRONSKULL_TRASH'), +(-1658069,'Brave champions, we owe you our lives, our freedom... Though it be a tiny gesture in the face of this enormous debt, I pledge that from this day forth, all will know of your deeds, and the blazing path of light you cut through the shadow of this dark citadel.',17151,1,0,0,'ironskull SAY_IRONSKULL_OUTRO_1'); + +-- -1 668 000 ICC: HALLS OF REFLECTION + +-- -1 724 000 RUBY SANCTUM +INSERT INTO script_texts (entry,content_default,sound,type,language,emote,comment) VALUES +(-1724000,'Help! I am trapped within this tree! I require aid!',17490,6,0,0,'xerestraza SAY_HELP'), +(-1724001,'Your power wanes, ancient one! Soon, you will join your friends!',17525,6,0,0,'baltharus SAY_INTRO'), +(-1724002,'Thank you! I could have not held out for much longer. A terrible thing has happened here.',17491,1,0,0,'xerestraza SAY_THANKS'), +(-1724003,'We believed that the Sanctum was well fortified, but we were not prepareted for the nature of this assault.',17492,0,0,0,'xerestraza SAY_OUTRO_1'), +(-1724004,'The Black Dragonkin materialized from thin air, and set upon us before we could react.',17493,0,0,0,'xerestraza SAY_OUTRO_2'), +(-1724005,'We did not stand a chance. As my brethren perished around me, I managed to retreat hear and bar the entrance.',17494,0,0,0,'xerestraza SAY_OUTRO_3'), +(-1724006,'They slaughtered us with cold efficiency, but the true focus of their interest seemed to be the eggs kept here in the sanctum.',17495,0,0,0,'xerestraza SAY_OUTRO_4'), +(-1724007,'The commander of the forces on the ground here is a cruel brute named Zarithrian. But I fear there are greater powers at work.',17496,0,0,0,'xerestraza SAY_OUTRO_5'), +(-1724008,'In their initial assault I caught a glimpse of their true leader, a fearsome full-grown Twilight Dragon.',17497,0,0,0,'xerestraza SAY_OUTRO_6'), +(-1724009,'I know not the extent of their plans heroes, but I know this: they cannot be allowed to succeed!',17498,0,0,0,'xerestraza SAY_OUTRO_7'), + +(-1724010,'Ah, the entertainment has arrived.',17520,1,0,0,'baltharus SAY_AGGRO'), +(-1724011,'Baltharus leaves no survivors!',17521,1,0,0,'baltharus SAY_SLAY_1'), +(-1724012,'This world has enough heroes.',17522,1,0,0,'baltharus SAY_SLAY_2'), +(-1724013,'I... Didn\'t see that coming...',17523,1,0,0,'baltharus SAY_DEATH'), +(-1724014,'Twice the pain and half the fun.',17524,1,0,0,'baltharus SAY_SPLIT'), + +(-1724015,'You will suffer for this intrusion!',17528,1,0,0,'saviana SAY_AGGRO'), +(-1724016,'As it should be...',17529,1,0,0,'saviana SAY_SLAY_1'), +(-1724017,'Halion will be pleased.',17530,1,0,0,'saviana SAY_SLAY_2'), +(-1724018,'Burn in the master\'s flame!',17532,1,0,0,'saviana SAY_SPECIAL'), + +(-1724019,'Alexstrasza has chosen capable allies... A pity that I must END YOU!',17512,1,0,0,'zarithrian SAY_AGGRO'), +(-1724020,'You thought you stood a chance?',17513,1,0,0,'zarithrian SAY_SLAY_1'), +(-1724021,'It\'s for the best.',17514,1,0,0,'zarithrian SAY_SLAY_2'), +(-1724022,'HALION! I...',17515,1,0,0,'zarithrian SAY_DEATH'), +(-1724023,'Turn them to ash, minions!',17516,1,0,0,'zarithrian SAY_SUMMON'), + +(-1724024,'Meddlesome insects! You\'re too late: The Ruby Sanctum\'s lost.',17499,6,0,0,'halion SAY_SPAWN'), +(-1724025,'Your world teeters on the brink of annihilation. You will ALL bear witness to the coming of a new age of DESTRUCTION!',17500,1,0,0,'halion SAY_AGGRO'), +(-1724026,'Another hero falls.',17501,1,0,0,'halion SAY_SLAY'), +(-1724027,'Relish this victory, mortals, for it will be your last! This world will burn with the master\'s return!',17503,1,0,0,'halion SAY_DEATH'), +(-1724028,'Not good enough.',17504,1,0,0,'halion SAY_BERSERK'), +(-1724029,'The heavens burn!',17505,1,0,0,'halion SAY_FIREBALL'), +(-1724030,'Beware the shadow!',17506,1,0,0,'halion SAY_SPHERES'), +(-1724031,'You will find only suffering within the realm of twilight! Enter if you dare!',17507,1,0,0,'halion SAY_PHASE_2'), +(-1724032,'I am the light and the darkness! Cower, mortals, before the herald of Deathwing!',17508,1,0,0,'halion SAY_PHASE_3'), +(-1724033,'The orbining spheres pulse with dark energy!',0,3,0,0,'halion EMOTE_SPHERES'), +(-1724034,'Your efforts force %s further out of the twillight realm!',0,3,0,0,'halion EMOTE_OUT_OF_TWILLIGHT'), +(-1724035,'Your efforts force %s further out of the physical realm!',0,3,0,0,'halion EMOTE_OUT_OF_PHYSICAL'), +(-1724036,'Your companions\' efforts force Halion further into the twillight realm!',0,3,0,0,'halion EMOTE_INTO_TWILLIGHT'), +(-1724037,'Your companions\' efforts force Halion further into the physical realm!',0,3,0,0,'halion EMOTE_INTO_PHYSICAL'), +(-1724038,'Without pressure in both realms %s begins to regenerate.',0,3,0,0,'halion EMOTE_REGENERATE'); + +-- -1 999 900 EXAMPLE TEXT +INSERT INTO script_texts (entry,content_default,sound,type,language,emote,comment) VALUES +(-1999900,'Let the games begin.',8280,1,0,0,'example_creature SAY_AGGRO'), +(-1999901,'I see endless suffering. I see torment. I see rage. I see everything.',8831,1,0,0,'example_creature SAY_RANDOM_0'), +(-1999902,'Muahahahaha',8818,1,0,0,'example_creature SAY_RANDOM_1'), +(-1999903,'These mortal infedels my lord, they have invaded your sanctum and seek to steal your secrets.',8041,1,0,0,'example_creature SAY_RANDOM_2'), +(-1999904,'You are already dead.',8581,1,0,0,'example_creature SAY_RANDOM_3'), +(-1999905,'Where to go? What to do? So many choices that all end in pain, end in death.',8791,1,0,0,'example_creature SAY_RANDOM_4'), +(-1999906,'$N, I sentance you to death!',0,1,0,0,'example_creature SAY_BESERK'), +(-1999907,'The suffering has just begun!',0,1,0,0,'example_creature SAY_PHASE'), +(-1999908,'I always thought I was a good dancer.',0,0,0,0,'example_creature SAY_DANCE'), +(-1999909,'Move out Soldier!',0,0,0,0,'example_creature SAY_SALUTE'), + +(-1999910,'Help $N! I\'m under attack!',0,0,0,0,'example_escort SAY_AGGRO1'), +(-1999911,'Die scum!',0,0,0,0,'example_escort SAY_AGGRO2'), +(-1999912,'Hmm a nice day for a walk alright',0,0,0,0,'example_escort SAY_WP_1'), +(-1999913,'Wild Felboar attack!',0,0,0,0,'example_escort SAY_WP_2'), +(-1999914,'Time for me to go! See ya around $N!',0,0,0,3,'example_escort SAY_WP_3'), +(-1999915,'Bye Bye!',0,0,0,3,'example_escort SAY_WP_4'), +(-1999916,'How dare you leave me like that! I hate you! =*(',0,3,0,0,'example_escort SAY_DEATH_1'), +(-1999917,'...no...how could you let me die $N',0,0,0,0,'example_escort SAY_DEATH_2'), +(-1999918,'ugh...',0,0,0,0,'example_escort SAY_DEATH_3'), +(-1999919,'Taste death!',0,0,0,0,'example_escort SAY_SPELL'), +(-1999920,'Fireworks!',0,0,0,0,'example_escort SAY_RAND_1'), +(-1999921,'Hmm, I think I could use a buff.',0,0,0,0,'example_escort SAY_RAND_2'), + +(-1999922,'Normal select, guess you\'re not interested.',0,0,0,0,'example_gossip_codebox SAY_NOT_INTERESTED'), +(-1999923,'Wrong!',0,0,0,0,'example_gossip_codebox SAY_WRONG'), +(-1999924,'You\'re right, you are allowed to see my inner secrets.',0,0,0,0,'example_gossip_codebox SAY_CORRECT'), + +(-1999925,'Hi!',0,0,0,0,'example_areatrigger SAY_HI'); + +-- +-- GOSSIP TEXTS +-- + +-- +-- Below contains data for table `gossip_texts` +-- valid entries for table are between -3000000 and -3999999 +-- + +TRUNCATE gossip_texts; + +-- -3 000 000 RESERVED (up to 100) +INSERT INTO gossip_texts (entry,content_default,comment) VALUES +(-3000000,'[PH] SD2 unknown text','GOSSIP_ID_UNKNOWN_TEXT'); + +-- -3 000 100 GENERAL MAPS (not instance maps) +INSERT INTO gossip_texts (entry,content_default,comment) VALUES +(-3000101,'Taruk send me to collect what you owe.','silvermoon harry GOSSIP_ITEM_GAMBLING_DEBT'), +(-3000102,'Pay up, Harry!','silvermoon harry GOSSIP_ITEM_PAYING'), +(-3000103,'I am ready to travel to you village now.','rainspeaker GOSSIP_ITEM_READY'), +(-3000104,'','mosswalker victim GOSSIP_ITEM_PULSE'), +(-3000105,'Ezekiel said that you might have a certain book...','dirty larry GOSSIP_ITEM_BOOK'), +(-3000106,'Let Marshal Windsor know that I am ready.','squire rowe GOSSIP_ITEM_WINDSOR'), +(-3000107,'I am ready, as are my forces. Let us end this masquerade!','reginald windsor GOSSIP_ITEM_START'), +(-3000108,'I need a moment of your time, sir.','prospector anvilward GOSSIP_ITEM_MOMENT'), +(-3000109,'I am ready, Oronok. Let us destroy Cyrukh and free the elements!','oronok torn-heart GOSSIP_ITEM_FIGHT'), +(-3000110,'Why... yes, of course. I\'ve something to show you right inside this building, Mr. Anvilward.','prospector anvilward GOSSIP_ITEM_SHOW'); + +-- -3 033 000 SHADOWFANG KEEP +INSERT INTO gossip_texts (entry,content_default,comment) VALUES +(-3033000,'Please unlock the courtyard door.','deathstalker adamant/ sorcerer ashcrombe - GOSSIP_ITEM_DOOR'); + +-- -3 043 000 WAILING CAVERNS +INSERT INTO gossip_texts (entry,content_default,comment) VALUES +(-3043000,'Let the event begin!','Disciple of Naralex - GOSSIP_ITEM_BEGIN'); + +-- -3 090 000 GNOMEREGAN +INSERT INTO gossip_texts (entry,content_default,comment) VALUES +(-3090000,'I am ready to begin.','emi shortfuse GOSSIP_ITEM_START'); + +-- -3 230 000 BLACKROCK DEPTHS +INSERT INTO gossip_texts (entry,content_default,comment) VALUES +(-3230000,'You\'re free, Dughal! Get out of here!','dughal GOSSIP_ITEM_DUGHAL'), +(-3230001,'Get out of here, Tobias, you\'re free!','tobias GOSSIP_ITEM_TOBIAS'), +(-3230002,'Your bondage is at an end, Doom\'rel. I challenge you!','doomrel GOSSIP_ITEM_CHALLENGE'); + +-- -3 409 000 MOLTEN CORE +INSERT INTO gossip_texts (entry,content_default,comment) VALUES +(-3409000,'Tell me more.','majordomo_executus GOSSIP_ITEM_SUMMON_1'), +(-3409001,'What else do you have to say?','majordomo_executus GOSSIP_ITEM_SUMMON_2'), +(-3409002,'You challenged us and we have come. Where is this master you speak of?','majordomo_executus GOSSIP_ITEM_SUMMON_3'); + +-- -3 469 000 BLACKWING LAIR +INSERT INTO gossip_texts (entry,content_default,comment) VALUES +(-3469000,'I\'ve made no mistakes.','victor_nefarius GOSSIP_ITEM_NEFARIUS_1'), +(-3469001,'You have lost your mind, Nefarius. You speak in riddles.','victor_nefarius GOSSIP_ITEM_NEFARIUS_2'), +(-3469002,'Please do.','victor_nefarius GOSSIP_ITEM_NEFARIUS_3'), + +(-3469003,'I cannot, Vaelastrasz! Surely something can be done to heal you!','vaelastrasz GOSSIP_ITEM_VAEL_1'), +(-3469004,'Vaelastrasz, no!!!','vaelastrasz GOSSIP_ITEM_VAEL_2'); + +-- -3 509 000 RUINS OF AHN'QIRAJ +INSERT INTO gossip_texts (entry,content_default,comment) VALUES +(-3509000,'Let\'s find out.','andorov GOSSIP_ITEM_START'), +(-3509001,'Let\'s see what you have.','andorov GOSSIP_ITEM_TRADE'); + +-- -3 532 000 KARAZHAN +INSERT INTO gossip_texts (entry,content_default,comment) VALUES +(-3532000,'Teleport me to the Guardian\'s Library','berthold GOSSIP_ITEM_TELEPORT'), +(-3532001,'I\'m not an actor.','barnes GOSSIP_ITEM_OPERA_1'), +(-3532002,'Ok, I\'ll give it a try, then.','barnes GOSSIP_ITEM_OPERA_2'), +(-3532003,'I\'ve never been more ready.','barnes GOSSIP_ITEM_OPERA_JULIANNE_WIPE'), +(-3532004,'The wolf\'s going down.','barnes GOSSIP_ITEM_OPERA_WOLF_WIPE'), +(-3532005,'What phat lewtz you have grandmother?','grandma GOSSIP_ITEM_GRANDMA'), + +(-3532006,'Control Orc Grunt','orc grunt GOSSIP_ITEM_ORC_GRUNT'), +(-3532007,'Control Orc Wolf','orc wolf GOSSIP_ITEM_ORC_WOLF'), +(-3532008,'Control Summoned Daemon','summoned deamon GOSSIP_ITEM_SUMMONED_DEAMON'), +(-3532009,'Control Orc Warlock','orc warlock GOSSIP_ITEM_ORC_WARLOCK'), +(-3532010,'Control Orc Necrolyte','orc necrolyte GOSSIP_ITEM_ORC_NECROLYTE'), +(-3532011,'Control Warchief Blackhand','warchief blackhand GOSSIP_ITEM_WARCHIEF_BLACKHAND'), +(-3532012,'Control Human Footman','human footman GOSSIP_ITEM_HUMAN_FOOTMAN'), +(-3532013,'Control Human Charger','human charger GOSSIP_ITEM_HUMAN_CHARGER'), +(-3532014,'Control Conjured Water Elemental','conjured water elemental GOSSIP_ITEM_WATER_ELEMENTAL'), +(-3532015,'Control Human Conjurer','human conjurer GOSSIP_ITEM_HUMAN_CONJURER'), +(-3532016,'Control Human Cleric','human cleric GOSSIP_ITEM_HUMAN_CLERIC'), +(-3532017,'Control King Llane','king llane GOSSIP_ITEM_KING_LLANE'), +(-3532018,'Please reset the chess board, we would like to play again.','medivh GOSSIP_ITEM_RESET_BOARD'); + +-- -3 534 000 THE BATTLE OF MT. HYJAL +INSERT INTO gossip_texts (entry,content_default,comment) VALUES +(-3534000,'My companions and I are with you, Lady Proudmoore.','jaina GOSSIP_ITEM_JAIN_START'), +(-3534001,'We are ready for whatever Archimonde might send our way, Lady Proudmoore.','jaina GOSSIP_ITEM_ANATHERON'), +(-3534002,'Until we meet again, Lady Proudmoore.','jaina GOSSIP_ITEM_SUCCESS'), +(-3534003,'I am with you, Thrall.','thrall GOSSIP_ITEM_THRALL_START'), +(-3534004,'We have nothing to fear.','thrall GOSSIP_ITEM_AZGALOR'), +(-3534005,'Until we meet again, Thrall.','thrall GOSSIP_ITEM_SUCCESS'), +(-3534006,'I would be grateful for any aid you can provide, Priestess.','tyrande GOSSIP_ITEM_AID'); + +-- -3 560 000 ESCAPE FROM DURNHOLDE (OLD HILLSBRAD) +INSERT INTO gossip_texts (entry,content_default,comment) VALUES +(-3560000,'We are ready to get you out of here, Thrall. Let\'s go!','thrall GOSSIP_ITEM_START'), +(-3560001,'I need a pack of Incendiary Bombs.','erozion GOSSIP_ITEM_NEED_BOMBS'), +(-3560002,'Taretha cannot see you, Thrall.','thrall GOSSIP_ITEM_SKARLOC1'), +(-3560003,'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.','thrall GOSSIP_ITEM_SKARLOC2'), +(-3560004,'We\'re ready, Thrall.','thrall GOSSIP_ITEM_TARREN_2'), +(-3560005,'Strange wizard?','taretha GOSSIP_ITEM_EPOCH1'), +(-3560006,'We\'ll get you out. Taretha. Don\'t worry. I doubt the wizard would wander too far away.','taretha GOSSIP_ITEM_EPOCH2'), +(-3560007,'Tarren Mill.','thrall GOSSIP_ITEM_TARREN_1'); + +-- -3 564 000 BLACK TEMPLE +INSERT INTO gossip_texts (entry,content_default,comment) VALUES +(-3564000,'I\'m with you, Akama.','akama(shade) GOSSIP_ITEM_START_ENCOUNTER'), +(-3564001,'I\'m ready, Akama.','akama(illidan) GOSSIP_ITEM_PREPARE'), +(-3564002,'We\'re ready to face Illidan.','akama(illidan) GOSSIP_ITEM_START_EVENT'); + +-- -3 568 000 ZUL'AMAN +INSERT INTO gossip_texts (entry,content_default,comment) VALUES +(-3568000,'Thanks for the concern, but we intend to explore Zul\'Aman.','harrison jones GOSSIP_ITEM_BEGIN'); + +-- -3 595 000 CULLING OF STRATHOLME +INSERT INTO gossip_texts (entry,content_default,comment) VALUES +(-3595000,'What do you think they\'re up to?','chromie GOSSIP_ITEM_ENTRANCE_1'), +(-3595001,'You want me to do what?','chromie GOSSIP_ITEM_ENTRANCE_2'), +(-3595002,'Very well, Chromie.','chromie GOSSIP_ITEM_ENTRANCE_3'), +(-3595003,'Why have I been sent back to this particular place and time?','chromie GOSSIP_ITEM_INN_1'), +(-3595004,'What was this decision?','chromie GOSSIP_ITEM_INN_2'), +(-3595005,'So how does the Infinite Dragonflight plan to interfere?','chromie GOSSIP_ITEM_INN_3'); + +-- -3 599 000 HALLS OF STONE +INSERT INTO gossip_texts (entry,content_default,comment) VALUES +(-3599000,'Brann, it would be our honor!','brann GOSSIP_ITEM_ID_START'), +(-3599001,'Let\'s move Brann, enough of the history lessons!','brann GOSSIP_ITEM_ID_PROGRESS'); + +-- -3 603 000 ULDUAR +INSERT INTO gossip_texts (entry,content_default,comment) VALUES +(-3603000,'Teleport to the Expedition Base Camp.','GOSSIP_ITEM_TELE_BASE_CAMP'), +(-3603001,'Teleport to the Formation Grounds.','GOSSIP_ITEM_TELE_FORMATION_GROUNDS'), +(-3603002,'Teleport to the Colossal Forge.','GOSSIP_ITEM_TELE_COLOSSAR_FORGE'), +(-3603003,'Teleport to the Scrapyard.','GOSSIP_ITEM_TELE_SCRAPYARD'), +(-3603004,'Teleport to the Antechamber of Ulduar.','GOSSIP_ITEM_TELE_ANTECHAMBER'), +(-3603005,'Teleport to the Shattered Walkway.','GOSSIP_ITEM_TELE_WALKWAY'), +(-3603006,'Teleport to the Conservatory of Life.','GOSSIP_ITEM_TELE_CONSERVATORY'), +(-3603007,'Teleport to the Spark of Imagination.','GOSSIP_ITEM_TELE_SPARK_IMAGINATION'), +(-3603008,'Teleport to the Prison of Yogg-Saron.','GOSSIP_ITEM_TELE_YOGG_SARON'); + +-- -3 608 000 VIOLET HOLD +INSERT INTO gossip_texts (entry,content_default,comment) VALUES +(-3608000,'Activate the crystals when we get in trouble, right?','sinclari GOSSIP_ITEM_INTRO'), +(-3608001,'Get your people to safety, we\'ll keep the Blue Dragonflight\'s forces at bay.','sinclari GOSSIP_ITEM_START'), +(-3608002,'I\'m not fighting, so send me in now!','sinclari GOSSIP_ITEM_TELEPORT'); + +-- -3 609 000 EBON HOLD (DK START) +INSERT INTO gossip_texts (entry,content_default,comment) VALUES +(-3609000,'I challenge you, death knight!','Death Knight Initiate GOSSIP_ITEM_ACCEPT_DUEL'), +(-3609001,'I am ready, Highlord. Let the siege of Light\'s Hope begin!','Highlord Darion Mograine GOSSIP_ITEM_READY'); + +-- -3 649 000 TRIAL OF CRUSADER +INSERT INTO gossip_texts (entry,content_default,comment) VALUES +(-3649000,'Yes. We are prepared for the challenges ahead of us.','barrett GOSSIP_ITEM_BEAST_INIT'), +(-3649001,'Bring forth the first challenge!','barrett GOSSIP_ITEM_BEAST_START'), +(-3649002,'We want another shot at those beasts!','barrett GOSSIP_ITEM_BEAST_WIPE_INIT'), +(-3649003,'What new challenge awaits us?','barrett GOSSIP_ITEM_JARAXXUS_INIT'), +(-3649004,'We\'re ready to fight the sorceror again.','barrett GOSSIP_ITEM_JARAXXUS_WIPE_INIT'), +(-3649005,'Of course!','barrett GOSSIP_ITEM_PVP_INIT'), +(-3649006,'Give the signal! We\'re ready to go!','barrett GOSSIP_ITEM_PVP_START'), +(-3649007,'That tough, huh?','barrett GOSSIP_ITEM_TWINS_INIT'), +(-3649008,'Val\'kyr? We\'re ready for them','barrett GOSSIP_ITEM_TWINS_START'), +(-3649009,'Your words of praise are appreciated, Coliseum Master.','barrett GOSSIP_ITEM_ANUB_INIT'), +(-3649010,'That is strange...','barrett GOSSIP_ITEM_ANUB_START'); + + +-- +-- Below just for beautiful view in table, run at own desire +-- + +-- ALTER TABLE script_texts ORDER BY entry desc; +-- ALTER TABLE gossip_texts ORDER BY entry desc; + +-- +-- Below contains all waypoints used by escortAI scripts +-- Entry is entry == creature_template.entry +-- + +DELETE FROM script_waypoint WHERE entry=467; +INSERT INTO script_waypoint VALUES +(467, 0, -10508.40, 1068.00, 55.21, 0, ''), +(467, 1, -10518.30, 1074.84, 53.96, 0, ''), +(467, 2, -10534.82, 1081.92, 49.88, 0, ''), +(467, 3, -10546.51, 1084.88, 50.13, 0, ''), +(467, 4, -10555.29, 1084.45, 45.75, 0, ''), +(467, 5, -10566.57, 1083.53, 42.10, 0, ''), +(467, 6, -10575.83, 1082.34, 39.46, 0, ''), +(467, 7, -10585.67, 1081.08, 37.77, 0, ''), +(467, 8, -10600.08, 1078.19, 36.23, 0, ''), +(467, 9, -10608.69, 1076.08, 35.88, 0, ''), +(467, 10, -10621.26, 1073.00, 35.40, 0, ''), +(467, 11, -10638.12, 1060.18, 33.61, 0, ''), +(467, 12, -10655.87, 1038.99, 33.48, 0, ''), +(467, 13, -10664.68, 1030.54, 32.70, 0, ''), +(467, 14, -10708.68, 1033.86, 33.32, 0, ''), +(467, 15, -10754.43, 1017.93, 32.79, 0, ''), +(467, 16, -10802.26, 1018.01, 32.16, 0, ''), +(467, 17, -10832.60, 1009.04, 32.71, 0, ''), +(467, 18, -10866.56, 1006.51, 31.71, 0, ''), +(467, 19, -10879.98, 1005.10, 32.84, 0, ''), +(467, 20, -10892.45, 1001.32, 34.46, 0, ''), +(467, 21, -10906.14, 997.11, 36.15, 0, ''), +(467, 22, -10922.26, 1002.23, 35.74, 0, ''), +(467, 23, -10936.32, 1023.38, 36.52, 0, ''), +(467, 24, -10933.35, 1052.61, 35.85, 0, ''), +(467, 25, -10940.25, 1077.66, 36.49, 0, ''), +(467, 26, -10957.09, 1099.33, 36.83, 0, ''), +(467, 27, -10956.53, 1119.90, 36.73, 0, ''), +(467, 28, -10939.30, 1150.75, 37.42, 0, ''), +(467, 29, -10915.14, 1202.09, 36.55, 0, ''), +(467, 30, -10892.59, 1257.03, 33.37, 0, ''), +(467, 31, -10891.93, 1306.66, 35.45, 0, ''), +(467, 32, -10896.17, 1327.86, 37.77, 0, ''), +(467, 33, -10906.03, 1368.05, 40.91, 0, ''), +(467, 34, -10910.18, 1389.33, 42.62, 0, ''), +(467, 35, -10915.42, 1417.72, 42.93, 0, ''), +(467, 36, -10926.37, 1421.18, 43.04, 0, 'walk here and say'), +(467, 37, -10952.31, 1421.74, 43.40, 0, ''), +(467, 38, -10980.04, 1411.38, 42.79, 0, ''), +(467, 39, -11006.06, 1420.47, 43.26, 0, ''), +(467, 40, -11021.98, 1450.59, 43.09, 0, ''), +(467, 41, -11025.36, 1491.59, 43.15, 0, ''), +(467, 42, -11036.09, 1508.32, 43.28, 0, ''), +(467, 43, -11060.68, 1526.72, 43.19, 0, ''), +(467, 44, -11072.75, 1527.77, 43.20, 5000, 'say and quest credit'); + +DELETE FROM script_waypoint WHERE entry=1978; +INSERT INTO script_waypoint VALUES +(1978, 0, 1406.32, 1083.10, 52.55, 0, ''), +(1978, 1, 1400.49, 1080.42, 52.50, 0, 'SAY_START_2'), +(1978, 2, 1388.48, 1083.10, 52.52, 0, ''), +(1978, 3, 1370.16, 1084.02, 52.30, 0, ''), +(1978, 4, 1359.02, 1080.85, 52.46, 0, ''), +(1978, 5, 1341.43, 1087.39, 52.69, 0, ''), +(1978, 6, 1321.93, 1090.51, 50.66, 0, ''), +(1978, 7, 1312.98, 1095.91, 47.49, 0, ''), +(1978, 8, 1301.09, 1102.94, 47.76, 0, ''), +(1978, 9, 1297.73, 1106.35, 50.18, 0, ''), +(1978, 10, 1295.49, 1124.32, 50.49, 0, ''), +(1978, 11, 1294.84, 1137.25, 51.75, 0, ''), +(1978, 12, 1292.89, 1158.99, 52.65, 0, ''), +(1978, 13, 1290.75, 1168.67, 52.56, 2000, 'quest complete SAY_END'), +(1978, 14, 1287.12, 1203.49, 52.66, 5000, 'SAY_RANE'), +(1978, 15, 1288.30, 1203.89, 52.68, 5000, 'SAY_RANE_REPLY'), +(1978, 16, 1288.30, 1203.89, 52.68, 5000, 'SAY_CHECK_NEXT'), +(1978, 17, 1290.72, 1207.44, 52.69, 0, ''), +(1978, 18, 1297.50, 1207.18, 53.74, 0, ''), +(1978, 19, 1301.32, 1220.90, 53.74, 0, ''), +(1978, 20, 1298.55, 1220.43, 53.74, 0, ''), +(1978, 21, 1297.38, 1212.87, 58.51, 0, ''), +(1978, 22, 1297.80, 1210.04, 58.51, 0, ''), +(1978, 23, 1305.01, 1206.10, 58.51, 0, ''), +(1978, 24, 1310.51, 1207.36, 58.51, 5000, 'SAY_QUINN'), +(1978, 25, 1312.59, 1207.21, 58.51, 5000, 'SAY_QUINN_REPLY'), +(1978, 26, 1312.59, 1207.21, 58.51, 30000, 'SAY_BYE'); + +DELETE FROM script_waypoint WHERE entry=2768; +INSERT INTO script_waypoint VALUES +(2768, 0, -2077.73, -2091.17, 9.49, 0, ''), +(2768, 1, -2077.99, -2105.33, 13.24, 0, ''), +(2768, 2, -2074.60, -2109.67, 14.24, 0, ''), +(2768, 3, -2076.60, -2117.46, 16.67, 0, ''), +(2768, 4, -2073.51, -2123.46, 18.42, 2000, ''), +(2768, 5, -2073.51, -2123.46, 18.42, 4000, ''), +(2768, 6, -2066.60, -2131.85, 21.56, 0, ''), +(2768, 7, -2053.85, -2143.19, 20.31, 0, ''), +(2768, 8, -2043.49, -2153.73, 20.20, 10000, ''), +(2768, 9, -2043.49, -2153.73, 20.20, 20000, ''), +(2768, 10, -2043.49, -2153.73, 20.20, 10000, ''), +(2768, 11, -2043.49, -2153.73, 20.20, 2000, ''), +(2768, 12, -2053.85, -2143.19, 20.31, 0, ''), +(2768, 13, -2066.60, -2131.85, 21.56, 0, ''), +(2768, 14, -2073.51, -2123.46, 18.42, 0, ''), +(2768, 15, -2076.60, -2117.46, 16.67, 0, ''), +(2768, 16, -2074.60, -2109.67, 14.24, 0, ''), +(2768, 17, -2077.99, -2105.33, 13.24, 0, ''), +(2768, 18, -2077.73, -2091.17, 9.49, 0, ''), +(2768, 19, -2066.41, -2086.21, 8.97, 6000, ''), +(2768, 20, -2066.41, -2086.21, 8.97, 2000, ''); + +DELETE FROM script_waypoint WHERE entry=2917; +INSERT INTO script_waypoint VALUES +(2917, 0, 4675.812500, 598.614563, 17.645658, 0, 'SAY_REM_START'), +(2917, 1, 4672.844238, 599.325378, 16.417622, 0, ''), +(2917, 2, 4663.449707, 607.430176, 10.494752, 0, ''), +(2917, 3, 4655.969238, 613.761353, 8.523270, 0, ''), +(2917, 4, 4640.804688, 623.999329, 8.377054, 0, ''), +(2917, 5, 4631.678711, 630.801086, 6.414999, 5000, 'SAY_REM_RAMP1_1'), +(2917, 6, 4633.533203, 632.476440, 6.509831, 0, 'ambush'), +(2917, 7, 4639.413574, 637.120789, 13.338119, 0, ''), +(2917, 8, 4642.352051, 637.668152, 13.437444, 0, ''), +(2917, 9, 4645.082031, 634.463989, 13.437208, 5000, 'SAY_REM_RAMP1_2'), +(2917, 10, 4642.345215, 637.584839, 13.435211, 0, ''), +(2917, 11, 4639.630859, 637.233765, 13.339752, 0, ''), +(2917, 12, 4633.363281, 632.462280, 6.488438, 0, ''), +(2917, 13, 4624.714844, 631.723511, 6.264030, 0, ''), +(2917, 14, 4623.525879, 629.718506, 6.201339, 5000, 'SAY_REM_BOOK'), +(2917, 15, 4623.452148, 630.369629, 6.218942, 0, 'SAY_REM_TENT1_1'), +(2917, 16, 4622.622070, 637.221558, 6.312845, 0, 'ambush'), +(2917, 17, 4619.755371, 637.386230, 6.312050, 5000, 'SAY_REM_TENT1_2'), +(2917, 18, 4620.027832, 637.367676, 6.312050, 0, ''), +(2917, 19, 4624.154785, 637.560303, 6.313898, 0, ''), +(2917, 20, 4622.967773, 634.016479, 6.294979, 0, ''), +(2917, 21, 4616.926758, 630.303284, 6.239193, 0, ''), +(2917, 22, 4614.546387, 616.983337, 5.687642, 0, ''), +(2917, 23, 4610.279297, 610.029419, 5.442539, 0, ''), +(2917, 24, 4601.149902, 604.111694, 2.054856, 0, ''), +(2917, 25, 4589.618164, 597.685730, 1.057147, 0, ''), +(2917, 26, 4577.588379, 592.145813, 1.120190, 0, 'SAY_REM_MOSS (?)'), +(2917, 27, 4569.848145, 592.177490, 1.260874, 5000, 'EMOTE_REM_MOSS (?)'), +(2917, 28, 4568.791992, 590.870911, 1.211338, 3000, 'SAY_REM_MOSS_PROGRESS (?)'), +(2917, 29, 4566.722656, 564.077881, 1.343084, 0, 'ambush'), +(2917, 30, 4568.269531, 551.958435, 5.004200, 0, ''), +(2917, 31, 4566.731934, 551.557861, 5.426314, 5000, 'SAY_REM_PROGRESS'), +(2917, 32, 4566.741699, 560.767639, 1.703257, 0, ''), +(2917, 33, 4573.916016, 582.566101, 0.749801, 0, ''), +(2917, 34, 4594.206055, 598.533020, 1.034056, 0, ''), +(2917, 35, 4601.194824, 604.283081, 2.060146, 0, ''), +(2917, 36, 4609.539551, 610.844727, 5.402220, 0, ''), +(2917, 37, 4624.800293, 618.076477, 5.851541, 0, ''), +(2917, 38, 4632.414063, 623.778442, 7.286243, 0, ''), +(2917, 39, 4645.915039, 621.983765, 8.579967, 0, ''), +(2917, 40, 4658.669922, 611.092651, 8.891747, 0, ''), +(2917, 41, 4671.924316, 599.752197, 16.01242, 5000, 'SAY_REM_REMEMBER'), +(2917, 42, 4676.976074, 600.649780, 17.82566, 5000, 'EMOTE_REM_END'); + +DELETE FROM script_waypoint WHERE entry=3439; +INSERT INTO script_waypoint VALUES +(3439, 0, 1105.090332, -3101.254150, 82.706, 1000, 'SAY_STARTUP1'), +(3439, 1, 1103.204468, -3104.345215, 83.113, 1000, ''), +(3439, 2, 1107.815186, -3106.495361, 82.739, 1000, ''), +(3439, 3, 1104.733276, -3100.830811, 82.747, 1000, ''), +(3439, 4, 1103.242554, -3106.270020, 83.133, 1000, ''), +(3439, 5, 1112.807373, -3106.285400, 82.320, 1000, ''), +(3439, 6, 1112.826782, -3108.908691, 82.377, 1000, ''), +(3439, 7, 1108.053955, -3115.156738, 82.894, 0, ''), +(3439, 8, 1108.355591, -3104.365234, 82.377, 5000, ''), +(3439, 9, 1100.306763, -3097.539063, 83.150, 0, 'SAY_STARTUP2'), +(3439, 10, 1100.562378, -3082.721924, 82.768, 0, ''), +(3439, 11, 1097.512939, -3069.226563, 82.206, 0, ''), +(3439, 12, 1092.964966, -3053.114746, 82.351, 0, ''), +(3439, 13, 1094.010986, -3036.958496, 82.888, 0, ''), +(3439, 14, 1095.623901, -3025.760254, 83.392, 0, ''), +(3439, 15, 1107.656494, -3013.530518, 85.653, 0, ''), +(3439, 16, 1119.647705, -3006.928223, 87.019, 0, ''), +(3439, 17, 1129.991211, -3002.410645, 91.232, 7000, 'SAY_MERCENARY'), +(3439, 18, 1133.328735, -2997.710693, 91.675, 1000, 'SAY_PROGRESS_1'), +(3439, 19, 1131.799316, -2987.948242, 91.976, 1000, ''), +(3439, 20, 1122.028687, -2993.397461, 91.536, 0, ''), +(3439, 21, 1116.614868, -2981.916748, 92.103, 0, ''), +(3439, 22, 1102.239136, -2994.245117, 92.074, 0, ''), +(3439, 23, 1096.366211, -2978.306885, 91.873, 0, ''), +(3439, 24, 1091.971558, -2985.919189, 91.730, 40000, 'SAY_PROGRESS_2'); + +DELETE FROM script_waypoint WHERE entry=3465; +INSERT INTO script_waypoint VALUES +(3465, 0, -2095.840820, -3650.001221, 61.716, 0, ''), +(3465, 1, -2100.193604, -3613.949219, 61.604, 0, ''), +(3465, 2, -2098.549561, -3601.557129, 59.154, 0, ''), +(3465, 3, -2093.796387, -3595.234375, 56.658, 0, ''), +(3465, 4, -2072.575928, -3578.827637, 48.844, 0, ''), +(3465, 5, -2023.858398, -3568.146240, 24.636, 0, ''), +(3465, 6, -2013.576416, -3571.499756, 22.203, 0, ''), +(3465, 7, -2009.813721, -3580.547852, 21.791, 0, ''), +(3465, 8, -2015.296021, -3597.387695, 21.760, 0, ''), +(3465, 9, -2020.677368, -3610.296143, 21.759, 0, ''), +(3465, 10, -2019.990845, -3640.155273, 21.759, 0, ''), +(3465, 11, -2016.110596, -3664.133301, 21.758, 0, ''), +(3465, 12, -1999.397095, -3679.435059, 21.316, 0, ''), +(3465, 13, -1987.455811, -3688.309326, 18.495, 0, ''), +(3465, 14, -1973.966553, -3687.666748, 14.996, 0, ''), +(3465, 15, -1949.163940, -3678.054932, 11.293, 0, ''), +(3465, 16, -1934.091187, -3682.859619, 9.897, 30000, 'SAY_GIL_AT_LAST'), +(3465, 17, -1935.383911, -3682.322021, 10.029, 1500, 'SAY_GIL_PROCEED'), +(3465, 18, -1879.039185, -3699.498047, 6.582, 7500, 'SAY_GIL_FREEBOOTERS'), +(3465, 19, -1852.728149, -3703.778809, 6.875, 0, ''), +(3465, 20, -1812.989990, -3718.500732, 10.572, 0, ''), +(3465, 21, -1788.171265, -3722.867188, 9.663, 0, ''), +(3465, 22, -1767.206665, -3739.923096, 10.082, 0, ''), +(3465, 23, -1750.194580, -3747.392090, 10.390, 0, ''), +(3465, 24, -1729.335571, -3776.665527, 11.779, 0, ''), +(3465, 25, -1715.997925, -3802.404541, 12.618, 0, ''), +(3465, 26, -1690.711548, -3829.262451, 13.905, 0, ''), +(3465, 27, -1674.700684, -3842.398682, 13.872, 0, ''), +(3465, 28, -1632.726318, -3846.109619, 14.401, 0, ''), +(3465, 29, -1592.734497, -3842.225342, 14.981, 0, ''), +(3465, 30, -1561.614746, -3839.320801, 19.118, 0, ''), +(3465, 31, -1544.567627, -3834.393311, 18.761, 0, ''), +(3465, 32, -1512.514404, -3831.715820, 22.914, 0, ''), +(3465, 33, -1486.889771, -3836.639893, 23.964, 0, ''), +(3465, 34, -1434.193604, -3852.702881, 18.843, 0, ''), +(3465, 35, -1405.794678, -3854.488037, 17.276, 0, ''), +(3465, 36, -1366.592041, -3852.383789, 19.273, 0, ''), +(3465, 37, -1337.360962, -3837.827148, 17.352, 2000, 'SAY_GIL_ALMOST'), +(3465, 38, -1299.744507, -3810.691406, 20.801, 0, ''), +(3465, 39, -1277.144409, -3782.785156, 25.918, 0, ''), +(3465, 40, -1263.686768, -3781.251953, 26.447, 0, ''), +(3465, 41, -1243.674438, -3786.328125, 25.281, 0, ''), +(3465, 42, -1221.875488, -3784.124512, 24.051, 0, ''), +(3465, 43, -1204.011230, -3775.943848, 24.437, 0, ''), +(3465, 44, -1181.706787, -3768.934082, 23.368, 0, ''), +(3465, 45, -1156.913818, -3751.559326, 21.074, 0, ''), +(3465, 46, -1138.830688, -3741.809326, 17.843, 0, ''), +(3465, 47, -1080.101196, -3738.780029, 19.805, 0, 'SAY_GIL_SWEET'), +(3465, 48, -1069.065186, -3735.006348, 19.302, 0, ''), +(3465, 49, -1061.941040, -3724.062256, 21.086, 0, ''), +(3465, 50, -1053.593262, -3697.608643, 27.320, 0, ''), +(3465, 51, -1044.110474, -3690.133301, 24.856, 0, ''), +(3465, 52, -1040.260986, -3690.739014, 25.342, 0, ''), +(3465, 53, -1028.146606, -3688.718750, 23.843, 7500, 'SAY_GIL_FREED'); + +DELETE FROM script_waypoint WHERE entry=3849; +INSERT INTO script_waypoint VALUES +(3849, 0, -250.923, 2116.26, 81.179, 0, 'SAY_FREE_AD'), +(3849, 1, -255.049, 2119.39, 81.179, 0, ''), +(3849, 2, -254.129, 2123.45, 81.179, 0, ''), +(3849, 3, -253.898, 2130.87, 81.179, 0, ''), +(3849, 4, -249.889, 2142.31, 86.972, 0, ''), +(3849, 5, -248.205, 2144.02, 87.013, 0, ''), +(3849, 6, -240.553, 2140.55, 87.012, 0, ''), +(3849, 7, -237.514, 2142.07, 87.012, 0, ''), +(3849, 8, -235.638, 2149.23, 90.587, 0, ''), +(3849, 9, -237.188, 2151.95, 90.624, 0, ''), +(3849, 10, -241.162, 2153.65, 90.624, 0, 'SAY_OPEN_DOOR_AD'), +(3849, 11, -241.13, 2154.56, 90.624, 2000, 'SAY_UNLOCK_DOOR_AD'), +(3849, 12, -241.13, 2154.56, 90.624, 3000, ''), +(3849, 13, -241.13, 2154.56, 90.624, 5000, 'SAY_POST1_DOOR_AD'), +(3849, 14, -241.13, 2154.56, 90.624, 0, 'SAY_POST2_DOOR_AD'), +(3849, 15, -208.764, 2141.6, 90.6257, 0, ''), +(3849, 16, -206.441, 2143.51, 90.4287, 0, ''), +(3849, 17, -203.715, 2145.85, 88.7052, 0, ''), +(3849, 18, -199.199, 2144.88, 86.501, 0, ''), +(3849, 19, -195.798, 2143.58, 86.501, 0, ''), +(3849, 20, -190.029, 2141.38, 83.2712, 0, ''), +(3849, 21, -189.353, 2138.65, 83.1102, 0, ''), +(3849, 22, -190.304, 2135.73, 81.5288, 0, ''), +(3849, 23, -207.325, 2112.43, 81.0548, 0, ''), +(3849, 24, -208.754, 2109.9, 81.0527, 0, ''), +(3849, 25, -206.248, 2108.62, 81.0555, 0, ''), +(3849, 26, -202.017, 2106.64, 78.6836, 0, ''), +(3849, 27, -200.928, 2104.49, 78.5569, 0, ''), +(3849, 28, -201.845, 2101.17, 76.9256, 0, ''), +(3849, 29, -202.844, 2100.11, 76.8911, 0, ''), +(3849, 30, -213.326, 2105.83, 76.8925, 0, ''), +(3849, 31, -226.993, 2111.47, 76.8892, 0, ''), +(3849, 32, -227.955, 2112.34, 76.8895, 0, ''), +(3849, 33, -230.05, 2106.64, 76.8895, 0, ''); + +DELETE FROM script_waypoint WHERE entry=3850; +INSERT INTO script_waypoint VALUES +(3850, 0, -241.817, 2122.9, 81.179, 0, 'SAY_FREE_AS'), +(3850, 1, -247.139, 2124.89, 81.179, 0, ''), +(3850, 2, -253.179, 2127.41, 81.179, 0, ''), +(3850, 3, -253.898, 2130.87, 81.179, 0, ''), +(3850, 4, -249.889, 2142.31, 86.972, 0, ''), +(3850, 5, -248.205, 2144.02, 87.013, 0, ''), +(3850, 6, -240.553, 2140.55, 87.012, 0, ''), +(3850, 7, -237.514, 2142.07, 87.012, 0, ''), +(3850, 8, -235.638, 2149.23, 90.587, 0, ''), +(3850, 9, -237.188, 2151.95, 90.624, 0, ''), +(3850, 10, -241.162, 2153.65, 90.624, 0, 'SAY_OPEN_DOOR_AS'), +(3850, 11, -241.13, 2154.56, 90.624, 5000, 'cast'), +(3850, 12, -241.13, 2154.56, 90.624, 0, ''), +(3850, 13, -241.13, 2154.56, 90.624, 5000, 'SAY_POST_DOOR_AS'), +(3850, 14, -241.13, 2154.56, 90.624, 2500, 'cast'), +(3850, 15, -241.13, 2154.56, 90.624, 0, 'SAY_VANISH_AS'); + +DELETE FROM script_waypoint WHERE entry=4500; +INSERT INTO script_waypoint VALUES +(4500, 0, -3125.597168, -2885.673828, 34.731, 2500, ''), +(4500, 1, -3120.257080, -2877.830322, 34.917, 0, ''), +(4500, 2, -3116.487305, -2850.670410, 34.869, 0, ''), +(4500, 3, -3093.474854, -2819.189697, 34.432, 0, ''), +(4500, 4, -3104.726318, -2802.020996, 33.954, 0, ''), +(4500, 5, -3105.906006, -2780.234375, 34.469, 0, ''), +(4500, 6, -3116.080811, -2757.902588, 34.734, 0, ''), +(4500, 7, -3125.234375, -2733.960205, 33.189, 0, ''); + +DELETE FROM script_waypoint WHERE entry=4962; +INSERT INTO script_waypoint VALUES +(4962, 0, -3804.438965, -828.048035, 10.093068, 0, ''), +(4962, 1, -3803.934326, -835.772400, 10.077722, 0, ''), +(4962, 2, -3792.629150, -835.670898, 9.655657, 0, ''), +(4962, 3, -3772.433838, -835.345947, 10.868981, 0, ''), +(4962, 4, -3765.937256, -840.128601, 10.885593, 0, ''), +(4962, 5, -3738.633789, -830.997498, 11.057384, 0, ''), +(4962, 6, -3690.224121, -862.261597, 9.960449, 0, ''); + +DELETE FROM script_waypoint WHERE entry=4983; +INSERT INTO script_waypoint VALUES +(4983, 0, -3322.649414, -3124.631836, 33.842, 0, ''), +(4983, 1, -3326.336670, -3126.833496, 34.426, 0, ''), +(4983, 2, -3336.984131, -3129.611816, 30.692, 0, ''), +(4983, 3, -3342.598389, -3132.146729, 30.422, 0, ''), +(4983, 4, -3355.827881, -3140.947998, 29.534, 0, ''), +(4983, 5, -3365.828125, -3144.284180, 35.176, 0, ''), +(4983, 6, -3368.904541, -3147.265381, 36.091, 0, ''), +(4983, 7, -3369.355957, -3169.828857, 36.325, 0, ''), +(4983, 8, -3371.443359, -3183.905029, 33.454, 0, ''), +(4983, 9, -3373.824951, -3190.861084, 34.717, 5000, 'SAY_OGR_SPOT'), +(4983, 10, -3368.529785, -3198.210205, 34.926, 0, 'SAY_OGR_RET_WHAT'), +(4983, 11, -3366.265625, -3210.867676, 33.733, 5000, 'pause'), +(4983, 12, -3368.529785, -3198.210205, 34.926, 0, ''), +(4983, 13, -3373.824951, -3190.861084, 34.717, 0, ''), +(4983, 14, -3371.443359, -3183.905029, 33.454, 0, ''), +(4983, 15, -3369.355957, -3169.828857, 36.325, 0, ''), +(4983, 16, -3368.904541, -3147.265381, 36.091, 0, ''), +(4983, 17, -3365.828125, -3144.284180, 35.176, 0, ''), +(4983, 18, -3355.827881, -3140.947998, 29.534, 0, ''), +(4983, 19, -3342.598389, -3132.146729, 30.422, 0, ''), +(4983, 20, -3336.984131, -3129.611816, 30.692, 0, ''), +(4983, 21, -3326.336670, -3126.833496, 34.426, 0, ''), +(4983, 22, -3322.649414, -3124.631836, 33.842, 0, ''); + +DELETE FROM script_waypoint WHERE entry = 5391; +INSERT INTO script_waypoint VALUES +(5391, 0, -9901.12, -3727.29, 22.11, 3000, ''), +(5391, 1, -9909.27, -3727.81, 23.25, 0, ''), +(5391, 2, -9935.25, -3729.02, 22.11, 0, ''), +(5391, 3, -9945.83, -3719.34, 21.68, 0, ''), +(5391, 4, -9963.41, -3710.18, 21.71, 0, ''), +(5391, 5, -9972.75, -3690.13, 21.68, 0, ''), +(5391, 6, -9989.70, -3669.67, 21.67, 0, ''), +(5391, 7, -9989.21, -3647.76, 23.00, 0, ''), +(5391, 8, -9992.27, -3633.74, 21.67, 0, ''), +(5391, 9,-10002.32, -3611.67, 22.26, 0, ''), +(5391,10, -9999.25, -3586.33, 21.85, 0, ''), +(5391,11,-10006.53, -3571.99, 21.67, 0, ''), +(5391,12,-10014.30, -3545.24, 21.67, 0, ''), +(5391,13,-10018.91, -3525.03, 21.68, 0, ''), +(5391,14,-10030.22, -3514.77, 21.67, 0, ''), +(5391,15,-10045.11, -3501.49, 21.67, 0, ''), +(5391,16,-10052.91, -3479.13, 21.67, 0, ''), +(5391,17,-10060.68, -3460.31, 21.67, 0, ''), +(5391,18,-10074.68, -3436.85, 20.97, 0, ''), +(5391,19,-10074.68, -3436.85, 20.97, 0, ''), +(5391,20,-10072.86, -3408.92, 20.43, 15000, ''), +(5391,21,-10108.01, -3406.05, 22.06, 0, ''); + +DELETE FROM script_waypoint WHERE entry=6182; +INSERT INTO script_waypoint VALUES +(6182, 0, -11480.684570, 1545.091187, 49.898571, 0, ''), +(6182, 1, -11466.825195, 1530.151733, 50.263611, 0, ''), +(6182, 2, -11465.213867, 1528.343750, 50.954369, 0, 'entrance hut'), +(6182, 3, -11462.990234, 1525.235596, 50.937702, 0, ''), +(6182, 4, -11461.000000, 1526.614014, 50.937702, 5000, 'pick up rifle'), +(6182, 5, -11462.990234, 1525.235596, 50.937702, 0, ''), +(6182, 6, -11465.213867, 1528.343750, 50.954369, 0, ''), +(6182, 7, -11468.353516, 1535.075562, 50.400948, 15000, 'hold, prepare for wave1'), +(6182, 8, -11468.353516, 1535.075562, 50.400948, 15000, 'hold, prepare for wave2'), +(6182, 9, -11468.353516, 1535.075562, 50.400948, 10000, 'hold, prepare for wave3'), +(6182, 10, -11467.898438, 1532.459595, 50.348885, 0, 'we are done'), +(6182, 11, -11466.064453, 1529.855225, 50.209351, 0, ''), +(6182, 12, -11462.990234, 1525.235596, 50.937702, 0, ''), +(6182, 13, -11461.000000, 1526.614014, 50.937702, 5000, 'deliver rifle'), +(6182, 14, -11462.990234, 1525.235596, 50.937702, 0, ''), +(6182, 15, -11465.213867, 1528.343750, 50.954369, 0, ''), +(6182, 16, -11470.260742, 1537.276733, 50.378487, 0, ''), +(6182, 17, -11475.581055, 1548.678833, 50.184380, 0, 'complete quest'), +(6182, 18, -11482.299805, 1557.410034, 48.624519, 0, ''); + +DELETE FROM script_waypoint WHERE entry=6575; +INSERT INTO script_waypoint VALUES +(6575, 0, 1945.81, -431.54, 16.36, 0, ''), +(6575, 1, 1946.21, -436.41, 16.36, 0, ''), +(6575, 2, 1950.01, -444.11, 14.63, 0, ''), +(6575, 3, 1956.08, -449.34, 13.12, 0, ''), +(6575, 4, 1966.59, -450.55, 11.27, 0, ''), +(6575, 5, 1976.09, -447.51, 11.27, 0, ''), +(6575, 6, 1983.42, -435.85, 11.27, 0, ''), +(6575, 7, 1978.17, -428.81, 11.27, 0, ''), +(6575, 8, 1973.97, -422.08, 9.04, 0, ''), +(6575, 9, 1963.84, -418.90, 6.17, 0, ''), +(6575, 10, 1961.22, -422.74, 6.17, 0, ''), +(6575, 11, 1964.80, -431.26, 6.17, 300000, ''); + +DELETE FROM script_waypoint WHERE entry=7780; +INSERT INTO script_waypoint VALUES +(7780, 0, 261.058868, -2757.876221, 122.553, 0, ''), +(7780, 1, 259.812195, -2758.249023, 122.555, 0, 'SAY_RIN_FREE'), +(7780, 2, 253.823441, -2758.619141, 122.562, 0, ''), +(7780, 3, 241.394791, -2769.754883, 123.309, 0, ''), +(7780, 4, 218.915588, -2783.397461, 123.355, 0, ''), +(7780, 5, 209.088196, -2789.676270, 122.001, 0, ''), +(7780, 6, 204.453568, -2792.205811, 120.620, 0, ''), +(7780, 7, 182.012604, -2809.995361, 113.887, 0, 'summon'), +(7780, 8, 164.411591, -2825.162842, 107.779, 0, ''), +(7780, 9, 149.727600, -2833.704346, 106.224, 0, ''), +(7780, 10, 142.448074, -2838.807373, 109.665, 0, ''), +(7780, 11, 133.274963, -2845.135254, 112.606, 0, ''), +(7780, 12, 111.247459, -2861.065674, 116.305, 0, ''), +(7780, 13, 96.104073, -2874.886230, 114.397, 0, 'summon'), +(7780, 14, 73.369942, -2881.184570, 117.666, 0, ''), +(7780, 15, 58.579178, -2889.151611, 116.253, 0, ''), +(7780, 16, 33.214249, -2906.343994, 115.083, 0, ''), +(7780, 17, 19.586519, -2908.712402, 117.276, 7500, 'SAY_RIN_COMPLETE'), +(7780, 18, 10.282522, -2911.607422, 118.394, 0, ''), +(7780, 19, -37.580383, -2942.730225, 117.145, 0, ''), +(7780, 20, -68.599411, -2953.694824, 116.685, 0, ''), +(7780, 21, -102.054253, -2956.965576, 116.677, 0, ''), +(7780, 22, -135.993637, -2955.743652, 115.788, 0, ''), +(7780, 23, -171.561600, -2951.417480, 115.451, 0, ''); + +DELETE FROM script_waypoint WHERE entry=7784; +INSERT INTO script_waypoint VALUES +(7784, 0, -8845.65, -4373.98, 43.87, 5000, 'SAY_START'), +(7784, 1, -8840.79, -4373.73, 44.24, 0, ''), +(7784, 2, -8837.43, -4373.56, 45.60, 0, ''), +(7784, 3, -8832.74, -4373.32, 45.68, 0, ''), +(7784, 4, -8829.37, -4373.14, 44.33, 0, ''), +(7784, 5, -8817.38, -4372.41, 35.58, 0, ''), +(7784, 6, -8803.47, -4371.60, 30.34, 0, ''), +(7784, 7, -8795.10, -4365.61, 26.08, 0, ''), +(7784, 8, -8766.78, -4367.13, 25.15, 0, ''), +(7784, 9, -8755.63, -4367.54, 24.63, 0, ''), +(7784, 10, -8754.42, -4365.59, 24.15, 0, ''), +(7784, 11, -8728.82, -4353.13, 20.90, 0, ''), +(7784, 12, -8706.60, -4356.55, 17.93, 0, ''), +(7784, 13, -8679.00, -4380.23, 12.64, 0, ''), +(7784, 14, -8642.96, -4393.82, 12.52, 0, ''), +(7784, 15, -8611.19, -4399.11, 9.55, 0, ''), +(7784, 16, -8554.87, -4409.32, 13.05, 0, ''), +(7784, 17, -8531.64, -4411.96, 11.20, 0, ''), +(7784, 18, -8510.40, -4414.38, 12.84, 0, ''), +(7784, 19, -8476.92, -4418.34, 9.71, 0, ''), +(7784, 20, -8435.89, -4426.74, 9.67, 0, ''), +(7784, 21, -8381.89, -4446.40, 10.23, 0, ''), +(7784, 22, -8351.15, -4447.79, 9.99, 5000, 'first ambush SAY_AMBUSH'), +(7784, 23, -8324.18, -4445.05, 9.71, 0, ''), +(7784, 24, -8138.94, -4384.78, 10.92, 0, ''), +(7784, 25, -8036.87, -4443.38, 9.65, 0, ''), +(7784, 26, -7780.92, -4761.81, 9.50, 0, ''), +(7784, 27, -7587.67, -4765.01, 8.96, 0, ''), +(7784, 28, -7497.65, -4792.86, 10.01, 0, 'second ambush SAY_AMBUSH'), +(7784, 29, -7391.54, -4774.26, 12.44, 0, ''), +(7784, 30, -7308.42, -4739.87, 12.65, 0, ''), +(7784, 31, -7016.11, -4751.12, 10.06, 0, ''), +(7784, 32, -6985.52, -4777.41, 10.26, 0, ''), +(7784, 33, -6953.02, -4786.00, 6.32, 0, ''), +(7784, 34, -6940.37, -4831.03, 0.67, 10000, 'quest complete SAY_END'); + +DELETE FROM script_waypoint WHERE entry=7806; +INSERT INTO script_waypoint VALUES +(7806, 0, 495.404358, -3478.350830, 114.837, 0, ''), +(7806, 1, 492.704742, -3486.112549, 108.627, 0, ''), +(7806, 2, 487.249756, -3485.764404, 107.890, 0, ''), +(7806, 3, 476.851959, -3489.875977, 99.985, 0, ''), +(7806, 4, 467.212402, -3493.355469, 99.819, 0, ''), +(7806, 5, 460.017029, -3496.984375, 104.481, 0, ''), +(7806, 6, 439.619446, -3500.730225, 110.534, 0, ''), +(7806, 7, 428.326385, -3495.874756, 118.662, 0, ''), +(7806, 8, 424.664032, -3489.381592, 121.999, 0, ''), +(7806, 9, 424.137299, -3470.952637, 124.333, 0, ''), +(7806, 10, 421.791107, -3449.242676, 119.126, 0, ''), +(7806, 11, 404.247070, -3429.376953, 117.644, 0, ''), +(7806, 12, 335.465271, -3430.717773, 116.456, 0, ''), +(7806, 13, 317.160126, -3426.708984, 116.226, 0, ''), +(7806, 14, 331.180115, -3464.002197, 117.143, 0, ''), +(7806, 15, 336.393616, -3501.877441, 118.201, 0, ''), +(7806, 16, 337.251312, -3544.764648, 117.284, 0, ''), +(7806, 17, 337.748932, -3565.415527, 116.797, 0, ''), +(7806, 18, 336.010925, -3597.363037, 118.225, 0, ''), +(7806, 19, 324.619141, -3622.884033, 119.811, 0, ''), +(7806, 20, 308.027466, -3648.600098, 123.047, 0, ''), +(7806, 21, 276.325409, -3685.738525, 128.356, 0, ''), +(7806, 22, 239.981064, -3717.330811, 131.874, 0, ''), +(7806, 23, 224.950974, -3730.169678, 132.125, 0, ''), +(7806, 24, 198.707870, -3768.292725, 129.420, 0, ''), +(7806, 25, 183.758316, -3791.068848, 128.045, 0, ''), +(7806, 26, 178.110657, -3801.575439, 128.370, 3000, 'SAY_OOX_DANGER'), +(7806, 27, 162.215225, -3827.014160, 129.424, 0, ''), +(7806, 28, 141.664734, -3864.519287, 131.419, 0, ''), +(7806, 29, 135.301697, -3880.089111, 132.120, 0, ''), +(7806, 30, 122.461151, -3910.071533, 135.605, 0, ''), +(7806, 31, 103.376175, -3937.725098, 137.342, 0, ''), +(7806, 32, 81.414474, -3958.614258, 138.469, 0, ''), +(7806, 33, 55.378139, -3982.004639, 136.520, 0, ''), +(7806, 34, 13.983131, -4013.952881, 126.903, 0, ''), +(7806, 35, -21.658007, -4048.713623, 118.068, 0, ''), +(7806, 36, -52.443058, -4081.209717, 117.477, 0, ''), +(7806, 37, -102.710854, -4116.760742, 118.666, 0, ''), +(7806, 38, -92.996193, -4135.847168, 119.310, 0, ''), +(7806, 39, -86.391273, -4153.331055, 122.502, 0, ''), +(7806, 40, -85.746086, -4163.600586, 121.892, 0, ''), +(7806, 41, -90.544006, -4183.577637, 117.587, 0, ''), +(7806, 42, -110.223564, -4205.861328, 121.878, 0, ''), +(7806, 43, -115.257607, -4211.962402, 121.878, 3000, 'SAY_OOX_DANGER'), +(7806, 44, -128.594650, -4233.343750, 117.766, 0, ''), +(7806, 45, -135.358917, -4258.120117, 117.562, 0, ''), +(7806, 46, -156.832428, -4258.961914, 120.059, 0, ''), +(7806, 47, -167.119873, -4274.102539, 117.062, 0, ''), +(7806, 48, -176.291016, -4287.594727, 118.721, 0, ''), +(7806, 49, -196.992981, -4315.815430, 117.588, 0, ''), +(7806, 50, -209.329300, -4331.671387, 115.142, 0, ''), +(7806, 51, -232.292236, -4356.015625, 108.543, 0, ''), +(7806, 52, -232.159683, -4370.904297, 102.815, 0, ''), +(7806, 53, -210.271133, -4389.896973, 84.167, 0, ''), +(7806, 54, -187.940186, -4407.532715, 70.987, 0, ''), +(7806, 55, -181.353577, -4418.771973, 64.778, 0, ''), +(7806, 56, -170.529861, -4440.438965, 58.943, 0, ''), +(7806, 57, -141.428543, -4465.323242, 45.963, 0, ''), +(7806, 58, -120.993629, -4487.088379, 32.075, 0, ''), +(7806, 59, -104.134621, -4501.837402, 25.051, 0, ''), +(7806, 60, -84.154663, -4529.436523, 11.952, 0, ''), +(7806, 61, -88.698898, -4544.626465, 9.055, 0, ''), +(7806, 62, -100.603447, -4575.034180, 11.388, 0, ''), +(7806, 63, -106.908669, -4600.407715, 11.046, 0, ''), +(7806, 64, -106.831703, -4620.503418, 11.057, 3000, 'SAY_OOX_COMPLETE'); + +DELETE FROM script_waypoint WHERE entry=7807; +INSERT INTO script_waypoint VALUES +(7807, 0, -4943.74, 1715.74, 62.74, 0, 'SAY_START'), +(7807, 1, -4944.93, 1706.66, 63.16, 0, ''), +(7807, 2, -4942.82, 1690.22, 64.25, 0, ''), +(7807, 3, -4946.47, 1669.62, 63.84, 0, ''), +(7807, 4, -4955.93, 1651.88, 63.00, 0, ''), +(7807, 5, -4967.58, 1643.86, 64.31, 0, ''), +(7807, 6, -4978.12, 1607.90, 64.30, 0, ''), +(7807, 7, -4975.38, 1596.16, 64.70, 0, ''), +(7807, 8, -4972.82, 1581.89, 61.75, 0, ''), +(7807, 9, -4958.65, 1581.05, 61.81, 0, ''), +(7807, 10, -4936.72, 1594.89, 65.96, 0, ''), +(7807, 11, -4885.69, 1598.10, 67.45, 4000, 'first ambush SAY_AMBUSH'), +(7807, 12, -4874.20, 1601.73, 68.54, 0, ''), +(7807, 13, -4816.64, 1594.47, 78.20, 0, ''), +(7807, 14, -4802.20, 1571.92, 87.01, 0, ''), +(7807, 15, -4746.40, 1576.11, 84.59, 0, ''), +(7807, 16, -4739.72, 1707.16, 94.04, 0, ''), +(7807, 17, -4674.03, 1840.44, 89.17, 0, ''), +(7807, 18, -4667.94, 1864.11, 85.18, 0, ''), +(7807, 19, -4668.08, 1886.39, 81.14, 0, ''), +(7807, 20, -4679.43, 1932.32, 73.76, 0, ''), +(7807, 21, -4674.17, 1946.66, 70.83, 5000, 'second ambush SAY_AMBUSH'), +(7807, 22, -4643.94, 1967.45, 65.27, 0, ''), +(7807, 23, -4595.60, 2010.75, 52.10, 0, ''), +(7807, 24, -4562.65, 2029.28, 45.41, 0, ''), +(7807, 25, -4538.56, 2032.65, 45.28, 0, ''), +(7807, 26, -4531.96, 2034.15, 48.34, 0, ''), +(7807, 27, -4507.75, 2039.32, 51.57, 0, ''), +(7807, 28, -4482.74, 2045.67, 48.15, 0, ''), +(7807, 29, -4460.87, 2051.54, 45.55, 0, ''), +(7807, 30, -4449.97, 2060.03, 45.51, 10000, 'third ambush SAY_AMBUSH'), +(7807, 31, -4448.99, 2079.05, 44.64, 0, ''), +(7807, 32, -4436.64, 2134.48, 28.83, 0, ''), +(7807, 33, -4429.25, 2170.20, 15.44, 0, ''), +(7807, 34, -4424.83, 2186.11, 11.48, 0, ''), +(7807, 35, -4416.71, 2209.76, 7.36, 0, ''), +(7807, 36, -4405.25, 2231.77, 5.94, 0, ''), +(7807, 37, -4377.61, 2265.45, 06.71, 15000, 'complete quest SAY_END'); + +DELETE FROM script_waypoint WHERE entry=9503; +INSERT INTO script_waypoint VALUES +(9503, 0, 883.294861, -188.926300, -43.703655, 0,''), +(9503, 1, 872.763550, -185.605621, -43.703655, 5000,'b1'), +(9503, 2, 867.923401, -188.006393, -43.703655, 5000,'b2'), +(9503, 3, 863.295898, -190.795212, -43.703655, 5000,'b3'), +(9503, 4, 856.139587, -194.652756, -43.703655, 5000,'b4'), +(9503, 5, 851.878906, -196.928131, -43.703655, 15000,'b5'), +(9503, 6, 877.035217, -187.048080, -43.703655, 0,''), +(9503, 7, 891.198000, -197.924000, -43.620400, 0,'home'); + +DELETE FROM script_waypoint WHERE entry=9623; +INSERT INTO script_waypoint VALUES +(9623, 0, -6383.070801, -1964.368896, -258.709, 0, 'SAY_AME_START'), +(9623, 1, -6393.649414, -1949.572266, -261.449, 0, ''), +(9623, 2, -6397.846680, -1931.099609, -263.366, 0, ''), +(9623, 3, -6397.501953, -1921.470703, -263.876, 0, ''), +(9623, 4, -6389.630371, -1909.995361, -259.601, 0, ''), +(9623, 5, -6380.065430, -1905.452881, -255.858, 0, ''), +(9623, 6, -6373.437988, -1900.275024, -254.774, 0, ''), +(9623, 7, -6372.868652, -1893.500854, -255.678, 0, ''), +(9623, 8, -6379.730469, -1877.627808, -259.654, 0, ''), +(9623, 9, -6380.264160, -1871.139648, -260.617, 0, ''), +(9623, 10, -6373.830566, -1855.620361, -259.566, 0, ''), +(9623, 11, -6368.824707, -1847.770508, -259.246, 0, ''), +(9623, 12, -6370.902832, -1835.038940, -260.212, 0, ''), +(9623, 13, -6376.591309, -1821.592285, -260.856, 0, ''), +(9623, 14, -6381.931152, -1810.434326, -266.180, 0, ''), +(9623, 15, -6396.713867, -1807.123535, -269.329, 0, ''), +(9623, 16, -6400.266602, -1795.053589, -269.744, 0, ''), +(9623, 17, -6402.675781, -1747.514648, -272.961, 0, ''), +(9623, 18, -6396.997559, -1710.052979, -273.719, 0, ''), +(9623, 19, -6388.105957, -1676.328125, -272.133, 5000, 'SAY_AME_PROGRESS'), +(9623, 20, -6370.711914, -1638.638306, -272.031, 0, ''), +(9623, 21, -6366.709473, -1592.645996, -272.201, 0, ''), +(9623, 22, -6333.869629, -1534.598755, -270.493, 0, ''), +(9623, 23, -6305.362305, -1477.913330, -269.518, 0, ''), +(9623, 24, -6311.588867, -1419.017456, -267.622, 0, ''), +(9623, 25, -6330.014648, -1400.064331, -266.425, 0, ''), +(9623, 26, -6356.021973, -1392.607422, -267.123, 0, ''), +(9623, 27, -6370.859375, -1386.179321, -270.218, 0, ''), +(9623, 28, -6381.529785, -1369.780273, -272.110, 0, ''), +(9623, 29, -6405.381348, -1321.522827, -271.699, 0, ''), +(9623, 30, -6406.583496, -1307.574585, -271.802, 0, ''), +(9623, 31, -6386.325684, -1286.851074, -272.074, 0, ''), +(9623, 32, -6364.254883, -1264.706299, -269.075, 0, ''), +(9623, 33, -6343.636230, -1239.844360, -268.364, 0, ''), +(9623, 34, -6335.568848, -1202.449585, -271.515, 0, ''), +(9623, 35, -6325.625000, -1184.455322, -270.461, 0, ''), +(9623, 36, -6317.797363, -1177.668091, -269.792, 0, ''), +(9623, 37, -6303.024414, -1180.252686, -269.332, 0, 'SAY_AME_END'), +(9623, 38, -6301.975098, -1184.787842, -269.371, 1000, ''), +(9623, 39, -6297.575684, -1186.412964, -268.962, 5000, ''); + +DELETE FROM script_waypoint WHERE entry=10096; +INSERT INTO script_waypoint VALUES +(10096, 0, 604.802673, -191.081985, -54.058590, 0,'ring'), +(10096, 1, 604.072998, -222.106918, -52.743759, 0,'first gate'), +(10096, 2, 621.400391, -214.499054, -52.814453, 0,'hiding in corner'), +(10096, 3, 601.300781, -198.556992, -53.950256, 0,'ring'), +(10096, 4, 631.818359, -180.548126, -52.654770, 0,'second gate'), +(10096, 5, 627.390381, -201.075974, -52.692917, 0,'hiding in corner'); + +DELETE FROM script_waypoint WHERE entry=10427; +INSERT INTO script_waypoint VALUES +(10427, 0, -5185.463, -1185.927, 45.951, 0, ''), +(10427, 1, -5184.880, -1154.210, 45.035, 0, ''), +(10427, 2, -5175.880, -1126.526, 43.701, 0, ''), +(10427, 3, -5138.651, -1111.874, 44.024, 0, ''), +(10427, 4, -5134.728, -1104.796, 47.365, 0, ''), +(10427, 5, -5129.681, -1097.878, 49.449, 2500, ''), +(10427, 6, -5125.303, -1080.572, 47.033, 0, ''), +(10427, 7, -5146.668, -1053.694, 28.415, 0, ''), +(10427, 8, -5147.463, -1027.539, 13.818, 0, ''), +(10427, 9, -5139.238, -1018.889, 8.220, 0, ''), +(10427, 10, -5121.168, -1013.126, -0.619, 0, ''), +(10427, 11, -5091.919, -1014.205, -4.902, 0, ''), +(10427, 12, -5069.240, -994.299, -4.631, 0, ''), +(10427, 13, -5059.975, -944.112, -5.377, 0, ''), +(10427, 14, -5013.546, -906.184, -5.490, 0, ''), +(10427, 15, -4992.461, -920.983, -4.980, 5000, 'SAY_WYVERN'), +(10427, 16, -4976.355, -1002.997, -5.380, 0, ''), +(10427, 17, -4958.478, -1033.185, -5.433, 0, ''), +(10427, 18, -4953.353, -1052.211, -10.836, 0, ''), +(10427, 19, -4937.447, -1056.351, -22.139, 0, ''), +(10427, 20, -4908.455, -1050.433, -33.458, 0, ''), +(10427, 21, -4905.530, -1056.885, -33.722, 0, ''), +(10427, 22, -4920.830, -1073.284, -45.515, 0, ''), +(10427, 23, -4933.368, -1082.700, -50.186, 0, ''), +(10427, 24, -4935.313, -1092.353, -52.785, 0, ''), +(10427, 25, -4929.553, -1101.268, -50.637, 0, ''), +(10427, 26, -4920.679, -1100.028, -51.944, 10000, 'SAY_COMPLETE'), +(10427, 27, -4920.679, -1100.028, -51.944, 0, 'quest complete'); + +DELETE FROM script_waypoint WHERE entry=10638; +INSERT INTO script_waypoint VALUES +(10638, 0, -4903.521973, -1368.339844, -52.611, 5000, 'SAY_KAN_START'), +(10638, 1, -4906.004395, -1367.048096, -52.611, 0, ''); + +DELETE FROM script_waypoint WHERE entry=10646; +INSERT INTO script_waypoint VALUES +(10646, 0, -4792.401855, -2137.775146, 82.423, 0, ''), +(10646, 1, -4813.508301, -2141.543457, 80.774, 0, ''), +(10646, 2, -4828.630859, -2154.309814, 82.074, 0, ''), +(10646, 3, -4833.772949, -2149.182617, 81.676, 0, ''), +(10646, 4, -4846.418945, -2136.045410, 77.871, 0, ''), +(10646, 5, -4865.076660, -2116.549561, 76.483, 0, ''), +(10646, 6, -4888.434570, -2090.729248, 80.907, 0, ''), +(10646, 7, -4893.068359, -2085.468994, 82.094, 0, ''), +(10646, 8, -4907.256836, -2074.929932, 84.437, 5000, 'SAY_LAKO_LOOK_OUT'), +(10646, 9, -4899.899902, -2062.143555, 83.780, 0, ''), +(10646, 10, -4897.762207, -2056.520020, 84.184, 0, ''), +(10646, 11, -4888.331543, -2033.182495, 83.654, 0, ''), +(10646, 12, -4876.343750, -2003.916138, 90.887, 0, ''), +(10646, 13, -4872.227051, -1994.173340, 91.513, 0, ''), +(10646, 14, -4879.569336, -1976.985229, 92.185, 5000, 'SAY_LAKO_HERE_COME'), +(10646, 15, -4879.049316, -1964.349609, 92.001, 0, ''), +(10646, 16, -4874.720215, -1956.939819, 90.737, 0, ''), +(10646, 17, -4869.474609, -1952.612671, 89.206, 0, ''), +(10646, 18, -4842.466797, -1929.000732, 84.147, 0, ''), +(10646, 19, -4804.444824, -1897.302734, 89.362, 0, ''), +(10646, 20, -4798.072754, -1892.383545, 89.368, 0, ''), +(10646, 21, -4779.447754, -1882.759155, 90.169, 5000, 'SAY_LAKO_MORE'), +(10646, 22, -4762.081055, -1866.530640, 89.481, 0, ''), +(10646, 23, -4766.267090, -1861.867798, 87.847, 0, ''), +(10646, 24, -4782.929688, -1852.174683, 78.354, 0, ''), +(10646, 25, -4793.605469, -1850.961182, 77.658, 0, ''), +(10646, 26, -4803.323730, -1855.102661, 78.958, 0, ''), +(10646, 27, -4807.971680, -1854.501221, 77.743, 0, ''), +(10646, 28, -4837.212891, -1848.493408, 64.488, 0, ''), +(10646, 29, -4884.619629, -1840.401123, 56.219, 0, ''), +(10646, 30, -4889.705566, -1839.623291, 54.417, 0, ''), +(10646, 31, -4893.904297, -1843.685791, 53.012, 0, ''), +(10646, 32, -4903.142090, -1872.383545, 32.266, 0, ''), +(10646, 33, -4910.940918, -1879.864868, 29.940, 0, ''), +(10646, 34, -4920.047363, -1880.940796, 30.597, 0, ''), +(10646, 35, -4924.457031, -1881.447144, 29.292, 0, ''), +(10646, 36, -4966.120117, -1886.033081, 10.977, 0, ''), +(10646, 37, -4999.369629, -1890.847290, 4.430, 0, ''), +(10646, 38, -5007.271484, -1891.669678, 2.771, 0, ''), +(10646, 39, -5013.334473, -1879.588257, -1.947, 0, ''), +(10646, 40, -5023.328613, -1855.959961, -17.103, 0, ''), +(10646, 41, -5038.513184, -1825.986694, -35.821, 0, ''), +(10646, 42, -5048.733887, -1809.798218, -46.457, 0, ''), +(10646, 43, -5053.188965, -1791.682983, -57.186, 0, ''), +(10646, 44, -5062.093750, -1794.399780, -56.515, 0, ''), +(10646, 45, -5052.657227, -1797.044800, -54.734, 5000, 'SAY_LAKO_END'); + +DELETE FROM script_waypoint WHERE entry=11856; +INSERT INTO script_waypoint VALUES +(11856, 0, 113.91, -350.13, 4.55, 0, ''), +(11856, 1, 109.54, -350.08, 3.74, 0, ''), +(11856, 2, 106.95, -353.40, 3.60, 0, ''), +(11856, 3, 100.28, -338.89, 2.97, 0, ''), +(11856, 4, 110.11, -320.26, 3.47, 0, ''), +(11856, 5, 109.78, -287.80, 5.30, 0, ''), +(11856, 6, 105.02, -269.71, 4.71, 0, ''), +(11856, 7, 86.71, -251.81, 5.34, 0, ''), +(11856, 8, 64.10, -246.38, 5.91, 0, ''), +(11856, 9, -2.55, -243.58, 6.3, 0, ''), +(11856, 10, -27.78, -267.53, -1.08, 0, ''), +(11856, 11, -31.27, -283.54, -4.36, 0, ''), +(11856, 12, -28.96, -322.44, -9.19, 0, ''), +(11856, 13, -35.63, -360.03, -16.59, 0, ''), +(11856, 14, -58.30, -412.26, -30.60, 0, ''), +(11856, 15, -58.88, -474.17, -44.54, 0, ''), +(11856, 16, -45.92, -496.57, -46.26, 5000, 'AMBUSH'), +(11856, 17, -40.25, -510.07, -46.05, 0, ''), +(11856, 18, -38.88, -520.72, -46.06, 5000, 'END'); + +DELETE FROM script_waypoint WHERE entry=12423; +INSERT INTO script_waypoint VALUES +(12423, 0, -9509.72, -147.03, 58.74, 0, ''), +(12423, 1, -9517.07, -172.82, 58.66, 0, ''); + +DELETE FROM script_waypoint WHERE entry=12427; +INSERT INTO script_waypoint VALUES +(12427, 0, -5689.20, -456.44, 391.08, 0, ''), +(12427, 1, -5700.37, -450.77, 393.19, 0, ''); + +DELETE FROM script_waypoint WHERE entry=12428; +INSERT INTO script_waypoint VALUES +(12428, 0, 2454.09, 361.26, 31.51, 0, ''), +(12428, 1, 2472.03, 378.08, 30.98, 0, ''); + +DELETE FROM script_waypoint WHERE entry=12429; +INSERT INTO script_waypoint VALUES +(12429, 0, 9654.19, 909.58, 1272.11, 0, ''), +(12429, 1, 9642.53, 908.11, 1269.10, 0, ''); + +DELETE FROM script_waypoint WHERE entry=12430; +INSERT INTO script_waypoint VALUES +(12430, 0, 161.65, -4779.34, 14.64, 0, ''), +(12430, 1, 140.71, -4813.56, 17.04, 0, ''); + +DELETE FROM script_waypoint WHERE entry=12717; +INSERT INTO script_waypoint VALUES +(12717, 0, 3346.247070, 1007.879028, 3.590, 0, 'SAY_MUG_START2'), +(12717, 1, 3367.388428, 1011.505859, 3.720, 0, ''), +(12717, 2, 3418.636963, 1013.963684, 2.905, 0, ''), +(12717, 3, 3426.844971, 1015.097534, 3.449, 0, ''), +(12717, 4, 3437.025391, 1020.786194, 2.742, 0, ''), +(12717, 5, 3460.563721, 1024.256470, 1.353, 0, ''), +(12717, 6, 3479.869629, 1037.957153, 1.023, 0, ''), +(12717, 7, 3490.526367, 1043.346313, 3.338, 0, ''), +(12717, 8, 3504.282959, 1047.772339, 8.205, 0, ''), +(12717, 9, 3510.733398, 1049.790771, 12.143, 0, ''), +(12717, 10, 3514.411133, 1051.167725, 13.235, 0, ''), +(12717, 11, 3516.939697, 1052.911377, 12.918, 0, ''), +(12717, 12, 3523.635742, 1056.297485, 7.563, 0, ''), +(12717, 13, 3531.939941, 1059.863525, 6.175, 0, ''), +(12717, 14, 3535.475342, 1069.959473, 1.697, 0, ''), +(12717, 15, 3546.978027, 1093.485474, 0.680, 0, ''), +(12717, 16, 3549.729980, 1101.882446, -1.123, 0, ''), +(12717, 17, 3555.140137, 1116.985718, -4.326, 0, ''), +(12717, 18, 3571.940430, 1132.175781, -0.634, 0, ''), +(12717, 19, 3574.283203, 1137.575928, 3.684, 0, ''), +(12717, 20, 3579.312744, 1137.252319, 8.205, 0, ''), +(12717, 21, 3590.218994, 1143.646973, 8.291, 0, ''), +(12717, 22, 3595.972900, 1145.827148, 6.773, 0, ''), +(12717, 23, 3603.650391, 1146.920776, 9.763, 0, ''), +(12717, 24, 3607.081787, 1146.014282, 10.692, 5000, 'SAY_MUG_BRAZIER'), +(12717, 25, 3614.518555, 1142.629150, 10.248, 0, ''), +(12717, 26, 3616.660889, 1140.837036, 10.682, 3000, 'SAY_MUG_PATROL'), +(12717, 27, 3621.078613, 1138.109497, 10.369, 0, 'SAY_MUG_RETURN'), +(12717, 28, 3615.478516, 1145.525879, 9.614, 0, ''), +(12717, 29, 3607.188232, 1152.715942, 8.871, 0, ''); + +DELETE FROM script_waypoint WHERE entry=12818; +INSERT INTO script_waypoint VALUES +(12818, 0, 3347.250089, -694.700989, 159.925995, 0, ''), +(12818, 1, 3341.527039, -694.725891, 161.124542, 4000, ''), +(12818, 2, 3338.351074, -686.088138, 163.444000, 0, ''), +(12818, 3, 3352.744873, -677.721741, 162.316269, 0, ''), +(12818, 4, 3370.291016, -669.366943, 160.751358, 0, ''), +(12818, 5, 3381.479492, -659.449097, 162.545303, 0, ''), +(12818, 6, 3389.554199, -648.500000, 163.651825, 0, ''), +(12818, 7, 3396.645020, -641.508911, 164.216019, 0, ''), +(12818, 8, 3410.498535, -634.299622, 165.773453, 0, ''), +(12818, 9, 3418.461426, -631.791992, 166.477615, 0, ''), +(12818, 10, 3429.500000, -631.588745, 166.921265, 0, ''), +(12818, 11,3434.950195, -629.245483, 168.333969, 0, ''), +(12818, 12,3438.927979, -618.503235, 171.503143, 0, ''), +(12818, 13,3444.217529, -609.293640, 173.077972, 1000, 'Ambush 1'), +(12818, 14,3460.505127, -593.794189, 174.342255, 0, ''), +(12818, 15,3480.283203, -578.210327, 176.652313, 0, ''), +(12818, 16,3492.912842, -562.335449, 181.396301, 0, ''), +(12818, 17,3495.230957, -550.977600, 184.652267, 0, ''), +(12818, 18,3496.247070, -529.194214, 188.172028, 0, ''), +(12818, 19,3497.619385, -510.411499, 188.345322, 1000, 'Ambush 2'), +(12818, 20,3498.498047, -497.787506, 185.806274, 0, ''), +(12818, 21,3484.218750, -489.717529, 182.389862, 4000, ''); + +DELETE FROM script_waypoint WHERE entry=12858; +INSERT INTO script_waypoint VALUES +(12858, 0, 1782.63, -2241.11, 109.73, 5000, ''), +(12858, 1, 1788.88, -2240.17, 111.71, 0, ''), +(12858, 2, 1797.49, -2238.11, 112.31, 0, ''), +(12858, 3, 1803.83, -2232.77, 111.22, 0, ''), +(12858, 4, 1806.65, -2217.83, 107.36, 0, ''), +(12858, 5, 1811.81, -2208.01, 107.45, 0, ''), +(12858, 6, 1820.85, -2190.82, 100.49, 0, ''), +(12858, 7, 1829.60, -2177.49, 96.44, 0, ''), +(12858, 8, 1837.98, -2164.19, 96.71, 0, 'prepare'), +(12858, 9, 1839.99, -2149.29, 96.78, 0, ''), +(12858, 10, 1835.14, -2134.98, 96.80, 0, ''), +(12858, 11, 1823.57, -2118.27, 97.43, 0, ''), +(12858, 12, 1814.99, -2110.35, 98.38, 0, ''), +(12858, 13, 1806.60, -2103.09, 99.19, 0, ''), +(12858, 14, 1798.27, -2095.77, 100.04, 0, ''), +(12858, 15, 1783.59, -2079.92, 100.81, 0, ''), +(12858, 16, 1776.79, -2069.48, 101.77, 0, ''), +(12858, 17, 1776.82, -2054.59, 109.82, 0, ''), +(12858, 18, 1776.88, -2047.56, 109.83, 0, ''), +(12858, 19, 1776.86, -2036.55, 109.83, 0, ''), +(12858, 20, 1776.90, -2024.56, 109.83, 0, 'win'), +(12858, 21, 1776.87, -2028.31, 109.83,60000, 'stay'), +(12858, 22, 1776.90, -2028.30, 109.83, 0, ''); + +DELETE FROM script_waypoint WHERE entry=15420; +INSERT INTO script_waypoint VALUES +(15420, 0, 9294.78, -6682.51, 22.42, 0, ''), +(15420, 1, 9298.27, -6667.99, 22.42, 0, ''), +(15420, 2, 9309.63, -6658.84, 22.43, 0, ''), +(15420, 3, 9304.43, -6649.31, 26.46, 0, ''), +(15420, 4, 9298.83, -6648.00, 28.61, 0, ''), +(15420, 5, 9291.06, -6653.46, 31.83,2500, ''), +(15420, 6, 9289.08, -6660.17, 31.85,5000, ''), +(15420, 7, 9291.06, -6653.46, 31.83, 0, ''); + +DELETE FROM script_waypoint WHERE entry=16295; +INSERT INTO script_waypoint VALUES +(16295, 0, 7545.070000, -7359.870000, 162.354000, 4000, 'SAY_START'), +(16295, 1, 7550.048340, -7362.237793, 162.235657, 0, ''), +(16295, 2, 7566.976074, -7364.315430, 161.738770, 0, ''), +(16295, 3, 7578.830566, -7361.677734, 161.738770, 0, ''), +(16295, 4, 7590.969238, -7359.053711, 162.257660, 0, ''), +(16295, 5, 7598.354004, -7362.815430, 162.256683, 4000, 'SAY_PROGRESS_1'), +(16295, 6, 7605.861328, -7380.424316, 161.937073, 0, ''), +(16295, 7, 7605.295410, -7387.382813, 157.253998, 0, ''), +(16295, 8, 7606.131836, -7393.893555, 156.941925, 0, ''), +(16295, 9, 7615.207520, -7400.187012, 157.142639, 0, ''), +(16295, 10, 7618.956543, -7402.652832, 158.202042, 0, ''), +(16295, 11, 7636.850586, -7401.756836, 162.144791, 0, 'SAY_PROGRESS_2'), +(16295, 12, 7637.058105, -7404.944824, 162.206970, 4000, ''), +(16295, 13, 7636.910645, -7412.585449, 162.366425, 0, ''), +(16295, 14, 7637.607910, -7425.591797, 162.630661, 0, ''), +(16295, 15, 7637.816895, -7459.057129, 163.302704, 0, ''), +(16295, 16, 7638.859863, -7470.902344, 162.517059, 0, ''), +(16295, 17, 7641.395996, -7488.217285, 157.381287, 0, ''), +(16295, 18, 7634.455566, -7505.451660, 154.682159, 0, 'SAY_PROGRESS_3'), +(16295, 19, 7631.906738, -7516.948730, 153.597382, 0, ''), +(16295, 20, 7622.231445, -7537.037598, 151.587112, 0, ''), +(16295, 21, 7610.921875, -7550.670410, 149.639374, 0, ''), +(16295, 22, 7598.229004, -7562.551758, 145.953888, 0, ''), +(16295, 23, 7588.509277, -7577.755371, 148.294479, 0, ''), +(16295, 24, 7567.339355, -7608.456055, 146.006485, 0, ''), +(16295, 25, 7562.547852, -7617.417969, 148.097504, 0, ''), +(16295, 26, 7561.508789, -7645.064453, 151.245163, 0, ''), +(16295, 27, 7563.337402, -7654.652344, 151.227158, 0, ''), +(16295, 28, 7565.533691, -7658.296387, 151.248886, 0, ''), +(16295, 39, 7571.155762, -7659.118652, 151.244568, 0, ''), +(16295, 30, 7579.119629, -7662.213867, 151.651505, 0, 'quest complete'), +(16295, 31, 7603.768066, -7667.000488, 153.997726, 0, ''), +(16295, 32, 7603.768066, -7667.000488, 153.997726, 4000, 'SAY_END_1'), +(16295, 33, 7603.768066, -7667.000488, 153.997726, 8000, 'SAY_END_2'), +(16295, 34, 7603.768066, -7667.000488, 153.997726, 0, ''); + +DELETE FROM script_waypoint WHERE entry=16812; +INSERT INTO script_waypoint VALUES +(16812, 0, -10868.260, -1779.836, 90.476, 2500, 'Open door, begin walking'), +(16812, 1, -10875.585, -1779.581, 90.478, 0, ''), +(16812, 2, -10887.447, -1779.258, 90.476, 0, ''), +(16812, 3, -10894.592, -1780.668, 90.476, 0, ''), +(16812, 4, -10895.015, -1782.036, 90.476, 2500, 'Begin Speech after this'), +(16812, 5, -10894.592, -1780.668, 90.476, 0, 'Resume walking (back to spawn point now) after speech'), +(16812, 6, -10887.447, -1779.258, 90.476, 0, ''), +(16812, 7, -10875.585, -1779.581, 90.478, 0, ''), +(16812, 8, -10868.260, -1779.836, 90.476, 5000, 'close door'), +(16812, 9, -10866.799, -1780.958, 90.470, 2000, 'Summon mobs, open curtains'); + +DELETE FROM script_waypoint WHERE entry=16993; +INSERT INTO script_waypoint VALUES +(16993, 0, -1137.72, 4272.10, 14.04, 5000, ''), +(16993, 1, -1141.34, 4232.42, 14.63, 0, ''), +(16993, 2, -1133.47, 4220.88, 11.78, 0, ''), +(16993, 3, -1126.18, 4213.26, 13.51, 0, ''), +(16993, 4, -1100.12, 4204.32, 16.41, 0, ''), +(16993, 5, -1063.68, 4197.92, 15.51, 0, ''), +(16993, 6, -1027.28, 4194.36, 15.52, 0, ''), +(16993, 7, -995.68, 4189.59, 19.84, 0, ''), +(16993, 8, -970.90, 4188.60, 24.61, 0, ''), +(16993, 9, -961.93, 4193.34, 26.11, 15000, 'Summon 1'), +(16993, 10, -935.52, 4210.99, 31.98, 0, ''), +(16993, 11, -913.42, 4218.27, 37.29, 0, ''), +(16993, 12, -896.53, 4207.73, 43.23, 0, ''), +(16993, 13, -868.49, 4194.77, 46.75, 30000, 'Kneel and Rest Here'), +(16993, 14, -852.83, 4198.29, 47.28, 15000, 'Summon 2'), +(16993, 15, -819.85, 4200.50, 46.37, 0, ''), +(16993, 16, -791.92, 4201.96, 44.19, 0, ''), +(16993, 17, -774.42, 4202.46, 47.41, 0, ''), +(16993, 18, -762.90, 4202.17, 48.81, 0, ''), +(16993, 19, -728.25, 4195.35, 50.68, 0, ''), +(16993, 20, -713.58, 4192.07, 53.98, 0, ''), +(16993, 21, -703.09, 4189.74, 56.96, 0, ''), +(16993, 22, -693.70, 4185.43, 57.06, 0, ''), +(16993, 23, -686.38, 4159.81, 60.26, 0, ''), +(16993, 24, -679.88, 4147.04, 64.20, 0, ''), +(16993, 25, -656.74, 4147.72, 64.11, 0, ''), +(16993, 26, -652.22, 4137.50, 64.58, 0, ''), +(16993, 27, -649.99, 4136.38, 64.63, 30000, 'Quest Credit'); + +DELETE FROM script_waypoint WHERE entry=17077; +INSERT INTO script_waypoint VALUES +(17077, 0, -16.950142, 3801.409424, 95.064, 5000, 'EMOTE_WOLF_LIFT_HEAD'), +(17077, 1, -15.577404, 3805.170898, 94.833, 2500, ''), +(17077, 2, -20.011766, 3806.609863, 92.476, 5000, 'EMOTE_WOLF_HOWL'), +(17077, 3, -18.594666, 3816.207764, 91.482, 0, ''), +(17077, 4, -19.293468, 3838.218750, 85.012, 0, ''), +(17077, 5, -16.504408, 3871.034668, 82.327, 0, ''), +(17077, 6, 2.064510, 3898.678711, 85.623, 0, ''), +(17077, 7, 16.403864, 3921.174072, 86.024, 0, ''), +(17077, 8, 47.307926, 3932.001465, 83.302, 0, ''), +(17077, 9, 90.067230, 3942.906250, 77.000, 0, ''), +(17077, 10, 106.886024, 3944.388428, 76.502, 0, ''), +(17077, 11, 139.085480, 3941.897217, 80.617, 0, ''), +(17077, 12, 150.092346, 3942.782959, 80.399, 0, ''), +(17077, 13, 193.511475, 3950.396484, 74.366, 0, ''), +(17077, 14, 226.274948, 3958.003418, 73.257, 0, ''), +(17077, 15, 246.686981, 3963.309326, 76.376, 0, ''), +(17077, 16, 264.206177, 3977.726563, 83.704, 0, ''), +(17077, 17, 279.857422, 3986.417236, 88.245, 0, ''), +(17077, 18, 304.039642, 3998.354004, 95.649, 0, ''), +(17077, 19, 328.071503, 3995.832764, 104.434, 0, ''), +(17077, 20, 347.485229, 3990.817627, 113.608, 0, ''), +(17077, 21, 351.257202, 3954.260254, 125.747, 0, ''), +(17077, 22, 345.625977, 3932.016113, 132.358, 0, ''), +(17077, 23, 347.971893, 3908.549561, 135.520, 0, ''), +(17077, 24, 351.887878, 3891.062744, 139.957, 0, ''), +(17077, 25, 346.116852, 3864.634277, 146.647, 0, ''), +(17077, 26, 330.012360, 3839.859375, 154.148, 0, ''), +(17077, 27, 297.250610, 3811.855225, 166.893, 0, ''), +(17077, 28, 290.783112, 3800.188477, 172.130, 0, ''), +(17077, 29, 288.125427, 3782.474365, 180.825, 0, ''), +(17077, 30, 296.817841, 3771.629639, 184.961, 0, ''), +(17077, 31, 305.256256, 3765.380615, 185.360, 0, ''), +(17077, 32, 311.447906, 3757.902100, 184.312, 0, ''), +(17077, 33, 325.258026, 3730.282227, 184.076, 0, ''), +(17077, 34, 341.158630, 3717.757080, 183.904, 0, ''), +(17077, 35, 365.589020, 3717.200684, 183.902, 0, ''), +(17077, 36, 387.395081, 3731.750732, 183.645, 0, ''), +(17077, 37, 396.574127, 3732.604248, 179.831, 0, ''), +(17077, 38, 404.303192, 3737.313232, 180.151, 0, ''), +(17077, 39, 410.995972, 3742.286865, 183.364, 0, ''), +(17077, 40, 434.904541, 3761.058838, 186.219, 0, ''), +(17077, 41, 460.128815, 3774.436768, 186.348, 0, ''), +(17077, 42, 467.643951, 3788.506104, 186.446, 0, ''), +(17077, 43, 491.551666, 3815.446777, 189.848, 0, ''), +(17077, 44, 496.957855, 3836.875244, 193.078, 0, ''), +(17077, 45, 502.889191, 3855.458740, 194.834, 0, ''), +(17077, 46, 508.208466, 3863.689453, 194.024, 0, ''), +(17077, 47, 528.907593, 3887.348633, 189.762, 0, ''), +(17077, 48, 527.722229, 3890.686523, 189.240, 0, ''), +(17077, 49, 524.637329, 3891.768066, 189.149, 0, ''), +(17077, 50, 519.146057, 3886.701660, 190.128, 60000, 'SAY_WOLF_WELCOME'); + +DELETE FROM script_waypoint WHERE entry=17312; +INSERT INTO script_waypoint VALUES +(17312, 0, -4784.532227, -11051.060547, 3.484263, 0, ''), +(17312, 1, -4805.509277, -11037.293945, 3.043942, 0, ''), +(17312, 2, -4827.826172, -11034.398438, 1.741959, 0, ''), +(17312, 3, -4852.630859, -11033.695313, 2.208656, 0, ''), +(17312, 4, -4876.791992, -11034.517578, 3.175228, 0, ''), +(17312, 5, -4895.486816, -11038.306641, 9.390890, 0, ''), +(17312, 6, -4915.464844, -11048.402344, 12.369793, 0, ''), +(17312, 7, -4937.288086, -11067.041992, 13.857983, 0, ''), +(17312, 8, -4966.577637, -11067.507813, 15.754786, 0, ''), +(17312, 9, -4993.799805, -11056.544922, 19.175295, 0, ''), +(17312, 10, -5017.836426, -11052.569336, 22.476587, 0, ''), +(17312, 11, -5039.706543, -11058.459961, 25.831593, 0, ''), +(17312, 12, -5057.289063, -11045.474609, 26.972496, 0, ''), +(17312, 13, -5078.828125, -11037.601563, 29.053417, 0, ''), +(17312, 14, -5104.158691, -11039.195313, 29.440195, 0, ''), +(17312, 15, -5120.780273, -11039.518555, 30.142139, 0, ''), +(17312, 16, -5140.833008, -11039.810547, 28.788074, 0, ''), +(17312, 17, -5161.201660, -11040.050781, 27.879545, 4000, ''), +(17312, 18, -5171.842285, -11046.803711, 27.183821, 0, ''), +(17312, 19, -5185.995117, -11056.359375, 20.234867, 0, ''), +(17312, 20, -5198.485840, -11065.065430, 18.872593, 0, ''), +(17312, 21, -5214.062500, -11074.653320, 19.215731, 0, ''), +(17312, 22, -5220.157227, -11088.377930, 19.818476, 0, ''), +(17312, 23, -5233.652832, -11098.846680, 18.349432, 0, ''), +(17312, 24, -5250.163086, -11111.653320, 16.438959, 0, ''), +(17312, 25, -5268.194336, -11125.639648, 12.668313, 0, ''), +(17312, 26, -5286.270508, -11130.669922, 6.912246, 0, ''), +(17312, 27, -5317.449707, -11137.392578, 4.963446, 0, ''), +(17312, 28, -5334.854492, -11154.384766, 6.742664, 0, ''), +(17312, 29, -5353.874512, -11171.595703, 6.903912, 20000, ''), +(17312, 30, -5354.240000, -11171.940000, 6.890000, 0, ''); + +DELETE FROM script_waypoint WHERE entry=17876; +INSERT INTO script_waypoint VALUES +(17876, 0, 2230.91, 118.765, 82.2947, 2000, 'open the prison door'), +(17876, 1, 2230.33, 114.980, 82.2946, 0, ''), +(17876, 2, 2233.36, 111.057, 82.2996, 0, ''), +(17876, 3, 2231.17, 108.486, 82.6624, 0, ''), +(17876, 4, 2220.22, 114.605, 89.4264, 0, ''), +(17876, 5, 2215.23, 115.990, 89.4549, 0, ''), +(17876, 6, 2210.00, 106.849, 89.4549, 0, ''), +(17876, 7, 2205.66, 105.234, 89.4549, 0, ''), +(17876, 8, 2192.26, 112.618, 89.4549, 2000, 'SAY_ARMORER_CALL_GUARDS'), +(17876, 9, 2185.32, 116.593, 89.4548, 2000, 'SAY_TH_ARMORER_HIT'), +(17876, 10, 2182.11, 120.328, 89.4548, 3000, 'SAY_TH_ARMORY_1'), +(17876, 11, 2182.11, 120.328, 89.4548, 5000, ''), +(17876, 12, 2182.11, 120.328, 89.4548, 3000, 'SAY_TH_ARMORY_2'), +(17876, 13, 2189.44, 113.922, 89.4549, 0, ''), +(17876, 14, 2195.63, 110.584, 89.4549, 0, ''), +(17876, 15, 2201.09, 115.115, 89.4549, 0, ''), +(17876, 16, 2204.34, 121.036, 89.4355, 0, ''), +(17876, 17, 2208.66, 129.127, 87.9560, 0, 'first ambush'), +(17876, 18, 2193.09, 137.940, 88.2164, 0, ''), +(17876, 19, 2173.39, 149.064, 87.9227, 0, ''), +(17876, 20, 2164.25, 137.965, 85.0595, 0, 'second ambush'), +(17876, 21, 2149.31, 125.645, 77.0858, 0, ''), +(17876, 22, 2142.78, 127.173, 75.5954, 0, ''), +(17876, 23, 2139.28, 133.952, 73.6386, 0, 'third ambush'), +(17876, 24, 2139.54, 155.235, 67.1269, 0, ''), +(17876, 25, 2145.38, 167.551, 64.8974, 0, 'fourth ambush'), +(17876, 26, 2134.28, 175.304, 67.9446, 0, ''), +(17876, 27, 2118.08, 187.387, 68.8141, 0, ''), +(17876, 28, 2105.88, 195.461, 65.1854, 0, ''), +(17876, 29, 2096.77, 196.939, 65.2117, 0, ''), +(17876, 30, 2083.90, 209.395, 64.8736, 0, ''), +(17876, 31, 2063.40, 229.509, 64.4883, 0, 'summon Skarloc'), +(17876, 32, 2063.40, 229.509, 64.4883, 10000, 'SAY_SKARLOC_ENTER'), +(17876, 33, 2063.40, 229.509, 64.4883, 5000, 'attack Skarloc'), +(17876, 34, 2063.40, 229.509, 64.4883, 0, 'gossip after skarloc'), +(17876, 35, 2046.70, 251.941, 62.7851, 4000, 'mount up'), +(17876, 36, 2046.70, 251.941, 62.7851, 3000, 'SAY_TH_MOUNTS_UP'), +(17876, 37, 2011.77, 278.478, 65.3388, 0, ''), +(17876, 38, 2005.08, 289.676, 66.1179, 0, ''), +(17876, 39, 2033.11, 337.450, 66.0948, 0, ''), +(17876, 40, 2070.30, 416.208, 66.0893, 0, ''), +(17876, 41, 2086.76, 469.768, 65.9182, 0, ''), +(17876, 42, 2101.70, 497.955, 61.7881, 0, ''), +(17876, 43, 2133.39, 530.933, 55.3700, 0, ''), +(17876, 44, 2157.91, 559.635, 48.5157, 0, ''), +(17876, 45, 2167.34, 586.191, 42.4394, 0, ''), +(17876, 46, 2174.17, 637.643, 33.9002, 0, ''), +(17876, 47, 2179.31, 656.053, 34.723, 0, ''), +(17876, 48, 2183.65, 670.941, 34.0318, 0, ''), +(17876, 49, 2201.50, 668.616, 36.1236, 0, ''), +(17876, 50, 2221.56, 652.747, 36.6153, 0, ''), +(17876, 51, 2238.97, 640.125, 37.2214, 0, ''), +(17876, 52, 2251.17, 620.574, 40.1473, 0, ''), +(17876, 53, 2261.98, 595.303, 41.4117, 0, ''), +(17876, 54, 2278.67, 560.172, 38.9090, 0, ''), +(17876, 55, 2336.72, 528.327, 40.9369, 0, ''), +(17876, 56, 2381.04, 519.612, 37.7312, 0, ''), +(17876, 57, 2412.20, 515.425, 39.2068, 0, ''), +(17876, 58, 2452.39, 516.174, 42.9387, 0, ''), +(17876, 59, 2467.38, 539.389, 47.4992, 0, ''), +(17876, 60, 2470.70, 554.333, 46.6668, 0, ''), +(17876, 61, 2478.07, 575.321, 55.4549, 0, ''), +(17876, 62, 2480.00, 585.408, 56.6921, 0, ''), +(17876, 63, 2482.67, 608.817, 55.6643, 0, ''), +(17876, 64, 2485.62, 626.061, 58.0132, 2000, 'dismount'), +(17876, 65, 2486.91, 626.356, 58.0761, 2000, 'EMOTE_TH_STARTLE_HORSE'), +(17876, 66, 2486.91, 626.356, 58.0761, 0, 'gossip before barn'), +(17876, 67, 2488.58, 660.940, 57.3913, 0, ''), +(17876, 68, 2502.56, 686.059, 55.6252, 0, ''), +(17876, 69, 2502.08, 694.360, 55.5083, 0, ''), +(17876, 70, 2491.46, 694.321, 55.7163, 0, 'enter barn'), +(17876, 71, 2491.10, 703.300, 55.7630, 0, ''), +(17876, 72, 2485.64, 702.992, 55.7917, 0, ''), +(17876, 73, 2479.63, 696.521, 55.7901, 0, 'spawn mobs'), +(17876, 74, 2476.24, 696.204, 55.8093, 0, 'start dialogue'), +(17876, 75, 2475.39, 695.983, 55.8146, 0, ''), +(17876, 76, 2477.75, 694.473, 55.7945, 0, ''), +(17876, 77, 2481.27, 697.747, 55.7910, 0, ''), +(17876, 78, 2486.31, 703.131, 55.7861, 0, ''), +(17876, 79, 2490.76, 703.511, 55.7662, 0, ''), +(17876, 80, 2491.30, 694.792, 55.7195, 0, 'exit barn'), +(17876, 81, 2502.08, 694.360, 55.5083, 0, ''), +(17876, 82, 2507.99, 679.298, 56.3760, 0, ''), +(17876, 83, 2524.79, 669.919, 54.9258, 0, ''), +(17876, 84, 2543.19, 665.289, 56.2957, 0, ''), +(17876, 85, 2566.49, 664.354, 54.5034, 0, ''), +(17876, 86, 2592.00, 664.611, 56.4394, 0, ''), +(17876, 87, 2614.43, 663.806, 55.3921, 2000, ''), +(17876, 88, 2616.14, 665.499, 55.1610, 0, ''), +(17876, 89, 2623.56, 666.965, 54.3983, 0, ''), +(17876, 90, 2629.99, 661.059, 54.2738, 0, ''), +(17876, 91, 2629.00, 656.982, 56.0651, 0, 'enter the church'), +(17876, 92, 2620.84, 633.007, 56.0300, 3000, 'SAY_TH_CHURCH_ENTER'), +(17876, 93, 2620.84, 633.007, 56.0300, 5000, 'church ambush'), +(17876, 94, 2620.84, 633.007, 56.0300, 0, 'SAY_TH_CHURCH_END'), +(17876, 95, 2622.99, 639.178, 56.0300, 0, ''), +(17876, 96, 2628.73, 656.693, 56.0610, 0, ''), +(17876, 97, 2630.34, 661.135, 54.2738, 0, ''), +(17876, 98, 2635.38, 672.243, 54.4508, 0, ''), +(17876, 99, 2644.13, 668.158, 55.3797, 0, ''), +(17876, 100, 2646.82, 666.740, 56.9898, 0, ''), +(17876, 101, 2658.22, 665.432, 57.1725, 0, ''), +(17876, 102, 2661.88, 674.849, 57.1725, 0, ''), +(17876, 103, 2656.23, 677.208, 57.1725, 0, ''), +(17876, 104, 2652.28, 670.270, 61.9353, 0, ''), +(17876, 105, 2650.79, 664.290, 61.9302, 0, 'inn ambush'), +(17876, 106, 2660.48, 659.409, 61.9370, 5000, 'SAY_TA_ESCAPED'), +(17876, 107, 2660.48, 659.409, 61.9370, 0, 'SAY_TH_MEET_TARETHA - gossip before epoch'), +(17876, 108, 2660.48, 659.409, 61.9370, 0, 'SAY_EPOCH_ENTER1'), +(17876, 109, 2650.62, 666.643, 61.9305, 0, ''), +(17876, 110, 2652.37, 670.561, 61.9368, 0, ''), +(17876, 111, 2656.05, 676.761, 57.1727, 0, ''), +(17876, 112, 2658.49, 677.166, 57.1727, 0, ''), +(17876, 113, 2659.28, 667.117, 57.1727, 0, ''), +(17876, 114, 2649.71, 665.387, 57.1727, 0, ''), +(17876, 115, 2634.79, 672.964, 54.4577, 0, 'outside inn'), +(17876, 116, 2635.06, 673.892, 54.4713, 18000, 'SAY_EPOCH_ENTER3'), +(17876, 117, 2635.06, 673.892, 54.4713, 0, 'fight begins'), +(17876, 118, 2635.06, 673.892, 54.4713, 0, 'fight ends'), +(17876, 119, 2634.30, 661.698, 54.4147, 0, 'run off'), +(17876, 120, 2652.21, 644.396, 56.1906, 0, ''); + +DELETE FROM script_waypoint WHERE entry=17969; +INSERT INTO script_waypoint VALUES +(17969, 0, -930.048950, 5288.080078, 23.848402, 0, ''), +(17969, 1, -925.677917, 5296.482910, 18.183748, 0, ''), +(17969, 2, -924.297180, 5299.016113, 17.710915, 0, ''), +(17969, 3, -928.390076, 5317.022949, 18.208593, 0, ''), +(17969, 4, -930.620972, 5329.915039, 18.773422, 0, 'SAY_AMBUSH1'), +(17969, 5, -931.490295, 5357.654785, 18.027155, 0, 'SAY_PROGRESS'), +(17969, 6, -934.777771, 5369.341797, 22.278048, 0, ''), +(17969, 7, -934.521851, 5373.407227, 22.834690, 0, ''), +(17969, 8, -937.008545, 5382.980469, 22.699078, 0, ''), +(17969, 9, -941.948059, 5404.141602, 22.669743, 0, ''), +(17969, 10, -931.244263, 5415.846680, 23.063961, 0, 'at crossroad'), +(17969, 11, -901.497925, 5420.315430, 24.213270, 0, ''), +(17969, 12, -860.311707, 5415.617676, 23.671139, 0, ''), +(17969, 13, -777.988953, 5391.982422, 23.001669, 0, ''), +(17969, 14, -750.362000, 5385.786621, 22.765791, 0, ''), +(17969, 15, -731.339417, 5382.449707, 22.517065, 0, ''), +(17969, 16, -681.235901, 5381.377930, 22.050159, 2500, 'end bridge SAY_AMBUSH2'), +(17969, 17, -637.944458, 5384.338379, 22.205647, 0, 'SAY_END'), +(17969, 18, -608.954407, 5408.715332, 21.630386, 0, ''), +(17969, 19, -598.134277, 5413.608398, 21.412275, 0, ''), +(17969, 20, -571.268982, 5420.771973, 21.184925, 0, ''), +(17969, 21, -553.099915, 5424.616211, 21.193716, 0, ''), +(17969, 22, -524.745483, 5443.945313, 20.977013, 0, ''), +(17969, 23, -502.984985, 5446.283691, 22.149435, 0, ''), +(17969, 24, -472.463959, 5449.546875, 22.561453, 0, ''), +(17969, 25, -454.533264, 5461.302246, 22.602837, 30000, 'quest complete'); + +DELETE FROM script_waypoint WHERE entry=18210; +INSERT INTO script_waypoint VALUES +(18210, 0, -1581.410034, 8557.933594, 2.726, 0, ''), +(18210, 1, -1579.908447, 8553.716797, 2.559, 0, ''), +(18210, 2, -1577.829102, 8549.880859, 2.001, 0, ''), +(18210, 3, -1571.161987, 8543.494141, 2.001, 0, ''), +(18210, 4, -1563.944824, 8530.334961, 1.605, 0, ''), +(18210, 5, -1554.565552, 8518.413086, 0.364, 0, ''), +(18210, 6, -1549.239136, 8515.518555, 0.293, 0, ''), +(18210, 7, -1518.490112, 8516.771484, 0.683, 2000, 'SAY_MAG_MORE'), +(18210, 8, -1505.038940, 8513.247070, 0.672, 0, ''), +(18210, 9, -1476.161133, 8496.066406, 2.157, 0, ''), +(18210, 10, -1464.450684, 8492.601563, 3.529, 0, ''), +(18210, 11, -1457.568359, 8492.183594, 4.449, 0, ''), +(18210, 12, -1444.100342, 8499.031250, 6.177, 0, ''), +(18210, 13, -1426.472168, 8510.116211, 7.686, 0, ''), +(18210, 14, -1403.685303, 8524.146484, 9.680, 0, ''), +(18210, 15, -1384.890503, 8542.014648, 11.180, 0, ''), +(18210, 16, -1382.286133, 8539.869141, 11.139, 7500, 'SAY_MAG_COMPLETE'), +(18210, 17, -1361.224609, 8521.440430, 11.144, 0, ''), +(18210, 18, -1324.803589, 8510.688477, 13.050, 0, ''), +(18210, 19, -1312.075439, 8492.709961, 14.235, 0, ''); + +DELETE FROM script_waypoint WHERE entry=18887; +INSERT INTO script_waypoint VALUES +(18887, 0, 2650.06, 665.473, 61.9305, 0, ''), +(18887, 1, 2652.44, 670.761, 61.9370, 0, ''), +(18887, 2, 2655.96, 676.913, 57.1725, 0, ''), +(18887, 3, 2659.40, 677.317, 57.1725, 0, ''), +(18887, 4, 2651.75, 664.482, 57.1725, 0, ''), +(18887, 5, 2647.49, 666.595, 57.0824, 0, ''), +(18887, 6, 2644.37, 668.167, 55.4182, 0, ''), +(18887, 7, 2638.57, 671.231, 54.5200, 0, 'start dialogue - escort paused'), +(18887, 8, 2636.56, 679.894, 54.6595, 0, ''), +(18887, 9, 2640.79, 689.647, 55.3215, 0, ''), +(18887, 10, 2639.35, 706.777, 56.0667, 0, ''), +(18887, 11, 2617.70, 731.884, 55.5571, 0, ''); + +DELETE FROM script_waypoint WHERE entry=19589; +INSERT INTO script_waypoint VALUES +(19589, 1, 3358.22, 3728.25, 141.204, 16000, ''), +(19589, 2, 3368.05, 3715.51, 142.057, 0, ''), +(19589, 3, 3389.04, 3701.21, 144.648, 0, ''), +(19589, 4, 3419.51, 3691.41, 146.598, 0, ''), +(19589, 5, 3437.83, 3699.2, 147.235, 0, ''), +(19589, 6, 3444.85, 3700.89, 147.088, 0, ''), +(19589, 7, 3449.89, 3700.14, 148.118, 12000, 'first object'), +(19589, 8, 3443.55, 3682.09, 149.219, 0, ''), +(19589, 9, 3452.6, 3674.65, 150.226, 0, ''), +(19589, 10, 3462.6, 3659.01, 152.436, 0, ''), +(19589, 11, 3469.18, 3649.47, 153.178, 0, ''), +(19589, 12, 3475.11, 3639.41, 157.213, 0, ''), +(19589, 13, 3482.26, 3617.69, 159.126, 0, ''), +(19589, 14, 3492.7, 3606.27, 156.419, 0, ''), +(19589, 15, 3493.52, 3595.06, 156.581, 0, ''), +(19589, 16, 3490.4, 3588.45, 157.764, 0, ''), +(19589, 17, 3485.21, 3585.69, 159.979, 12000, 'second object'), +(19589, 18, 3504.68, 3594.44, 152.862, 0, ''), +(19589, 19, 3523.6, 3594.48, 145.393, 0, ''), +(19589, 20, 3537.01, 3576.71, 135.748, 0, ''), +(19589, 21, 3551.73, 3573.12, 128.013, 0, ''), +(19589, 22, 3552.12, 3614.08, 127.847, 0, ''), +(19589, 23, 3536.14, 3639.78, 126.031, 0, ''), +(19589, 24, 3522.94, 3646.47, 131.989, 0, ''), +(19589, 25, 3507.21, 3645.69, 138.1527, 0, ''), +(19589, 26, 3485.15, 3645.64, 137.755, 0, ''), +(19589, 27, 3472.18, 3633.88, 140.352, 0, ''), +(19589, 28, 3435.34, 3613.69, 140.725, 0, ''), +(19589, 29, 3417.4, 3612.4, 141.143, 12000, 'third object'), +(19589, 30, 3411.04, 3621.14, 142.454, 0, ''), +(19589, 31, 3404.47, 3636.89, 144.434, 0, ''), +(19589, 32, 3380.55, 3657.06, 144.332, 0, ''), +(19589, 33, 3375, 3676.86, 145.298, 0, ''), +(19589, 34, 3388.87, 3685.48, 146.818, 0, ''), +(19589, 35, 3393.99, 3699.4, 144.858, 0, ''), +(19589, 36, 3354.95, 3726.02, 141.428, 0, ''), +(19589, 37, 3351.40, 3722.33, 141.40, 0, 'home position'); + +DELETE FROM script_waypoint WHERE entry=19685; +INSERT INTO script_waypoint VALUES +(19685, 0, -1860.536987, 5416.987793, -10.480, 2500, ''), +(19685, 1, -1855.899048, 5412.805664, -12.427, 0, 'SAY_KHAD_SERV_0'), +(19685, 2, -1845.518433, 5385.352539, -12.427, 0, ''), +(19685, 3, -1815.247803, 5340.255371, -12.427, 0, ''), +(19685, 4, -1799.338379, 5312.777344, -12.427, 0, ''), +(19685, 5, -1780.491455, 5278.535156, -33.877, 2500, 'pause'), +(19685, 6, -1776.057983, 5270.247559, -38.809, 0, ''), +(19685, 7, -1772.219727, 5262.777344, -38.810, 0, ''), +(19685, 8, -1762.195557, 5261.720215, -38.850, 0, ''), +(19685, 9, -1759.242798, 5259.751465, -40.208, 0, ''), +(19685, 10, -1743.427612, 5259.661621, -40.208, 0, ''), +(19685, 11, -1744.361816, 5251.179199, -44.523, 0, ''), +(19685, 12, -1740.121582, 5240.120117, -47.740, 0, ''), +(19685, 13, -1737.636719, 5238.288086, -49.793, 0, ''), +(19685, 14, -1727.411621, 5233.874512, -50.477, 0, ''), +(19685, 15, -1707.489746, 5230.437988, -51.050, 0, ''), +(19685, 16, -1684.122925, 5223.633301, -49.415, 0, ''), +(19685, 17, -1669.973267, 5221.929688, -46.336, 0, ''), +(19685, 18, -1662.870117, 5221.712891, -44.959, 0, ''), +(19685, 19, -1657.170410, 5225.206055, -45.708, 0, ''), +(19685, 20, -1645.025635, 5238.360352, -40.212, 0, ''), +(19685, 21, -1631.657471, 5252.759766, -40.962, 0, ''), +(19685, 22, -1631.368286, 5276.543945, -41.032, 0, ''), +(19685, 23, -1621.732544, 5298.553711, -40.209, 0, ''), +(19685, 24, -1615.498169, 5298.098145, -40.209, 2500, 'pause'), +(19685, 25, -1636.979370, 5302.677734, -40.209, 0, ''), +(19685, 26, -1655.330322, 5315.736328, -40.207, 0, ''), +(19685, 27, -1656.884155, 5321.649414, -40.209, 0, ''), +(19685, 28, -1663.975586, 5335.206055, -46.526, 0, ''), +(19685, 29, -1659.141602, 5359.131836, -45.846, 0, ''), +(19685, 30, -1644.207520, 5390.886230, -45.542, 0, ''), +(19685, 31, -1646.183594, 5405.273926, -44.649, 0, ''), +(19685, 32, -1650.202637, 5414.541992, -46.324, 0, ''), +(19685, 33, -1656.052490, 5424.683594, -40.461, 0, ''), +(19685, 34, -1661.628296, 5423.929199, -40.405, 0, ''), +(19685, 35, -1664.651855, 5423.659180, -38.848, 0, ''), +(19685, 36, -1681.772339, 5425.999512, -38.809, 0, ''), +(19685, 37, -1729.785767, 5427.246094, -12.445, 0, ''), +(19685, 38, -1735.371460, 5423.663086, -12.427, 0, ''), +(19685, 39, -1741.627075, 5386.767578, -12.427, 0, ''), +(19685, 40, -1764.786133, 5363.735840, -12.427, 0, ''), +(19685, 41, -1816.372314, 5340.664063, -12.427, 0, ''), +(19685, 42, -1880.022705, 5309.796387, -12.427, 0, ''), +(19685, 43, -1887.374146, 5315.426270, -12.427, 0, ''), +(19685, 44, -1888.768066, 5324.518066, -5.146, 0, ''), +(19685, 45, -1888.399170, 5334.149902, 0.151, 0, ''), +(19685, 46, -1890.221680, 5337.659668, 0.921, 0, ''), +(19685, 47, -1897.542725, 5323.042969, 1.256, 0, ''), +(19685, 48, -1900.250244, 5319.804688, 0.831, 0, ''), +(19685, 49, -1910.039673, 5291.258789, 1.288, 0, ''), +(19685, 50, -1915.219482, 5275.572266, 2.502, 2500, 'pause'), +(19685, 51, -1927.226196, 5273.250977, 2.703, 0, ''), +(19685, 52, -1926.980225, 5278.467285, 0.109, 0, ''), +(19685, 53, -1927.665894, 5299.210449, -12.427, 0, ''), +(19685, 54, -1922.841797, 5319.263672, -12.427, 0, ''), +(19685, 55, -1925.779053, 5347.405273, -12.427, 0, ''), +(19685, 56, -1954.912476, 5384.230957, -12.427, 0, ''), +(19685, 57, -1966.727295, 5428.203613, -12.427, 0, ''), +(19685, 58, -1979.477661, 5448.415527, -12.427, 0, ''), +(19685, 59, -1977.533569, 5453.861328, -12.385, 0, ''), +(19685, 60, -1968.064087, 5455.781250, -4.343, 0, ''), +(19685, 61, -1959.223145, 5454.895020, 0.202, 0, ''), +(19685, 62, -1954.629028, 5457.011230, 0.900, 0, ''), +(19685, 63, -1967.760010, 5464.953125, 1.220, 2500, 'pause'), +(19685, 64, -1952.874023, 5462.962402, 0.956, 0, ''), +(19685, 65, -1955.339478, 5467.116699, 0.445, 0, ''), +(19685, 66, -1962.033203, 5472.804688, -4.243, 0, ''), +(19685, 67, -1968.007690, 5480.914551, -12.427, 0, ''), +(19685, 68, -1945.900146, 5515.948242, -12.427, 0, ''), +(19685, 69, -1874.867310, 5549.783691, -12.427, 0, ''), +(19685, 70, -1840.641602, 5544.234375, -12.427, 0, ''), +(19685, 71, -1838.963501, 5536.059570, -5.639, 0, ''), +(19685, 72, -1839.582275, 5525.627930, 0.193, 0, ''), +(19685, 73, -1837.931763, 5521.119629, 0.844, 0, ''), +(19685, 74, -1829.182495, 5533.433594, 1.209, 2500, 'pause'), +(19685, 75, -1848.397095, 5476.073730, 0.856, 40000, 'end'); + +DELETE FROM script_waypoint WHERE entry=20129; +INSERT INTO script_waypoint VALUES +(20129, 0, -8374.93,-4250.21, -204.38,5000, ''), +(20129, 1, -8374.93,-4250.21, -204.38,16000, ''), +(20129, 2, -8374.93,-4250.21, -204.38,10000, ''), +(20129, 3, -8374.93,-4250.21, -204.38,2000, ''), +(20129, 4, -8439.40,-4180.05, -209.25, 0, ''), +(20129, 5, -8437.82,-4120.84, -208.59,10000, ''), +(20129, 6, -8437.82,-4120.84, -208.59,16000, ''), +(20129, 7, -8437.82,-4120.84, -208.59,13000, ''), +(20129, 8, -8437.82,-4120.84, -208.59,18000, ''), +(20129, 9, -8437.82,-4120.84, -208.59,15000, ''), +(20129, 10, -8437.82,-4120.84, -208.59,2000, ''), +(20129, 11, -8467.26,-4198.63, -214.21, 0, ''), +(20129, 12, -8667.76,-4252.13, -209.56, 0, ''), +(20129, 13, -8703.71,-4234.58, -209.5,14000, ''), +(20129, 14, -8703.71,-4234.58, -209.5,2000, ''), +(20129, 15, -8642.81,-4304.37, -209.57, 0, ''), +(20129, 16, -8649.06,-4394.36, -208.46,6000, ''), +(20129, 17, -8649.06,-4394.36, -208.46,18000, ''), +(20129, 18, -8649.06,-4394.36, -208.46,2000, ''), +(20129, 19, -8468.72,-4437.67, -215.45, 0, ''), +(20129, 20, -8427.54,-4426, -211.13, 0, ''), +(20129, 21, -8364.83,-4393.32, -205.91, 0, ''), +(20129, 22, -8304.54,-4357.2, -208.2,18000, ''), +(20129, 23, -8304.54,-4357.2, -208.2,2000, ''), +(20129, 24, -8375.42,-4250.41, -205.14,5000, ''), +(20129, 25, -8375.42,-4250.41, -205.14,5000, ''); + +DELETE FROM script_waypoint WHERE entry=20415; +INSERT INTO script_waypoint VALUES +(20415, 0, 2488.77, 2184.89, 104.64, 0, ""), +(20415, 1, 2478.72, 2184.77, 98.58, 0, ""), +(20415, 2, 2473.52, 2184.71, 99.00, 0, ""), +(20415, 3, 2453.15, 2184.96, 97.09,4000, ""), +(20415, 4, 2424.18, 2184.15, 94.11, 0, ""), +(20415, 5, 2413.18, 2184.15, 93.42, 0, ""), +(20415, 6, 2402.02, 2183.90, 87.59, 0, ""), +(20415, 7, 2333.31, 2181.63, 90.03,4000, ""), +(20415, 8, 2308.73, 2184.34, 92.04, 0, ""), +(20415, 9, 2303.10, 2196.89, 94.94, 0, ""), +(20415, 10, 2304.58, 2272.23, 96.67, 0, ""), +(20415, 11, 2297.09, 2271.40, 95.16, 0, ""), +(20415, 12, 2297.68, 2266.79, 95.07,4000, ""), +(20415, 13, 2297.67, 2266.76, 95.07,4000, ""); + +DELETE FROM script_waypoint WHERE entry=21027; +INSERT INTO script_waypoint VALUES +(21027, 0, -2714.697266, 1326.879395, 34.306953, 0, ''), +(21027, 1, -2666.364990, 1348.222656, 34.445557, 0, ''), +(21027, 2, -2693.789307, 1336.964966, 34.445557, 0, ''), +(21027, 3, -2715.495361, 1328.054443, 34.106014, 0, ''), +(21027, 4, -2742.530762, 1314.138550, 33.606144, 0, ''), +(21027, 5, -2745.077148, 1311.108765, 33.630898, 0, ''), +(21027, 6, -2749.855225, 1302.737915, 33.475632, 0, ''), +(21027, 7, -2753.639648, 1294.059448, 33.314930, 0, ''), +(21027, 8, -2756.796387, 1285.122192, 33.391262, 0, ''), +(21027, 9, -2750.042969, 1273.661987, 33.188259, 0, ''), +(21027, 10, -2740.378418, 1258.846680, 33.212521, 0, ''), +(21027, 11, -2733.629395, 1248.259766, 33.640598, 0, ''), +(21027, 12, -2727.212646, 1238.606445, 33.520847, 0, ''), +(21027, 13, -2726.377197, 1237.264526, 33.461823, 3000, 'SAY_WIL_PROGRESS1'), +(21027, 14, -2746.383301, 1266.390625, 33.191952, 2000, ''), +(21027, 15, -2746.383301, 1266.390625, 33.191952, 4000, 'SAY_WIL_FIND_EXIT'), +(21027, 16, -2758.927734, 1285.134155, 33.341728, 0, ''), +(21027, 17, -2761.845703, 1292.313599, 33.209042, 0, ''), +(21027, 18, -2758.871826, 1300.677612, 33.285332, 0, ''), +(21027, 19, -2753.928955, 1307.755859, 33.452457, 0, ''), +(21027, 20, -2738.612061, 1316.191284, 33.482975, 0, ''), +(21027, 21, -2727.897461, 1320.013916, 33.381111, 0, ''), +(21027, 22, -2709.458740, 1315.739990, 33.301838, 0, ''), +(21027, 23, -2704.658936, 1301.620361, 32.463303, 0, ''), +(21027, 24, -2704.120117, 1298.922607, 32.768162, 0, ''), +(21027, 25, -2691.798340, 1292.846436, 33.852642, 0, ''), +(21027, 26, -2682.879639, 1288.853882, 32.995399, 0, ''), +(21027, 27, -2661.869141, 1279.682495, 26.686783, 0, ''), +(21027, 28, -2648.943604, 1270.272827, 24.147522, 0, ''), +(21027, 29, -2642.506836, 1262.938721, 23.512444, 0, ''), +(21027, 30, -2636.984863, 1252.429077, 20.418257, 0, ''), +(21027, 31, -2648.113037, 1224.984863, 8.691818, 0, ''), +(21027, 32, -2658.393311, 1200.136719, 5.492243, 0, ''), +(21027, 33, -2668.504395, 1190.450562, 3.127407, 0, ''), +(21027, 34, -2685.930420, 1174.360840, 5.163924, 0, ''), +(21027, 35, -2701.613770, 1160.026367, 5.611311, 0, ''), +(21027, 36, -2714.659668, 1149.980347, 4.342373, 0, ''), +(21027, 37, -2721.443359, 1145.002808, 1.913474, 0, ''), +(21027, 38, -2733.962158, 1143.436279, 2.620415, 0, ''), +(21027, 39, -2757.876709, 1146.937500, 6.184002, 2000, 'SAY_WIL_JUST_AHEAD'), +(21027, 40, -2772.300537, 1166.052734, 6.331811, 0, ''), +(21027, 41, -2790.265381, 1189.941650, 5.207958, 0, ''), +(21027, 42, -2805.448975, 1208.663940, 5.557623, 0, ''), +(21027, 43, -2820.617676, 1225.870239, 6.266103, 0, ''), +(21027, 44, -2831.926758, 1237.725830, 5.808506, 0, ''), +(21027, 45, -2842.578369, 1252.869629, 6.807481, 0, ''), +(21027, 46, -2846.344971, 1258.727295, 7.386168, 0, ''), +(21027, 47, -2847.556396, 1266.771729, 8.208790, 0, ''), +(21027, 48, -2841.654541, 1285.809204, 7.933223, 0, ''), +(21027, 49, -2841.754883, 1289.832520, 6.990304, 0, ''), +(21027, 50, -2871.398438, 1302.348145, 6.807335, 7500, 'SAY_WIL_END'); + +DELETE FROM script_waypoint WHERE entry=22377; +INSERT INTO script_waypoint VALUES +(22377, 0, -2770.457520, 5418.410645, -34.538, 0, ''), +(22377, 1, -2778.180420, 5416.253906, -34.538, 0, ''), +(22377, 2, -2816.960449, 5414.944336, -34.529, 0, ''), +(22377, 3, -2827.533203, 5414.737305, -28.265, 0, ''), +(22377, 4, -2841.610596, 5413.021973, -28.261, 0, ''), +(22377, 5, -2863.605957, 5411.964355, -28.262, 1000, 'SAY_AKU_AMBUSH_A'), +(22377, 6, -2874.559570, 5413.799316, -28.260, 0, ''), +(22377, 7, -2878.775879, 5413.812012, -28.261, 0, ''), +(22377, 8, -2892.586914, 5413.478516, -18.784, 0, ''), +(22377, 9, -2896.040527, 5413.137207, -18.589, 0, ''), +(22377, 10, -2896.318848, 5409.431641, -18.450, 0, ''), +(22377, 11, -2895.997803, 5396.909668, -8.855, 0, ''), +(22377, 12, -2895.734131, 5386.623535, -9.260, 0, ''), +(22377, 13, -2895.318359, 5367.613281, -9.456, 0, ''), +(22377, 14, -2890.306641, 5353.883301, -11.280, 1000, 'SAY_AKU_AMBUSH_B'), +(22377, 15, -2880.419189, 5334.625977, -10.629, 0, ''), +(22377, 16, -2866.394043, 5314.253906, -9.678, 0, ''), +(22377, 17, -2864.753174, 5277.734375, -11.087, 0, ''), +(22377, 18, -2856.330322, 5255.902344, -11.496, 5000, 'SAY_AKU_COMPLETE'); + +DELETE FROM script_waypoint WHERE entry=22458; +INSERT INTO script_waypoint VALUES +(22458, 0, -3739.907959, 5393.691895, -4.213, 5000, 'SAY_LE_KEEP_SAFE'), +(22458, 1, -3733.334229, 5389.243164, -5.331, 0, ''), +(22458, 2, -3728.771729, 5385.649414, -3.704, 0, ''), +(22458, 3, -3717.267090, 5379.179199, -4.400, 0, ''), +(22458, 4, -3705.626465, 5379.261719, -7.711, 0, ''), +(22458, 5, -3688.279541, 5379.716309, -9.400, 0, ''), +(22458, 6, -3649.186523, 5389.111816, -11.917, 0, ''), +(22458, 7, -3612.791504, 5392.812500, -13.655, 0, ''), +(22458, 8, -3574.865479, 5412.704590, -16.543, 0, ''), +(22458, 9, -3564.438232, 5422.615723, -16.104, 0, ''), +(22458, 10, -3553.387695, 5444.732910, -12.184, 2500, 'arivve dig site SAY_LE_ARRIVE'), +(22458, 11, -3557.291016, 5465.319336, -9.282, 7500, 'dig 1'), +(22458, 12, -3548.102051, 5453.417969, -12.282, 10000, 'dig 2 SAY_LE_BURIED pause'), +(22458, 13, -3556.580322, 5446.475098, -11.920, 0, 'start returning'), +(22458, 14, -3564.438232, 5422.615723, -16.104, 0, ''), +(22458, 15, -3574.865479, 5412.704590, -16.543, 0, ''), +(22458, 16, -3612.791504, 5392.812500, -13.655, 0, ''), +(22458, 17, -3649.186523, 5389.111816, -11.917, 0, ''), +(22458, 18, -3688.279541, 5379.716309, -9.400, 0, ''), +(22458, 19, -3705.626465, 5379.261719, -7.711, 0, ''), +(22458, 20, -3717.267090, 5379.179199, -4.400, 0, ''), +(22458, 21, -3728.771729, 5385.649414, -3.704, 0, ''), +(22458, 22, -3733.334229, 5389.243164, -5.331, 0, ''), +(22458, 23, -3739.907959, 5393.691895, -4.213, 0, ''); + +DELETE FROM script_waypoint WHERE entry=22916; +INSERT INTO script_waypoint VALUES +(22916, 0, 7461.49, -3121.06, 438.210, 7000, 'SAY_START'), +(22916, 1, 7465.26, -3115.50, 439.315, 0, ''), +(22916, 2, 7470.03, -3109.29, 439.333, 0, ''), +(22916, 3, 7473.77, -3104.65, 442.366, 0, ''), +(22916, 4, 7478.67, -3098.55, 443.551, 0, ''), +(22916, 5, 7482.78, -3093.35, 441.883, 0, ''), +(22916, 6, 7486.23, -3089.19, 439.698, 0, ''), +(22916, 7, 7484.64, -3084.55, 439.566, 0, ''), +(22916, 8, 7477.09, -3084.43, 442.132, 0, ''), +(22916, 9, 7470.66, -3084.86, 443.194, 0, ''), +(22916, 10, 7456.51, -3085.83, 438.863, 0, ''), +(22916, 11, 7446.00, -3085.59, 438.210, 0, ''), +(22916, 12, 7444.60, -3084.10, 438.323, 0, ''), +(22916, 13, 7445.58, -3080.92, 439.374, 5000, 'collect 1'), +(22916, 14, 7446.18, -3085.36, 438.210, 5000, 'SAY_RELIC1'), +(22916, 15, 7453.90, -3086.69, 439.454, 0, ''), +(22916, 16, 7459.41, -3085.50, 439.158, 0, ''), +(22916, 17, 7465.90, -3085.01, 442.329, 0, ''), +(22916, 18, 7472.80, -3084.81, 443.085, 0, ''), +(22916, 19, 7480.58, -3084.56, 440.642, 0, ''), +(22916, 20, 7484.59, -3084.71, 439.568, 0, ''), +(22916, 21, 7491.81, -3090.55, 440.052, 0, ''), +(22916, 22, 7497.13, -3095.34, 437.505, 0, ''), +(22916, 23, 7496.61, -3113.62, 434.554, 0, ''), +(22916, 24, 7501.79, -3123.79, 435.347, 0, ''), +(22916, 25, 7506.60, -3130.78, 434.179, 0, ''), +(22916, 26, 7504.53, -3133.46, 435.579, 5000, 'collect 2'), +(22916, 27, 7505.20, -3130.03, 434.151, 15000, 'SAY_RELIC2'), +(22916, 28, 7502.04, -3124.44, 435.298, 0, ''), +(22916, 29, 7495.90, -3113.93, 434.538, 0, ''), +(22916, 30, 7488.79, -3111.10, 434.310, 0, ''), +(22916, 31, 7477.81, -3105.37, 430.541, 0, 'summon'), +(22916, 32, 7471.49, -3092.55, 429.006, 0, ''), +(22916, 33, 7472.35, -3062.72, 428.341, 0, ''), +(22916, 34, 7472.26, -3054.92, 427.150, 0, ''), +(22916, 35, 7475.03, -3053.39, 428.672, 5000, 'collect 3'), +(22916, 36, 7472.40, -3057.21, 426.870, 5000, 'SAY_RELIC3'), +(22916, 37, 7472.39, -3062.86, 428.301, 0, ''), +(22916, 38, 7470.24, -3087.69, 429.045, 0, ''), +(22916, 39, 7475.24, -3099.03, 429.917, 0, ''), +(22916, 40, 7484.24, -3109.85, 432.719, 0, ''), +(22916, 41, 7489.10, -3111.31, 434.400, 0, ''), +(22916, 42, 7497.02, -3108.54, 434.798, 0, ''), +(22916, 43, 7497.75, -3097.70, 437.031, 0, ''), +(22916, 44, 7492.53, -3090.12, 440.041, 0, ''), +(22916, 45, 7490.43, -3085.44, 439.807, 0, ''), +(22916, 46, 7501.02, -3069.70, 441.875, 0, ''), +(22916, 47, 7509.15, -3064.67, 445.012, 0, ''), +(22916, 48, 7515.78, -3060.16, 445.727, 0, ''), +(22916, 49, 7516.46, -3058.11, 445.682, 10000, 'quest credit'); + +DELETE FROM script_waypoint WHERE entry=23002; +INSERT INTO script_waypoint VALUES +(23002, 0, 3687.11, -3960.69, 31.8726, 0, ''), +(23002, 1, 3676.28, -3953.76, 29.9396, 0, ''), +(23002, 2, 3658.54, -3952.15, 30.0414, 0, ''), +(23002, 3, 3628.91, -3956.90, 29.4050, 0, ''), +(23002, 4, 3602.54, -3968.16, 31.5110, 0, ''), +(23002, 5, 3564.96, -3978.00, 30.3622, 0, ''), +(23002, 6, 3542.47, -3981.81, 29.1465, 0, ''), +(23002, 7, 3511.34, -3981.25, 30.2822, 0, ''), +(23002, 8, 3473.45, -3992.67, 30.2861, 0, ''), +(23002, 9, 3439.10, -4006.73, 29.2737, 0, ''), +(23002, 10, 3415.66, -4026.24, 25.2498, 0, ''), +(23002, 11, 3380.88, -4045.38, 26.3114, 0, ''), +(23002, 12, 3355.23, -4051.42, 25.5665, 0, ''), +(23002, 13, 3312.00, -4055.65, 28.3297, 0, ''), +(23002, 14, 3286.34, -4079.27, 28.2464, 0, ''), +(23002, 15, 3260.68, -4087.29, 31.4043, 0, ''), +(23002, 16, 3236.83, -4087.65, 32.6894, 0, ''), +(23002, 17, 3215.06, -4082.10, 32.4181, 0, ''), +(23002, 18, 3203.59, -4082.47, 32.7436, 0, ''), +(23002, 19, 3166.41, -4062.09, 33.2357, 0, ''), +(23002, 20, 3147.51, -4055.33, 33.5683, 0, ''), +(23002, 21, 3125.41, -4050.01, 34.6100, 0, ''), +(23002, 22, 3121.16, -4045.07, 36.5481, 0, ''), +(23002, 23, 3101.54, -4023.78, 33.7169, 0, ''), +(23002, 24, 3094.16, -4016.89, 33.8487, 0, ''), +(23002, 25, 3079.57, -4011.01, 35.7546, 0, ''), +(23002, 26, 3058.83, -4001.71, 34.3039, 0, ''), +(23002, 27, 3037.83, -3986.60, 33.4216, 0, ''), +(23002, 28, 3016.93, -3970.83, 33.3743, 0, ''), +(23002, 29, 2998.05, -3954.89, 33.2338, 0, ''), +(23002, 30, 2969.35, -3929.27, 33.4831, 0, ''), +(23002, 31, 2941.23, -3909.56, 31.3506, 0, ''), +(23002, 32, 2911.42, -3895.07, 32.0950, 0, ''), +(23002, 33, 2892.44, -3875.52, 30.8123, 0, ''), +(23002, 34, 2870.52, -3858.97, 32.1977, 0, ''), +(23002, 35, 2865.84, -3836.99, 32.1108, 0, ''), +(23002, 36, 2850.52, -3814.52, 32.8635, 0, ''), +(23002, 37, 2836.63, -3796.94, 33.1473, 0, ''), +(23002, 38, 2820.73, -3780.22, 28.6916, 0, ''), +(23002, 39, 2795.82, -3770.13, 30.1327, 0, ''), +(23002, 40, 2773.15, -3765.54, 30.2947, 0, ''), +(23002, 41, 2742.31, -3761.65, 30.1218, 0, ''), +(23002, 42, 2708.43, -3748.46, 21.2468, 0, ''), +(23002, 43, 2661.45, -3741.11, 21.9603, 0, ''), +(23002, 44, 2623.89, -3735.29, 25.8979, 0, ''), +(23002, 45, 2585.93, -3728.85, 28.5146, 0, ''), +(23002, 46, 2554.93, -3730.10, 26.6795, 0, ''), +(23002, 47, 2538.68, -3721.28, 28.1589, 0, ''), +(23002, 48, 2508.54, -3708.71, 29.6718, 0, ''), +(23002, 49, 2474.69, -3710.37, 31.0805, 0, ''), +(23002, 50, 2456.40, -3698.83, 31.6187, 0, ''), +(23002, 51, 2430.54, -3701.87, 31.0494, 0, ''), +(23002, 52, 2390.13, -3681.76, 29.5484, 0, ''), +(23002, 53, 2357.06, -3673.96, 29.8845, 0, ''), +(23002, 54, 2330.15, -3672.73, 31.1314, 0, ''), +(23002, 55, 2302.77, -3665.22, 29.4110, 0, ''), +(23002, 56, 2279.24, -3659.46, 29.6247, 0, ''), +(23002, 57, 2254.65, -3661.12, 29.6984, 0, ''), +(23002, 58, 2223.32, -3654.92, 31.0149, 0, ''), +(23002, 59, 2194.29, -3645.40, 32.0417, 0, ''), +(23002, 60, 2153.05, -3650.82, 31.2292, 0, ''), +(23002, 61, 2114.15, -3639.96, 31.7371, 0, ''), +(23002, 62, 2093.68, -3646.65, 31.3745, 0, ''), +(23002, 63, 2069.86, -3670.59, 30.6172, 0, ''), +(23002, 64, 2024.40, -3677.64, 29.7682, 0, ''), +(23002, 65, 1988.61, -3680.02, 31.8937, 0, ''), +(23002, 66, 1962.68, -3692.17, 32.7811, 0, ''), +(23002, 67, 1931.94, -3708.48, 31.3641, 0, ''), +(23002, 68, 1893.36, -3710.02, 33.0193, 0, ''), +(23002, 69, 1865.73, -3718.35, 32.1664, 0, ''), +(23002, 70, 1839.74, -3732.92, 32.5322, 0, ''), +(23002, 71, 1805.08, -3757.76, 32.6295, 0, ''), +(23002, 72, 1780.24, -3775.53, 30.5931, 0, ''), +(23002, 73, 1753.28, -3786.79, 30.7445, 0, ''), +(23002, 74, 1731.09, -3796.64, 36.8866, 0, ''); + +DELETE FROM script_waypoint WHERE entry=24358; +INSERT INTO script_waypoint VALUES +(24358, 0, 121.193970, 1645.619385, 42.021, 0, ''), +(24358, 1, 132.051468, 1642.176025, 42.021, 5000, 'SAY_AT_GONG'), +(24358, 2, 120.670631, 1636.346802, 42.415, 0, ''), +(24358, 3, 120.536003, 1611.654663, 43.473, 10000, 'SAY_OPEN_ENTRANCE'), +(24358, 4, 120.536003, 1611.654663, 43.473, 0, ''); + +DELETE FROM script_waypoint WHERE entry=28070; +INSERT INTO script_waypoint VALUES +(28070, 0, 1053.789795, 476.639343, 207.744, 0, ''), +(28070, 1, 1032.293945, 467.623444, 207.736, 0, ''), +(28070, 2, 1017.908752, 454.765656, 207.719, 0, ''), +(28070, 3, 1004.810120, 441.305115, 207.373, 0, ''), +(28070, 4, 988.694214, 424.422485, 207.425, 0, ''), +(28070, 5, 984.816345, 422.177917, 205.994, 0, ''), +(28070, 6, 977.204468, 420.026917, 205.994, 0, ''), +(28070, 7, 962.388123, 421.983307, 205.994, 0, ''), +(28070, 8, 950.419556, 416.515198, 205.994, 0, ''), +(28070, 9, 943.972290, 403.071228, 205.994, 0, ''), +(28070, 10, 947.921936, 387.683563, 205.994, 0, ''), +(28070, 11, 946.554749, 383.270782, 205.994, 0, ''), +(28070, 12, 944.654724, 380.630859, 207.286, 0, ''), +(28070, 13, 941.101563, 377.373413, 207.421, 0, 'reach tribunal, set pause'), +(28070, 14, 935.217896, 370.557343, 207.421, 0, ''), +(28070, 15, 928.035950, 363.026733, 204.018, 0, ''), +(28070, 16, 909.287292, 344.392792, 203.706, 0, ''), +(28070, 17, 897.946838, 333.634735, 203.706, 0, 'reach panel'), +(28070, 18, 918.914429, 351.312866, 203.706, 0, 'reach floor disc (end event begin)'), +(28070, 19, 928.070068, 363.296326, 204.091, 0, 'stealth'), +(28070, 20, 934.817627, 370.136261, 207.421, 0, ''), +(28070, 21, 941.501465, 377.254456, 207.421, 0, ''); + +DELETE FROM script_waypoint WHERE entry=28217; +INSERT INTO script_waypoint VALUES +(28217, 0, 5384.218262, 4533.261230, -129.518799, 0, ''), +(28217, 1, 5394.103027, 4531.190918, -131.758179, 0, ''), +(28217, 2, 5401.982910, 4527.303711, -137.599258, 0, ''), +(28217, 3, 5407.979492, 4526.484375, -143.597122, 0, ''), +(28217, 4, 5420.837402, 4519.582520, -144.921677, 0, ''), +(28217, 5, 5428.551758, 4522.227051, -148.790253, 0, ''), +(28217, 6, 5438.542480, 4536.080566, -149.651520, 0, ''), +(28217, 7, 5452.433105, 4553.935059, -149.093414, 0, ''), +(28217, 8, 5460.834961, 4564.371582, -148.660049, 0, ''), +(28217, 9, 5463.245605, 4584.000000, -148.961945, 0, ''), +(28217,10, 5463.708984, 4603.705566, -147.329636, 0, ''), +(28217,11, 5470.239258, 4609.115234, -145.223984, 0, ''), +(28217,12, 5479.432617, 4609.195313, -141.364014, 0, ''), +(28217,13, 5487.466309, 4615.625000, -138.139740, 0, ''), +(28217,14, 5497.967773, 4634.802734, -134.696869, 0, ''), +(28217,15, 5527.621582, 4648.053711, -136.170990, 0, ''), +(28217,16, 5547.706055, 4651.724121, -134.740707, 0, ''), +(28217,17, 5559.466309, 4652.008301, -134.154831, 0, ''), +(28217,18, 5579.070313, 4652.293945, -136.745895, 0, ''), +(28217,19, 5593.437500, 4643.722168, -136.405670, 0, ''), +(28217,20, 5608.825684, 4630.810547, -136.833588, 0, ''), +(28217,21, 5629.032227, 4607.479492, -137.093552, 0, ''), +(28217,22, 5634.952148, 4600.204102, -137.246063, 5000, 'thanks and quest credit'), +(28217,23, 5638.541504, 4594.924805, -137.495117, 0, 'summon'), +(28217,24, 5638.061523, 4579.945801, -138.029465, 0, ''); + +DELETE FROM script_waypoint WHERE entry=28787; +INSERT INTO script_waypoint (entry, pointid, location_x, location_y, location_z, waittime, point_comment) VALUES +(28787, 1, 5913.516113, 5379.034668, -98.896118, 0, ''), +(28787, 2, 5917.750977, 5374.519043, -98.869781, 0, 'SAY_HELICE_EXPLOSIVES_1'), +(28787, 3, 5926.428711, 5372.145020, -98.884453, 0, ''), +(28787, 4, 5929.214844, 5377.803223, -99.020065, 0, ''), +(28787, 5, 5927.621582, 5378.564941, -99.047890, 0, ''), +(28787, 6, 5917.622070, 5383.494629, -106.310204, 0, ''), +(28787, 7, 5908.991211, 5387.655762, -106.310204, 0, ''), +(28787, 8, 5906.287109, 5390.496582, -106.041801, 0, ''), +(28787, 9, 5902.415039, 5399.741211, -99.306595, 0, ''), +(28787, 10, 5901.444336, 5404.593262, -96.759636, 0, ''), +(28787, 11, 5897.860352, 5406.656250, -96.029709, 0, ''), +(28787, 12, 5892.254395, 5401.291504, -95.848808, 0, ''), +(28787, 13, 5887.421875, 5386.701172, -95.400146, 0, 'SAY_HELICE_EXPLOSIVES_2'), +(28787, 14, 5883.308105, 5385.057617, -94.423698, 0, ''), +(28787, 15, 5879.180664, 5375.897461, -95.088066, 0, ''), +(28787, 16, 5872.613281, 5363.473633, -97.703575, 0, ''), +(28787, 17, 5857.971191, 5354.929688, -98.586090, 0, ''), +(28787, 18, 5848.729004, 5345.326660, -99.428978, 0, ''), +(28787, 19, 5842.330566, 5335.018555, -100.421455, 0, ''), +(28787, 20, 5832.164551, 5323.145020, -98.703285, 0, ''), +(28787, 21, 5824.738770, 5315.712891, -97.758018, 0, ''), +(28787, 22, 5819.650879, 5305.409668, -97.481796, 10000, 'SAY_HELICE_COMPLETE'); + +DELETE FROM script_waypoint WHERE entry=28912; +INSERT INTO script_waypoint VALUES +(28912, 0, 1653.518, -6038.374, 127.585, 0, 'Jump off'), +(28912, 1, 1653.978, -6034.614, 127.585, 5000, 'To Box'), +(28912, 2, 1653.854, -6034.726, 127.585, 500, 'Equip'), +(28912, 3, 1652.297, -6035.671, 127.585, 3000, 'Recover'), +(28912, 4, 1639.762, -6046.343, 127.948, 0, 'Escape'), +(28912, 5, 1640.963, -6028.119, 134.740, 0, ''), +(28912, 6, 1625.805, -6029.197, 134.740, 0, ''), +(28912, 7, 1626.845, -6015.085, 134.740, 0, ''), +(28912, 8, 1649.150, -6016.975, 133.240, 0, ''), +(28912, 9, 1653.063, -5974.844, 132.652, 5000, 'Mount'), +(28912, 10, 1654.747, -5926.424, 121.191, 0, 'Disappear'); + +DELETE FROM script_waypoint WHERE entry=30658; +INSERT INTO script_waypoint VALUES +(30658, 0, 1830.504517, 799.356506, 44.341801, 5000, 'use activation'), +(30658, 1, 1832.461792, 800.431396, 44.311745, 10000, 'SAY_BEGIN call back guards'), +(30658, 2, 1824.786987, 803.828369, 44.363434, 3000, 'SAY_LOCK_DOOR'), +(30658, 3, 1824.786987, 803.828369, 44.363434, 0, 'close door'), +(30658, 4, 1817.315674, 804.060608, 44.363998, 0, 'escort paused - allow teleport inside'), +(30658, 5, 1826.889648, 803.929993, 44.363239, 30000, 'SAY_VICTORY'); + +DELETE FROM script_waypoint WHERE entry = 349; +INSERT INTO script_waypoint VALUES +(349, 01, -8769.591797, -2185.733643, 141.974564, 0, ''), +(349, 02, -8776.540039, -2193.782959, 140.960159, 0, ''), +(349, 03, -8783.289063, -2194.818604, 140.461731, 0, ''), +(349, 04, -8792.520508, -2188.802002, 142.077728, 0, ''), +(349, 05, -8807.547852, -2186.100830, 141.504135, 0, ''), +(349, 06, -8818, -2184.8, 139.153, 0, ''), +(349, 07, -8825.805664, -2188.840576, 138.458832, 0, ''), +(349, 08, -8827.522461, -2199.805664, 139.621933, 0, ''), +(349, 09, -8821.140625, -2212.642334, 143.126419, 0, ''), +(349, 10, -8809.175781, -2230.456299, 143.438431, 0, ''), +(349, 11, -8797.040039, -2240.718262, 146.548203, 0, ''), +(349, 12, -8795.242188, -2251.809814, 146.808044, 0, ''), +(349, 13, -8780.159180, -2258.615967, 148.553772, 0, ''), +(349, 14, -8762.650391, -2259.559326, 151.144241, 0, ''), +(349, 15, -8754.357422, -2253.735352, 152.243073, 0, ''), +(349, 16, -8741.869141, -2250.997070, 154.485718, 0, ''), +(349, 17, -8733.218750, -2251.010742, 154.360031, 0, ''), +(349, 18, -8717.474609, -2245.044678, 154.68614, 0, ''), +(349, 19, -8712.240234, -2246.826172, 154.709473, 0, ''), +(349, 20, -8693.840820, -2240.410889, 152.909714, 0, ''), +(349, 21, -8681.818359, -2245.332764, 155.517838, 0, ''), +(349, 22, -8669.86, -2252.77, 154.854, 0, ''), +(349, 23, -8670.56, -2264.69, 156.978, 0, ''), +(349, 24, -8676.557617, -2269.204346, 155.411316, 0, ''), +(349, 25, -8673.340820, -2288.650146, 157.054123, 0, ''), +(349, 26, -8677.760742, -2302.563965, 155.916580, 16000, 'Corp. Keeshan - Short Break Outside'), +(349, 27, -8682.462891, -2321.688232, 155.916946, 0, ''), +(349, 28, -8690.402344, -2331.779297, 155.970505, 0, ''), +(349, 29, -8715.1, -2353.95, 156.188, 0, ''), +(349, 30, -8748.042969, -2370.701904, 157.988342, 0, ''), +(349, 31, -8780.900391, -2421.370361, 156.108871, 0, ''), +(349, 32, -8792.009766, -2453.379883, 142.746002, 0, ''), +(349, 33, -8804.780273, -2472.429932, 134.192001, 0, ''), +(349, 34, -8841.348633, -2503.626221, 132.276138, 0, ''), +(349, 35, -8867.565430, -2529.892822, 134.738586, 0, ''), +(349, 36, -8870.67, -2542.08, 131.044, 0, ''), +(349, 37, -8922.05, -2585.31, 132.446, 0, ''), +(349, 38, -8949.08, -2596.87, 132.537, 0, ''), +(349, 39, -8993.460938, -2604.042725, 130.756210, 0, ''), +(349, 40, -9006.709961, -2598.469971, 127.966003, 0, ''), +(349, 41, -9038.96, -2572.71, 124.748, 0, ''), +(349, 42, -9046.92, -2560.64, 124.447, 0, ''), +(349, 43, -9066.693359, -2546.633301, 123.110138, 0, ''), +(349, 44, -9077.54, -2541.67, 121.17, 0, ''), +(349, 45, -9125.320313, -2490.059326, 116.057274, 0, ''), +(349, 46, -9145.063477, -2442.239990, 108.231689, 0, ''), +(349, 47, -9158.197266, -2425.363281, 105.500038, 0, ''), +(349, 48, -9151.922852, -2393.671631, 100.856010, 0, ''), +(349, 49, -9165.193359, -2376.031738, 94.821518, 0, ''), +(349, 50, -9187.099609, -2360.520020, 89.923103, 0, ''), +(349, 51, -9235.443359, -2305.239014, 77.925316, 0, ''), +(349, 52, -9264.73, -2292.92, 70.0089, 0, ''), +(349, 53, -9277.468750, -2296.188721, 68.089630, 2500, 'Corp. Keeshan - quest-finish'), +(349, 54, -9277.468750, -2296.188721, 68.089630, 0, 'Corp. Keeshan - Say Goodbye'); + +DELETE FROM script_waypoint WHERE entry=1379; +INSERT INTO script_waypoint VALUES +(1379,01,-5751.12,-3441.01,301.743,0,''), +(1379,02,-5738.58,-3485.14,302.410,0,''), +(1379,03,-5721.62,-3507.85,304.011,0,''), +(1379,04,-5710.21,-3527.97,304.708,0,''), +(1379,05,-5706.92,-3542.89,304.871,0,''), +(1379,06,-5701.53,-3551.24,305.962,0,''), +(1379,07,-5699.53,-3555.69,306.505,0,''), +(1379,08,-5690.56,-3571.98,309.035,0,''), +(1379,09,-5678.61,-3587.17,310.607,0,''), +(1379,10,-5677.05,-3594.35,311.527,0,''), +(1379,11,-5674.39,-3605.19,312.239,0,''), +(1379,12,-5674.45,-3614.39,312.337,0,''), +(1379,13,-5673.05,-3630.56,311.105,0,''), +(1379,14,-5680.34,-3645.44,315.185,0,''), +(1379,15,-5684.46,-3650.05,314.687,0,''), +(1379,16,-5693.9,-3674.14,313.03,0,''), +(1379,17,-5701.43,-3712.54,313.959,0,''), +(1379,18,-5698.79,-3720.88,316.943,0,''), +(1379,19,-5699.95,-3733.63,318.597,0,'Protecting the Shipment - Ambush'), +(1379,20,-5698.61,-3754.74,322.047,0,''), +(1379,21,-5688.68,-3769,323.957,0,''), +(1379,22,-5688.14,-3782.65,322.667,0,''), +(1379,23,-5699.23,-3792.65,322.448,30000,'Protecting the Shipment - End'), +(1379,24,-5700.80,-3792.78,322.588,0,''); + +DELETE FROM script_waypoint WHERE entry = 25208; +INSERT INTO script_waypoint VALUES +(25208,0,4013.51,6390.33,29.970,15000,'Lurgglbr - after escaped from cage'), +(25208,1,4023.06,6379.43,29.210,0,''), +(25208,2,4033.61,6370.94,28.430,0,''), +(25208,3,4052.03,6367.42,27.370,0,''), +(25208,4,4061.13,6353.36,25.450,0,''), +(25208,5,4064.28,6330.76,25.310,0,''), +(25208,6,4057.94,6307.85,24.900,0,''), +(25208,7,4057.40,6290.12,24.430,0,''), +(25208,8,4065.63,6277.64,23.900,0,''), +(25208,9,4080.71,6280.77,26.920,0,''), +(25208,10,4098.90,6279.00,25.950,0,''), +(25208,11,4118.00,6277.81,25.720,0,''), +(25208,12,4129.47,6281.65,28.860,0,''), +(25208,13,4143.86,6282.57,29.180,4000,'Lurgglbr - outside cave'), +(25208,14,4159.54,6280.08,30.520,0,''), +(25208,15,4171.95,6291.50,22.250,0,''), +(25208,16,4184.86,6293.45,16.570,0,''), +(25208,17,4194.14,6301.28,13.310,0,''), +(25208,18,4210.34,6285.81,09.500,0,''), +(25208,19,4220.05,6272.75,07.770,0,''), +(25208,20,4242.45,6272.24,01.750,0,''), +(25208,21,4257.79,6252.91,-0.050,0,''), +(25208,22,4256.81,6230.74,-0.090,0,''), +(25208,23,4241.09,6217.87,-0.140,0,''), +(25208,24,4254.66,6205.16,-0.170,0,''), +(25208,25,4270.07,6188.42,0.059,15000,'Lurgglbr - final point'); + +DELETE FROM script_waypoint WHERE entry=7998; +INSERT INTO script_waypoint VALUES +(7998, 01, -510.1305, -132.6899, -152.5, 0, ''), +(7998, 02, -511.0994, -129.74, -153.8453, 0, ''), +(7998, 03, -511.7897, -127.4764, -155.5505, 0, ''), +(7998, 04, -512.9688, -124.926, -156.1146, 5000, ''), +(7998, 05, -513.9719, -120.2358, -156.1161, 0, ''), +(7998, 06, -514.3875, -115.1896, -156.1165, 0, ''), +(7998, 07, -514.3039, -111.4777, -155.5205, 0, ''), +(7998, 08, -514.8401, -107.6633, -154.8925, 0, ''), +(7998, 09, -518.9943, -101.4161, -154.6482, 27000, ''), +(7998, 10, -526.9984, -98.14884, -155.6253, 0, ''), +(7998, 11, -534.5686, -105.4101, -155.989, 30000, ''), +(7998, 12, -535.5336, -104.6947, -155.9713, 0, ''), +(7998, 13, -541.6304, -98.6583, -155.8584, 25000, ''), +(7998, 14, -535.0923, -99.91748, -155.9742, 0, ''), +(7998, 15, -519.0099, -101.5097, -154.6766, 3000, ''), +(7998, 16, -504.4659, -97.84802, -150.9554, 30000, ''), +(7998, 17, -506.9069, -89.14736, -151.083, 23000, ''), +(7998, 18, -512.7576, -101.9025, -153.198, 0, ''), +(7998, 19, -519.9883, -124.8479, -156.128, 86400000, 'this npc should not reset on wp end'); + +DELETE FROM script_waypoint WHERE entry = 18760; +INSERT INTO script_waypoint (entry, pointid, location_x, location_y, location_z, waittime, point_comment) VALUES +(18760, 01, -2267.07, 3091.46, 13.8271, 0, ''), +(18760, 02, -2270.92, 3094.19, 13.8271, 0, ''), +(18760, 03, -2279.08, 3100.04, 13.8271, 0, ''), +(18760, 04, -2290.05, 3105.07, 13.8271, 0, ''), +(18760, 05, -2297.64, 3112.32, 13.8271, 0, ''), +(18760, 06, -2303.89, 3118.22, 13.8231, 10000, 'building exit'), +(18760, 07, -2307.77, 3123.47, 13.7257, 0, ''), +(18760, 08, -2310.67, 3126.2, 12.5841, 0, ''), +(18760, 09, -2311.48, 3126.98, 12.2769, 0, ''), +(18760, 10, -2316.91, 3132.13, 11.9261, 0, ''), +(18760, 11, -2320.43, 3135.54, 11.7436, 0, ''), +(18760, 12, -2327.38, 3139.36, 10.9431, 0, ''), +(18760, 13, -2332.02, 3142.05, 9.81277, 0, ''), +(18760, 14, -2338.21, 3145.32, 9.31001, 0, ''), +(18760, 15, -2343.1, 3148.91, 8.84879, 0, ''), +(18760, 16, -2347.76, 3153.15, 7.71049, 0, ''), +(18760, 17, -2351.04, 3156.12, 6.66476, 0, ''), +(18760, 18, -2355.15, 3163.18, 5.11997, 0, ''), +(18760, 19, -2359.01, 3169.83, 3.64343, 0, ''), +(18760, 20, -2364.85, 3176.81, 2.32802, 0, ''), +(18760, 21, -2368.77, 3181.69, 1.53285, 0, ''), +(18760, 22, -2371.76, 3185.11, 0.979932, 0, ''), +(18760, 23, -2371.85, 3191.89, -0.293048, 0, ''), +(18760, 24, -2370.99, 3199.6, -1.10504, 0, 'turn left 1'), +(18760, 25, -2376.24, 3205.54, -1.04152, 0, ''), +(18760, 26, -2380.99, 3211.61, -1.16891, 0, ''), +(18760, 27, -2384.04, 3218.4, -1.15279, 0, ''), +(18760, 28, -2385.41, 3226.22, -1.23403, 0, ''), +(18760, 29, -2386.02, 3233.89, -1.31723, 0, ''), +(18760, 30, -2384.7, 3239.82, -1.51903, 0, ''), +(18760, 31, -2382.98, 3247.94, -1.39163, 0, ''), +(18760, 32, -2379.68, 3254.22, -1.25927, 0, ''), +(18760, 33, -2375.27, 3259.69, -1.22925, 0, ''), +(18760, 34, -2369.62, 3264.55, -1.1879, 0, ''), +(18760, 35, -2364.01, 3268.32, -1.48348, 0, ''), +(18760, 36, -2356.61, 3272.31, -1.5505, 0, ''), +(18760, 37, -2352.3, 3274.63, -1.35693, 0, ''), +(18760, 38, -2348.54, 3278.04, -1.04161, 0, 'turn left 2'), +(18760, 39, -2347.56, 3282.41, -0.75979, 0, ''), +(18760, 40, -2348.29, 3288.91, -0.63215, 0, ''), +(18760, 41, -2349.68, 3298.84, -1.07864, 0, ''), +(18760, 42, -2351.15, 3308.52, -1.38864, 0, ''), +(18760, 43, -2352.2, 3317.14, -1.59873, 0, ''), +(18760, 44, -2351.59, 3325.51, -1.92624, 0, ''), +(18760, 45, -2350.88, 3333.38, -2.32506, 0, ''), +(18760, 46, -2350.05, 3342.68, -2.51886, 0, ''), +(18760, 47, -2350.12, 3347.32, -2.57528, 0, ''), +(18760, 48, -2348.72, 3353.7, -2.72689, 0, ''), +(18760, 49, -2346.53, 3360.85, -2.9756, 0, ''), +(18760, 50, -2344.83, 3365.46, -3.3311, 0, ''), +(18760, 51, -2342.68, 3368.91, -3.74526, 0, ''), +(18760, 52, -2340.25, 3371.44, -4.10499, 0, ''), +(18760, 53, -2337.4, 3373.47, -4.44362, 0, ''), +(18760, 54, -2332.68, 3376.02, -5.19648, 0, ''), +(18760, 55, -2326.69, 3379.64, -6.24757, 0, ''), +(18760, 56, -2321.2, 3383.98, -7.28247, 0, ''), +(18760, 57, -2317.81, 3387.78, -8.40093, 0, ''), +(18760, 58, -2315.3, 3392.47, -9.63431, 0, ''), +(18760, 59, -2314.69, 3396.6, -10.2031, 0, ''), +(18760, 60, -2315.48, 3402.35, -10.8211, 0, 'gate'), +(18760, 61, -2317.55, 3409.27, -11.3309, 5000, 'Firewing point exit'), +(18760, 62, -2320.69, 3412.99, -11.5207, 0, ''), +(18760, 63, -2326.88, 3417.89, -11.6105, 0, ''), +(18760, 64, -2332.73, 3421.74, -11.5659, 0, ''), +(18760, 65, -2337.23, 3424.89, -11.496, 0, ''), +(18760, 66, -2339.57, 3429.17, -11.3782, 0, ''), +(18760, 67, -2341.66, 3435.86, -11.3746, 5000, 'Wave and transform'), +(18760, 68, -2342.15, 3443.94, -11.2562, 2000, 'final destination'); + +DELETE FROM script_waypoint WHERE entry=3678; +INSERT INTO script_waypoint VALUES +(3678, 0, -134.925, 125.468, -78.16, 0, ''), +(3678, 1, -125.684, 132.937, -78.42, 0, ''), +(3678, 2, -113.812, 139.295, -80.98, 0, ''), +(3678, 3, -109.854, 157.538, -80.20, 0, ''), +(3678, 4, -108.640, 175.207, -79.74, 0, ''), +(3678, 5, -108.668, 195.457, -80.64, 0, ''), +(3678, 6, -111.007, 219.007, -86.58, 0, ''), +(3678, 7, -102.408, 232.821, -91.52, 0, 'first corner SAY_FIRST_CORNER'), +(3678, 8, -92.434, 227.742, -90.75, 0, ''), +(3678, 9, -82.456, 224.853, -93.57, 0, ''), +(3678, 10, -67.789, 208.073, -93.34, 0, ''), +(3678, 11, -43.343, 205.295, -96.37, 0, ''), +(3678, 12, -34.676, 221.394, -95.82, 0, ''), +(3678, 13, -32.582, 238.573, -93.51, 0, ''), +(3678, 14, -42.149, 258.672, -92.88, 0, ''), +(3678, 15, -55.257, 274.696, -92.83, 0, 'circle of flames SAY_CIRCLE_BANISH'), +(3678, 16, -48.604, 287.584, -92.46, 0, ''), +(3678, 17, -47.236, 296.093, -90.88, 0, ''), +(3678, 18, -35.618, 309.067, -89.73, 0, ''), +(3678, 19, -23.573, 311.376, -88.60, 0, ''), +(3678, 20, -8.692, 302.389, -87.43, 0, ''), +(3678, 21, -1.237, 293.268, -85.55, 0, ''), +(3678, 22, 10.398, 279.294, -85.86, 0, ''), +(3678, 23, 23.108, 264.693, -86.69, 0, ''), +(3678, 24, 31.996, 251.436, -87.62, 0, ''), +(3678, 25, 43.374, 233.073, -87.61, 0, ''), +(3678, 26, 54.438, 212.048, -89.50, 3000, 'chamber entrance SAY_NARALEX_CHAMBER'), +(3678, 27, 78.794, 208.895, -92.84, 0, ''), +(3678, 28, 88.392, 225.231, -94.46, 0, ''), +(3678, 29, 98.758, 233.938, -95.84, 0, ''), +(3678, 30, 107.248, 233.054, -95.98, 0, ''), +(3678, 31, 112.825, 233.907, -96.39, 0, ''), +(3678, 32, 114.634, 236.969, -96.04, 1000, 'naralex SAY_BEGIN_RITUAL'), +(3678, 33, 127.385, 252.279, -90.07, 0, ''), +(3678, 34, 121.595, 264.488, -91.55, 0, ''), +(3678, 35, 115.472, 264.253, -91.50, 0, ''), +(3678, 36, 99.988, 252.790, -91.51, 0, ''), +(3678, 37, 96.347, 245.038, -90.34, 0, ''), +(3678, 38, 82.201, 216.273, -86.10, 0, ''), +(3678, 39, 75.112, 206.494, -84.80, 0, ''), +(3678, 40, 27.174, 201.064, -72.31, 0, ''), +(3678, 41, -41.114, 204.149, -78.94, 0, ''); + +DELETE FROM script_waypoint WHERE entry=5644; +INSERT INTO script_waypoint (entry, pointid, location_x, location_y, location_z, waittime, point_comment) VALUES +(5644, 1, -339.679, 1752.04, 139.482, 0, ''), +(5644, 2, -328.957, 1734.95, 139.327, 0, ''), +(5644, 3, -338.29, 1731.36, 139.327, 0, ''), +(5644, 4, -350.747, 1731.12, 139.338, 0, ''), +(5644, 5, -365.064, 1739.04, 139.376, 0, ''), +(5644, 6, -371.105, 1746.03, 139.374, 0, ''), +(5644, 7, -383.141, 1738.62, 138.93, 0, ''), +(5644, 8, -390.445, 1733.98, 136.353, 0, ''), +(5644, 9, -401.368, 1726.77, 131.071, 0, ''), +(5644, 10, -416.016, 1721.19, 129.807, 0, ''), +(5644, 11, -437.139, 1709.82, 126.342, 0, ''), +(5644, 12, -455.83, 1695.61, 119.305, 0, ''), +(5644, 13, -459.862, 1687.92, 116.059, 0, ''), +(5644, 14, -463.565, 1679.1, 111.653, 0, ''), +(5644, 15, -461.485, 1670.94, 109.033, 0, ''), +(5644, 16, -471.786, 1647.34, 102.862, 0, ''), +(5644, 17, -477.146, 1625.69, 98.342, 0, ''), +(5644, 18, -475.815, 1615.815, 97.07, 0, ''), +(5644, 19, -474.329, 1590.01, 94.4982, 0, ''); + +DELETE FROM script_waypoint WHERE entry=4508; +INSERT INTO script_waypoint VALUES +(4508, 0, 2194.38, 1791.65, 65.48, 5000, ''), +(4508, 1, 2188.56, 1805.87, 64.45, 0, ''), +(4508, 2, 2186.2, 1836.278, 59.859, 5000, 'SAY_WILLIX_1'), +(4508, 3, 2163.27, 1851.67, 56.73, 0, ''), +(4508, 4, 2140.22, 1845.02, 48.32, 0, ''), +(4508, 5, 2131.5, 1804.29, 46.85, 0, ''), +(4508, 6, 2096.18, 1789.03, 51.13, 3000, 'SAY_WILLIX_2'), +(4508, 7, 2074.46, 1780.09, 55.64, 0, ''), +(4508, 8, 2055.12, 1768.67, 58.46, 0, ''), +(4508, 9, 2037.83, 1748.62, 60.27, 5000, 'SAY_WILLIX_3'), +(4508, 10, 2037.51, 1728.94, 60.85, 0, ''), +(4508, 11, 2044.7, 1711.71, 59.71, 0, ''), +(4508, 12, 2067.66, 1701.84, 57.77, 0, ''), +(4508, 13, 2078.91, 1704.54, 56.77, 0, ''), +(4508, 14, 2097.65, 1715.24, 54.74, 3000, 'SAY_WILLIX_4'), +(4508, 15, 2106.44, 1720.98, 54.41, 0, ''), +(4508, 16, 2123.96, 1732.56, 52.27, 0, ''), +(4508, 17, 2153.82, 1728.73, 51.92, 0, ''), +(4508, 18, 2163.49, 1706.33, 54.42, 0, ''), +(4508, 19, 2158.75, 1695.98, 55.70, 0, ''), +(4508, 20, 2142.6, 1680.72, 58.24, 0, ''), +(4508, 21, 2118.31, 1671.54, 59.21, 0, ''), +(4508, 22, 2086.02, 1672.04, 61.24, 0, ''), +(4508, 23, 2068.81, 1658.93, 61.24, 0, ''), +(4508, 24, 2062.82, 1633.31, 64.35, 0, ''), +(4508, 25, 2060.92, 1600.11, 62.41, 3000, 'SAY_WILLIX_5'), +(4508, 26, 2063.05, 1589.16, 63.26, 0, ''), +(4508, 27, 2063.67, 1577.22, 65.89, 0, ''), +(4508, 28, 2057.94, 1560.68, 68.40, 0, ''), +(4508, 29, 2052.56, 1548.05, 73.35, 0, ''), +(4508, 30, 2045.22, 1543.4, 76.65, 0, ''), +(4508, 31, 2034.35, 1543.01, 79.70, 0, ''), +(4508, 32, 2029.95, 1542.94, 80.79, 0, ''), +(4508, 33, 2021.34, 1538.67, 80.8, 0, 'SAY_WILLIX_6'), +(4508, 34, 2012.45, 1549.48, 79.93, 0, ''), +(4508, 35, 2008.05, 1554.92, 80.44, 0, ''), +(4508, 36, 2006.54, 1562.72, 81.11, 0, ''), +(4508, 37, 2003.8, 1576.43, 81.57, 0, ''), +(4508, 38, 2000.57, 1590.06, 80.62, 0, ''), +(4508, 39, 1998.96, 1596.87, 80.22, 0, ''), +(4508, 40, 1991.19, 1600.82, 79.39, 0, ''), +(4508, 41, 1980.71, 1601.44, 79.77, 0, ''), +(4508, 42, 1967.22, 1600.18, 80.62, 0, ''), +(4508, 43, 1956.43, 1596.97, 81.75, 0, ''), +(4508, 44, 1954.87, 1592.02, 82.18, 3000, 'SAY_WILLIX_7'), +(4508, 45, 1948.35, 1571.35, 80.96, 30000, 'SAY_WILLIX_END'), +(4508, 46, 1947.02, 1566.42, 81.80, 30000, ''); + +DELETE FROM script_waypoint WHERE entry = 8516; +INSERT INTO script_waypoint VALUES +(8516, 1,2603.18, 725.259, 54.6927, 0, ''), +(8516, 2,2587.13, 734.392, 55.231, 0, ''), +(8516, 3,2570.69, 753.572, 54.5855, 0, ''), +(8516, 4,2558.51, 747.66, 54.4482, 0, ''), +(8516, 5,2544.23, 772.924, 47.9255, 0, ''), +(8516, 6,2530.08, 797.475, 45.97, 0, ''), +(8516, 7,2521.83, 799.127, 44.3061, 0, ''), +(8516, 8,2502.61, 789.222, 39.5074, 0, ''), +(8516, 9,2495.25, 789.406, 39.499, 0, ''), +(8516, 10,2488.07, 802.455, 42.9834, 0, ''), +(8516, 11,2486.64, 826.649, 43.6363, 0, ''), +(8516, 12,2492.64, 835.166, 45.1427, 0, ''), +(8516, 13,2505.02, 847.564, 47.6487, 0, ''), +(8516, 14,2538.96, 877.362, 47.6781, 0, ''), +(8516, 15,2546.07, 885.672, 47.6789, 0, ''), +(8516, 16,2548.02, 897.584, 47.7277, 0, ''), +(8516, 17,2544.29, 909.116, 46.2506, 0, ''), +(8516, 18,2523.60, 920.306, 45.8717, 0, ''), +(8516, 19,2522.69, 933.546, 47.5769, 0, ''), +(8516, 20,2531.63, 959.893, 49.4111, 0, ''), +(8516, 21,2540.23, 973.338, 50.1241, 0, ''), +(8516, 22,2547.21, 977.489, 49.9759, 0, ''), +(8516, 23,2558.75, 969.243, 50.7353, 0, ''), +(8516, 24,2575.60, 950.138, 52.8460, 0, ''), +(8516, 25,2575.60, 950.138, 52.8460, 0, ''); + +DELETE FROM `script_waypoint` WHERE entry=29173; +INSERT INTO `script_waypoint` VALUES +(29173, 0, 2411.322, -5152.227, 83.777, 0,''), +(29173, 1, 2386.443, -5177.385, 74.049, 0,''), +(29173, 2, 2357.140, -5209.571, 79.642, 0,'SAY_LIGHT_OF_DAWN_STAND_1'), +(29173, 3, 2342.683, -5232.791, 85.259, 0,'SAY_LIGHT_OF_DAWN_STAND_2'), +(29173, 4, 2281.354, -5278.533, 82.227, 0,'Start battle'), +(29173, 5, 2280.302, -5284.489, 82.657, 600000,'Go in front of the chapel for outro'); + +DELETE FROM script_waypoint WHERE entry=11832; +INSERT INTO script_waypoint VALUES +(11832, 0, 7848.385645, -2216.356670, 470.888333, 15000, 'SAY_REMULOS_INTRO_1'), +(11832, 1, 7848.385645, -2216.356670, 470.888333, 5000, 'SAY_REMULOS_INTRO_2'), +(11832, 2, 7829.785645, -2244.836670, 463.853333, 0, ''), +(11832, 3, 7819.010742, -2304.344238, 455.956726, 0, ''), +(11832, 4, 7931.099121, -2314.350830, 473.054047, 0, ''), +(11832, 5, 7943.553223, -2324.688721, 477.676819, 0, ''), +(11832, 6, 7952.017578, -2351.135010, 485.234924, 0, ''), +(11832, 7, 7963.672852, -2412.990967, 488.953369, 0, ''), +(11832, 8, 7975.178223, -2551.602051, 490.079926, 0, ''), +(11832, 9, 7948.046875, -2570.828613, 489.750732, 0, ''), +(11832, 10, 7947.161133, -2583.396729, 490.066284, 0, ''), +(11832, 11, 7951.086426, -2596.215088, 489.831268, 0, ''), +(11832, 12, 7948.267090, -2610.062988, 492.340424, 0, ''), +(11832, 13, 7928.521973, -2625.954346, 492.447540, 0, 'escort paused - SAY_REMULOS_INTRO_3'), +(11832, 14, 7948.267090, -2610.062988, 492.340424, 0, ''), +(11832, 15, 7952.318848, -2594.118408, 490.070374, 0, ''), +(11832, 16, 7913.988770, -2567.002686, 488.330566, 0, ''), +(11832, 17, 7835.454102, -2571.099121, 489.289246, 0, 'escort paused - SAY_REMULOS_DEFEND_2'), +(11832, 18, 7897.283691, -2560.652344, 487.461304, 0, 'escort paused'); + +DELETE FROM script_waypoint WHERE entry=10300; +INSERT INTO script_waypoint VALUES +(10300, 1, 5728.81, -4801.15, 778.18, 0, ''), +(10300, 2, 5730.22, -4818.34, 777.11, 0, ''), +(10300, 3, 5728.12, -4835.76, 778.15, 1000, 'SAY_ENTER_OWL_THICKET'), +(10300, 4, 5718.85, -4865.62, 787.56, 0, ''), +(10300, 5, 5697.13, -4909.12, 801.53, 0, ''), +(10300, 6, 5684.20, -4913.75, 801.60, 0, ''), +(10300, 7, 5674.67, -4915.78, 802.13, 0, ''), +(10300, 8, 5665.61, -4919.22, 804.85, 0, ''), +(10300, 9, 5638.22, -4897.58, 804.97, 0, ''), +(10300, 10, 5632.67, -4892.05, 805.44, 0, 'Cavern 1 - EMOTE_CHANT_SPELL'), +(10300, 11, 5664.58, -4921.84, 804.91, 0, ''), +(10300, 12, 5684.21, -4943.81, 802.80, 0, ''), +(10300, 13, 5724.92, -4983.69, 808.25, 0, ''), +(10300, 14, 5753.39, -4990.73, 809.84, 0, ''), +(10300, 15, 5765.62, -4994.89, 809.42, 0, 'Cavern 2 - EMOTE_CHANT_SPELL'), +(10300, 16, 5724.94, -4983.58, 808.29, 0, ''), +(10300, 17, 5699.61, -4989.82, 808.03, 0, ''), +(10300, 18, 5686.80, -5012.17, 807.27, 0, ''), +(10300, 19, 5691.43, -5037.43, 807.73, 0, ''), +(10300, 20, 5694.24, -5054.64, 808.85, 0, 'Cavern 3 - EMOTE_CHANT_SPELL'), +(10300, 21, 5686.88, -5012.18, 807.23, 0, ''), +(10300, 22, 5664.94, -5001.12, 807.78, 0, ''), +(10300, 23, 5647.12, -5002.84, 807.54, 0, ''), +(10300, 24, 5629.23, -5014.88, 807.94, 0, ''), +(10300, 25, 5611.26, -5025.62, 808.36, 0, 'Cavern 4 - EMOTE_CHANT_SPELL'), +(10300, 26, 5647.13, -5002.85, 807.57, 0, ''), +(10300, 27, 5641.12, -4973.22, 809.39, 0, ''), +(10300, 28, 5622.97, -4953.58, 811.12, 0, ''), +(10300, 29, 5601.52, -4939.49, 820.77, 0, ''), +(10300, 30, 5571.87, -4936.22, 831.35, 0, ''), +(10300, 31, 5543.23, -4933.67, 838.33, 0, ''), +(10300, 32, 5520.86, -4942.05, 843.02, 0, ''), +(10300, 33, 5509.15, -4946.31, 849.36, 0, ''), +(10300, 34, 5498.45, -4950.08, 849.98, 0, ''), +(10300, 35, 5485.78, -4963.40, 850.43, 0, ''), +(10300, 36, 5467.92, -4980.67, 851.89, 0, 'Cavern 5 - EMOTE_CHANT_SPELL'), +(10300, 37, 5498.68, -4950.45, 849.96, 0, ''), +(10300, 38, 5518.68, -4921.94, 844.65, 0, ''), +(10300, 39, 5517.66, -4920.82, 845.12, 0, 'SAY_REACH_ALTAR_1'), +(10300, 40, 5518.38, -4913.47, 845.57, 0, ''), +(10300, 41, 5511.31, -4913.82, 847.17, 5000, 'light the spotlights'), +(10300, 42, 5511.31, -4913.82, 847.17, 0, 'start altar cinematic - SAY_RANSHALLA_ALTAR_2'), +(10300, 43, 5510.36, -4921.17, 846.33, 0, ''), +(10300, 44, 5517.66, -4920.82, 845.12, 0, 'escort paused'); + +DELETE FROM script_waypoint WHERE entry=4484; +INSERT INTO script_waypoint VALUES +(4484, 0, 3178.57, 188.52, 4.27, 0, 'SAY_QUEST_START'), +(4484, 1, 3189.82, 198.56, 5.62, 0, ''), +(4484, 2, 3215.21, 185.78, 6.43, 0, ''), +(4484, 3, 3224.05, 183.08, 6.74, 0, ''), +(4484, 4, 3228.11, 194.97, 7.51, 0, ''), +(4484, 5, 3225.33, 201.78, 7.25, 0, ''), +(4484, 6, 3233.33, 226.88, 10.18, 0, ''), +(4484, 7, 3274.12, 225.83, 10.72, 0, ''), +(4484, 8, 3321.63, 209.82, 12.36, 0, ''), +(4484, 9, 3369.66, 226.21, 11.69, 0, ''), +(4484, 10, 3402.35, 227.20, 9.48, 0, ''), +(4484, 11, 3441.92, 224.75, 10.85, 0, ''), +(4484, 12, 3453.87, 220.31, 12.52, 0, ''), +(4484, 13, 3472.51, 213.68, 13.26, 0, ''), +(4484, 14, 3515.49, 212.96, 9.76, 5000, 'SAY_FIRST_AMBUSH_START'), +(4484, 15, 3516.21, 212.84, 9.52, 20000, 'SAY_FIRST_AMBUSH_END'), +(4484, 16, 3548.22, 217.12, 7.34, 0, ''), +(4484, 17, 3567.57, 219.43, 5.22, 0, ''), +(4484, 18, 3659.85, 209.68, 2.27, 0, ''), +(4484, 19, 3734.90, 177.64, 6.75, 0, ''), +(4484, 20, 3760.24, 162.51, 7.49, 5000, 'SAY_SECOND_AMBUSH_START'), +(4484, 21, 3761.58, 161.14, 7.37, 20000, 'SAY_SECOND_AMBUSH_END'), +(4484, 22, 3801.17, 129.87, 9.38, 0, ''), +(4484, 23, 3815.53, 118.53, 10.14, 0, ''), +(4484, 24, 3894.58, 44.88, 15.49, 0, ''), +(4484, 25, 3972.83, 0.42, 17.34, 0, ''), +(4484, 26, 4026.41, -7.63, 16.77, 0, ''), +(4484, 27, 4086.24, 12.32, 16.12, 0, ''), +(4484, 28, 4158.79, 50.67, 25.86, 0, ''), +(4484, 29, 4223.48, 99.52, 35.47, 5000, 'SAY_FINAL_AMBUSH_START'), +(4484, 30, 4224.28, 100.02, 35.49, 10000, 'SAY_QUEST_END'), +(4484, 31, 4243.45, 117.44, 38.83, 0, ''), +(4484, 32, 4264.18, 134.22, 42.96, 0, ''); + +DELETE FROM script_waypoint WHERE entry=12277; +INSERT INTO script_waypoint VALUES +(12277, 1, -1154.87, 2708.16, 111.123, 1000, 'SAY_MELIZZA_START'), +(12277, 2, -1162.62, 2712.86, 111.549, 0, ''), +(12277, 3, -1183.37, 2709.45, 111.601, 0, ''), +(12277, 4, -1245.09, 2676.43, 111.572, 0, ''), +(12277, 5, -1260.54, 2672.48, 111.55, 0, ''), +(12277, 6, -1272.71, 2666.38, 111.555, 0, ''), +(12277, 7, -1342.95, 2580.82, 111.557, 0, ''), +(12277, 8, -1362.24, 2561.74, 110.848, 0, ''), +(12277, 9, -1376.56, 2514.06, 95.6146, 0, ''), +(12277, 10, -1379.06, 2510.88, 93.3256, 0, ''), +(12277, 11, -1383.14, 2489.17, 89.009, 0, ''), +(12277, 12, -1395.34, 2426.15, 88.6607, 0, 'SAY_MELIZZA_FINISH'), +(12277, 13, -1366.23, 2317.17, 91.8086, 0, ''), +(12277, 14, -1353.81, 2213.52, 90.726, 0, ''), +(12277, 15, -1354.19, 2208.28, 88.7386, 0, ''), +(12277, 16, -1354.59, 2193.77, 77.6702, 0, ''), +(12277, 17, -1367.62, 2160.64, 67.1482, 0, ''), +(12277, 18, -1379.44, 2132.77, 64.1326, 0, ''), +(12277, 19, -1404.81, 2088.68, 61.8162, 0, 'SAY_MELIZZA_1'), +(12277, 20, -1417.15, 2082.65, 62.4112, 0, ''), +(12277, 21, -1423.28, 2074.19, 62.2046, 0, ''), +(12277, 22, -1432.99, 2070.56, 61.7811, 0, ''), +(12277, 23, -1469.27, 2078.68, 63.1141, 0, ''), +(12277, 24, -1507.21, 2115.12, 62.3578, 0, ''); + +DELETE FROM script_waypoint WHERE entry=3692; +INSERT INTO script_waypoint VALUES +(3692, 1, 4608.43, -6.32, 69.74, 1000, 'stand up'), +(3692, 2, 4608.43, -6.32, 69.74, 4000, 'SAY_START'), +(3692, 3, 4604.54, -5.17, 69.51, 0, ''), +(3692, 4, 4604.26, -2.02, 69.42, 0, ''), +(3692, 5, 4607.75, 3.79, 70.13, 1000, 'first ambush'), +(3692, 6, 4607.75, 3.79, 70.13, 0, 'SAY_FIRST_AMBUSH'), +(3692, 7, 4619.77, 27.47, 70.40, 0, ''), +(3692, 8, 4626.28, 42.46, 68.75, 0, ''), +(3692, 9, 4633.13, 51.17, 67.40, 0, ''), +(3692, 10, 4639.67, 79.03, 61.74, 0, ''), +(3692, 11, 4647.54, 94.25, 59.92, 0, 'second ambush'), +(3692, 12, 4682.08, 113.47, 54.83, 0, ''), +(3692, 13, 4705.28, 137.81, 53.36, 0, 'last ambush'), +(3692, 14, 4730.30, 158.76, 52.33, 0, ''), +(3692, 15, 4756.47, 195.65, 53.61, 10000, 'SAY_END'), +(3692, 16, 4608.43, -6.32, 69.74, 1000, 'bow'), +(3692, 17, 4608.43, -6.32, 69.74, 4000, 'SAY_ESCAPE'), +(3692, 18, 4608.43, -6.32, 69.74, 4000, 'SPELL_MOONSTALKER_FORM'), +(3692, 19, 4604.54, -5.17, 69.51, 0, ''), +(3692, 20, 4604.26, -2.02, 69.42, 0, ''), +(3692, 21, 4607.75, 3.79, 70.13, 0, ''), +(3692, 22, 4607.75, 3.79, 70.13, 0, ''), +(3692, 23, 4619.77, 27.47, 70.40, 0, ''), +(3692, 24, 4640.33, 33.74, 68.22, 0, 'quest complete'); + +DELETE FROM script_waypoint WHERE entry=22424; +INSERT INTO script_waypoint VALUES +(22424, 1, -3620.54, 4164.57, 1.81, 0, 'SKYWING_START'), +(22424, 2, -3624.78, 4149.65, 7.44, 0, ''), +(22424, 3, -3630.30, 4124.84, 21.28, 0, ''), +(22424, 4, -3629.14, 4093.65, 44.35, 0, ''), +(22424, 5, -3626.34, 4080.29, 52.39, 0, ''), +(22424, 6, -3619.35, 4063.86, 60.86, 3000, 'SAY_SKYWING_TREE_DOWN'), +(22424, 7, -3615.09, 4054.17, 62.46, 0, ''), +(22424, 8, -3611.39, 4046.60, 65.07, 0, ''), +(22424, 9, -3606.68, 4040.50, 66.00, 0, ''), +(22424, 10, -3600.88, 4038.69, 67.14, 0, ''), +(22424, 11, -3597.88, 4033.84, 68.53, 0, ''), +(22424, 12, -3602.19, 4027.89, 69.36, 0, ''), +(22424, 13, -3609.85, 4028.37, 70.78, 0, ''), +(22424, 14, -3613.01, 4031.10, 72.14, 0, ''), +(22424, 15, -3613.18, 4035.63, 73.52, 0, ''), +(22424, 16, -3609.84, 4039.73, 75.25, 0, ''), +(22424, 17, -3604.55, 4040.12, 77.01, 0, ''), +(22424, 18, -3600.61, 4036.03, 78.84, 0, ''), +(22424, 19, -3602.63, 4029.99, 81.01, 0, ''), +(22424, 20, -3608.87, 4028.64, 83.27, 0, ''), +(22424, 21, -3612.53, 4032.74, 85.24, 0, ''), +(22424, 22, -3611.08, 4038.13, 87.31, 0, ''), +(22424, 23, -3605.09, 4039.35, 89.55, 0, ''), +(22424, 24, -3601.87, 4035.44, 91.64, 0, ''), +(22424, 25, -3603.08, 4030.58, 93.66, 0, ''), +(22424, 26, -3608.47, 4029.23, 95.91, 0, ''), +(22424, 27, -3611.68, 4033.35, 98.09, 0, ''), +(22424, 28, -3609.51, 4038.25, 100.45, 0, ''), +(22424, 29, -3604.54, 4038.01, 102.72, 0, ''), +(22424, 30, -3602.40, 4033.48, 105.12, 0, ''), +(22424, 31, -3606.17, 4029.69, 107.63, 0, ''), +(22424, 32, -3609.93, 4031.26, 109.53, 0, ''), +(22424, 33, -3609.38, 4035.86, 110.67, 0, ''), +(22424, 34, -3603.58, 4043.03, 112.89, 0, ''), +(22424, 35, -3600.99, 4046.49, 111.81, 0, ''), +(22424, 36, -3602.32, 4051.77, 111.81, 3000, 'SAY_SKYWING_TREE_UP'), +(22424, 37, -3609.55, 4055.95, 112.00, 0, ''), +(22424, 38, -3620.93, 4043.77, 111.99, 0, ''), +(22424, 39, -3622.44, 4038.95, 111.99, 0, ''), +(22424, 40, -3621.64, 4025.39, 111.99, 0, ''), +(22424, 41, -3609.62, 4015.20, 111.99, 0, ''), +(22424, 42, -3598.37, 4017.72, 111.99, 0, ''), +(22424, 43, -3590.21, 4026.62, 111.99, 0, ''), +(22424, 44, -3586.55, 4034.13, 112.00, 0, ''), +(22424, 45, -3580.39, 4033.46, 112.00, 0, ''), +(22424, 46, -3568.83, 4032.53, 107.16, 0, ''), +(22424, 47, -3554.81, 4031.23, 105.31, 0, ''), +(22424, 48, -3544.39, 4030.10, 106.58, 0, ''), +(22424, 49, -3531.91, 4029.25, 111.70, 0, ''), +(22424, 50, -3523.50, 4030.24, 112.47, 0, ''), +(22424, 51, -3517.48, 4037.42, 112.66, 0, ''), +(22424, 52, -3510.40, 4040.77, 112.92, 0, ''), +(22424, 53, -3503.83, 4041.35, 113.17, 0, ''), +(22424, 54, -3498.31, 4040.65, 113.11, 0, ''), +(22424, 55, -3494.05, 4031.67, 113.11, 0, ''), +(22424, 56, -3487.71, 4025.58, 113.12, 0, ''), +(22424, 57, -3500.42, 4012.93, 113.11, 0, ''), +(22424, 58, -3510.86, 4010.15, 113.10, 0, ''), +(22424, 59, -3518.07, 4008.62, 112.97, 0, ''), +(22424, 60, -3524.74, 4014.55, 112.41, 2000, 'SAY_SKYWING_JUMP'), +(22424, 61, -3537.81, 4008.59, 92.53, 0, ''), +(22424, 62, -3546.25, 4008.81, 92.79, 0, ''), +(22424, 63, -3552.07, 4006.48, 92.84, 0, ''), +(22424, 64, -3556.29, 4000.14, 92.92, 0, ''), +(22424, 65, -3556.16, 3991.24, 92.92, 0, ''), +(22424, 66, -3551.48, 3984.28, 92.91, 0, ''), +(22424, 67, -3542.90, 3981.64, 92.91, 0, ''), +(22424, 68, -3534.82, 3983.98, 92.92, 0, ''), +(22424, 69, -3530.58, 3989.91, 92.85, 0, ''), +(22424, 70, -3529.85, 3998.77, 92.59, 0, ''), +(22424, 71, -3534.15, 4008.45, 92.34, 0, ''), +(22424, 72, -3532.87, 4012.97, 91.64, 0, ''), +(22424, 73, -3530.57, 4023.42, 86.82, 0, ''), +(22424, 74, -3528.24, 4033.91, 85.69, 0, ''), +(22424, 75, -3526.22, 4043.75, 87.26, 0, ''), +(22424, 76, -3523.84, 4054.29, 92.42, 0, ''), +(22424, 77, -3522.44, 4059.06, 92.92, 0, ''), +(22424, 78, -3514.26, 4060.72, 92.92, 0, ''), +(22424, 79, -3507.76, 4065.21, 92.92, 0, ''), +(22424, 80, -3503.24, 4076.63, 92.92, 0, 'SAY_SKYWING_SUMMON'), +(22424, 81, -3504.23, 4080.47, 92.92, 7000, 'SPELL_TRANSFORM'), +(22424, 82, -3504.23, 4080.47, 92.92, 20000, 'SAY_SKYWING_END'); + +DELETE FROM script_waypoint WHERE entry=17804; +INSERT INTO script_waypoint VALUES +(17804, 0, -9054.86, 443.58, 93.05, 0, ''), +(17804, 1, -9079.33, 424.49, 92.52, 0, ''), +(17804, 2, -9086.21, 419.02, 92.32, 3000, ''), +(17804, 3, -9086.21, 419.02, 92.32, 1000, ''), +(17804, 4, -9079.33, 424.49, 92.52, 0, ''), +(17804, 5, -9054.38, 436.30, 93.05, 0, ''), +(17804, 6, -9042.23, 434.24, 93.37, 5000, 'SAY_SIGNAL_SENT'); + +DELETE FROM script_waypoint WHERE entry=12580; +INSERT INTO script_waypoint VALUES +(12580, 0, -8997.63, 486.402, 96.622, 0, ''), +(12580, 1, -8971.08, 507.541, 96.349, 0, 'SAY_DIALOG_1'), +(12580, 2, -8953.17, 518.537, 96.355, 0, ''), +(12580, 3, -8936.33, 501.777, 94.066, 0, ''), +(12580, 4, -8922.52, 498.45, 93.869, 0, ''), +(12580, 5, -8907.64, 509.941, 93.840, 0, ''), +(12580, 6, -8925.26, 542.51, 94.274, 0, ''), +(12580, 7, -8832.28, 622.285, 93.686, 0, ''), +(12580, 8, -8824.8, 621.713, 94.084, 0, ''), +(12580, 9, -8796.46, 590.922, 97.466, 0, ''), +(12580, 10, -8769.85, 607.883, 97.118, 0, ''), +(12580, 11, -8737.14, 574.741, 97.398, 0, 'reset jonathan'), +(12580, 12, -8746.27, 563.446, 97.399, 0, ''), +(12580, 13, -8745.5, 557.877, 97.704, 0, ''), +(12580, 14, -8730.95, 541.477, 101.12, 0, ''), +(12580, 15, -8713.16, 520.692, 97.227, 0, ''), +(12580, 16, -8677.09, 549.614, 97.438, 0, ''), +(12580, 17, -8655.72, 552.732, 96.941, 0, ''), +(12580, 18, -8641.68, 540.516, 98.972, 0, ''), +(12580, 19, -8620.08, 520.120, 102.812, 0, ''), +(12580, 20, -8591.09, 492.553, 104.032, 0, ''), +(12580, 21, -8562.45, 463.583, 104.517, 0, ''), +(12580, 22, -8548.63, 467.38, 104.517, 0, 'SAY_WINDSOR_BEFORE_KEEP'), +(12580, 23, -8487.77, 391.44, 108.386, 0, ''), +(12580, 24, -8455.95, 351.225, 120.88, 0, ''), +(12580, 25, -8446.87, 339.904, 121.33, 0, 'SAY_WINDSOR_KEEP_1'), +(12580, 26, -8446.87, 339.904, 121.33, 10000, ''); + +DELETE FROM script_waypoint WHERE entry=9520; +INSERT INTO script_waypoint VALUES +(9520, 1, -7699.62, -1444.29, 139.87, 4000, 'SAY_START'), +(9520, 2, -7670.67, -1458.25, 140.74, 0, ''), +(9520, 3, -7675.26, -1465.58, 140.74, 0, ''), +(9520, 4, -7685.84, -1472.66, 140.75, 0, ''), +(9520, 5, -7700.08, -1473.41, 140.79, 0, ''), +(9520, 6, -7712.55, -1470.19, 140.79, 0, ''), +(9520, 7, -7717.27, -1481.70, 140.72, 5000, 'SAY_PAY'), +(9520, 8, -7726.23, -1500.78, 132.99, 0, ''), +(9520, 9, -7744.61, -1531.61, 132.69, 0, ''), +(9520, 10, -7763.08, -1536.22, 131.93, 0, ''), +(9520, 11, -7815.32, -1522.61, 134.16, 0, ''), +(9520, 12, -7850.26, -1516.87, 138.17, 0, 'SAY_FIRST_AMBUSH_START'), +(9520, 13, -7850.26, -1516.87, 138.17, 3000, 'SAY_FIRST_AMBUSH_END'), +(9520, 14, -7881.01, -1508.49, 142.37, 0, ''), +(9520, 15, -7888.91, -1458.09, 144.79, 0, ''), +(9520, 16, -7889.18, -1430.21, 145.31, 0, ''), +(9520, 17, -7900.53, -1427.01, 150.26, 0, ''), +(9520, 18, -7904.15, -1429.91, 150.27, 0, ''), +(9520, 19, -7921.48, -1425.47, 140.54, 0, ''), +(9520, 20, -7941.43, -1413.10, 134.35, 0, ''), +(9520, 21, -7964.85, -1367.45, 132.99, 0, ''), +(9520, 22, -7989.95, -1319.121, 133.71, 0, ''), +(9520, 23, -8010.43, -1270.23, 133.42, 0, ''), +(9520, 24, -8025.62, -1243.78, 133.91, 0, 'SAY_SEC_AMBUSH_START'), +(9520, 25, -8025.62, -1243.78, 133.91, 3000, 'SAY_SEC_AMBUSH_END'), +(9520, 26, -8015.22, -1196.98, 146.76, 0, ''), +(9520, 27, -7994.68, -1151.38, 160.70, 0, ''), +(9520, 28, -7970.91, -1132.81, 170.16, 0, 'summon Searscale Drakes'), +(9520, 29, -7927.59, -1122.79, 185.86, 0, ''), +(9520, 30, -7897.67, -1126.67, 194.32, 0, 'SAY_THIRD_AMBUSH_START'), +(9520, 31, -7897.67, -1126.67, 194.32, 3000, 'SAY_THIRD_AMBUSH_END'), +(9520, 32, -7864.11, -1135.98, 203.29, 0, ''), +(9520, 33, -7837.31, -1137.73, 209.63, 0, ''), +(9520, 34, -7808.72, -1134.90, 214.84, 0, ''), +(9520, 35, -7786.85, -1127.24, 214.84, 0, ''), +(9520, 36, -7746.58, -1125.16, 215.08, 5000, 'EMOTE_LAUGH'), +(9520, 37, -7746.41, -1103.62, 215.62, 0, ''), +(9520, 38, -7740.25, -1090.51, 216.69, 0, ''), +(9520, 39, -7730.97, -1085.55, 217.12, 0, ''), +(9520, 40, -7697.89, -1089.43, 217.62, 0, ''), +(9520, 41, -7679.30, -1059.15, 220.09, 0, ''), +(9520, 42, -7661.39, -1038.24, 226.24, 0, ''), +(9520, 43, -7634.49, -1020.96, 234.30, 0, ''), +(9520, 44, -7596.22, -1013.16, 244.03, 0, ''), +(9520, 45, -7556.53, -1021.74, 253.21, 0, 'SAY_LAST_STAND'); + +DELETE FROM script_waypoint WHERE entry=9023; +INSERT INTO script_waypoint VALUES +(9023, 1, 316.336, -225.528, -77.7258, 2000, 'SAY_WINDSOR_START'), +(9023, 2, 322.96, -207.13, -77.87, 0, ''), +(9023, 3, 281.05, -172.16, -75.12, 0, ''), +(9023, 4, 272.19, -139.14, -70.61, 0, ''), +(9023, 5, 283.62, -116.09, -70.21, 0, ''), +(9023, 6, 296.18, -94.30, -74.08, 0, ''), +(9023, 7, 294.57, -93.11, -74.08, 0, 'escort paused - SAY_WINDSOR_CELL_DUGHAL_1'), +(9023, 8, 294.57, -93.11, -74.08, 10000, ''), +(9023, 9, 294.57, -93.11, -74.08, 3000, 'SAY_WINDSOR_CELL_DUGHAL_3'), +(9023, 10, 314.31, -74.31, -76.09, 0, ''), +(9023, 11, 360.22, -62.93, -66.77, 0, ''), +(9023, 12, 383.38, -69.40, -63.25, 0, ''), +(9023, 13, 389.99, -67.86, -62.57, 0, ''), +(9023, 14, 400.98, -72.01, -62.31, 0, 'SAY_WINDSOR_EQUIPMENT_1'), +(9023, 15, 404.22, -62.30, -63.50, 2000, ''), +(9023, 16, 404.22, -62.30, -63.50, 1500, 'open supply door'), +(9023, 17, 407.65, -51.86, -63.96, 0, ''), +(9023, 18, 403.61, -51.71, -63.92, 1000, 'SAY_WINDSOR_EQUIPMENT_2'), +(9023, 19, 403.61, -51.71, -63.92, 2000, ''), +(9023, 20, 403.61, -51.71, -63.92, 1000, 'open supply crate'), +(9023, 21, 403.61, -51.71, -63.92, 1000, 'update entry to Reginald Windsor'), +(9023, 22, 403.61, -52.71, -63.92, 4000, 'SAY_WINDSOR_EQUIPMENT_3'), +(9023, 23, 403.61, -52.71, -63.92, 4000, 'SAY_WINDSOR_EQUIPMENT_4'), +(9023, 24, 406.33, -54.87, -63.95, 0, ''), +(9023, 25, 403.86, -73.88, -62.02, 0, ''), +(9023, 26, 428.80, -81.34, -64.91, 0, ''), +(9023, 27, 557.03, -119.71, -61.83, 0, ''), +(9023, 28, 573.40, -124.39, -65.07, 0, ''), +(9023, 29, 593.91, -130.29, -69.25, 0, ''), +(9023, 30, 593.21, -132.16, -69.25, 0, 'escort paused - SAY_WINDSOR_CELL_JAZ_1'), +(9023, 31, 593.21, -132.16, -69.25, 1000, ''), +(9023, 32, 593.21, -132.16, -69.25, 3000, 'SAY_WINDSOR_CELL_JAZ_2'), +(9023, 33, 622.81, -135.55, -71.92, 0, ''), +(9023, 34, 634.68, -151.29, -70.32, 0, ''), +(9023, 35, 635.06, -153.25, -70.32, 0, 'escort paused - SAY_WINDSOR_CELL_SHILL_1'), +(9023, 36, 635.06, -153.25, -70.32, 3000, ''), +(9023, 37, 635.06, -153.25, -70.32, 5000, 'SAY_WINDSOR_CELL_SHILL_2'), +(9023, 38, 635.06, -153.25, -70.32, 2000, 'SAY_WINDSOR_CELL_SHILL_3'), +(9023, 39, 655.25, -172.39, -73.72, 0, ''), +(9023, 40, 654.79, -226.30, -83.06, 0, ''), +(9023, 41, 622.85, -268.85, -83.96, 0, ''), +(9023, 42, 579.45, -275.56, -80.44, 0, ''), +(9023, 43, 561.19, -266.85, -75.59, 0, ''), +(9023, 44, 547.91, -253.92, -70.34, 0, ''), +(9023, 45, 549.20, -252.40, -70.34, 0, 'escort paused - SAY_WINDSOR_CELL_CREST_1'), +(9023, 46, 549.20, -252.40, -70.34, 1000, ''), +(9023, 47, 549.20, -252.40, -70.34, 4000, 'SAY_WINDSOR_CELL_CREST_2'), +(9023, 48, 555.33, -269.16, -74.40, 0, ''), +(9023, 49, 554.31, -270.88, -74.40, 0, 'escort paused - SAY_WINDSOR_CELL_TOBIAS_1'), +(9023, 50, 554.31, -270.88, -74.40, 10000, ''), +(9023, 51, 554.31, -270.88, -74.40, 4000, 'SAY_WINDSOR_CELL_TOBIAS_2'), +(9023, 52, 536.10, -249.60, -67.47, 0, ''), +(9023, 53, 520.94, -216.65, -59.28, 0, ''), +(9023, 54, 505.99, -148.74, -62.17, 0, ''), +(9023, 55, 484.21, -56.24, -62.43, 0, ''), +(9023, 56, 470.39, -6.01, -70.10, 0, ''), +(9023, 57, 452.45, 29.85, -70.37, 1500, 'SAY_WINDSOR_FREE_1'), +(9023, 58, 452.45, 29.85, -70.37, 15000, 'SAY_WINDSOR_FREE_2'); + +DELETE FROM script_waypoint WHERE entry=17225; +INSERT INTO script_waypoint VALUES +(17225, 0, -11033.51, -1784.65, 182.284, 3000, ''), +(17225, 1, -11107.57, -1873.36, 136.878, 0, ''), +(17225, 2, -11118.71, -1883.65, 132.441, 0, ''), +(17225, 3, -11132.92, -1888.12, 128.969, 0, ''), +(17225, 4, -11150.31, -1890.54, 126.557, 0, ''), +(17225, 5, -11160.64, -1891.63, 124.793, 0, ''), +(17225, 6, -11171.52, -1889.45, 123.417, 0, ''), +(17225, 7, -11183.46, -1884.09, 119.754, 0, ''), +(17225, 8, -11196.25, -1874.01, 115.227, 0, ''), +(17225, 9, -11205.59, -1859.66, 110.216, 0, ''), +(17225, 10, -11236.53, -1818.03, 97.3972, 0, ''), +(17225, 11, -11253.11, -1794.48, 93.3101, 0, ''), +(17225, 12, -11254.86, -1787.13, 92.5174, 0, ''), +(17225, 13, -11253.32, -1777.08, 91.7739, 0, ''), +(17225, 14, -11247.48, -1770.27, 92.4183, 0, ''), +(17225, 15, -11238.61, -1766.51, 94.6417, 0, ''), +(17225, 16, -11227.56, -1767.22, 100.256, 0, ''), +(17225, 17, -11218.41, -1770.55, 107.859, 0, ''), +(17225, 18, -11204.81, -1781.77, 110.383, 0, ''), +(17225, 19, -11195.77, -1801.07, 110.833, 0, ''), +(17225, 20, -11195.81, -1824.66, 113.936, 0, ''), +(17225, 21, -11197.11, -1860.01, 117.945, 0, ''), +(17225, 22, -11194.60, -1884.23, 121.401, 0, ''), +(17225, 23, -11184.21, -1894.78, 120.326, 0, ''), +(17225, 24, -11176.91, -1899.84, 119.844, 0, ''), +(17225, 25, -11168.13, -1901.77, 118.958, 0, ''), +(17225, 26, -11154.91, -1901.66, 117.218, 0, ''), +(17225, 27, -11143.15, -1901.22, 115.885, 0, ''), +(17225, 28, -11131.19, -1897.59, 113.722, 0, ''), +(17225, 29, -11121.31, -1890.25, 111.643, 0, ''), +(17225, 30, -11118.22, -1883.83, 110.595, 3000, ''), +(17225, 31, -11118.45, -1883.68, 91.473, 0, 'start combat'); + +DELETE FROM script_waypoint WHERE entry=20802; +INSERT INTO script_waypoint VALUES +(20802, 0, 4017.864, 2325.038, 114.029, 3000, 'SAY_INTRO'), +(20802, 1, 4006.373, 2324.593, 111.455, 0, ''), +(20802, 2, 3998.391, 2326.364, 113.164, 0, ''), +(20802, 3, 3982.309, 2330.261, 113.846, 7000, 'SAY_STAGING_GROUNDS'), +(20802, 4, 3950.646, 2329.249, 113.924, 0, 'SAY_TOXIC_HORROR'), +(20802, 5, 3939.229, 2330.994, 112.197, 0, ''), +(20802, 6, 3927.858, 2333.644, 111.330, 0, ''), +(20802, 7, 3917.851, 2337.696, 113.493, 0, ''), +(20802, 8, 3907.743, 2343.336, 114.062, 0, ''), +(20802, 9, 3878.760, 2378.611, 114.037, 8000, 'SAY_SALHADAAR'), +(20802, 10, 3863.153, 2355.876, 114.987, 0, ''), +(20802, 11, 3861.241, 2344.893, 115.201, 0, ''), +(20802, 12, 3872.463, 2323.114, 114.671, 0, 'escort paused - SAY_DISRUPTOR'), +(20802, 13, 3863.740, 2349.790, 115.382, 0, 'SAY_FINISH_2'); + +DELETE FROM script_waypoint WHERE entry=20763; +INSERT INTO script_waypoint VALUES +(20763, 0, 4084.092, 2297.254, 110.277, 0, ''), +(20763, 1, 4107.174, 2294.916, 106.625, 0, ''), +(20763, 2, 4154.129, 2296.789, 102.331, 0, ''), +(20763, 3, 4166.021, 2302.819, 103.422, 0, ''), +(20763, 4, 4195.039, 2301.094, 113.786, 0, ''), +(20763, 5, 4205.246, 2297.116, 117.992, 0, ''), +(20763, 6, 4230.429, 2294.642, 127.307, 0, ''), +(20763, 7, 4238.981, 2293.579, 129.332, 0, ''), +(20763, 8, 4250.184, 2293.272, 129.009, 0, ''), +(20763, 9, 4262.810, 2290.768, 126.485, 0, ''), +(20763, 10, 4265.845, 2278.562, 128.235, 0, ''), +(20763, 11, 4265.609, 2265.734, 128.452, 0, ''), +(20763, 12, 4258.838, 2245.354, 132.804, 0, ''), +(20763, 13, 4247.976, 2221.211, 137.668, 0, ''), +(20763, 14, 4247.973, 2213.876, 137.721, 0, ''), +(20763, 15, 4249.876, 2204.265, 137.121, 4000, ''), +(20763, 16, 4249.876, 2204.265, 137.121, 0, 'SAY_VANGUARD_FINISH'), +(20763, 17, 4252.455, 2170.885, 137.677, 3000, 'EMOTE_VANGUARD_FINISH'), +(20763, 18, 4252.455, 2170.885, 137.677, 5000, ''); + +DELETE FROM script_waypoint WHERE entry=23089; +INSERT INTO script_waypoint VALUES +(23089, 0, 660.22, 305.74, 271.688, 0, 'escort paused - GOSSIP_ITEM_PREPARE'), +(23089, 1, 675.10, 343.30, 271.688, 0, ''), +(23089, 2, 694.01, 374.84, 271.687, 0, ''), +(23089, 3, 706.22, 375.75, 274.888, 0, ''), +(23089, 4, 720.48, 370.38, 281.300, 0, ''), +(23089, 5, 733.30, 357.66, 292.477, 0, ''), +(23089, 6, 740.40, 344.39, 300.920, 0, ''), +(23089, 7, 747.54, 329.03, 308.509, 0, ''), +(23089, 8, 748.24, 318.78, 311.781, 0, ''), +(23089, 9, 752.41, 304.31, 312.077, 0, 'escort paused - SAY_AKAMA_OPEN_DOOR_1'), +(23089, 10, 770.27, 304.89, 312.35, 0, ''), +(23089, 11, 780.18, 305.26, 319.71, 0, ''), +(23089, 12, 791.45, 289.27, 319.80, 0, ''), +(23089, 13, 790.41, 262.70, 341.42, 0, ''), +(23089, 14, 782.88, 250.20, 341.60, 0, ''), +(23089, 15, 765.35, 241.40, 353.62, 0, ''), +(23089, 16, 750.61, 235.63, 353.02, 0, 'escort paused - GOSSIP_ITEM_START_EVENT'), +(23089, 17, 748.87, 304.93, 352.99, 0, 'escort paused - SAY_ILLIDAN_SPEECH_1'), +(23089, 18, 737.92, 368.15, 352.99, 0, ''), +(23089, 19, 749.64, 378.69, 352.99, 0, ''), +(23089, 20, 766.49, 371.79, 353.63, 0, ''), +(23089, 21, 784.98, 361.89, 341.41, 0, ''), +(23089, 22, 791.44, 347.10, 341.41, 0, ''), +(23089, 23, 794.80, 319.47, 319.75, 0, ''), +(23089, 24, 794.34, 304.34, 319.75, 0, 'escort paused - fight illidari elites'), +(23089, 25, 794.80, 319.47, 319.75, 0, ''), +(23089, 26, 791.44, 347.10, 341.41, 0, ''), +(23089, 27, 784.98, 361.89, 341.41, 0, ''), +(23089, 28, 766.49, 371.79, 353.63, 0, ''), +(23089, 29, 749.64, 378.69, 352.99, 0, ''), +(23089, 30, 737.92, 368.15, 352.99, 0, 'escort paused'); + +DELETE FROM script_waypoint WHERE entry=3584; +INSERT INTO script_waypoint VALUES +(3584, 0, 4520.4, 420.235, 33.5284, 2000, ''), +(3584, 1, 4512.26, 408.881, 32.9308, 0, ''), +(3584, 2, 4507.94, 396.47, 32.9476, 0, ''), +(3584, 3, 4507.53, 383.781, 32.995, 0, ''), +(3584, 4, 4512.1, 374.02, 33.166, 0, ''), +(3584, 5, 4519.75, 373.241, 33.1574, 0, ''), +(3584, 6, 4592.41, 369.127, 31.4893, 0, ''), +(3584, 7, 4598.55, 364.801, 31.4947, 0, ''), +(3584, 8, 4602.76, 357.649, 32.9265, 0, ''), +(3584, 9, 4597.88, 352.629, 34.0317, 0, ''), +(3584, 10, 4590.23, 350.9, 36.2977, 0, ''), +(3584, 11, 4581.5, 348.254, 38.3878, 0, ''), +(3584, 12, 4572.05, 348.059, 42.3539, 0, ''), +(3584, 13, 4564.75, 344.041, 44.2463, 0, ''), +(3584, 14, 4556.63, 341.003, 47.6755, 0, ''), +(3584, 15, 4554.38, 334.968, 48.8003, 0, ''), +(3584, 16, 4557.63, 329.783, 49.9532, 0, ''), +(3584, 17, 4563.32, 316.829, 53.2409, 0, ''), +(3584, 18, 4566.09, 303.127, 55.0396, 0, ''), +(3584, 19, 4561.65, 295.456, 57.0984, 4000, 'SAY_THERYLUNE_FINISH'), +(3584, 20, 4551.03, 293.333, 57.1534, 2000, ''); + +DELETE FROM script_waypoint WHERE entry=17238; +INSERT INTO script_waypoint VALUES +(17238, 0, 954.21, -1433.72, 63.00, 0, ''), +(17238, 1, 972.70, -1438.85, 65.56, 0, ''), +(17238, 2, 984.79, -1444.15, 64.13, 0, ''), +(17238, 3, 999.00, -1451.74, 61.20, 0, ''), +(17238, 4, 1030.94, -1470.39, 63.49, 25000, 'SAY_FIRST_STOP'), +(17238, 5, 1030.94, -1470.39, 63.49, 3000, 'SAY_CONTINUE'), +(17238, 6, 1036.50, -1484.25, 64.60, 0, ''), +(17238, 7, 1039.11, -1501.22, 65.32, 0, ''), +(17238, 8, 1038.44, -1522.18, 64.55, 0, ''), +(17238, 9, 1037.19, -1543.15, 62.33, 0, ''), +(17238, 10, 1036.79, -1563.88, 61.93, 5000, 'SAY_FIRST_ATTACK'), +(17238, 11, 1036.79, -1563.88, 61.93, 5000, 'SAY_PURITY'), +(17238, 12, 1035.61, -1587.64, 61.66, 0, ''), +(17238, 13, 1035.43, -1612.97, 61.54, 0, ''), +(17238, 14, 1035.36, -1630.66, 61.53, 0, ''), +(17238, 15, 1038.85, -1653.02, 60.35, 0, ''), +(17238, 16, 1042.27, -1669.36, 60.75, 0, ''), +(17238, 17, 1050.41, -1687.22, 60.52, 0, ''), +(17238, 18, 1061.15, -1704.45, 60.59, 0, ''), +(17238, 19, 1073.51, -1716.99, 60.65, 0, ''), +(17238, 20, 1084.20, -1727.24, 60.95, 0, ''), +(17238, 21, 1100.71, -1739.89, 60.64, 5000, 'SAY_SECOND_ATTACK'), +(17238, 22, 1100.71, -1739.89, 60.64, 0, 'SAY_CLEANSE'), +(17238, 23, 1117.03, -1749.01, 60.87, 0, ''), +(17238, 24, 1123.58, -1762.29, 62.40, 0, ''), +(17238, 25, 1123.36, -1769.29, 62.83, 0, ''), +(17238, 26, 1115.78, -1779.59, 62.09, 0, ''), +(17238, 27, 1109.56, -1789.78, 61.03, 0, ''), +(17238, 28, 1094.81, -1797.62, 61.22, 0, ''), +(17238, 29, 1079.30, -1801.58, 64.95, 0, ''), +(17238, 30, 1060.24, -1803.40, 70.36, 0, ''), +(17238, 31, 1047.69, -1804.49, 73.92, 0, ''), +(17238, 32, 1032.59, -1805.99, 76.13, 0, ''), +(17238, 33, 1013.60, -1812.36, 77.32, 0, ''), +(17238, 34, 1007.01, -1814.38, 80.48, 0, ''), +(17238, 35, 999.93, -1816.39, 80.48, 2000, 'SAY_WELCOME'), +(17238, 36, 984.72, -1822.05, 80.48, 0, ''), +(17238, 37, 977.77, -1824.80, 80.79, 0, ''), +(17238, 38, 975.33, -1824.91, 81.24, 12000, 'event complete'), +(17238, 39, 975.33, -1824.91, 81.24, 10000, 'SAY_EPILOGUE_1'), +(17238, 40, 975.33, -1824.91, 81.24, 8000, 'SAY_EPILOGUE_2'), +(17238, 41, 975.33, -1824.91, 81.24, 30000, ''); + +DELETE FROM script_waypoint WHERE entry=2713; +INSERT INTO script_waypoint VALUES +(2713, 0, -1416.91, -3044.12, 36.21, 0, ''), +(2713, 1, -1408.43, -3051.35, 37.79, 0, ''), +(2713, 2, -1399.45, -3069.20, 31.25, 0, ''), +(2713, 3, -1400.28, -3083.14, 27.06, 0, ''), +(2713, 4, -1405.30, -3096.72, 26.36, 0, ''), +(2713, 5, -1406.12, -3105.95, 24.82, 0, ''), +(2713, 6, -1417.41, -3106.80, 16.61, 0, ''), +(2713, 7, -1433.06, -3101.55, 12.56, 0, ''), +(2713, 8, -1439.86, -3086.36, 12.29, 0, ''), +(2713, 9, -1450.48, -3065.16, 12.58, 5000, 'SAY_REACH_BOTTOM'), +(2713, 10, -1456.15, -3055.53, 12.54, 0, ''), +(2713, 11, -1459.41, -3035.16, 12.11, 0, ''), +(2713, 12, -1472.47, -3034.18, 12.44, 0, ''), +(2713, 13, -1495.57, -3034.48, 12.55, 0, ''), +(2713, 14, -1524.91, -3035.47, 13.15, 0, ''), +(2713, 15, -1549.05, -3037.77, 12.98, 0, ''), +(2713, 16, -1555.69, -3028.02, 13.64, 3000, 'SAY_WATCH_BACK'), +(2713, 17, -1555.69, -3028.02, 13.64, 5000, 'SAY_DATA_FOUND'), +(2713, 18, -1555.69, -3028.02, 13.64, 2000, 'SAY_ESCAPE'), +(2713, 19, -1551.19, -3037.78, 12.96, 0, ''), +(2713, 20, -1584.60, -3048.77, 13.67, 0, ''), +(2713, 21, -1602.14, -3042.82, 15.12, 0, ''), +(2713, 22, -1610.68, -3027.42, 17.22, 0, ''), +(2713, 23, -1601.65, -3007.97, 24.65, 0, ''), +(2713, 24, -1581.05, -2992.32, 30.85, 0, ''), +(2713, 25, -1559.95, -2979.51, 34.30, 0, ''), +(2713, 26, -1536.51, -2969.78, 32.64, 0, ''), +(2713, 27, -1511.81, -2961.09, 29.12, 0, ''), +(2713, 28, -1484.83, -2960.87, 32.54, 0, ''), +(2713, 29, -1458.23, -2966.80, 40.52 , 0, ''), +(2713, 30, -1440.20, -2971.20, 43.15, 0, ''), +(2713, 31, -1427.85, -2989.15, 38.09, 0, ''), +(2713, 32, -1420.27, -3008.91, 35.01, 0, ''), +(2713, 33, -1427.58, -3032.53, 32.31, 5000, 'SAY_FINISH'), +(2713, 34, -1427.40, -3035.17, 32.26, 0, ''); + +DELETE FROM script_waypoint WHERE entry=4880; +INSERT INTO script_waypoint VALUES +(4880, 0, -2674.53, -3440.48, 33.686, 0, ''), +(4880, 1, -2711.17, -3435.06, 33.1926, 0, ''), +(4880, 2, -2734.04, -3456.12, 33.2254, 0, ''), +(4880, 3, -2749.64, -3457.26, 32.8249, 0, ''), +(4880, 4, -2762.25, -3457.77, 30.6813, 0, ''), +(4880, 5, -2777.0, -3456.12, 30.2484, 0, ''), +(4880, 6, -2805.49, -3450.27, 29.0624, 0, ''), +(4880, 7, -2809.77, -3447.14, 30.0948, 0, ''), +(4880, 8, -2824, -3440.62, 33.405, 0, ''), +(4880, 9, -2840.2, -3439.02, 34.1008, 0, ''), +(4880, 10, -2878.49, -3482.81, 34.362, 0, ''), +(4880, 11, -2878.35, -3511.51, 34.4826, 0, 'SAY_STINKY_FIRST_STOP'), +(4880, 12, -2873.99, -3514.84, 34.5298, 0, ''), +(4880, 13, -2866.71, -3519.06, 36.3674, 0, ''), +(4880, 14, -2850.75, -3539.38, 36.4573, 0, ''), +(4880, 15, -2844.49, -3557.7, 35.5588, 0, ''), +(4880, 16, -2841.36, -3574.59, 35.5056, 0, ''), +(4880, 17, -2841.13, -3596.95, 36.7699, 30000, 'SAY_STINKY_2_MONSTERS'), +(4880, 18, -2828.83, -3597.3, 31.2891, 0, ''), +(4880, 19, -2822.13, -3596.33, 31.2684, 5000, 'SAY_STINKY_GATHERING'), +(4880, 20, -2829.08, -3597.82, 31.307, 0, ''), +(4880, 21, -2859.28, -3602.33, 42.298, 0, ''), +(4880, 22, -2881.64, -3601.28, 42.2111, 0, ''), +(4880, 23, -2904.04, -3601.35, 34.969, 0, ''), +(4880, 24, -2907.6, -3612.73, 34.2434, 10000, 'SAY_STINKY_END'); + +-- EOF diff --git a/src/modules/SD2/sql/updates/r2923_mangos.sql b/src/modules/SD2/sql/updates/r2923_mangos.sql new file mode 100644 index 000000000..74d53aa18 --- /dev/null +++ b/src/modules/SD2/sql/updates/r2923_mangos.sql @@ -0,0 +1,6 @@ +UPDATE creature_template SET ScriptName='boss_ignis' WHERE entry=33118; +UPDATE creature_template SET ScriptName='npc_iron_construct' WHERE entry=33121; +UPDATE creature_template SET ScriptName='npc_scorch' WHERE entry=33221; +DELETE FROM scripted_event_id WHERE id IN (21620); +INSERT INTO scripted_event_id VALUES +(21620,'event_ulduar'); diff --git a/src/modules/SD2/sql/updates/r2925_mangos.sql b/src/modules/SD2/sql/updates/r2925_mangos.sql new file mode 100644 index 000000000..4951c7237 --- /dev/null +++ b/src/modules/SD2/sql/updates/r2925_mangos.sql @@ -0,0 +1 @@ +UPDATE creature_template SET ScriptName='boss_xt_002' WHERE entry=33293; diff --git a/src/modules/SD2/sql/updates/r2926_mangos.sql b/src/modules/SD2/sql/updates/r2926_mangos.sql new file mode 100644 index 000000000..816b05c92 --- /dev/null +++ b/src/modules/SD2/sql/updates/r2926_mangos.sql @@ -0,0 +1 @@ +UPDATE creature_template SET ScriptName='npc_hogger' WHERE entry=448; diff --git a/src/modules/SD2/sql/updates/r2926_scriptdev2.sql b/src/modules/SD2/sql/updates/r2926_scriptdev2.sql new file mode 100644 index 000000000..97eb8e713 --- /dev/null +++ b/src/modules/SD2/sql/updates/r2926_scriptdev2.sql @@ -0,0 +1,5 @@ +DELETE FROM script_texts WHERE entry BETWEEN -1004002 AND -1004000; +INSERT INTO script_texts (entry,content_default,sound,type,language,emote,comment) VALUES +(-1004000,'Yipe! Help Hogger!',0,1,0,0,'hogger SAY_CALL_HELP'), +(-1004001,'Hogger is eating! Stop him!',0,5,0,0,'hogger WHISPER_EATING'), +(-1004002,'No hurt Hogger!',0,1,0,0,'hogger SAY_HOGGER_BEATEN'); diff --git a/src/modules/SD2/sql/updates/r2927_scriptdev2.sql b/src/modules/SD2/sql/updates/r2927_scriptdev2.sql new file mode 100644 index 000000000..02d2f2c63 --- /dev/null +++ b/src/modules/SD2/sql/updates/r2927_scriptdev2.sql @@ -0,0 +1 @@ +UPDATE script_texts SET sound=0 WHERE entry IN (-1554022,-1999906); diff --git a/src/modules/SD2/system/ScriptLoader.cpp b/src/modules/SD2/system/ScriptLoader.cpp new file mode 100644 index 000000000..b9c97995d --- /dev/null +++ b/src/modules/SD2/system/ScriptLoader.cpp @@ -0,0 +1,1068 @@ +/** + * ScriptDev2 is an extension for mangos providing enhanced features for + * area triggers, creatures, game objects, instances, items, and spells beyond + * the default database scripting in mangos. + * + * Copyright (C) 2006-2013 ScriptDev2 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * World of Warcraft, and all World of Warcraft or Warcraft art, images, + * and lore are copyrighted by Blizzard Entertainment, Inc. + */ + +#include "precompiled.h" + +// battlegrounds +extern void AddSC_battleground(); + +// custom + +// world +extern void AddSC_areatrigger_scripts(); +extern void AddSC_bosses_emerald_dragons(); +extern void AddSC_generic_creature(); +extern void AddSC_go_scripts(); +extern void AddSC_guards(); +extern void AddSC_item_scripts(); +extern void AddSC_npc_professions(); +extern void AddSC_npcs_special(); +extern void AddSC_spell_scripts(); +extern void AddSC_world_map_scripts(); +extern void AddSC_world_map_ebon_hold(); + +// eastern kingdoms +extern void AddSC_ascendant_council(); // bastion_of_twilight +extern void AddSC_boss_chogall(); +extern void AddSC_boss_halfus_wyrmbreaker(); +extern void AddSC_boss_sinestra(); +extern void AddSC_boss_valiona_and_theralion(); +extern void AddSC_instance_bastion_of_twilight(); +extern void AddSC_boss_beauty(); // BM, blackrock_caverns +extern void AddSC_boss_corla(); +extern void AddSC_boss_karsh_steelbender(); +extern void AddSC_boss_lord_obsidius(); +extern void AddSC_boss_romogg(); +extern void AddSC_instance_blackrock_caverns(); +extern void AddSC_blackrock_depths(); // blackrock_depths +extern void AddSC_boss_ambassador_flamelash(); +extern void AddSC_boss_coren_direbrew(); +extern void AddSC_boss_draganthaurissan(); +extern void AddSC_boss_general_angerforge(); +extern void AddSC_boss_high_interrogator_gerstahn(); +extern void AddSC_instance_blackrock_depths(); +extern void AddSC_boss_overlordwyrmthalak(); // blackrock_spire +extern void AddSC_boss_pyroguard_emberseer(); +extern void AddSC_boss_gyth(); +extern void AddSC_instance_blackrock_spire(); +extern void AddSC_boss_adramedes(); // BM, blackwing_descent +extern void AddSC_boss_chimaeron(); +extern void AddSC_boss_magmaw(); +extern void AddSC_boss_maloriak(); +extern void AddSC_boss_nefarian_descent(); +extern void AddSC_instance_blackwing_descent(); +extern void AddSC_omnotron_defense(); +extern void AddSC_boss_razorgore(); // blackwing_lair +extern void AddSC_boss_vaelastrasz(); +extern void AddSC_boss_broodlord(); +extern void AddSC_boss_firemaw(); +extern void AddSC_boss_ebonroc(); +extern void AddSC_boss_flamegor(); +extern void AddSC_boss_chromaggus(); +extern void AddSC_boss_nefarian(); +extern void AddSC_boss_victor_nefarius(); +extern void AddSC_instance_blackwing_lair(); +extern void AddSC_boss_mr_smite(); // deadmines +extern void AddSC_deadmines(); +extern void AddSC_instance_deadmines(); +extern void AddSC_gnomeregan(); // gnomeregan +extern void AddSC_boss_thermaplugg(); +extern void AddSC_instance_gnomeregan(); +extern void AddSC_boss_drahga_shadowburner(); // grim_batol +extern void AddSC_boss_erudax(); +extern void AddSC_boss_forgemaster_throngus(); +extern void AddSC_boss_general_umbriss(); +extern void AddSC_instance_grim_batol(); +extern void AddSC_boss_attumen(); // karazhan +extern void AddSC_boss_curator(); +extern void AddSC_boss_maiden_of_virtue(); +extern void AddSC_boss_shade_of_aran(); +extern void AddSC_boss_netherspite(); +extern void AddSC_boss_nightbane(); +extern void AddSC_boss_prince_malchezaar(); +extern void AddSC_boss_terestian_illhoof(); +extern void AddSC_boss_moroes(); +extern void AddSC_bosses_opera(); +extern void AddSC_chess_event(); +extern void AddSC_instance_karazhan(); +extern void AddSC_karazhan(); +extern void AddSC_boss_felblood_kaelthas(); // magisters_terrace +extern void AddSC_boss_selin_fireheart(); +extern void AddSC_boss_vexallus(); +extern void AddSC_boss_priestess_delrissa(); +extern void AddSC_instance_magisters_terrace(); +extern void AddSC_magisters_terrace(); +extern void AddSC_boss_lucifron(); // molten_core +extern void AddSC_boss_magmadar(); +extern void AddSC_boss_gehennas(); +extern void AddSC_boss_garr(); +extern void AddSC_boss_baron_geddon(); +extern void AddSC_boss_shazzrah(); +extern void AddSC_boss_golemagg(); +extern void AddSC_boss_sulfuron(); +extern void AddSC_boss_majordomo(); +extern void AddSC_boss_ragnaros(); +extern void AddSC_instance_molten_core(); +extern void AddSC_molten_core(); +extern void AddSC_ebon_hold(); // scarlet_enclave +extern void AddSC_boss_arcanist_doan(); // scarlet_monastery +extern void AddSC_boss_herod(); +extern void AddSC_boss_mograine_and_whitemane(); +extern void AddSC_boss_headless_horseman(); +extern void AddSC_instance_scarlet_monastery(); +extern void AddSC_boss_darkmaster_gandling(); // scholomance +extern void AddSC_boss_jandicebarov(); +extern void AddSC_instance_scholomance(); +extern void AddSC_boss_hummel(); // shadowfang_keep +extern void AddSC_shadowfang_keep(); +extern void AddSC_instance_shadowfang_keep(); +extern void AddSC_boss_maleki_the_pallid(); // stratholme +extern void AddSC_boss_cannon_master_willey(); +extern void AddSC_boss_baroness_anastari(); +extern void AddSC_boss_dathrohan_balnazzar(); +extern void AddSC_boss_order_of_silver_hand(); +extern void AddSC_instance_stratholme(); +extern void AddSC_stratholme(); +extern void AddSC_instance_sunken_temple(); // sunken_temple +extern void AddSC_sunken_temple(); +extern void AddSC_boss_brutallus(); // sunwell_plateau +extern void AddSC_boss_eredar_twins(); +extern void AddSC_boss_felmyst(); +extern void AddSC_boss_kalecgos(); +extern void AddSC_boss_kiljaeden(); +extern void AddSC_boss_muru(); +extern void AddSC_instance_sunwell_plateau(); +extern void AddSC_boss_commander_ulthok(); // throne_of_the_tides +extern void AddSC_boss_erunak_and_ghursha(); +extern void AddSC_boss_lady_nazjar(); +extern void AddSC_boss_ozumat(); +extern void AddSC_instance_throne_of_the_tides(); +extern void AddSC_boss_archaedas(); // uldaman +extern void AddSC_instance_uldaman(); +extern void AddSC_uldaman(); +extern void AddSC_boss_akilzon(); // zulaman +extern void AddSC_boss_halazzi(); +extern void AddSC_boss_janalai(); +extern void AddSC_boss_malacrass(); +extern void AddSC_boss_nalorakk(); +extern void AddSC_instance_zulaman(); +extern void AddSC_zulaman(); +extern void AddSC_boss_zuljin(); +extern void AddSC_boss_arlokk(); // zulgurub +extern void AddSC_boss_hakkar(); +extern void AddSC_boss_hazzarah(); +extern void AddSC_boss_jeklik(); +extern void AddSC_boss_jindo(); +extern void AddSC_boss_mandokir(); +extern void AddSC_boss_marli(); +extern void AddSC_boss_ouro(); +extern void AddSC_boss_renataki(); +extern void AddSC_boss_thekal(); +extern void AddSC_boss_venoxis(); +extern void AddSC_instance_zulgurub(); + +extern void AddSC_alterac_mountains(); +extern void AddSC_arathi_highlands(); +extern void AddSC_blasted_lands(); +extern void AddSC_burning_steppes(); +extern void AddSC_dun_morogh(); +extern void AddSC_eastern_plaguelands(); +extern void AddSC_elwynn_forest(); +extern void AddSC_eversong_woods(); +extern void AddSC_ghostlands(); +extern void AddSC_gilneas(); +extern void AddSC_gilneas_city(); +extern void AddSC_hinterlands(); +extern void AddSC_ironforge(); +extern void AddSC_isle_of_queldanas(); +extern void AddSC_loch_modan(); +extern void AddSC_redridge_mountains(); +extern void AddSC_searing_gorge(); +extern void AddSC_silvermoon_city(); +extern void AddSC_silverpine_forest(); +extern void AddSC_stormwind_city(); +extern void AddSC_stranglethorn_vale(); +extern void AddSC_swamp_of_sorrows(); +extern void AddSC_tirisfal_glades(); +extern void AddSC_twilight_highlands(); +extern void AddSC_undercity(); +extern void AddSC_vashjir(); +extern void AddSC_western_plaguelands(); +extern void AddSC_westfall(); +extern void AddSC_wetlands(); + +// kalimdor +extern void AddSC_instance_blackfathom_deeps(); // blackfathom_deeps +extern void AddSC_boss_aeonus(); // COT, dark_portal +extern void AddSC_boss_chrono_lord_deja(); +extern void AddSC_boss_temporus(); +extern void AddSC_dark_portal(); +extern void AddSC_instance_dark_portal(); +extern void AddSC_boss_deathwing(); // COT, dragon_soul +extern void AddSC_boss_hagara(); +extern void AddSC_boss_morchok(); +extern void AddSC_boss_ultraxion(); +extern void AddSC_boss_warlord_zonozz(); +extern void AddSC_boss_warmaster_blackhorn(); +extern void AddSC_boss_yorsahj(); +extern void AddSC_dragon_soul(); +extern void AddSC_instance_dragon_soul(); +extern void AddSC_end_of_time(); // COT, end_of_time +extern void AddSC_instance_end_of_time(); +extern void AddSC_boss_archbishop_benedictus(); // COT, hour_of_twilight +extern void AddSC_boss_arcurion(); +extern void AddSC_boss_asira_dawnslayer(); +extern void AddSC_instance_hour_of_twilight(); +extern void AddSC_hyjal(); // COT, hyjal +extern void AddSC_boss_archimonde(); +extern void AddSC_instance_mount_hyjal(); +extern void AddSC_instance_old_hillsbrad(); // COT, old_hillsbrad +extern void AddSC_old_hillsbrad(); +extern void AddSC_culling_of_stratholme(); // COT, culling_of_stratholme +extern void AddSC_instance_culling_of_stratholme(); +extern void AddSC_dire_maul(); // dire_maul +extern void AddSC_instance_dire_maul(); +extern void AddSC_boss_noxxion(); // maraudon +extern void AddSC_boss_onyxia(); // onyxias_lair +extern void AddSC_instance_onyxias_lair(); +extern void AddSC_razorfen_downs(); // razorfen_downs +extern void AddSC_instance_razorfen_kraul(); // razorfen_kraul +extern void AddSC_razorfen_kraul(); +extern void AddSC_boss_ayamiss(); // ruins_of_ahnqiraj +extern void AddSC_boss_buru(); +extern void AddSC_boss_kurinnaxx(); +extern void AddSC_boss_ossirian(); +extern void AddSC_boss_moam(); +extern void AddSC_boss_rajaxx(); +extern void AddSC_ruins_of_ahnqiraj(); +extern void AddSC_instance_ruins_of_ahnqiraj(); +extern void AddSC_boss_cthun(); // temple_of_ahnqiraj +extern void AddSC_boss_fankriss(); +extern void AddSC_boss_huhuran(); +extern void AddSC_bug_trio(); +extern void AddSC_boss_sartura(); +extern void AddSC_boss_skeram(); +extern void AddSC_boss_twinemperors(); +extern void AddSC_boss_viscidus(); +extern void AddSC_mob_anubisath_sentinel(); +extern void AddSC_instance_temple_of_ahnqiraj(); +extern void AddSC_boss_alakir(); // throne_of_the_four_winds +extern void AddSC_conclave_of_the_wind(); +extern void AddSC_instance_throne_of_the_four_winds(); +extern void AddSC_boss_altairus(); // vortex_pinnacle +extern void AddSC_boss_asaad(); +extern void AddSC_boss_grand_vizier_etan(); +extern void AddSC_instance_vortex_pinnacle(); +extern void AddSC_instance_wailing_caverns(); // wailing_caverns +extern void AddSC_wailing_caverns(); +extern void AddSC_boss_zumrah(); // zulfarrak +extern void AddSC_instance_zulfarrak(); +extern void AddSC_zulfarrak(); + +extern void AddSC_ashenvale(); +extern void AddSC_azshara(); +extern void AddSC_azuremyst_isle(); +extern void AddSC_bloodmyst_isle(); +extern void AddSC_boss_azuregos(); +extern void AddSC_darkshore(); +extern void AddSC_desolace(); +extern void AddSC_durotar(); +extern void AddSC_dustwallow_marsh(); +extern void AddSC_felwood(); +extern void AddSC_feralas(); +extern void AddSC_molten_front(); +extern void AddSC_moonglade(); +extern void AddSC_mount_hyjal(); +extern void AddSC_mulgore(); +extern void AddSC_orgrimmar(); +extern void AddSC_silithus(); +extern void AddSC_stonetalon_mountains(); +extern void AddSC_tanaris(); +extern void AddSC_teldrassil(); +extern void AddSC_the_barrens(); +extern void AddSC_thousand_needles(); +extern void AddSC_thunder_bluff(); +extern void AddSC_uldum(); +extern void AddSC_ungoro_crater(); +extern void AddSC_winterspring(); + +// maelstrom +extern void AddSC_boss_corborus(); // stonecore +extern void AddSC_boss_ozruk(); +extern void AddSC_priestess_azil(); +extern void AddSC_boss_slabhide(); +extern void AddSC_instance_stonecore(); + +extern void AddSC_deepholm(); +extern void AddSC_kezan(); +extern void AddSC_lost_isles(); + +// northrend +extern void AddSC_boss_amanitar(); // azjol-nerub, ahnkahet +extern void AddSC_boss_jedoga(); +extern void AddSC_boss_nadox(); +extern void AddSC_boss_taldaram(); +extern void AddSC_boss_volazj(); +extern void AddSC_instance_ahnkahet(); +extern void AddSC_boss_anubarak(); // azjol-nerub, azjol-nerub +extern void AddSC_boss_hadronox(); +extern void AddSC_boss_krikthir(); +extern void AddSC_instance_azjol_nerub(); +extern void AddSC_trial_of_the_champion(); // CC, trial_of_the_champion +extern void AddSC_boss_grand_champions(); +extern void AddSC_instance_trial_of_the_champion(); +extern void AddSC_boss_anubarak_trial(); // CC, trial_of_the_crusader +extern void AddSC_boss_faction_champions(); +extern void AddSC_boss_jaraxxus(); +extern void AddSC_instance_trial_of_the_crusader(); +extern void AddSC_northrend_beasts(); +extern void AddSC_trial_of_the_crusader(); +extern void AddSC_twin_valkyr(); +extern void AddSC_boss_novos(); // draktharon_keep +extern void AddSC_boss_tharonja(); +extern void AddSC_boss_trollgore(); +extern void AddSC_instance_draktharon_keep(); +extern void AddSC_boss_colossus(); // gundrak +extern void AddSC_boss_eck(); +extern void AddSC_boss_galdarah(); +extern void AddSC_boss_moorabi(); +extern void AddSC_boss_sladran(); +extern void AddSC_instance_gundrak(); +extern void AddSC_boss_bronjahm(); // ICC, forge_of_souls +extern void AddSC_boss_devourer_of_souls(); +extern void AddSC_instance_forge_of_souls(); +extern void AddSC_boss_falric(); // ICC, halls_of_reflection +extern void AddSC_boss_lich_king(); +extern void AddSC_boss_marwyn(); +extern void AddSC_halls_of_reflection(); +extern void AddSC_instance_halls_of_reflection(); +extern void AddSC_boss_garfrost(); // ICC, pit_of_saron +extern void AddSC_boss_krick_and_ick(); +extern void AddSC_boss_tyrannus(); +extern void AddSC_instance_pit_of_saron(); +extern void AddSC_pit_of_saron(); +extern void AddSC_blood_prince_council(); // ICC, icecrown_citadel +extern void AddSC_boss_blood_queen_lanathel(); +extern void AddSC_boss_deathbringer_saurfang(); +extern void AddSC_boss_festergut(); +extern void AddSC_boss_lady_deathwhisper(); +extern void AddSC_boss_lord_marrowgar(); +extern void AddSC_boss_professor_putricide(); +extern void AddSC_boss_rotface(); +extern void AddSC_boss_sindragosa(); +extern void AddSC_boss_the_lich_king(); +extern void AddSC_boss_valithria_dreamwalker(); +extern void AddSC_gunship_battle(); +extern void AddSC_instance_icecrown_citadel(); +extern void AddSC_boss_anubrekhan(); // naxxramas +extern void AddSC_boss_four_horsemen(); +extern void AddSC_boss_faerlina(); +extern void AddSC_boss_gluth(); +extern void AddSC_boss_gothik(); +extern void AddSC_boss_grobbulus(); +extern void AddSC_boss_kelthuzad(); +extern void AddSC_boss_loatheb(); +extern void AddSC_boss_maexxna(); +extern void AddSC_boss_noth(); +extern void AddSC_boss_heigan(); +extern void AddSC_boss_patchwerk(); +extern void AddSC_boss_razuvious(); +extern void AddSC_boss_sapphiron(); +extern void AddSC_boss_thaddius(); +extern void AddSC_instance_naxxramas(); +extern void AddSC_boss_malygos(); // nexus, eye_of_eternity +extern void AddSC_instance_eye_of_eternity(); +extern void AddSC_boss_anomalus(); // nexus, nexus +extern void AddSC_boss_keristrasza(); +extern void AddSC_boss_ormorok(); +extern void AddSC_boss_telestra(); +extern void AddSC_instance_nexus(); +extern void AddSC_boss_eregos(); // nexus, oculus +extern void AddSC_boss_urom(); +extern void AddSC_boss_varos(); +extern void AddSC_instance_oculus(); +extern void AddSC_oculus(); +extern void AddSC_boss_sartharion(); // obsidian_sanctum +extern void AddSC_instance_obsidian_sanctum(); +extern void AddSC_boss_baltharus(); // ruby_sanctum +extern void AddSC_boss_halion(); +extern void AddSC_boss_saviana(); +extern void AddSC_boss_zarithrian(); +extern void AddSC_instance_ruby_sanctum(); +extern void AddSC_boss_bjarngrim(); // ulduar, halls_of_lightning +extern void AddSC_boss_ionar(); +extern void AddSC_boss_loken(); +extern void AddSC_boss_volkhan(); +extern void AddSC_instance_halls_of_lightning(); +extern void AddSC_boss_maiden_of_grief(); // ulduar, halls_of_stone +extern void AddSC_boss_sjonnir(); +extern void AddSC_halls_of_stone(); +extern void AddSC_instance_halls_of_stone(); +extern void AddSC_boss_assembly_of_iron(); // ulduar, ulduar +extern void AddSC_boss_algalon(); +extern void AddSC_boss_auriaya(); +extern void AddSC_boss_flame_leviathan(); +extern void AddSC_boss_freya(); +extern void AddSC_boss_general_vezax(); +extern void AddSC_boss_hodir(); +extern void AddSC_boss_ignis(); +extern void AddSC_boss_kologarn(); +extern void AddSC_boss_mimiron(); +extern void AddSC_boss_razorscale(); +extern void AddSC_boss_thorim(); +extern void AddSC_boss_xt_002(); +extern void AddSC_boss_yogg_saron(); +extern void AddSC_instance_ulduar(); +extern void AddSC_ulduar(); +extern void AddSC_boss_ingvar(); // utgarde_keep, utgarde_keep +extern void AddSC_boss_keleseth(); +extern void AddSC_boss_skarvald_and_dalronn(); +extern void AddSC_instance_utgarde_keep(); +extern void AddSC_utgarde_keep(); +extern void AddSC_boss_gortok(); // utgarde_keep, utgarde_pinnacle +extern void AddSC_boss_skadi(); +extern void AddSC_boss_svala(); +extern void AddSC_boss_ymiron(); +extern void AddSC_instance_pinnacle(); +extern void AddSC_boss_archavon(); // vault_of_archavon +extern void AddSC_boss_emalon(); +extern void AddSC_boss_koralon(); +extern void AddSC_boss_toravon(); +extern void AddSC_instance_vault_of_archavon(); +extern void AddSC_boss_erekem(); // violet_hold +extern void AddSC_boss_ichoron(); +extern void AddSC_instance_violet_hold(); +extern void AddSC_violet_hold(); + +extern void AddSC_borean_tundra(); +extern void AddSC_dalaran(); +extern void AddSC_dragonblight(); +extern void AddSC_grizzly_hills(); +extern void AddSC_howling_fjord(); +extern void AddSC_icecrown(); +extern void AddSC_sholazar_basin(); +extern void AddSC_storm_peaks(); +extern void AddSC_zuldrak(); + +// outland +extern void AddSC_boss_exarch_maladaar(); // auchindoun, auchenai_crypts +extern void AddSC_boss_shirrak(); +extern void AddSC_boss_nexusprince_shaffar(); // auchindoun, mana_tombs +extern void AddSC_boss_pandemonius(); +extern void AddSC_boss_anzu(); // auchindoun, sethekk_halls +extern void AddSC_boss_darkweaver_syth(); +extern void AddSC_boss_talon_king_ikiss(); +extern void AddSC_instance_sethekk_halls(); +extern void AddSC_boss_ambassador_hellmaw(); // auchindoun, shadow_labyrinth +extern void AddSC_boss_blackheart_the_inciter(); +extern void AddSC_boss_grandmaster_vorpil(); +extern void AddSC_boss_murmur(); +extern void AddSC_instance_shadow_labyrinth(); +extern void AddSC_black_temple(); // black_temple +extern void AddSC_boss_illidan(); +extern void AddSC_boss_shade_of_akama(); +extern void AddSC_boss_supremus(); +extern void AddSC_boss_gurtogg_bloodboil(); +extern void AddSC_boss_mother_shahraz(); +extern void AddSC_boss_reliquary_of_souls(); +extern void AddSC_boss_teron_gorefiend(); +extern void AddSC_boss_najentus(); +extern void AddSC_boss_illidari_council(); +extern void AddSC_instance_black_temple(); +extern void AddSC_boss_fathomlord_karathress(); // CR, serpent_shrine +extern void AddSC_boss_hydross_the_unstable(); +extern void AddSC_boss_lady_vashj(); +extern void AddSC_boss_leotheras_the_blind(); +extern void AddSC_boss_morogrim_tidewalker(); +extern void AddSC_boss_the_lurker_below(); +extern void AddSC_instance_serpentshrine_cavern(); +extern void AddSC_boss_ahune(); // CR, slave_pens +extern void AddSC_boss_hydromancer_thespia(); // CR, steam_vault +extern void AddSC_boss_mekgineer_steamrigger(); +extern void AddSC_boss_warlord_kalithresh(); +extern void AddSC_instance_steam_vault(); +extern void AddSC_boss_hungarfen(); // CR, Underbog +extern void AddSC_boss_gruul(); // gruuls_lair +extern void AddSC_boss_high_king_maulgar(); +extern void AddSC_instance_gruuls_lair(); +extern void AddSC_boss_broggok(); // HC, blood_furnace +extern void AddSC_boss_kelidan_the_breaker(); +extern void AddSC_boss_the_maker(); +extern void AddSC_instance_blood_furnace(); +extern void AddSC_boss_nazan_and_vazruden(); // HC, hellfire_ramparts +extern void AddSC_boss_omor_the_unscarred(); +extern void AddSC_boss_watchkeeper_gargolmar(); +extern void AddSC_instance_ramparts(); +extern void AddSC_boss_magtheridon(); // HC, magtheridons_lair +extern void AddSC_instance_magtheridons_lair(); +extern void AddSC_boss_grand_warlock_nethekurse(); // HC, shattered_halls +extern void AddSC_boss_warbringer_omrogg(); +extern void AddSC_boss_warchief_kargath_bladefist(); +extern void AddSC_instance_shattered_halls(); +extern void AddSC_arcatraz(); // TK, arcatraz +extern void AddSC_boss_dalliah(); +extern void AddSC_boss_harbinger_skyriss(); +extern void AddSC_boss_soccothrates(); +extern void AddSC_instance_arcatraz(); +extern void AddSC_boss_high_botanist_freywinn(); // TK, botanica +extern void AddSC_boss_laj(); +extern void AddSC_boss_warp_splinter(); +extern void AddSC_boss_alar(); // TK, the_eye +extern void AddSC_boss_high_astromancer_solarian(); +extern void AddSC_boss_kaelthas(); +extern void AddSC_boss_void_reaver(); +extern void AddSC_instance_the_eye(); +extern void AddSC_boss_nethermancer_sepethrea(); // TK, the_mechanar +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(); +extern void AddSC_hellfire_peninsula(); +extern void AddSC_nagrand(); +extern void AddSC_netherstorm(); +extern void AddSC_shadowmoon_valley(); +extern void AddSC_shattrath_city(); +extern void AddSC_terokkar_forest(); +extern void AddSC_zangarmarsh(); + +void AddScripts() +{ + // battlegrounds + AddSC_battleground(); + + // custom + + // examples + AddSC_example_creature(); + AddSC_example_escort(); + AddSC_example_gossip_codebox(); + AddSC_example_misc(); + + // world + AddSC_areatrigger_scripts(); + AddSC_bosses_emerald_dragons(); + AddSC_generic_creature(); + AddSC_go_scripts(); + AddSC_guards(); + AddSC_item_scripts(); + AddSC_npc_professions(); + AddSC_npcs_special(); + AddSC_spell_scripts(); + AddSC_world_map_scripts(); + AddSC_world_map_ebon_hold(); + + // eastern kingdoms + AddSC_ascendant_council(); // bastion_of_twilight + AddSC_boss_chogall(); + AddSC_boss_halfus_wyrmbreaker(); + AddSC_boss_sinestra(); + AddSC_boss_valiona_and_theralion(); + AddSC_instance_bastion_of_twilight(); + AddSC_boss_beauty(); // BM, blackrock_caverns + AddSC_boss_corla(); + AddSC_boss_karsh_steelbender(); + AddSC_boss_lord_obsidius(); + AddSC_boss_romogg(); + AddSC_instance_blackrock_caverns(); + AddSC_blackrock_depths(); // blackrock_depths + AddSC_boss_ambassador_flamelash(); + AddSC_boss_coren_direbrew(); + AddSC_boss_draganthaurissan(); + AddSC_boss_general_angerforge(); + AddSC_boss_high_interrogator_gerstahn(); + AddSC_instance_blackrock_depths(); + AddSC_boss_overlordwyrmthalak(); // blackrock_spire + AddSC_boss_pyroguard_emberseer(); + AddSC_boss_gyth(); + AddSC_instance_blackrock_spire(); + AddSC_boss_adramedes(); // BM, blackwing_descent + AddSC_boss_chimaeron(); + AddSC_boss_magmaw(); + AddSC_boss_maloriak(); + AddSC_boss_nefarian_descent(); + AddSC_instance_blackwing_descent(); + AddSC_omnotron_defense(); + AddSC_boss_razorgore(); // blackwing_lair + AddSC_boss_vaelastrasz(); + AddSC_boss_broodlord(); + AddSC_boss_firemaw(); + AddSC_boss_ebonroc(); + AddSC_boss_flamegor(); + AddSC_boss_chromaggus(); + AddSC_boss_nefarian(); + AddSC_boss_victor_nefarius(); + AddSC_instance_blackwing_lair(); + AddSC_deadmines(); // deadmines + AddSC_boss_mr_smite(); + AddSC_instance_deadmines(); + AddSC_gnomeregan(); // gnomeregan + AddSC_boss_thermaplugg(); + AddSC_instance_gnomeregan(); + AddSC_boss_drahga_shadowburner(); // grim_batol + AddSC_boss_erudax(); + AddSC_boss_forgemaster_throngus(); + AddSC_boss_general_umbriss(); + AddSC_instance_grim_batol(); + AddSC_boss_attumen(); // karazhan + AddSC_boss_curator(); + AddSC_boss_maiden_of_virtue(); + AddSC_boss_shade_of_aran(); + AddSC_boss_netherspite(); + AddSC_boss_nightbane(); + AddSC_boss_prince_malchezaar(); + AddSC_boss_terestian_illhoof(); + AddSC_boss_moroes(); + AddSC_bosses_opera(); + AddSC_chess_event(); + AddSC_instance_karazhan(); + AddSC_karazhan(); + AddSC_boss_felblood_kaelthas(); // magisters_terrace + AddSC_boss_selin_fireheart(); + AddSC_boss_vexallus(); + AddSC_boss_priestess_delrissa(); + AddSC_instance_magisters_terrace(); + AddSC_magisters_terrace(); + AddSC_boss_lucifron(); // molten_core + AddSC_boss_magmadar(); + AddSC_boss_gehennas(); + AddSC_boss_garr(); + AddSC_boss_baron_geddon(); + AddSC_boss_shazzrah(); + AddSC_boss_golemagg(); + AddSC_boss_sulfuron(); + AddSC_boss_majordomo(); + AddSC_boss_ragnaros(); + AddSC_instance_molten_core(); + AddSC_molten_core(); + AddSC_ebon_hold(); // scarlet_enclave + AddSC_boss_arcanist_doan(); // scarlet_monastery + AddSC_boss_herod(); + AddSC_boss_mograine_and_whitemane(); + AddSC_boss_headless_horseman(); + AddSC_instance_scarlet_monastery(); + AddSC_boss_darkmaster_gandling(); // scholomance + AddSC_boss_jandicebarov(); + AddSC_instance_scholomance(); + AddSC_boss_hummel(); // shadowfang_keep + AddSC_shadowfang_keep(); + AddSC_instance_shadowfang_keep(); + AddSC_boss_maleki_the_pallid(); // stratholme + AddSC_boss_cannon_master_willey(); + AddSC_boss_baroness_anastari(); + AddSC_boss_dathrohan_balnazzar(); + AddSC_boss_order_of_silver_hand(); + AddSC_instance_stratholme(); + AddSC_stratholme(); + AddSC_instance_sunken_temple(); // sunken_temple + AddSC_sunken_temple(); + AddSC_boss_brutallus(); // sunwell_plateau + AddSC_boss_eredar_twins(); + AddSC_boss_felmyst(); + AddSC_boss_kalecgos(); + AddSC_boss_kiljaeden(); + AddSC_boss_muru(); + AddSC_instance_sunwell_plateau(); + AddSC_boss_commander_ulthok(); // throne_of_the_tides + AddSC_boss_erunak_and_ghursha(); + AddSC_boss_lady_nazjar(); + AddSC_boss_ozumat(); + AddSC_instance_throne_of_the_tides(); + AddSC_boss_archaedas(); // uldaman + AddSC_instance_uldaman(); + AddSC_uldaman(); + AddSC_boss_akilzon(); // zulaman + AddSC_boss_halazzi(); + AddSC_boss_janalai(); + AddSC_boss_malacrass(); + AddSC_boss_nalorakk(); + AddSC_instance_zulaman(); + AddSC_zulaman(); + AddSC_boss_zuljin(); + AddSC_boss_arlokk(); // zulgurub + AddSC_boss_hakkar(); + AddSC_boss_hazzarah(); + AddSC_boss_jeklik(); + AddSC_boss_jindo(); + AddSC_boss_mandokir(); + AddSC_boss_marli(); + AddSC_boss_ouro(); + AddSC_boss_renataki(); + AddSC_boss_thekal(); + AddSC_boss_venoxis(); + AddSC_instance_zulgurub(); + + AddSC_alterac_mountains(); + AddSC_arathi_highlands(); + AddSC_blasted_lands(); + AddSC_burning_steppes(); + AddSC_dun_morogh(); + AddSC_eastern_plaguelands(); + AddSC_elwynn_forest(); + AddSC_eversong_woods(); + AddSC_ghostlands(); + AddSC_gilneas(); + AddSC_gilneas_city(); + AddSC_hinterlands(); + AddSC_ironforge(); + AddSC_isle_of_queldanas(); + AddSC_loch_modan(); + AddSC_redridge_mountains(); + AddSC_searing_gorge(); + AddSC_silvermoon_city(); + AddSC_silverpine_forest(); + AddSC_stormwind_city(); + AddSC_stranglethorn_vale(); + AddSC_swamp_of_sorrows(); + AddSC_tirisfal_glades(); + AddSC_twilight_highlands(); + AddSC_undercity(); + AddSC_vashjir(); + AddSC_western_plaguelands(); + AddSC_westfall(); + AddSC_wetlands(); + + // kalimdor + AddSC_instance_blackfathom_deeps(); // blackfathom deeps + AddSC_boss_aeonus(); // CoT, dark_portal + AddSC_boss_chrono_lord_deja(); + AddSC_boss_temporus(); + AddSC_dark_portal(); + AddSC_instance_dark_portal(); + AddSC_hyjal(); // CoT, hyjal + AddSC_boss_archimonde(); + AddSC_instance_mount_hyjal(); + AddSC_instance_old_hillsbrad(); // CoT, old_hillsbrand + AddSC_old_hillsbrad(); + AddSC_culling_of_stratholme(); // CoT, culling_of_stratholme + AddSC_instance_culling_of_stratholme(); + AddSC_dire_maul(); // dire_maul + AddSC_instance_dire_maul(); + AddSC_boss_noxxion(); // maraudon + AddSC_boss_onyxia(); // onyxias_lair + AddSC_instance_onyxias_lair(); + AddSC_razorfen_downs(); // razorfen_downs + AddSC_instance_razorfen_kraul(); // razorfen_kraul + AddSC_razorfen_kraul(); + AddSC_boss_ayamiss(); // ruins_of_ahnqiraj + AddSC_boss_buru(); + AddSC_boss_kurinnaxx(); + AddSC_boss_ossirian(); + AddSC_boss_moam(); + AddSC_boss_rajaxx(); + AddSC_ruins_of_ahnqiraj(); + AddSC_instance_ruins_of_ahnqiraj(); + AddSC_boss_cthun(); // temple_of_ahnqiraj + AddSC_boss_fankriss(); + AddSC_boss_huhuran(); + AddSC_bug_trio(); + AddSC_boss_sartura(); + AddSC_boss_skeram(); + AddSC_boss_twinemperors(); + AddSC_boss_viscidus(); + AddSC_mob_anubisath_sentinel(); + AddSC_instance_temple_of_ahnqiraj(); + AddSC_boss_alakir(); // throne_of_the_four_winds + AddSC_conclave_of_the_wind(); + AddSC_instance_throne_of_the_four_winds(); + AddSC_boss_altairus(); // vortex_pinnacle + AddSC_boss_asaad(); + AddSC_boss_grand_vizier_etan(); + AddSC_instance_vortex_pinnacle(); + AddSC_instance_wailing_caverns(); // wailing_caverns + AddSC_wailing_caverns(); + AddSC_boss_zumrah(); // zulfarrak + AddSC_zulfarrak(); + AddSC_instance_zulfarrak(); + + AddSC_ashenvale(); + AddSC_azshara(); + AddSC_azuremyst_isle(); + AddSC_bloodmyst_isle(); + AddSC_boss_azuregos(); + AddSC_darkshore(); + AddSC_desolace(); + AddSC_durotar(); + AddSC_dustwallow_marsh(); + AddSC_felwood(); + AddSC_feralas(); + AddSC_molten_front(); + AddSC_moonglade(); + AddSC_mount_hyjal(); + AddSC_mulgore(); + AddSC_orgrimmar(); + AddSC_silithus(); + AddSC_stonetalon_mountains(); + AddSC_tanaris(); + AddSC_teldrassil(); + AddSC_the_barrens(); + AddSC_thousand_needles(); + AddSC_thunder_bluff(); + AddSC_uldum(); + AddSC_ungoro_crater(); + AddSC_winterspring(); + + // maelstrom + AddSC_boss_corborus(); // stonecore + AddSC_boss_ozruk(); + AddSC_priestess_azil(); + AddSC_boss_slabhide(); + AddSC_instance_stonecore(); + + AddSC_deepholm(); + AddSC_kezan(); + AddSC_lost_isles(); + + // northrend + AddSC_boss_amanitar(); // azjol-nerub, ahnkahet + AddSC_boss_jedoga(); + AddSC_boss_nadox(); + AddSC_boss_taldaram(); + AddSC_boss_volazj(); + AddSC_instance_ahnkahet(); + AddSC_boss_anubarak(); // azjol-nerub, azjol-nerub + AddSC_boss_hadronox(); + AddSC_boss_krikthir(); + AddSC_instance_azjol_nerub(); + AddSC_boss_grand_champions(); // CC, trial_of_the_champion + AddSC_instance_trial_of_the_champion(); + AddSC_trial_of_the_champion(); + AddSC_boss_anubarak_trial(); // CC, trial_of_the_crusader + AddSC_boss_faction_champions(); + AddSC_boss_jaraxxus(); + AddSC_instance_trial_of_the_crusader(); + AddSC_northrend_beasts(); + AddSC_trial_of_the_crusader(); + AddSC_twin_valkyr(); + AddSC_boss_novos(); // draktharon_keep + AddSC_boss_tharonja(); + AddSC_boss_trollgore(); + AddSC_instance_draktharon_keep(); + AddSC_boss_colossus(); // gundrak + AddSC_boss_eck(); + AddSC_boss_galdarah(); + AddSC_boss_moorabi(); + AddSC_boss_sladran(); + AddSC_instance_gundrak(); + AddSC_boss_bronjahm(); // ICC, FH, forge_of_souls + AddSC_boss_devourer_of_souls(); + AddSC_instance_forge_of_souls(); + AddSC_boss_falric(); // ICC, FH, halls_of_reflection + AddSC_boss_lich_king(); + AddSC_boss_marwyn(); + AddSC_halls_of_reflection(); + AddSC_instance_halls_of_reflection(); + AddSC_boss_garfrost(); // ICC, FH, pit_of_saron + AddSC_boss_krick_and_ick(); + AddSC_boss_tyrannus(); + AddSC_instance_pit_of_saron(); + AddSC_pit_of_saron(); + AddSC_blood_prince_council(); // ICC, icecrown_citadel + AddSC_boss_blood_queen_lanathel(); + AddSC_boss_deathbringer_saurfang(); + AddSC_boss_festergut(); + AddSC_boss_lady_deathwhisper(); + AddSC_boss_lord_marrowgar(); + AddSC_boss_professor_putricide(); + AddSC_boss_rotface(); + AddSC_boss_sindragosa(); + AddSC_boss_the_lich_king(); + AddSC_boss_valithria_dreamwalker(); + AddSC_gunship_battle(); + AddSC_instance_icecrown_citadel(); + AddSC_boss_anubrekhan(); // naxxramas + AddSC_boss_four_horsemen(); + AddSC_boss_faerlina(); + AddSC_boss_gluth(); + AddSC_boss_gothik(); + AddSC_boss_grobbulus(); + AddSC_boss_kelthuzad(); + AddSC_boss_loatheb(); + AddSC_boss_maexxna(); + AddSC_boss_noth(); + AddSC_boss_heigan(); + AddSC_boss_patchwerk(); + AddSC_boss_razuvious(); + AddSC_boss_sapphiron(); + AddSC_boss_thaddius(); + AddSC_instance_naxxramas(); + AddSC_boss_malygos(); // nexus, eye_of_eternity + AddSC_instance_eye_of_eternity(); + AddSC_boss_anomalus(); // nexus, nexus + AddSC_boss_keristrasza(); + AddSC_boss_ormorok(); + AddSC_boss_telestra(); + AddSC_instance_nexus(); + AddSC_boss_eregos(); // nexus, oculus + AddSC_boss_urom(); + AddSC_boss_varos(); + AddSC_instance_oculus(); + AddSC_oculus(); + AddSC_boss_sartharion(); // obsidian_sanctum + AddSC_instance_obsidian_sanctum(); + AddSC_boss_baltharus(); // ruby_sanctum + AddSC_boss_halion(); + AddSC_boss_saviana(); + AddSC_boss_zarithrian(); + AddSC_instance_ruby_sanctum(); + AddSC_boss_bjarngrim(); // ulduar, halls_of_lightning + AddSC_boss_ionar(); + AddSC_boss_loken(); + AddSC_boss_volkhan(); + AddSC_instance_halls_of_lightning(); + AddSC_boss_maiden_of_grief(); // ulduar, halls_of_stone + AddSC_boss_sjonnir(); + AddSC_halls_of_stone(); + AddSC_instance_halls_of_stone(); + AddSC_boss_assembly_of_iron(); // ulduar, ulduar + AddSC_boss_algalon(); + AddSC_boss_auriaya(); + AddSC_boss_flame_leviathan(); + AddSC_boss_freya(); + AddSC_boss_general_vezax(); + AddSC_boss_hodir(); + AddSC_boss_ignis(); + AddSC_boss_kologarn(); + AddSC_boss_mimiron(); + AddSC_boss_razorscale(); + AddSC_boss_thorim(); + AddSC_boss_xt_002(); + AddSC_boss_yogg_saron(); + AddSC_instance_ulduar(); + AddSC_ulduar(); + AddSC_boss_ingvar(); // UK, utgarde_keep + AddSC_boss_keleseth(); + AddSC_boss_skarvald_and_dalronn(); + AddSC_instance_utgarde_keep(); + AddSC_utgarde_keep(); + AddSC_boss_gortok(); // UK, utgarde_pinnacle + AddSC_boss_skadi(); + AddSC_boss_svala(); + AddSC_boss_ymiron(); + AddSC_instance_pinnacle(); + AddSC_boss_archavon(); // vault_of_archavon + AddSC_boss_emalon(); + AddSC_boss_koralon(); + AddSC_boss_toravon(); + AddSC_instance_vault_of_archavon(); + AddSC_boss_erekem(); // violet_hold + AddSC_boss_ichoron(); + AddSC_instance_violet_hold(); + AddSC_violet_hold(); + + AddSC_borean_tundra(); + AddSC_dalaran(); + AddSC_dragonblight(); + AddSC_grizzly_hills(); + AddSC_howling_fjord(); + AddSC_icecrown(); + AddSC_sholazar_basin(); + AddSC_storm_peaks(); + AddSC_zuldrak(); + + // outland + AddSC_boss_exarch_maladaar(); // auchindoun, auchenai_crypts + AddSC_boss_shirrak(); + AddSC_boss_nexusprince_shaffar(); // auchindoun, mana_tombs + AddSC_boss_pandemonius(); + AddSC_boss_anzu(); // auchindoun, sethekk_halls + AddSC_boss_darkweaver_syth(); + AddSC_boss_talon_king_ikiss(); + AddSC_instance_sethekk_halls(); + AddSC_boss_ambassador_hellmaw(); // auchindoun, shadow_labyrinth + AddSC_boss_blackheart_the_inciter(); + AddSC_boss_grandmaster_vorpil(); + AddSC_boss_murmur(); + AddSC_instance_shadow_labyrinth(); + AddSC_black_temple(); // black_temple + AddSC_boss_illidan(); + AddSC_boss_shade_of_akama(); + AddSC_boss_supremus(); + AddSC_boss_gurtogg_bloodboil(); + AddSC_boss_mother_shahraz(); + AddSC_boss_reliquary_of_souls(); + AddSC_boss_teron_gorefiend(); + AddSC_boss_najentus(); + AddSC_boss_illidari_council(); + AddSC_instance_black_temple(); + AddSC_boss_fathomlord_karathress(); // CR, serpent_shrine + AddSC_boss_hydross_the_unstable(); + AddSC_boss_lady_vashj(); + AddSC_boss_leotheras_the_blind(); + AddSC_boss_morogrim_tidewalker(); + AddSC_boss_the_lurker_below(); + AddSC_instance_serpentshrine_cavern(); + AddSC_boss_ahune(); // CR, slave_pens + AddSC_boss_hydromancer_thespia(); // CR, steam_vault + AddSC_boss_mekgineer_steamrigger(); + AddSC_boss_warlord_kalithresh(); + AddSC_instance_steam_vault(); + AddSC_boss_hungarfen(); // CR, Underbog + AddSC_boss_gruul(); // gruuls_lair + AddSC_boss_high_king_maulgar(); + AddSC_instance_gruuls_lair(); + AddSC_boss_broggok(); // HC, blood_furnace + AddSC_boss_kelidan_the_breaker(); + AddSC_boss_the_maker(); + AddSC_instance_blood_furnace(); + AddSC_boss_nazan_and_vazruden(); // HC, hellfire_ramparts + AddSC_boss_omor_the_unscarred(); + AddSC_boss_watchkeeper_gargolmar(); + AddSC_instance_ramparts(); + AddSC_boss_magtheridon(); // HC, magtheridons_lair + AddSC_instance_magtheridons_lair(); + AddSC_boss_grand_warlock_nethekurse(); // HC, shattered_halls + AddSC_boss_warbringer_omrogg(); + AddSC_boss_warchief_kargath_bladefist(); + AddSC_instance_shattered_halls(); + AddSC_arcatraz(); // TK, arcatraz + AddSC_boss_dalliah(); + AddSC_boss_harbinger_skyriss(); + AddSC_boss_soccothrates(); + AddSC_instance_arcatraz(); + AddSC_boss_high_botanist_freywinn(); // TK, botanica + AddSC_boss_laj(); + AddSC_boss_warp_splinter(); + AddSC_boss_alar(); // TK, the_eye + AddSC_boss_high_astromancer_solarian(); + AddSC_boss_kaelthas(); + AddSC_boss_void_reaver(); + AddSC_instance_the_eye(); + AddSC_boss_nethermancer_sepethrea(); // TK, the_mechanar + AddSC_boss_pathaleon_the_calculator(); + AddSC_instance_mechanar(); + + AddSC_blades_edge_mountains(); + AddSC_boss_doomlordkazzak(); + AddSC_boss_doomwalker(); + AddSC_hellfire_peninsula(); + AddSC_nagrand(); + AddSC_netherstorm(); + AddSC_shadowmoon_valley(); + AddSC_shattrath_city(); + AddSC_terokkar_forest(); + AddSC_zangarmarsh(); +} diff --git a/src/modules/SD2/system/ScriptLoader.h b/src/modules/SD2/system/ScriptLoader.h new file mode 100644 index 000000000..3fccf6bdc --- /dev/null +++ b/src/modules/SD2/system/ScriptLoader.h @@ -0,0 +1,31 @@ +/** + * ScriptDev2 is an extension for mangos providing enhanced features for + * area triggers, creatures, game objects, instances, items, and spells beyond + * the default database scripting in mangos. + * + * Copyright (C) 2006-2013 ScriptDev2 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * World of Warcraft, and all World of Warcraft or Warcraft art, images, + * and lore are copyrighted by Blizzard Entertainment, Inc. + */ + +#ifndef SC_SCRIPTLOADER_H +#define SC_SCRIPTLOADER_H + +void AddScripts(); + +#endif diff --git a/src/modules/SD2/system/system.cpp b/src/modules/SD2/system/system.cpp new file mode 100644 index 000000000..03bdba8e3 --- /dev/null +++ b/src/modules/SD2/system/system.cpp @@ -0,0 +1,159 @@ +/** + * ScriptDev2 is an extension for mangos providing enhanced features for + * area triggers, creatures, game objects, instances, items, and spells beyond + * the default database scripting in mangos. + * + * Copyright (C) 2006-2013 ScriptDev2 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * World of Warcraft, and all World of Warcraft or Warcraft art, images, + * and lore are copyrighted by Blizzard Entertainment, Inc. + */ + +#include "precompiled.h" +#include "system.h" +#include "../config-sd2.h" +#include "ProgressBar.h" +#include "ObjectMgr.h" +#include "Database/DatabaseEnv.h" + +DatabaseType SD2Database; +std::string strSD2Version; + +SystemMgr::SystemMgr() +{ +} + +SystemMgr& SystemMgr::Instance() +{ + static SystemMgr pSysMgr; + return pSysMgr; +} + +void SystemMgr::LoadVersion() +{ + // Get Version information + QueryResult* pResult = SD2Database.PQuery("SELECT version FROM sd2_db_version LIMIT 1"); + + if (pResult) + { + Field* pFields = pResult->Fetch(); + + strSD2Version = pFields[0].GetCppString(); + + delete pResult; + } + else + { + script_error_log("Missing `sd2_db_version` information."); + } + + // Setup version info and display it + if (strSD2Version.empty()) + { + strSD2Version.append("ScriptDev2 "); + } + + strSD2Version.append(MANGOS_FULLVERSION("*", "*", "*", "*")); + + outstring_log("Loading %s", strSD2Version.c_str()); +} + +void SystemMgr::LoadScriptTexts() +{ + outstring_log("SD2: Loading Script Texts..."); + LoadMangosStrings(SD2Database, "script_texts", TEXT_SOURCE_TEXT_START, TEXT_SOURCE_TEXT_END, true); +} + +void SystemMgr::LoadScriptTextsCustom() +{ + outstring_log("SD2: Loading Custom Texts..."); + LoadMangosStrings(SD2Database, "custom_texts", TEXT_SOURCE_CUSTOM_START, TEXT_SOURCE_CUSTOM_END, true); +} + +void SystemMgr::LoadScriptGossipTexts() +{ + outstring_log("SD2: Loading Gossip Texts..."); + LoadMangosStrings(SD2Database, "gossip_texts", TEXT_SOURCE_GOSSIP_START, TEXT_SOURCE_GOSSIP_END); +} + +void SystemMgr::LoadScriptWaypoints() +{ + // Drop Existing Waypoint list + m_mPointMoveMap.clear(); + + uint64 uiCreatureCount = 0; + + // Load Waypoints + QueryResult* pResult = SD2Database.PQuery("SELECT COUNT(entry) FROM script_waypoint GROUP BY entry"); + if (pResult) + { + uiCreatureCount = pResult->GetRowCount(); + delete pResult; + } + + outstring_log("SD2: Loading Script Waypoints for " UI64FMTD " creature(s)...", uiCreatureCount); + + pResult = SD2Database.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(); + pTemp.fX = pFields[2].GetFloat(); + 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("SD2: DB table script_waypoint has waypoint for nonexistent creature entry %u", pTemp.uiCreatureEntry); + continue; + } + + if (!pCInfo->ScriptID) + { + error_db_log("SD2: 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(">> Loaded %u Script Waypoint nodes.", uiNodeCount); + } + else + { + BarGoLink bar(1); + bar.step(); + outstring_log(">> Loaded 0 Script Waypoints."); + } +} diff --git a/src/modules/SD2/system/system.h b/src/modules/SD2/system/system.h new file mode 100644 index 000000000..054fa063c --- /dev/null +++ b/src/modules/SD2/system/system.h @@ -0,0 +1,90 @@ +/** + * ScriptDev2 is an extension for mangos providing enhanced features for + * area triggers, creatures, game objects, instances, items, and spells beyond + * the default database scripting in mangos. + * + * Copyright (C) 2006-2013 ScriptDev2 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * World of Warcraft, and all World of Warcraft or Warcraft art, images, + * and lore are copyrighted by Blizzard Entertainment, Inc. + */ + +#ifndef SC_SYSTEM_H +#define SC_SYSTEM_H + +extern DatabaseType SD2Database; +extern std::string strSD2Version; // version info: database entry and revision + +#define TEXT_SOURCE_RANGE -1000000 // the amount of entries each text source has available + +#define TEXT_SOURCE_TEXT_START TEXT_SOURCE_RANGE +#define TEXT_SOURCE_TEXT_END TEXT_SOURCE_RANGE*2 + 1 + +#define TEXT_SOURCE_CUSTOM_START TEXT_SOURCE_RANGE*2 +#define TEXT_SOURCE_CUSTOM_END TEXT_SOURCE_RANGE*3 + 1 + +#define TEXT_SOURCE_GOSSIP_START TEXT_SOURCE_RANGE*3 +#define TEXT_SOURCE_GOSSIP_END TEXT_SOURCE_RANGE*4 + 1 + +struct ScriptPointMove +{ + uint32 uiCreatureEntry; + uint32 uiPointId; + float fX; + float fY; + float fZ; + uint32 uiWaitTime; +}; + +#define pSystemMgr SystemMgr::Instance() + +class SystemMgr +{ + public: + SystemMgr(); + ~SystemMgr() {} + + static SystemMgr& Instance(); + + typedef UNORDERED_MAP > PointMoveMap; + + // Database + void LoadVersion(); + void LoadScriptTexts(); + void LoadScriptTextsCustom(); + void LoadScriptGossipTexts(); + void LoadScriptWaypoints(); + + std::vector const& GetPointMoveList(uint32 uiCreatureEntry) const + { + static std::vector vEmpty; + + PointMoveMap::const_iterator itr = m_mPointMoveMap.find(uiCreatureEntry); + + if (itr == m_mPointMoveMap.end()) + { + return vEmpty; + } + + return itr->second; + } + + protected: + PointMoveMap m_mPointMoveMap; // coordinates for waypoints +}; + +#endif