Merge branch 'master' into Playerbot

This commit is contained in:
Yunfan Li
2023-07-30 14:37:00 +08:00
43 changed files with 831 additions and 648 deletions
+1 -1
View File
@@ -23,7 +23,7 @@ jobs:
COMPILER: ${{ matrix.compiler }} COMPILER: ${{ matrix.compiler }}
if: github.repository == 'azerothcore/azerothcore-wotlk' && !github.event.pull_request.draft if: github.repository == 'azerothcore/azerothcore-wotlk' && !github.event.pull_request.draft
steps: steps:
- uses: actions/checkout@v2 - uses: actions/checkout@v3
- name: Cache - name: Cache
uses: actions/cache@v3 uses: actions/cache@v3
env: env:
+1 -1
View File
@@ -6,6 +6,6 @@ jobs:
check-pending-sql: check-pending-sql:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- uses: actions/checkout@v2 - uses: actions/checkout@v3
- name: Check pending SQL - name: Check pending SQL
run: source ./apps/ci/ci-pending.sh run: source ./apps/ci/ci-pending.sh
+1 -1
View File
@@ -11,6 +11,6 @@ jobs:
runs-on: ${{ matrix.os }} runs-on: ${{ matrix.os }}
name: check codestyle name: check codestyle
steps: steps:
- uses: actions/checkout@v2 - uses: actions/checkout@v3
- name: Check core codestyle - name: Check core codestyle
run: source ./apps/ci/ci-codestyle.sh run: source ./apps/ci/ci-codestyle.sh
+2 -2
View File
@@ -23,9 +23,9 @@ jobs:
COMPILER: ${{ matrix.compiler }} COMPILER: ${{ matrix.compiler }}
if: github.repository == 'azerothcore/azerothcore-wotlk' && !github.event.pull_request.draft if: github.repository == 'azerothcore/azerothcore-wotlk' && !github.event.pull_request.draft
steps: steps:
- uses: actions/checkout@v2 - uses: actions/checkout@v3
- name: Cache - name: Cache
uses: actions/cache@v2 uses: actions/cache@v3
with: with:
path: var/ccache path: var/ccache
key: ccache:${{ matrix.os }}:${{ matrix.compiler }}:${{ github.ref }}:${{ github.sha }} key: ccache:${{ matrix.os }}:${{ matrix.compiler }}:${{ github.ref }}:${{ github.sha }}
+2 -2
View File
@@ -40,9 +40,9 @@ jobs:
|| github.event.label.name == 'run-build') || github.event.label.name == 'run-build')
) )
steps: steps:
- uses: actions/checkout@v2 - uses: actions/checkout@v3
- name: Cache - name: Cache
uses: actions/cache@v2 uses: actions/cache@v3
with: with:
path: var/ccache path: var/ccache
key: ccache:${{ matrix.os }}:${{ matrix.compiler }}:${{ github.ref }}:${{ github.sha }} key: ccache:${{ matrix.os }}:${{ matrix.compiler }}:${{ github.ref }}:${{ github.sha }}
+2 -2
View File
@@ -34,12 +34,12 @@ jobs:
|| github.event.label.name == 'run-build') || github.event.label.name == 'run-build')
) )
steps: steps:
- uses: actions/checkout@v2 - uses: actions/checkout@v3
- name: Checkout modules - name: Checkout modules
run: ./apps/ci/ci-install-modules.sh run: ./apps/ci/ci-install-modules.sh
if: matrix.modules == 'with' if: matrix.modules == 'with'
- name: Cache - name: Cache
uses: actions/cache@v2 uses: actions/cache@v3
with: with:
path: var/ccache path: var/ccache
key: ccache:${{ matrix.os }}:${{ matrix.compiler }}:${{ matrix.modules }}-modules:${{ github.ref }}:${{ github.sha }} key: ccache:${{ matrix.os }}:${{ matrix.compiler }}:${{ matrix.modules }}-modules:${{ github.ref }}:${{ github.sha }}
+1 -1
View File
@@ -20,7 +20,7 @@ jobs:
runs-on: ubuntu-22.04 runs-on: ubuntu-22.04
name: cpp check name: cpp check
steps: steps:
- uses: actions/checkout@v2 - uses: actions/checkout@v3
- name: cpp check - name: cpp check
run: | run: |
sudo apt update -y sudo apt update -y
+1 -1
View File
@@ -124,7 +124,7 @@ jobs:
password: ${{ secrets.DOCKERHUB_TOKEN }} password: ${{ secrets.DOCKERHUB_TOKEN }}
- name: Cache - name: Cache
uses: actions/cache@v2 uses: actions/cache@v3
with: with:
path: var/docker/ccache path: var/docker/ccache
key: ccache:${{ matrix.os }}:clang:without-modules:${{ github.ref }}:${{ github.sha }} key: ccache:${{ matrix.os }}:clang:without-modules:${{ github.ref }}:${{ github.sha }}
+1 -1
View File
@@ -11,7 +11,7 @@ jobs:
runs-on: ubuntu-20.04 runs-on: ubuntu-20.04
if: github.repository == 'azerothcore/azerothcore-wotlk' && !github.event.pull_request.draft if: github.repository == 'azerothcore/azerothcore-wotlk' && !github.event.pull_request.draft
steps: steps:
- uses: actions/checkout@v2 - uses: actions/checkout@v3
with: with:
persist-credentials: false persist-credentials: false
- name: Extract branch name - name: Extract branch name
+2 -2
View File
@@ -25,9 +25,9 @@ jobs:
&& !github.event.pull_request.draft && !github.event.pull_request.draft
&& (github.ref == 'refs/heads/master' || contains(github.event.pull_request.labels.*.name, 'run-build') || github.event.label.name == 'run-build') && (github.ref == 'refs/heads/master' || contains(github.event.pull_request.labels.*.name, 'run-build') || github.event.label.name == 'run-build')
steps: steps:
- uses: actions/checkout@v2 - uses: actions/checkout@v3
- name: Cache - name: Cache
uses: actions/cache@v2 uses: actions/cache@v3
with: with:
path: ~/Library/Caches/ccache path: ~/Library/Caches/ccache
key: ccache:${{ matrix.os }}:${{ github.ref }}:${{ github.sha }} key: ccache:${{ matrix.os }}:${{ github.ref }}:${{ github.sha }}
+1 -1
View File
@@ -23,7 +23,7 @@ jobs:
COMPILER: ${{ matrix.compiler }} COMPILER: ${{ matrix.compiler }}
if: github.repository == 'azerothcore/azerothcore-wotlk' && !github.event.pull_request.draft if: github.repository == 'azerothcore/azerothcore-wotlk' && !github.event.pull_request.draft
steps: steps:
- uses: actions/checkout@v2 - uses: actions/checkout@v3
- name: Cache - name: Cache
uses: actions/cache@v3 uses: actions/cache@v3
env: env:
+7 -1
View File
@@ -25,7 +25,9 @@ jobs:
&& !github.event.pull_request.draft && !github.event.pull_request.draft
&& (github.ref == 'refs/heads/master' || contains(github.event.pull_request.labels.*.name, 'run-build') || github.event.label.name == 'run-build') && (github.ref == 'refs/heads/master' || contains(github.event.pull_request.labels.*.name, 'run-build') || github.event.label.name == 'run-build')
steps: steps:
- uses: actions/checkout@v2 - uses: actions/checkout@v3
- name: ccache
uses: hendrikmuhs/ccache-action@v1.2.9
- name: Configure OS - name: Configure OS
shell: bash shell: bash
env: env:
@@ -40,6 +42,10 @@ jobs:
run: | run: |
export CTOOLS_BUILD=all export CTOOLS_BUILD=all
./acore.sh compiler build ./acore.sh compiler build
- name: Copy dll files
shell: bash
run: |
cp "/c/Program Files/OpenSSL/bin/legacy.dll" "env/dist"
- name: Dry run authserver - name: Dry run authserver
shell: bash shell: bash
run: | run: |
+1 -1
View File
@@ -1,6 +1,6 @@
#!/usr/bin/env bash #!/usr/bin/env bash
export OPENSSL_ROOT_DIR=$(brew --prefix openssl@1.1) export OPENSSL_ROOT_DIR=$(brew --prefix openssl@3)
export CCACHE_CPP2=true export CCACHE_CPP2=true
export CCACHE_MAXSIZE='500M' export CCACHE_MAXSIZE='500M'
+1 -1
View File
@@ -26,4 +26,4 @@ if ! command -v cmake &>/dev/null ; then
fi fi
########################################## ##########################################
brew install openssl@1.1 readline boost bash-completion curl unzip mysql ccache brew install openssl@3 readline boost bash-completion curl unzip mysql ccache
@@ -0,0 +1,2 @@
-- DB update 2023_07_24_00 -> 2023_07_26_00
UPDATE `spell_proc_event` SET `procFlags` = 4 WHERE `entry` = 23689;
@@ -0,0 +1,3 @@
-- DB update 2023_07_26_00 -> 2023_07_26_01
--
DELETE FROM `smart_scripts` WHERE (`entryorguid` = 17671) AND (`source_type` = 0) AND (`id` IN (4));
@@ -0,0 +1,46 @@
-- DB update 2023_07_26_01 -> 2023_07_26_02
--
UPDATE `item_template` SET `spellcooldown_1` = -1 WHERE (`entry` = 37111);
UPDATE `item_template` SET `spellcooldown_1` = -1, `spellcategorycooldown_1` = 2000 WHERE (`entry` = 47477);
UPDATE `item_template` SET `spellcooldown_1` = -1, `spellcategorycooldown_1` = 2000 WHERE (`entry` = 47316);
UPDATE `item_template` SET `spellcooldown_1` = -1, `spellcooldown_2` = -1, `spellcooldown_3` = -1 WHERE (`entry` = 45507);
UPDATE `item_template` SET `spellcooldown_2` = -1, `spellcooldown_3` = -1 WHERE (`entry` = 45522);
UPDATE `item_template` SET `spellcooldown_2` = -1, `spellcooldown_3` = -1 WHERE (`entry` = 45929);
UPDATE `item_template` SET `spellcooldown_2` = -1, `spellcooldown_3` = -1 WHERE (`entry` = 45931);
UPDATE `item_template` SET `spelltrigger_2` = 0, `spellcooldown_2` = -1, `spellcooldown_3` = -1 WHERE (`entry` = 45866);
UPDATE `item_template` SET `spellcooldown_1` = -1, `spellcategorycooldown_1` = 45000 WHERE (`entry` = 40255);
UPDATE `item_template` SET `spellcooldown_1` = -1 WHERE (`entry` = 40373);
UPDATE `item_template` SET `spellcooldown_1` = 0, `spellcategorycooldown_1` = -1 WHERE (`entry` = 37835);
UPDATE `item_template` SET `spellcooldown_1` = 0, `spellcategorycooldown_1` = -1 WHERE (`entry` = 39229);
UPDATE `item_template` SET `spellcooldown_1` = 0 WHERE (`entry` = 37390);
UPDATE `item_template` SET `spellcooldown_1` = 0 WHERE (`entry` = 37657);
UPDATE `item_template` SET `spellcooldown_1` = 0 WHERE (`entry` = 37660);
UPDATE `item_template` SET `spellppmRate_1` = 0 WHERE (`entry` = 50348);
UPDATE `item_template` SET `spellppmRate_1` = 0 WHERE (`entry` = 50353);
UPDATE `item_template` SET `spellppmRate_1` = 0 WHERE (`entry` = 54588);
UPDATE `spell_proc_event` SET `CustomChance` = 45 WHERE `entry` = 71545;
UPDATE `spell_proc_event` SET `CustomChance` = 35 WHERE `entry` = 71562;
UPDATE `spell_proc_event` SET `Cooldown` = 50000 WHERE `entry` = 71585;
UPDATE `spell_proc_event` SET `Cooldown` = 50000 WHERE `entry` = 64738;
UPDATE `spell_proc_event` SET `Cooldown` = 50000 WHERE `entry` = 64792;
UPDATE `spell_proc_event` SET `Cooldown` = 50000 WHERE `entry` = 62114;
UPDATE `spell_proc_event` SET `Cooldown` = 45000 WHERE `entry` = 58901;
UPDATE `spell_proc_event` SET `Cooldown` = 50000 WHERE `entry` = 33648;
UPDATE `spell_proc_event` SET `Cooldown` = 50000 WHERE `entry` = 49622;
UPDATE `spell_proc_event` SET `Cooldown` = 50000 WHERE `entry` = 65013;
UPDATE `spell_proc_event` SET `Cooldown` = 50000 WHERE `entry` = 65002;
UPDATE `spell_proc_event` SET `Cooldown` = 50000 WHERE `entry` = 62115;
UPDATE `spell_proc_event` SET `Cooldown` = 50000 WHERE `entry` = 67672;
UPDATE `spell_proc_event` SET `Cooldown` = 50000 WHERE `entry` = 33648;
UPDATE `spell_proc_event` SET `Cooldown` = 50000 WHERE `entry` = 60221;
UPDATE `spell_proc_event` SET `Cooldown` = 50000 WHERE `entry` = 60519;
UPDATE `spell_proc_event` SET `Cooldown` = 50000 WHERE `entry` = 63251;
DELETE FROM `spell_proc_event` WHERE `entry` IN (75474, 71602, 64764);
INSERT INTO `spell_proc_event` (`entry`, `SchoolMask`, `SpellFamilyName`, `SpellFamilyMask0`, `SpellFamilyMask1`, `SpellFamilyMask2`, `procFlags`, `procEx`, `procPhase`, `ppmRate`, `CustomChance`, `Cooldown`) VALUES
(75474,0,0,0,0,0,0,0,0,0,0,50000),
(64764,0,0,0,0,0,0,0,0,0,0,50000),
(71602,0,0,0,0,0,0,0,0,0,0,45000);
@@ -0,0 +1,4 @@
-- DB update 2023_07_26_02 -> 2023_07_26_03
--
DELETE FROM `areatrigger_teleport` WHERE `ID` = 2068;
INSERT INTO `areatrigger_teleport` (`ID`, `Name`, `target_map`, `target_position_x`, `target_position_y`, `target_position_z`, `target_orientation`) VALUES (2068, 'Blackrock Spire - Jump Exit', 0, -7558.39, -1309.43, 248.454, 1.5708);
@@ -0,0 +1,5 @@
-- DB update 2023_07_26_03 -> 2023_07_26_04
--
UPDATE `creature_template` SET `AIName` = '' WHERE `entry` = 12298;
DELETE FROM `smart_scripts` WHERE (`entryorguid` = 12298) AND (`source_type` = 0);
@@ -0,0 +1,29 @@
-- DB update 2023_07_26_04 -> 2023_07_27_00
-- path of Garn Mathers
SET @NPC := 30758 * 10; -- Garn mathers GUID * 10
DELETE FROM `waypoint_data` WHERE `id` = @NPC;
INSERT INTO `waypoint_data` (`id`, `point`, `position_x`, `position_y`, `position_z`, `orientation`) VALUES
(@NPC, 1, -3050.0679, -4329.487, 8.319094, 0),
(@NPC, 2, -3044.8179, -4326.487, 7.819094, 0),
(@NPC, 3, -3036.245, -4321.0723, 7.388595, 0),
(@NPC, 4, -3023.495, -4328.3223, 7.888595, 0),
(@NPC, 5, -3007.757, -4334.8223, 6.9061613, 0),
(@NPC, 6, -2997.757, -4344.0723, 6.6561613, 0),
(@NPC, 7, -2981.6763, -4353.217, 8.946001, 0),
(@NPC, 8, -2970.2686, -4358.55, 10.112873, 0),
(@NPC, 9, -2972.253, -4368.476, 9.980476, 0),
(@NPC, 10, -2983.7305, -4381.0786, 10.497398, 0),
(@NPC, 11, -3000.9695, -4383.346, 10.6955185, 0),
(@NPC, 12, -3011.9897, -4373.612, 9.795075, 0),
(@NPC, 13, -3023.2007, -4366.656, 10.3360815, 0),
(@NPC, 14, -3026.9507, -4364.906, 10.0860815, 0),
(@NPC, 15, -3040.5425, -4358.04, 8.381775, 0),
(@NPC, 16, -3048.0571, -4343.4937, 8.053871, 0);
DELETE FROM `creature` WHERE `id1` = 23679 AND `guid` = 30758;
INSERT INTO `creature` (`guid`, `id1`, `id2`, `id3`, `map`, `zoneId`, `areaId`, `spawnMask`, `phaseMask`, `equipment_id`, `position_x`, `position_y`, `position_z`, `orientation`, `spawntimesecs`, `wander_distance`, `currentwaypoint`, `curhealth`, `curmana`, `MovementType`, `npcflag`, `unit_flags`, `dynamicflags`, `ScriptName`, `VerifiedBuild`) VALUES
(30758, 23679, 0, 0, 1, 0, 0, 1, 1, 1, -3050.214, -4329.639, 8.156482, 1.730132341384887695, 180, 0, 0, 1536, 0, 2, 0, 0, 0, '', 50375);
DELETE FROM `creature_addon` WHERE `guid` = 30758;
INSERT INTO `creature_addon` (`guid`, `path_id`, `mount`, `bytes1`, `bytes2`, `emote`, `visibilityDistanceType`, `auras`) VALUES
(30758, @NPC, 0, 0, 1, 0, 0, '');
@@ -0,0 +1,3 @@
-- DB update 2023_07_27_00 -> 2023_07_27_01
--
UPDATE `creature_template` SET `ScriptName`='npc_crashin_thrashin_robot' WHERE `entry`=17299;
@@ -0,0 +1,5 @@
-- DB update 2023_07_27_01 -> 2023_07_27_02
--
DELETE FROM `spell_linked_spell` WHERE `spell_trigger` = 29683 AND `spell_effect` = 32214;
INSERT INTO `spell_linked_spell` (`spell_trigger`, `spell_effect`, `type`, `comment`) VALUES
(29683, 32214, 0, "Spotlight 20% player buff");
@@ -0,0 +1,4 @@
-- DB update 2023_07_27_02 -> 2023_07_27_03
--
UPDATE `creature` SET `wander_distance` = 0, `MovementType` = 0 WHERE `guid` = 15542;
@@ -0,0 +1,6 @@
-- DB update 2023_07_27_03 -> 2023_07_27_04
--
DELETE FROM `smart_scripts` WHERE `entryorguid` = 17157 AND `source_type` = 0;
INSERT INTO `smart_scripts` (`entryorguid`, `source_type`, `id`, `link`, `event_type`, `event_phase_mask`, `event_chance`, `event_flags`, `event_param1`, `event_param2`, `event_param3`, `event_param4`, `event_param5`, `action_type`, `action_param1`, `action_param2`, `action_param3`, `action_param4`, `action_param5`, `action_param6`, `target_type`, `target_param1`, `target_param2`, `target_param3`, `target_param4`, `target_x`, `target_y`, `target_z`, `target_o`, `comment`) VALUES
(17157, 0, 0, 0, 0, 0, 100, 0, 9600, 12900, 30600, 43600, 0, 11, 33840, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 'Shattered Rumbler - In Combat - Cast \'Earth Rumble\' '),
(17157, 0, 1, 0, 8, 0, 100, 513, 32001, 0, 0, 0, 0, 80, 1715700, 2, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Shattered Rumbler - On Spellhit \'Throw Gordawg\'s Boulder\' - Run Script (No Repeat)');
@@ -0,0 +1,4 @@
-- DB update 2023_07_27_04 -> 2023_07_27_05
--
DELETE FROM `smart_scripts` WHERE `entryorguid` = 94 AND `source_type` = 0;
UPDATE `creature_template` SET `AIName` = '' WHERE `entry` = 94;
@@ -0,0 +1,3 @@
-- DB update 2023_07_27_05 -> 2023_07_28_00
--
UPDATE `creature` SET `position_x`=-33.3555, `position_y`=-927.892, `position_z`=54.5261, `orientation`=1.54421, `spawntimesecs`=600, `wander_distance`=1, `MovementType`=1, `VerifiedBuild`=50375 WHERE `guid`=15542;
@@ -0,0 +1,4 @@
-- DB update 2023_07_28_00 -> 2023_07_29_00
--
DELETE FROM `spell_script_names` WHERE `spell_id` = 38048;
INSERT INTO `spell_script_names` (`spell_id`,`ScriptName`) VALUES (38048, 'spell_gen_curse_of_pain');
@@ -0,0 +1,3 @@
-- DB update 2023_07_29_00 -> 2023_07_29_01
--
UPDATE `creature_model_info` SET `BoundingRadius`=9, `CombatReach`=15.75 WHERE `DisplayID`=17887;
@@ -1563,6 +1563,14 @@ AllowTwoSide.Interaction.Group = 0
AllowTwoSide.Interaction.Guild = 0 AllowTwoSide.Interaction.Guild = 0
#
# AllowTwoSide.Interaction.Arena
# Description: Allow joining arena teams between factions.
# Default: 0 - (Disabled)
# 1 - (Enabled)
AllowTwoSide.Interaction.Arena = 0
# #
# AllowTwoSide.Interaction.Auction # AllowTwoSide.Interaction.Auction
# Description: Allow auctions between factions. # Description: Allow auctions between factions.
+42
View File
@@ -17,6 +17,7 @@
#include "Arena.h" #include "Arena.h"
#include "ArenaTeamMgr.h" #include "ArenaTeamMgr.h"
#include "GroupMgr.h"
#include "Log.h" #include "Log.h"
#include "ObjectAccessor.h" #include "ObjectAccessor.h"
#include "Player.h" #include "Player.h"
@@ -93,6 +94,47 @@ void Arena::AddPlayer(Player* player)
} }
UpdateArenaWorldState(); UpdateArenaWorldState();
Group* group = player->GetGroup();
if (group)
{
// Hackfix for crossfaction arenas, recreate group when joining
// Without this, players in a crossfaction arena group would not be able to cast beneficial spells on their teammates
std::vector<Player*> members;
bool isCrossfaction = false;
for (Group::member_citerator mitr = group->GetMemberSlots().begin(); mitr != group->GetMemberSlots().end(); ++mitr)
{
Player* member = ObjectAccessor::FindPlayer(mitr->guid);
if (!member || member->GetGUID() == player->GetGUID())
{
continue;
}
members.push_back(member);
if (member->GetTeamId(true) != player->GetTeamId(true))
{
isCrossfaction = true;
}
}
if (isCrossfaction)
{
for (Player* member : members)
{
member->RemoveFromGroup();
}
group->Disband();
group = new Group();
SetBgRaid(player->GetBgTeamId(), group);
group->Create(player);
sGroupMgr->AddGroup(group);
for (Player* member : members)
{
group->AddMember(member);
}
}
}
} }
void Arena::RemovePlayer(Player* /*player*/) void Arena::RemovePlayer(Player* /*player*/)
@@ -133,7 +133,7 @@ void WorldSession::HandleArenaTeamInviteOpcode(WorldPacket& recvData)
if (player->GetSocial()->HasIgnore(GetPlayer()->GetGUID())) if (player->GetSocial()->HasIgnore(GetPlayer()->GetGUID()))
return; return;
if (!sWorld->getBoolConfig(CONFIG_ALLOW_TWO_SIDE_INTERACTION_GUILD) && player->GetTeamId() != GetPlayer()->GetTeamId()) if (!sWorld->getBoolConfig(CONFIG_ALLOW_TWO_SIDE_INTERACTION_ARENA) && player->GetTeamId() != GetPlayer()->GetTeamId())
{ {
SendArenaTeamCommandResult(ERR_ARENA_TEAM_INVITE_SS, "", "", ERR_ARENA_TEAM_NOT_ALLIED); SendArenaTeamCommandResult(ERR_ARENA_TEAM_INVITE_SS, "", "", ERR_ARENA_TEAM_NOT_ALLIED);
return; return;
@@ -185,7 +185,7 @@ void WorldSession::HandleArenaTeamAcceptOpcode(WorldPacket& /*recvData*/)
} }
// Only allow members of the other faction to join the team if cross faction interaction is enabled // Only allow members of the other faction to join the team if cross faction interaction is enabled
if (!sWorld->getBoolConfig(CONFIG_ALLOW_TWO_SIDE_INTERACTION_GUILD) && _player->GetTeamId() != sCharacterCache->GetCharacterTeamByGuid(arenaTeam->GetCaptain())) if (!sWorld->getBoolConfig(CONFIG_ALLOW_TWO_SIDE_INTERACTION_ARENA) && _player->GetTeamId() != sCharacterCache->GetCharacterTeamByGuid(arenaTeam->GetCaptain()))
{ {
SendArenaTeamCommandResult(ERR_ARENA_TEAM_CREATE_S, "", "", ERR_ARENA_TEAM_NOT_ALLIED); SendArenaTeamCommandResult(ERR_ARENA_TEAM_CREATE_S, "", "", ERR_ARENA_TEAM_NOT_ALLIED);
return; return;
+18 -13
View File
@@ -414,18 +414,14 @@ void WorldSession::HandlePetitionSignOpcode(WorldPacket& recvData)
if (!signatures) if (!signatures)
return; return;
// not let enemies sign guild charter
if (!sWorld->getBoolConfig(CONFIG_ALLOW_TWO_SIDE_INTERACTION_GUILD) && GetPlayer()->GetTeamId() != sCharacterCache->GetCharacterTeamByGuid(petition->ownerGuid))
{
if (type != GUILD_CHARTER_TYPE) if (type != GUILD_CHARTER_TYPE)
{
if (!sWorld->getBoolConfig(CONFIG_ALLOW_TWO_SIDE_INTERACTION_ARENA) && GetPlayer()->GetTeamId() != sCharacterCache->GetCharacterTeamByGuid(petition->ownerGuid))
{
SendArenaTeamCommandResult(ERR_ARENA_TEAM_INVITE_SS, "", "", ERR_ARENA_TEAM_NOT_ALLIED); SendArenaTeamCommandResult(ERR_ARENA_TEAM_INVITE_SS, "", "", ERR_ARENA_TEAM_NOT_ALLIED);
else
Guild::SendCommandResult(this, GUILD_COMMAND_CREATE, ERR_GUILD_NOT_ALLIED);
return; return;
} }
if (type != GUILD_CHARTER_TYPE)
{
if (_player->GetLevel() < sWorld->getIntConfig(CONFIG_MAX_PLAYER_LEVEL)) if (_player->GetLevel() < sWorld->getIntConfig(CONFIG_MAX_PLAYER_LEVEL))
{ {
SendArenaTeamCommandResult(ERR_ARENA_TEAM_CREATE_S, "", _player->GetName().c_str(), ERR_ARENA_TEAM_TARGET_TOO_LOW_S); SendArenaTeamCommandResult(ERR_ARENA_TEAM_CREATE_S, "", _player->GetName().c_str(), ERR_ARENA_TEAM_TARGET_TOO_LOW_S);
@@ -450,6 +446,12 @@ void WorldSession::HandlePetitionSignOpcode(WorldPacket& recvData)
} }
else else
{ {
if (!sWorld->getBoolConfig(CONFIG_ALLOW_TWO_SIDE_INTERACTION_GUILD) && GetPlayer()->GetTeamId() != sCharacterCache->GetCharacterTeamByGuid(petition->ownerGuid))
{
Guild::SendCommandResult(this, GUILD_COMMAND_CREATE, ERR_GUILD_NOT_ALLIED);
return;
}
if (_player->GetGuildId()) if (_player->GetGuildId())
{ {
Guild::SendCommandResult(this, GUILD_COMMAND_INVITE, ERR_ALREADY_IN_GUILD_S, _player->GetName()); Guild::SendCommandResult(this, GUILD_COMMAND_INVITE, ERR_ALREADY_IN_GUILD_S, _player->GetName());
@@ -567,17 +569,14 @@ void WorldSession::HandleOfferPetitionOpcode(WorldPacket& recvData)
if (!petition) if (!petition)
return; return;
if (GetPlayer()->GetTeamId() != player->GetTeamId())
{
if (petition->petitionType != GUILD_CHARTER_TYPE) if (petition->petitionType != GUILD_CHARTER_TYPE)
{
if (GetPlayer()->GetTeamId() != player->GetTeamId() && !sWorld->getBoolConfig(CONFIG_ALLOW_TWO_SIDE_INTERACTION_ARENA))
{
SendArenaTeamCommandResult(ERR_ARENA_TEAM_INVITE_SS, "", "", ERR_ARENA_TEAM_NOT_ALLIED); SendArenaTeamCommandResult(ERR_ARENA_TEAM_INVITE_SS, "", "", ERR_ARENA_TEAM_NOT_ALLIED);
else
Guild::SendCommandResult(this, GUILD_COMMAND_CREATE, ERR_GUILD_NOT_ALLIED);
return; return;
} }
if (petition->petitionType != GUILD_CHARTER_TYPE)
{
if (player->GetLevel() < sWorld->getIntConfig(CONFIG_MAX_PLAYER_LEVEL)) if (player->GetLevel() < sWorld->getIntConfig(CONFIG_MAX_PLAYER_LEVEL))
{ {
// player is too low level to join an arena team // player is too low level to join an arena team
@@ -604,6 +603,12 @@ void WorldSession::HandleOfferPetitionOpcode(WorldPacket& recvData)
} }
else else
{ {
if (GetPlayer()->GetTeamId() != player->GetTeamId() && !sWorld->getBoolConfig(CONFIG_ALLOW_TWO_SIDE_INTERACTION_GUILD))
{
Guild::SendCommandResult(this, GUILD_COMMAND_CREATE, ERR_GUILD_NOT_ALLIED);
return;
}
if (player->GetGuildId()) if (player->GetGuildId())
{ {
Guild::SendCommandResult(this, GUILD_COMMAND_INVITE, ERR_ALREADY_IN_GUILD_S, _player->GetName()); Guild::SendCommandResult(this, GUILD_COMMAND_INVITE, ERR_ALREADY_IN_GUILD_S, _player->GetName());
+1
View File
@@ -79,6 +79,7 @@ enum WorldBoolConfigs
CONFIG_ALLOW_TWO_SIDE_INTERACTION_CHANNEL, CONFIG_ALLOW_TWO_SIDE_INTERACTION_CHANNEL,
CONFIG_ALLOW_TWO_SIDE_INTERACTION_GROUP, CONFIG_ALLOW_TWO_SIDE_INTERACTION_GROUP,
CONFIG_ALLOW_TWO_SIDE_INTERACTION_GUILD, CONFIG_ALLOW_TWO_SIDE_INTERACTION_GUILD,
CONFIG_ALLOW_TWO_SIDE_INTERACTION_ARENA,
CONFIG_ALLOW_TWO_SIDE_INTERACTION_AUCTION, CONFIG_ALLOW_TWO_SIDE_INTERACTION_AUCTION,
CONFIG_ALLOW_TWO_SIDE_INTERACTION_MAIL, CONFIG_ALLOW_TWO_SIDE_INTERACTION_MAIL,
CONFIG_ALLOW_TWO_SIDE_WHO_LIST, CONFIG_ALLOW_TWO_SIDE_WHO_LIST,
+1
View File
@@ -731,6 +731,7 @@ void World::LoadConfigSettings(bool reload)
_bool_configs[CONFIG_ALLOW_TWO_SIDE_INTERACTION_CHANNEL] = sConfigMgr->GetOption<bool>("AllowTwoSide.Interaction.Channel", false); _bool_configs[CONFIG_ALLOW_TWO_SIDE_INTERACTION_CHANNEL] = sConfigMgr->GetOption<bool>("AllowTwoSide.Interaction.Channel", false);
_bool_configs[CONFIG_ALLOW_TWO_SIDE_INTERACTION_GROUP] = sConfigMgr->GetOption<bool>("AllowTwoSide.Interaction.Group", false); _bool_configs[CONFIG_ALLOW_TWO_SIDE_INTERACTION_GROUP] = sConfigMgr->GetOption<bool>("AllowTwoSide.Interaction.Group", false);
_bool_configs[CONFIG_ALLOW_TWO_SIDE_INTERACTION_GUILD] = sConfigMgr->GetOption<bool>("AllowTwoSide.Interaction.Guild", false); _bool_configs[CONFIG_ALLOW_TWO_SIDE_INTERACTION_GUILD] = sConfigMgr->GetOption<bool>("AllowTwoSide.Interaction.Guild", false);
_bool_configs[CONFIG_ALLOW_TWO_SIDE_INTERACTION_ARENA] = sConfigMgr->GetOption<bool>("AllowTwoSide.Interaction.Arena", false);
_bool_configs[CONFIG_ALLOW_TWO_SIDE_INTERACTION_AUCTION] = sConfigMgr->GetOption<bool>("AllowTwoSide.Interaction.Auction", false); _bool_configs[CONFIG_ALLOW_TWO_SIDE_INTERACTION_AUCTION] = sConfigMgr->GetOption<bool>("AllowTwoSide.Interaction.Auction", false);
_bool_configs[CONFIG_ALLOW_TWO_SIDE_INTERACTION_MAIL] = sConfigMgr->GetOption<bool>("AllowTwoSide.Interaction.Mail", false); _bool_configs[CONFIG_ALLOW_TWO_SIDE_INTERACTION_MAIL] = sConfigMgr->GetOption<bool>("AllowTwoSide.Interaction.Mail", false);
_bool_configs[CONFIG_ALLOW_TWO_SIDE_WHO_LIST] = sConfigMgr->GetOption<bool>("AllowTwoSide.WhoList", false); _bool_configs[CONFIG_ALLOW_TWO_SIDE_WHO_LIST] = sConfigMgr->GetOption<bool>("AllowTwoSide.WhoList", false);
@@ -121,8 +121,6 @@ struct boss_moroes : public BossAI
context.Repeat(5s); context.Repeat(5s);
}).Schedule(1min, 2min, GROUP_PRECOMBAT_TALK, [this](TaskContext context) }).Schedule(1min, 2min, GROUP_PRECOMBAT_TALK, [this](TaskContext context)
{ {
//this was not scheduled in the previous commit
//does this have to be removed?
Talk(SAY_OUT_OF_COMBAT); Talk(SAY_OUT_OF_COMBAT);
context.Repeat(1min, 2min); context.Repeat(1min, 2min);
}); });
@@ -15,13 +15,6 @@
* with this program. If not, see <http://www.gnu.org/licenses/>. * with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
/* ScriptData
SDName: Boss_Netherspite
SD%Complete: 90
SDComment: Not sure about timing and portals placing
SDCategory: Karazhan
EndScriptData */
#include "Player.h" #include "Player.h"
#include "ScriptMgr.h" #include "ScriptMgr.h"
#include "ScriptedCreature.h" #include "ScriptedCreature.h"
@@ -29,11 +22,14 @@ EndScriptData */
#include "SpellAuraEffects.h" #include "SpellAuraEffects.h"
#include "SpellScript.h" #include "SpellScript.h"
enum Netherspite enum Emotes
{ {
EMOTE_PHASE_PORTAL = 0, EMOTE_PHASE_PORTAL = 0,
EMOTE_PHASE_BANISH = 1, EMOTE_PHASE_BANISH = 1
};
enum Spells
{
SPELL_NETHERBURN_AURA = 30522, SPELL_NETHERBURN_AURA = 30522,
SPELL_VOIDZONE = 37063, SPELL_VOIDZONE = 37063,
SPELL_NETHER_INFUSION = 38688, SPELL_NETHER_INFUSION = 38688,
@@ -41,7 +37,20 @@ enum Netherspite
SPELL_BANISH_VISUAL = 39833, SPELL_BANISH_VISUAL = 39833,
SPELL_BANISH_ROOT = 42716, SPELL_BANISH_ROOT = 42716,
SPELL_EMPOWERMENT = 38549, SPELL_EMPOWERMENT = 38549,
SPELL_NETHERSPITE_ROAR = 38684, SPELL_NETHERSPITE_ROAR = 38684
};
enum Portals
{
RED_PORTAL = 0, // Perseverence
GREEN_PORTAL = 1, // Serenity
BLUE_PORTAL = 2 // Dominance
};
enum Groups
{
PORTAL_PHASE = 0,
VANISH_PHASE = 1
}; };
const float PortalCoord[3][3] = const float PortalCoord[3][3] =
@@ -51,13 +60,6 @@ const float PortalCoord[3][3] =
{-11094.493164f, -1591.969238f, 279.949188f} // Back side {-11094.493164f, -1591.969238f, 279.949188f} // Back side
}; };
enum Netherspite_Portal
{
RED_PORTAL = 0, // Perseverence
GREEN_PORTAL = 1, // Serenity
BLUE_PORTAL = 2 // Dominance
};
const uint32 PortalID[3] = {17369, 17367, 17368}; const uint32 PortalID[3] = {17369, 17367, 17368};
const uint32 PortalVisual[3] = {30487, 30490, 30491}; const uint32 PortalVisual[3] = {30487, 30490, 30491};
const uint32 PortalBeam[3] = {30465, 30464, 30463}; const uint32 PortalBeam[3] = {30465, 30464, 30463};
@@ -65,36 +67,9 @@ const uint32 PlayerBuff[3] = {30421, 30422, 30423};
const uint32 NetherBuff[3] = {30466, 30467, 30468}; const uint32 NetherBuff[3] = {30466, 30467, 30468};
const uint32 PlayerDebuff[3] = {38637, 38638, 38639}; const uint32 PlayerDebuff[3] = {38637, 38638, 38639};
class boss_netherspite : public CreatureScript struct boss_netherspite : public BossAI
{ {
public: boss_netherspite(Creature* creature) : BossAI(creature, DATA_NETHERSPITE) {}
boss_netherspite() : CreatureScript("boss_netherspite") { }
CreatureAI* GetAI(Creature* creature) const override
{
return GetKarazhanAI<boss_netherspiteAI>(creature);
}
struct boss_netherspiteAI : public ScriptedAI
{
boss_netherspiteAI(Creature* creature) : ScriptedAI(creature)
{
instance = creature->GetInstanceScript();
}
InstanceScript* instance;
bool PortalPhase;
bool Berserk;
uint32 PhaseTimer; // timer for phase switching
uint32 VoidZoneTimer;
uint32 NetherInfusionTimer; // berserking timer
uint32 NetherbreathTimer;
uint32 EmpowermentTimer;
uint32 PortalTimer; // timer for beam checking
ObjectGuid PortalGUID[3]; // guid's of portals
ObjectGuid BeamerGUID[3]; // guid's of auxiliary beaming portals
ObjectGuid BeamTarget[3]; // guid's of portals' current targets
bool IsBetween(WorldObject* u1, WorldObject* target, WorldObject* u2) // the in-line checker bool IsBetween(WorldObject* u1, WorldObject* target, WorldObject* u2) // the in-line checker
{ {
@@ -123,11 +98,8 @@ public:
void Reset() override void Reset() override
{ {
Berserk = false; BossAI::Reset();
NetherInfusionTimer = 540000; berserk = false;
VoidZoneTimer = 15000;
NetherbreathTimer = 3000;
HandleDoors(true); HandleDoors(true);
DestroyPortals(); DestroyPortals();
} }
@@ -139,23 +111,28 @@ public:
pos[RED_PORTAL] = ((r % 2) ? (r > 1 ? 2 : 1) : 0); pos[RED_PORTAL] = ((r % 2) ? (r > 1 ? 2 : 1) : 0);
pos[GREEN_PORTAL] = ((r % 2) ? 0 : (r > 1 ? 2 : 1)); pos[GREEN_PORTAL] = ((r % 2) ? 0 : (r > 1 ? 2 : 1));
pos[BLUE_PORTAL] = (r > 1 ? 1 : 2); // Blue Portal not on the left side (0) pos[BLUE_PORTAL] = (r > 1 ? 1 : 2); // Blue Portal not on the left side (0)
for (int i = 0; i < 3; ++i) for (int i = 0; i < 3; ++i)
{
if (Creature* portal = me->SummonCreature(PortalID[i], PortalCoord[pos[i]][0], PortalCoord[pos[i]][1], PortalCoord[pos[i]][2], 0, TEMPSUMMON_TIMED_DESPAWN, 60000)) if (Creature* portal = me->SummonCreature(PortalID[i], PortalCoord[pos[i]][0], PortalCoord[pos[i]][1], PortalCoord[pos[i]][2], 0, TEMPSUMMON_TIMED_DESPAWN, 60000))
{ {
PortalGUID[i] = portal->GetGUID(); PortalGUID[i] = portal->GetGUID();
portal->AddAura(PortalVisual[i], portal); portal->AddAura(PortalVisual[i], portal);
} }
} }
}
void DestroyPortals() void DestroyPortals()
{ {
for (int i = 0; i < 3; ++i) for (int i = 0; i < 3; ++i)
{ {
if (Creature* portal = ObjectAccessor::GetCreature(*me, PortalGUID[i])) if (Creature* portal = ObjectAccessor::GetCreature(*me, PortalGUID[i]))
{
portal->DisappearAndDie(); portal->DisappearAndDie();
}
if (Creature* portal = ObjectAccessor::GetCreature(*me, BeamerGUID[i])) if (Creature* portal = ObjectAccessor::GetCreature(*me, BeamerGUID[i]))
{
portal->DisappearAndDie(); portal->DisappearAndDie();
}
PortalGUID[i].Clear(); PortalGUID[i].Clear();
BeamTarget[i].Clear(); BeamTarget[i].Clear();
} }
@@ -164,6 +141,7 @@ public:
void UpdatePortals() // Here we handle the beams' behavior void UpdatePortals() // Here we handle the beams' behavior
{ {
for (int j = 0; j < 3; ++j) // j = color for (int j = 0; j < 3; ++j) // j = color
{
if (Creature* portal = ObjectAccessor::GetCreature(*me, PortalGUID[j])) if (Creature* portal = ObjectAccessor::GetCreature(*me, PortalGUID[j]))
{ {
// the one who's been cast upon before // the one who's been cast upon before
@@ -190,9 +168,13 @@ public:
} }
// buff the target // buff the target
if (target->GetTypeId() == TYPEID_PLAYER) if (target->GetTypeId() == TYPEID_PLAYER)
{
target->AddAura(PlayerBuff[j], target); target->AddAura(PlayerBuff[j], target);
}
else else
{
target->AddAura(NetherBuff[j], target); target->AddAura(NetherBuff[j], target);
}
// cast visual beam on the chosen target if switched // cast visual beam on the chosen target if switched
// simple target switching isn't working -> using BeamerGUID to cast (workaround) // simple target switching isn't working -> using BeamerGUID to cast (workaround)
if (!current || target != current) if (!current || target != current)
@@ -214,52 +196,98 @@ public:
} }
// aggro target if Red Beam // aggro target if Red Beam
if (j == RED_PORTAL && me->GetVictim() != target && target->GetTypeId() == TYPEID_PLAYER) if (j == RED_PORTAL && me->GetVictim() != target && target->GetTypeId() == TYPEID_PLAYER)
{
me->GetThreatMgr().AddThreat(target, 100000.0f + DoGetThreat(me->GetVictim())); me->GetThreatMgr().AddThreat(target, 100000.0f + DoGetThreat(me->GetVictim()));
} }
} }
}
}
void SwitchToPortalPhase() void SwitchToPortalPhase()
{ {
scheduler.CancelGroup(VANISH_PHASE);
me->RemoveAurasDueToSpell(SPELL_BANISH_ROOT); me->RemoveAurasDueToSpell(SPELL_BANISH_ROOT);
me->RemoveAurasDueToSpell(SPELL_BANISH_VISUAL); me->RemoveAurasDueToSpell(SPELL_BANISH_VISUAL);
SummonPortals(); SummonPortals();
PhaseTimer = 60000; scheduler.Schedule(60s, [this](TaskContext /*context*/)
PortalPhase = true; {
PortalTimer = 10000; if (!me->IsNonMeleeSpellCast(false))
EmpowermentTimer = 10000; {
SwitchToBanishPhase();
return;
}
}).Schedule(10s, PORTAL_PHASE, [this](TaskContext context)
{
UpdatePortals();
context.Repeat(1s);
}).Schedule(10s, PORTAL_PHASE, [this](TaskContext context)
{
DoCastSelf(SPELL_EMPOWERMENT);
me->AddAura(SPELL_NETHERBURN_AURA, me);
context.Repeat(90s);
});
Talk(EMOTE_PHASE_PORTAL); Talk(EMOTE_PHASE_PORTAL);
} }
void SwitchToBanishPhase() void SwitchToBanishPhase()
{ {
scheduler.CancelGroup(PORTAL_PHASE);
me->RemoveAurasDueToSpell(SPELL_EMPOWERMENT); me->RemoveAurasDueToSpell(SPELL_EMPOWERMENT);
me->RemoveAurasDueToSpell(SPELL_NETHERBURN_AURA); me->RemoveAurasDueToSpell(SPELL_NETHERBURN_AURA);
DoCast(me, SPELL_BANISH_VISUAL, true); DoCastSelf(SPELL_BANISH_VISUAL, true);
DoCast(me, SPELL_BANISH_ROOT, true); DoCastSelf(SPELL_BANISH_ROOT, true);
DestroyPortals(); DestroyPortals();
PhaseTimer = 30000; scheduler.Schedule(30s, [this](TaskContext /*context*/)
PortalPhase = false; {
if (!me->IsNonMeleeSpellCast(false))
{
SwitchToPortalPhase();
return;
}
}).Schedule(10s, VANISH_PHASE, [this](TaskContext context)
{
DoCastRandomTarget(SPELL_NETHERBREATH, 0, 40.0f, true);
context.Repeat(5s, 7s);
});
Talk(EMOTE_PHASE_BANISH); Talk(EMOTE_PHASE_BANISH);
for (uint8 i = 0; i < 3; ++i) for (uint8 i = 0; i < 3; ++i)
{
me->RemoveAurasDueToSpell(NetherBuff[i]); me->RemoveAurasDueToSpell(NetherBuff[i]);
} }
void HandleDoors(bool open) // Massive Door switcher
{
if (GameObject* Door = ObjectAccessor::GetGameObject(*me, instance->GetGuidData(DATA_GO_MASSIVE_DOOR) ))
Door->SetGoState(open ? GO_STATE_ACTIVE : GO_STATE_READY);
} }
void JustEngagedWith(Unit* /*who*/) override void HandleDoors(bool open)
{ {
if (GameObject* door = ObjectAccessor::GetGameObject(*me, instance->GetGuidData(DATA_GO_MASSIVE_DOOR)))
{
door->SetGoState(open ? GO_STATE_ACTIVE : GO_STATE_READY);
}
}
void JustEngagedWith(Unit* who) override
{
BossAI::JustEngagedWith(who);
HandleDoors(false); HandleDoors(false);
SwitchToPortalPhase(); SwitchToPortalPhase();
DoZoneInCombat(); DoZoneInCombat();
scheduler.Schedule(15s, [this](TaskContext context)
{
DoCastRandomTarget(SPELL_VOIDZONE, 1, 45.0f, true, true);
context.Repeat(15s);
}).Schedule(9min, [this](TaskContext /*context*/)
{
if (!berserk)
{
DoCastSelf(SPELL_NETHER_INFUSION);
DoCastAOE(SPELL_NETHERSPITE_ROAR);
berserk = true;
}
});
} }
void JustDied(Unit* /*killer*/) override void JustDied(Unit* killer) override
{ {
BossAI::JustDied(killer);
HandleDoors(true); HandleDoors(true);
DestroyPortals(); DestroyPortals();
} }
@@ -269,84 +297,18 @@ public:
if (!UpdateVictim()) if (!UpdateVictim())
return; return;
// Void Zone scheduler.Update(diff);
if (VoidZoneTimer <= diff) if (me->HasUnitState(UNIT_STATE_CASTING))
{
DoCast(SelectTarget(SelectTargetMethod::Random, 1, 45, true), SPELL_VOIDZONE, true);
VoidZoneTimer = 15000;
}
else
VoidZoneTimer -= diff;
// NetherInfusion Berserk
if (!Berserk && NetherInfusionTimer <= diff)
{
me->AddAura(SPELL_NETHER_INFUSION, me);
DoCast(me, SPELL_NETHERSPITE_ROAR);
Berserk = true;
}
else
NetherInfusionTimer -= diff;
if (PortalPhase) // PORTAL PHASE
{
// Distribute beams and buffs
if (PortalTimer <= diff)
{
UpdatePortals();
PortalTimer = 1000;
}
else
PortalTimer -= diff;
// Empowerment & Nether Burn
if (EmpowermentTimer <= diff)
{
DoCast(me, SPELL_EMPOWERMENT);
me->AddAura(SPELL_NETHERBURN_AURA, me);
EmpowermentTimer = 90000;
}
else
EmpowermentTimer -= diff;
if (PhaseTimer <= diff)
{
if (!me->IsNonMeleeSpellCast(false))
{
SwitchToBanishPhase();
return; return;
}
}
else
PhaseTimer -= diff;
}
else // BANISH PHASE
{
// Netherbreath
if (NetherbreathTimer <= diff)
{
if (Unit* target = SelectTarget(SelectTargetMethod::Random, 0, 40, true))
DoCast(target, SPELL_NETHERBREATH);
NetherbreathTimer = urand(5000, 7000);
}
else
NetherbreathTimer -= diff;
if (PhaseTimer <= diff)
{
if (!me->IsNonMeleeSpellCast(false))
{
SwitchToPortalPhase();
return;
}
}
else
PhaseTimer -= diff;
}
DoMeleeAttackIfReady(); DoMeleeAttackIfReady();
} }
};
private:
bool berserk;
ObjectGuid PortalGUID[3]; // guid's of portals
ObjectGuid BeamerGUID[3]; // guid's of auxiliary beaming portals
ObjectGuid BeamTarget[3]; // guid's of portals' current targets
}; };
class spell_nether_portal_perseverence : public AuraScript class spell_nether_portal_perseverence : public AuraScript
@@ -366,6 +328,6 @@ class spell_nether_portal_perseverence : public AuraScript
void AddSC_boss_netherspite() void AddSC_boss_netherspite()
{ {
new boss_netherspite(); RegisterKarazhanCreatureAI(boss_netherspite);
RegisterSpellScript(spell_nether_portal_perseverence); RegisterSpellScript(spell_nether_portal_perseverence);
} }
@@ -25,6 +25,7 @@ EndScriptData */
#include "Player.h" #include "Player.h"
#include "ScriptMgr.h" #include "ScriptMgr.h"
#include "ScriptedCreature.h" #include "ScriptedCreature.h"
#include "TaskScheduler.h"
#include "karazhan.h" #include "karazhan.h"
enum Spells enum Spells
@@ -52,6 +53,12 @@ enum Says
EMOTE_BREATH = 4 EMOTE_BREATH = 4
}; };
enum Groups
{
GROUP_GROUND = 0,
GROUP_FLYING = 1
};
float IntroWay[8][3] = float IntroWay[8][3] =
{ {
{-11053.37f, -1794.48f, 149.00f}, {-11053.37f, -1794.48f, 149.00f},
@@ -62,70 +69,30 @@ float IntroWay[8][3] =
{-11128.73f, -1929.75f, 125.00f}, {-11128.73f, -1929.75f, 125.00f},
{-11140.00f, -1915.00f, 122.00f}, {-11140.00f, -1915.00f, 122.00f},
{-11163.00f, -1903.00f, 91.473f} {-11163.00f, -1903.00f, 91.473f}
}; }; //TODO: move to table
class boss_nightbane : public CreatureScript struct boss_nightbane : public BossAI
{ {
public: boss_nightbane(Creature* creature) : BossAI(creature, DATA_NIGHTBANE)
boss_nightbane() : CreatureScript("boss_nightbane") { }
CreatureAI* GetAI(Creature* creature) const override
{ {
return GetKarazhanAI<boss_nightbaneAI>(creature); _intro = true;
_skeletonCount = 5;
scheduler.SetValidator([this]
{
return !me->HasUnitState(UNIT_STATE_CASTING);
});
} }
struct boss_nightbaneAI : public ScriptedAI
{
boss_nightbaneAI(Creature* creature) : ScriptedAI(creature)
{
instance = creature->GetInstanceScript();
Intro = true;
}
InstanceScript* instance;
uint32 Phase;
bool RainBones;
bool Skeletons;
uint32 BellowingRoarTimer;
uint32 CharredEarthTimer;
uint32 DistractingAshTimer;
uint32 SmolderingBreathTimer;
uint32 TailSweepTimer;
uint32 RainofBonesTimer;
uint32 SmokingBlastTimer;
uint32 FireballBarrageTimer;
uint32 SearingCindersTimer;
uint32 FlyCount;
uint32 FlyTimer;
bool Intro;
bool Flying;
bool Movement;
uint32 MovePhase;
void Reset() override void Reset() override
{ {
BellowingRoarTimer = 30000; BossAI::Reset();
CharredEarthTimer = 15000; _skeletonscheduler.CancelAll();
DistractingAshTimer = 20000;
SmolderingBreathTimer = 10000;
TailSweepTimer = 12000;
RainofBonesTimer = 10000;
SmokingBlastTimer = 20000;
FireballBarrageTimer = 13000;
SearingCindersTimer = 14000;
Phase = 1; Phase = 1;
FlyCount = 0;
MovePhase = 0; MovePhase = 0;
me->SetUnitFlag(UNIT_FLAG_NOT_SELECTABLE);
me->SetSpeed(MOVE_RUN, 2.0f); me->SetSpeed(MOVE_RUN, 2.0f);
me->SetDisableGravity(Intro); me->SetDisableGravity(_intro);
me->SetWalk(false); me->SetWalk(false);
me->setActive(true); me->setActive(true);
@@ -139,14 +106,25 @@ public:
HandleTerraceDoors(true); HandleTerraceDoors(true);
Flying = false; _flying = false;
Movement = false; _movement = false;
if (!Intro) if (!_intro)
{ {
me->SetHomePosition(IntroWay[7][0], IntroWay[7][1], IntroWay[7][2], 0); //when boss is reset and we're past the intro
me->GetMotionMaster()->MoveTargetedHome(); //cannot despawn, but have to move to a location where he normally is
//me->SetHomePosition(IntroWay[7][0], IntroWay[7][1], IntroWay[7][2], 0);
Position preSpawnPosis = me->GetHomePosition();
me->NearTeleportTo(preSpawnPosis);
instance->SetData(DATA_NIGHTBANE, NOT_STARTED);
_intro = true;
Phase = 1;
MovePhase = 0;
} }
ScheduleHealthCheckEvent({25, 50, 70}, [&]{
TakeOff();
});
} }
void HandleTerraceDoors(bool open) void HandleTerraceDoors(bool open)
@@ -160,16 +138,82 @@ public:
void JustEngagedWith(Unit* /*who*/) override void JustEngagedWith(Unit* /*who*/) override
{ {
_JustEngagedWith();
if (instance) if (instance)
instance->SetData(DATA_NIGHTBANE, IN_PROGRESS); instance->SetData(DATA_NIGHTBANE, IN_PROGRESS);
HandleTerraceDoors(false); HandleTerraceDoors(false);
Talk(YELL_AGGRO); Talk(YELL_AGGRO);
ScheduleGround();
}
void ScheduleGround() {
scheduler.Schedule(30s, GROUP_GROUND, [this](TaskContext context)
{
DoCastAOE(SPELL_BELLOWING_ROAR);
context.Repeat(30s, 40s);
}).Schedule(15s, GROUP_GROUND, [this](TaskContext context)
{
DoCastRandomTarget(SPELL_CHARRED_EARTH, 0, 100.0f, true);
context.Repeat(20s);
}).Schedule(10s, GROUP_GROUND, [this](TaskContext context)
{
DoCastVictim(SPELL_SMOLDERING_BREATH);
context.Repeat(20s);
}).Schedule(12s, GROUP_GROUND, [this](TaskContext context)
{
if (Unit* target = SelectTarget(SelectTargetMethod::Random, 0, 100, true))
{
if (!me->HasInArc(M_PI, target))
{
DoCast(target, SPELL_TAIL_SWEEP);
}
}
context.Repeat(15s);
}).Schedule(14s, GROUP_GROUND, [this](TaskContext context)
{
DoCastRandomTarget(SPELL_SEARING_CINDERS);
context.Repeat(10s);
});
}
void ScheduleFly() {
_skeletonSpawnCounter = 0;
scheduler.Schedule(2s, GROUP_FLYING, [this](TaskContext)
{
DoCastVictim(SPELL_RAIN_OF_BONES);
_skeletonscheduler.Schedule(50ms, [this](TaskContext context)
{
//spawns skeletons every second until skeletonCount is reached
if(_skeletonSpawnCounter < _skeletonCount)
{
DoCastVictim(SPELL_SUMMON_SKELETON, true);
_skeletonSpawnCounter++;
context.Repeat(2s);
}
});
}).Schedule(20s, GROUP_FLYING, [this](TaskContext context)
{
DoCastRandomTarget(SPELL_DISTRACTING_ASH);
context.Repeat(2s); //timer wrong?
}).Schedule(25s, GROUP_FLYING, [this](TaskContext context)
{
//5 seconds added due to double trigger?
//trigger for timer in original + in rain of bones
//timers need some investigation
DoCastVictim(SPELL_SMOKING_BLAST);
context.Repeat(1500ms); //timer wrong?
}).Schedule(13s, GROUP_FLYING, [this](TaskContext context)
{
DoCastOnFarAwayPlayers(SPELL_FIREBALL_BARRAGE, false, 80.0f);
context.Repeat(20s);
});
} }
void AttackStart(Unit* who) override void AttackStart(Unit* who) override
{ {
if (!Intro && !Flying) if (!_intro && !_flying)
ScriptedAI::AttackStart(who); ScriptedAI::AttackStart(who);
} }
@@ -183,7 +227,7 @@ public:
void MoveInLineOfSight(Unit* who) override void MoveInLineOfSight(Unit* who) override
{ {
if (!Intro && !Flying) if (!_intro && !_flying)
ScriptedAI::MoveInLineOfSight(who); ScriptedAI::MoveInLineOfSight(who);
} }
@@ -192,12 +236,15 @@ public:
if (type != POINT_MOTION_TYPE) if (type != POINT_MOTION_TYPE)
return; return;
if (Intro) if (_intro)
{ {
if (id >= 8) if (id >= 8)
{ {
Intro = false; _intro = false;
me->SetHomePosition(IntroWay[7][0], IntroWay[7][1], IntroWay[7][2], 0); //me->SetHomePosition(IntroWay[7][0], IntroWay[7][1], IntroWay[7][2], 0);
//doesn't need home position because we have to "despawn" boss on reset
me->RemoveUnitFlag(UNIT_FLAG_NOT_SELECTABLE);
me->SetInCombatWithZone();
return; return;
} }
@@ -205,12 +252,12 @@ public:
return; return;
} }
if (Flying) if (_flying)
{ {
if (id == 0) if (id == 0)
{ {
Talk(EMOTE_BREATH); Talk(EMOTE_BREATH);
Flying = false; _flying = false;
Phase = 2; Phase = 2;
return; return;
} }
@@ -220,21 +267,39 @@ public:
else else
{ {
Phase = 1; Phase = 1;
Flying = false; _flying = false;
Movement = true; _movement = true;
return; return;
} }
} }
} }
void JustSummoned(Creature* summoned) override void JustSummoned(Creature* summon) override
{ {
summoned->AI()->AttackStart(me->GetVictim()); summon->AI()->AttackStart(me->GetVictim());
summons.Summon(summon);
}
void DoCastOnFarAwayPlayers(uint32 spellid, bool triggered, float tresholddistance)
{
//resembles DoCastToAllHostilePlayers a bit/lot
ThreatContainer::StorageType targets = me->GetThreatMgr().GetThreatList();
for (ThreatContainer::StorageType::const_iterator itr = targets.begin(); itr != targets.end(); ++itr)
{
if (Unit* unit = ObjectAccessor::GetUnit(*me, (*itr)->getUnitGuid()))
{
if (unit->IsPlayer() && !unit->IsWithinDist(me, tresholddistance, false))
{
me->CastSpell(unit, spellid, triggered);
}
}
}
} }
void TakeOff() void TakeOff()
{ {
Talk(YELL_FLY_PHASE); Talk(YELL_FLY_PHASE);
scheduler.CancelGroup(GROUP_GROUND);
me->InterruptSpell(CURRENT_GENERIC_SPELL); me->InterruptSpell(CURRENT_GENERIC_SPELL);
me->HandleEmoteCommand(EMOTE_ONESHOT_LIFTOFF); me->HandleEmoteCommand(EMOTE_ONESHOT_LIFTOFF);
@@ -242,19 +307,30 @@ public:
me->GetMotionMaster()->Clear(false); me->GetMotionMaster()->Clear(false);
me->GetMotionMaster()->MovePoint(0, IntroWay[2][0], IntroWay[2][1], IntroWay[2][2]); me->GetMotionMaster()->MovePoint(0, IntroWay[2][0], IntroWay[2][1], IntroWay[2][2]);
Flying = true; _flying = true;
FlyTimer = urand(45000, 60000); //timer wrong between 45 and 60 seconds ScheduleFly();
++FlyCount;
RainofBonesTimer = 5000; //timer wrong (maybe) //handle landing again
RainBones = false; scheduler.Schedule(45s, 60s, [this](TaskContext)
Skeletons = false; {
Talk(YELL_LAND_PHASE);
me->GetMotionMaster()->Clear(false);
me->GetMotionMaster()->MovePoint(3, IntroWay[3][0], IntroWay[3][1], IntroWay[3][2]);
_flying = true;
scheduler.CancelGroup(GROUP_FLYING);
scheduler.Schedule(2s, [this](TaskContext)
{
ScheduleGround();
});
});
} }
void UpdateAI(uint32 diff) override void UpdateAI(uint32 diff) override
{ {
if (Intro) if (_intro)
{ {
if (MovePhase) if (MovePhase)
{ {
@@ -273,7 +349,7 @@ public:
return; return;
} }
if (Flying && MovePhase) if (_flying && MovePhase)
{ {
if (MovePhase >= 7) if (MovePhase >= 7)
{ {
@@ -290,150 +366,45 @@ public:
if (!UpdateVictim()) if (!UpdateVictim())
return; return;
if (Flying) if (_flying)
return; return;
scheduler.Update(diff);
_skeletonscheduler.Update(diff);
// Phase 1 "GROUND FIGHT" // Phase 1 "GROUND FIGHT"
if (Phase == 1) if (Phase == 1)
{ {
if (Movement) if (_movement)
{ {
DoStartMovement(me->GetVictim()); DoStartMovement(me->GetVictim());
Movement = false; _movement = false;
} }
if (BellowingRoarTimer <= diff)
{
DoCastVictim(SPELL_BELLOWING_ROAR);
BellowingRoarTimer = urand(30000, 40000);
}
else
BellowingRoarTimer -= diff;
if (SmolderingBreathTimer <= diff)
{
DoCastVictim(SPELL_SMOLDERING_BREATH);
SmolderingBreathTimer = 20000;
}
else
SmolderingBreathTimer -= diff;
if (CharredEarthTimer <= diff)
{
if (Unit* target = SelectTarget(SelectTargetMethod::Random, 0, 100, true))
DoCast(target, SPELL_CHARRED_EARTH);
CharredEarthTimer = 20000;
}
else
CharredEarthTimer -= diff;
if (TailSweepTimer <= diff)
{
if (Unit* target = SelectTarget(SelectTargetMethod::Random, 0, 100, true))
if (!me->HasInArc(M_PI, target))
DoCast(target, SPELL_TAIL_SWEEP);
TailSweepTimer = 15000;
}
else
TailSweepTimer -= diff;
if (SearingCindersTimer <= diff)
{
if (Unit* target = SelectTarget(SelectTargetMethod::Random, 0, 100, true))
DoCast(target, SPELL_SEARING_CINDERS);
SearingCindersTimer = 10000;
}
else
SearingCindersTimer -= diff;
uint32 Prozent = uint32(me->GetHealthPct());
if (Prozent < 75 && FlyCount == 0) // first take off 75%
TakeOff();
if (Prozent < 50 && FlyCount == 1) // secound take off 50%
TakeOff();
if (Prozent < 25 && FlyCount == 2) // third take off 25%
TakeOff();
DoMeleeAttackIfReady(); DoMeleeAttackIfReady();
} }
//Phase 2 "FLYING FIGHT"
if (Phase == 2)
{
if (!RainBones)
{
if (!Skeletons)
{
for (uint8 i = 0; i <= 3; ++i)
{
DoCastVictim(SPELL_SUMMON_SKELETON);
Skeletons = true;
}
} }
if (RainofBonesTimer < diff && !RainBones) // only once at the beginning of phase 2 private:
{ uint32 Phase;
DoCastVictim(SPELL_RAIN_OF_BONES);
RainBones = true;
SmokingBlastTimer = 20000;
}
else
RainofBonesTimer -= diff;
if (DistractingAshTimer <= diff) TaskScheduler _skeletonscheduler;
{
if (Unit* target = SelectTarget(SelectTargetMethod::Random, 0, 100, true))
DoCast(target, SPELL_DISTRACTING_ASH);
DistractingAshTimer = 2000; //timer wrong
}
else
DistractingAshTimer -= diff;
}
if (RainBones) bool _intro;
{ bool _flying;
if (SmokingBlastTimer <= diff) bool _movement;
{
DoCastVictim(SPELL_SMOKING_BLAST);
SmokingBlastTimer = 1500; //timer wrong
}
else
SmokingBlastTimer -= diff;
}
if (FireballBarrageTimer <= diff) uint32 MovePhase;
{ uint8 _skeletonCount;
if (Unit* target = SelectTarget(SelectTargetMethod::MinDistance, 0)) uint8 _skeletonSpawnCounter;
DoCast(target, SPELL_FIREBALL_BARRAGE);
FireballBarrageTimer = 20000;
}
else
FireballBarrageTimer -= diff;
if (FlyTimer <= diff) //landing
{
Talk(YELL_LAND_PHASE);
me->GetMotionMaster()->Clear(false);
me->GetMotionMaster()->MovePoint(3, IntroWay[3][0], IntroWay[3][1], IntroWay[3][2]);
Flying = true;
}
else
FlyTimer -= diff;
}
}
}; };
};
class go_blackened_urn : public GameObjectScript class go_blackened_urn : public GameObjectScript
{ {
public: public:
go_blackened_urn() : GameObjectScript("go_blackened_urn") { } go_blackened_urn() : GameObjectScript("go_blackened_urn") { }
//if we summon an entity instead of using a sort of invisible entity, we could unsummon boss on reset
//right now that doesn't work because of how the urn works
bool OnGossipHello(Player* player, GameObject* go) override bool OnGossipHello(Player* player, GameObject* go) override
{ {
if (InstanceScript* pInstance = go->GetInstanceScript()) if (InstanceScript* pInstance = go->GetInstanceScript())
@@ -449,6 +420,6 @@ public:
void AddSC_boss_nightbane() void AddSC_boss_nightbane()
{ {
new boss_nightbane(); RegisterKarazhanCreatureAI(boss_nightbane);
new go_blackened_urn(); new go_blackened_urn();
} }
@@ -292,6 +292,7 @@ public:
{ {
instance->SetData(DATA_ARCHIMONDEEVENT, NOT_STARTED); instance->SetData(DATA_ARCHIMONDEEVENT, NOT_STARTED);
me->SetReactState(REACT_AGGRESSIVE);
DoomfireSpiritGUID.Clear(); DoomfireSpiritGUID.Clear();
WorldTreeGUID.Clear(); WorldTreeGUID.Clear();
WispCount = 0; WispCount = 0;
@@ -618,6 +619,7 @@ public:
break; break;
} }
case EVENT_BELOW_10_PERCENT_HP: case EVENT_BELOW_10_PERCENT_HP:
me->SetReactState(REACT_PASSIVE);
DoCastProtection(); // Protection of Elune against Finger and Hand of Death DoCastProtection(); // Protection of Elune against Finger and Hand of Death
BelowTenPercent = true; BelowTenPercent = true;
me->GetMotionMaster()->Clear(false); me->GetMotionMaster()->Clear(false);
@@ -218,9 +218,9 @@ struct boss_magtheridon : public BossAI
Talk(SAY_EMOTE_FREE); Talk(SAY_EMOTE_FREE);
Talk(SAY_FREE); Talk(SAY_FREE);
scheduler.CancelGroup(GROUP_EARLY_RELEASE_CHECK); //cancel regular countdown scheduler.CancelGroup(GROUP_EARLY_RELEASE_CHECK); //cancel regular countdown
_magReleased = true;
scheduler.Schedule(3s, [this](TaskContext) scheduler.Schedule(3s, [this](TaskContext)
{ {
_magReleased = true; //redundancy
ScheduleCombatEvents(); ScheduleCombatEvents();
}); });
} }
@@ -239,6 +239,7 @@ struct boss_magtheridon : public BossAI
{ {
Talk(SAY_EMOTE_FREE); Talk(SAY_EMOTE_FREE);
Talk(SAY_FREE); Talk(SAY_FREE);
_magReleased = true;
}).Schedule(123s, GROUP_EARLY_RELEASE_CHECK, [this](TaskContext /*context*/) }).Schedule(123s, GROUP_EARLY_RELEASE_CHECK, [this](TaskContext /*context*/)
{ {
ScheduleCombatEvents(); ScheduleCombatEvents();
@@ -4884,6 +4884,39 @@ class spell_gen_shriveling_gaze : public AuraScript
} }
}; };
// 38048 - Curse of Pain
enum CurseOfPain
{
SPELL_CURSE_OF_PAIN = 38048,
};
class spell_gen_curse_of_pain : public AuraScript
{
PrepareAuraScript(spell_gen_curse_of_pain);
bool Validate(SpellInfo const* /*spellInfo*/) override
{
return ValidateSpellInfo({ SPELL_CURSE_OF_PAIN });
}
void OnPeriodic(AuraEffect const* /*aurEff*/)
{
Unit* target = GetTarget();
if (target && target->ToPlayer())
{
if (target->GetHealthPct() < 50.f)
{
target->RemoveAurasDueToSpell(SPELL_CURSE_OF_PAIN);
}
}
}
void Register() override
{
OnEffectPeriodic += AuraEffectPeriodicFn(spell_gen_curse_of_pain::OnPeriodic, EFFECT_0, SPELL_AURA_PERIODIC_DAMAGE);
}
};
void AddSC_generic_spell_scripts() void AddSC_generic_spell_scripts()
{ {
RegisterSpellScript(spell_silithyst); RegisterSpellScript(spell_silithyst);
@@ -5029,4 +5062,5 @@ void AddSC_generic_spell_scripts()
RegisterSpellScript(spell_freezing_circle); RegisterSpellScript(spell_freezing_circle);
RegisterSpellScript(spell_gen_threshalisk_charge); RegisterSpellScript(spell_gen_threshalisk_charge);
RegisterSpellScript(spell_gen_shriveling_gaze); RegisterSpellScript(spell_gen_shriveling_gaze);
RegisterSpellScript(spell_gen_curse_of_pain);
} }
@@ -974,6 +974,9 @@ class spell_q6124_6129_apply_salve : public SpellScript
if (newEntry) if (newEntry)
{ {
creatureTarget->UpdateEntry(newEntry); creatureTarget->UpdateEntry(newEntry);
creatureTarget->GetMotionMaster()->Clear();
creatureTarget->GetMotionMaster()->MoveFleeing(caster);
creatureTarget->SetUnitFlag(UNIT_FLAG_NOT_ATTACKABLE_1);
creatureTarget->DespawnOrUnsummon(DESPAWN_TIME); creatureTarget->DespawnOrUnsummon(DESPAWN_TIME);
caster->KilledMonsterCredit(newEntry); caster->KilledMonsterCredit(newEntry);
} }
+1 -1
View File
@@ -486,7 +486,7 @@ public:
if (creature->IsTrainer()) if (creature->IsTrainer())
AddGossipItemFor(player, GOSSIP_ICON_TRAINER, GOSSIP_TEXT_TRAIN, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_TRAIN); AddGossipItemFor(player, GOSSIP_ICON_TRAINER, GOSSIP_TEXT_TRAIN, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_TRAIN);
if (player->HasSkill(SKILL_ALCHEMY) && player->GetBaseSkillValue(SKILL_ALCHEMY) >= 350 && player->GetLevel() > 67) if (player->HasSkill(SKILL_ALCHEMY) && player->GetBaseSkillValue(SKILL_ALCHEMY) >= 325 && player->GetLevel() > 67)
{ {
if (player->GetQuestRewardStatus(Q_MASTER_TRANSMUTE) || player->GetQuestRewardStatus(Q_MASTER_ELIXIR) || player->GetQuestRewardStatus(Q_MASTER_POTION)) if (player->GetQuestRewardStatus(Q_MASTER_TRANSMUTE) || player->GetQuestRewardStatus(Q_MASTER_ELIXIR) || player->GetQuestRewardStatus(Q_MASTER_POTION))
{ {
+28
View File
@@ -32,6 +32,7 @@
#include "ScriptedGossip.h" #include "ScriptedGossip.h"
#include "SmartAI.h" #include "SmartAI.h"
#include "SpellAuras.h" #include "SpellAuras.h"
#include "TaskScheduler.h"
#include "WaypointMgr.h" #include "WaypointMgr.h"
#include "World.h" #include "World.h"
@@ -2653,6 +2654,32 @@ public:
} }
}; };
struct npc_crashin_thrashin_robot : public ScriptedAI
{
public:
npc_crashin_thrashin_robot(Creature* creature) : ScriptedAI(creature)
{
}
void IsSummonedBy(WorldObject* /*summoner*/) override
{
_scheduler.Schedule(180s, [this](TaskContext /*context*/)
{
me->KillSelf();
});
}
void UpdateAI(uint32 diff) override
{
_scheduler.Update(diff);
ScriptedAI::UpdateAI(diff);
}
private:
TaskScheduler _scheduler;
};
void AddSC_npcs_special() void AddSC_npcs_special()
{ {
// Ours // Ours
@@ -2680,4 +2707,5 @@ void AddSC_npcs_special()
new npc_spring_rabbit(); new npc_spring_rabbit();
new npc_stable_master(); new npc_stable_master();
RegisterCreatureAI(npc_arcanite_dragonling); RegisterCreatureAI(npc_arcanite_dragonling);
RegisterCreatureAI(npc_crashin_thrashin_robot);
} }