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;
+24 -19
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)
SendArenaTeamCommandResult(ERR_ARENA_TEAM_INVITE_SS, "", "", ERR_ARENA_TEAM_NOT_ALLIED);
else
Guild::SendCommandResult(this, GUILD_COMMAND_CREATE, ERR_GUILD_NOT_ALLIED);
return;
}
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);
return;
}
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)
SendArenaTeamCommandResult(ERR_ARENA_TEAM_INVITE_SS, "", "", ERR_ARENA_TEAM_NOT_ALLIED);
else
Guild::SendCommandResult(this, GUILD_COMMAND_CREATE, ERR_GUILD_NOT_ALLIED);
return;
}
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);
return;
}
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,19 +22,35 @@ 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
};
SPELL_NETHERBURN_AURA = 30522, enum Spells
SPELL_VOIDZONE = 37063, {
SPELL_NETHER_INFUSION = 38688, SPELL_NETHERBURN_AURA = 30522,
SPELL_NETHERBREATH = 38523, SPELL_VOIDZONE = 37063,
SPELL_BANISH_VISUAL = 39833, SPELL_NETHER_INFUSION = 38688,
SPELL_BANISH_ROOT = 42716, SPELL_NETHERBREATH = 38523,
SPELL_EMPOWERMENT = 38549, SPELL_BANISH_VISUAL = 39833,
SPELL_NETHERSPITE_ROAR = 38684, SPELL_BANISH_ROOT = 42716,
SPELL_EMPOWERMENT = 38549,
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,302 +60,255 @@ const float PortalCoord[3][3] =
{-11094.493164f, -1591.969238f, 279.949188f} // Back side {-11094.493164f, -1591.969238f, 279.949188f} // Back side
}; };
enum Netherspite_Portal const uint32 PortalID[3] = {17369, 17367, 17368};
{
RED_PORTAL = 0, // Perseverence
GREEN_PORTAL = 1, // Serenity
BLUE_PORTAL = 2 // Dominance
};
const uint32 PortalID[3] = {17369, 17367, 17368};
const uint32 PortalVisual[3] = {30487, 30490, 30491}; const uint32 PortalVisual[3] = {30487, 30490, 30491};
const uint32 PortalBeam[3] = {30465, 30464, 30463}; const uint32 PortalBeam[3] = {30465, 30464, 30463};
const uint32 PlayerBuff[3] = {30421, 30422, 30423}; 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 bool IsBetween(WorldObject* u1, WorldObject* target, WorldObject* u2) // the in-line checker
{ {
return GetKarazhanAI<boss_netherspiteAI>(creature); if (!u1 || !u2 || !target)
return false;
float xn, yn, xp, yp, xh, yh;
xn = u1->GetPositionX();
yn = u1->GetPositionY();
xp = u2->GetPositionX();
yp = u2->GetPositionY();
xh = target->GetPositionX();
yh = target->GetPositionY();
// check if target is between (not checking distance from the beam yet)
if (dist(xn, yn, xh, yh) >= dist(xn, yn, xp, yp) || dist(xp, yp, xh, yh) >= dist(xn, yn, xp, yp))
return false;
// check distance from the beam
return (std::abs((xn - xp) * yh + (yp - yn) * xh - xn * yp + xp * yn) / dist(xn, yn, xp, yp) < 1.5f);
} }
struct boss_netherspiteAI : public ScriptedAI float dist(float xa, float ya, float xb, float yb) // auxiliary method for distance
{ {
boss_netherspiteAI(Creature* creature) : ScriptedAI(creature) return std::sqrt((xa - xb) * (xa - xb) + (ya - yb) * (ya - yb));
}
void Reset() override
{
BossAI::Reset();
berserk = false;
HandleDoors(true);
DestroyPortals();
}
void SummonPortals()
{
uint8 r = rand() % 4;
uint8 pos[3];
pos[RED_PORTAL] = ((r % 2) ? (r > 1 ? 2 : 1) : 0);
pos[GREEN_PORTAL] = ((r % 2) ? 0 : (r > 1 ? 2 : 1));
pos[BLUE_PORTAL] = (r > 1 ? 1 : 2); // Blue Portal not on the left side (0)
for (int i = 0; i < 3; ++i)
{ {
instance = creature->GetInstanceScript(); if (Creature* portal = me->SummonCreature(PortalID[i], PortalCoord[pos[i]][0], PortalCoord[pos[i]][1], PortalCoord[pos[i]][2], 0, TEMPSUMMON_TIMED_DESPAWN, 60000))
}
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
{
if (!u1 || !u2 || !target)
return false;
float xn, yn, xp, yp, xh, yh;
xn = u1->GetPositionX();
yn = u1->GetPositionY();
xp = u2->GetPositionX();
yp = u2->GetPositionY();
xh = target->GetPositionX();
yh = target->GetPositionY();
// check if target is between (not checking distance from the beam yet)
if (dist(xn, yn, xh, yh) >= dist(xn, yn, xp, yp) || dist(xp, yp, xh, yh) >= dist(xn, yn, xp, yp))
return false;
// check distance from the beam
return (std::abs((xn - xp) * yh + (yp - yn) * xh - xn * yp + xp * yn) / dist(xn, yn, xp, yp) < 1.5f);
}
float dist(float xa, float ya, float xb, float yb) // auxiliary method for distance
{
return std::sqrt((xa - xb) * (xa - xb) + (ya - yb) * (ya - yb));
}
void Reset() override
{
Berserk = false;
NetherInfusionTimer = 540000;
VoidZoneTimer = 15000;
NetherbreathTimer = 3000;
HandleDoors(true);
DestroyPortals();
}
void SummonPortals()
{
uint8 r = rand() % 4;
uint8 pos[3];
pos[RED_PORTAL] = ((r % 2) ? (r > 1 ? 2 : 1) : 0);
pos[GREEN_PORTAL] = ((r % 2) ? 0 : (r > 1 ? 2 : 1));
pos[BLUE_PORTAL] = (r > 1 ? 1 : 2); // Blue Portal not on the left side (0)
for (int i = 0; i < 3; ++i)
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();
portal->AddAura(PortalVisual[i], portal);
}
}
void DestroyPortals()
{
for (int i = 0; i < 3; ++i)
{ {
if (Creature* portal = ObjectAccessor::GetCreature(*me, PortalGUID[i])) PortalGUID[i] = portal->GetGUID();
portal->DisappearAndDie(); portal->AddAura(PortalVisual[i], portal);
if (Creature* portal = ObjectAccessor::GetCreature(*me, BeamerGUID[i]))
portal->DisappearAndDie();
PortalGUID[i].Clear();
BeamTarget[i].Clear();
} }
} }
}
void UpdatePortals() // Here we handle the beams' behavior void DestroyPortals()
{
for (int i = 0; i < 3; ++i)
{ {
for (int j = 0; j < 3; ++j) // j = color if (Creature* portal = ObjectAccessor::GetCreature(*me, PortalGUID[i]))
if (Creature* portal = ObjectAccessor::GetCreature(*me, PortalGUID[j])) {
portal->DisappearAndDie();
}
if (Creature* portal = ObjectAccessor::GetCreature(*me, BeamerGUID[i]))
{
portal->DisappearAndDie();
}
PortalGUID[i].Clear();
BeamTarget[i].Clear();
}
}
void UpdatePortals() // Here we handle the beams' behavior
{
for (int j = 0; j < 3; ++j) // j = color
{
if (Creature* portal = ObjectAccessor::GetCreature(*me, PortalGUID[j]))
{
// the one who's been cast upon before
Unit* current = ObjectAccessor::GetUnit(*portal, BeamTarget[j]);
// temporary store for the best suitable beam reciever
Unit* target = me;
if (Map* map = me->GetMap())
{ {
// the one who's been cast upon before Map::PlayerList const& players = map->GetPlayers();
Unit* current = ObjectAccessor::GetUnit(*portal, BeamTarget[j]);
// temporary store for the best suitable beam reciever
Unit* target = me;
if (Map* map = me->GetMap()) // get the best suitable target
for (Map::PlayerList::const_iterator i = players.begin(); i != players.end(); ++i)
{ {
Map::PlayerList const& players = map->GetPlayers(); Player* p = i->GetSource();
if (p && p->IsAlive() // alive
// get the best suitable target && (!target || target->GetDistance2d(portal) > p->GetDistance2d(portal)) // closer than current best
for (Map::PlayerList::const_iterator i = players.begin(); i != players.end(); ++i) && !p->HasAura(PlayerDebuff[j]) // not exhausted
{ && !p->HasAura(PlayerBuff[(j + 1) % 3]) // not on another beam
Player* p = i->GetSource(); && !p->HasAura(PlayerBuff[(j + 2) % 3])
if (p && p->IsAlive() // alive && IsBetween(me, p, portal)) // on the beam
&& (!target || target->GetDistance2d(portal) > p->GetDistance2d(portal)) // closer than current best target = p;
&& !p->HasAura(PlayerDebuff[j]) // not exhausted
&& !p->HasAura(PlayerBuff[(j + 1) % 3]) // not on another beam
&& !p->HasAura(PlayerBuff[(j + 2) % 3])
&& IsBetween(me, p, portal)) // on the beam
target = p;
}
} }
// buff the target
if (target->GetTypeId() == TYPEID_PLAYER)
target->AddAura(PlayerBuff[j], target);
else
target->AddAura(NetherBuff[j], target);
// cast visual beam on the chosen target if switched
// simple target switching isn't working -> using BeamerGUID to cast (workaround)
if (!current || target != current)
{
BeamTarget[j] = target->GetGUID();
// remove currently beaming portal
if (Creature* beamer = ObjectAccessor::GetCreature(*portal, BeamerGUID[j]))
{
beamer->CastSpell(target, PortalBeam[j], false);
beamer->DisappearAndDie();
BeamerGUID[j].Clear();
}
// create new one and start beaming on the target
if (Creature* beamer = portal->SummonCreature(PortalID[j], portal->GetPositionX(), portal->GetPositionY(), portal->GetPositionZ(), portal->GetOrientation(), TEMPSUMMON_TIMED_DESPAWN, 60000))
{
beamer->CastSpell(target, PortalBeam[j], false);
BeamerGUID[j] = beamer->GetGUID();
}
}
// aggro target if Red Beam
if (j == RED_PORTAL && me->GetVictim() != target && target->GetTypeId() == TYPEID_PLAYER)
me->GetThreatMgr().AddThreat(target, 100000.0f + DoGetThreat(me->GetVictim()));
} }
// buff the target
if (target->GetTypeId() == TYPEID_PLAYER)
{
target->AddAura(PlayerBuff[j], target);
}
else
{
target->AddAura(NetherBuff[j], target);
}
// cast visual beam on the chosen target if switched
// simple target switching isn't working -> using BeamerGUID to cast (workaround)
if (!current || target != current)
{
BeamTarget[j] = target->GetGUID();
// remove currently beaming portal
if (Creature* beamer = ObjectAccessor::GetCreature(*portal, BeamerGUID[j]))
{
beamer->CastSpell(target, PortalBeam[j], false);
beamer->DisappearAndDie();
BeamerGUID[j].Clear();
}
// create new one and start beaming on the target
if (Creature* beamer = portal->SummonCreature(PortalID[j], portal->GetPositionX(), portal->GetPositionY(), portal->GetPositionZ(), portal->GetOrientation(), TEMPSUMMON_TIMED_DESPAWN, 60000))
{
beamer->CastSpell(target, PortalBeam[j], false);
BeamerGUID[j] = beamer->GetGUID();
}
}
// aggro target if Red Beam
if (j == RED_PORTAL && me->GetVictim() != target && target->GetTypeId() == TYPEID_PLAYER)
{
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_VISUAL);
SummonPortals();
scheduler.Schedule(60s, [this](TaskContext /*context*/)
{ {
me->RemoveAurasDueToSpell(SPELL_BANISH_ROOT); if (!me->IsNonMeleeSpellCast(false))
me->RemoveAurasDueToSpell(SPELL_BANISH_VISUAL); {
SummonPortals(); SwitchToBanishPhase();
PhaseTimer = 60000;
PortalPhase = true;
PortalTimer = 10000;
EmpowermentTimer = 10000;
Talk(EMOTE_PHASE_PORTAL);
}
void SwitchToBanishPhase()
{
me->RemoveAurasDueToSpell(SPELL_EMPOWERMENT);
me->RemoveAurasDueToSpell(SPELL_NETHERBURN_AURA);
DoCast(me, SPELL_BANISH_VISUAL, true);
DoCast(me, SPELL_BANISH_ROOT, true);
DestroyPortals();
PhaseTimer = 30000;
PortalPhase = false;
Talk(EMOTE_PHASE_BANISH);
for (uint8 i = 0; i < 3; ++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
{
HandleDoors(false);
SwitchToPortalPhase();
DoZoneInCombat();
}
void JustDied(Unit* /*killer*/) override
{
HandleDoors(true);
DestroyPortals();
}
void UpdateAI(uint32 diff) override
{
if (!UpdateVictim())
return; return;
// Void Zone
if (VoidZoneTimer <= diff)
{
DoCast(SelectTarget(SelectTargetMethod::Random, 1, 45, true), SPELL_VOIDZONE, true);
VoidZoneTimer = 15000;
} }
else }).Schedule(10s, PORTAL_PHASE, [this](TaskContext context)
VoidZoneTimer -= diff; {
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);
}
// NetherInfusion Berserk void SwitchToBanishPhase()
if (!Berserk && NetherInfusionTimer <= diff) {
scheduler.CancelGroup(PORTAL_PHASE);
me->RemoveAurasDueToSpell(SPELL_EMPOWERMENT);
me->RemoveAurasDueToSpell(SPELL_NETHERBURN_AURA);
DoCastSelf(SPELL_BANISH_VISUAL, true);
DoCastSelf(SPELL_BANISH_ROOT, true);
DestroyPortals();
scheduler.Schedule(30s, [this](TaskContext /*context*/)
{
if (!me->IsNonMeleeSpellCast(false))
{ {
me->AddAura(SPELL_NETHER_INFUSION, me); SwitchToPortalPhase();
DoCast(me, SPELL_NETHERSPITE_ROAR); return;
Berserk = true;
} }
else }).Schedule(10s, VANISH_PHASE, [this](TaskContext context)
NetherInfusionTimer -= diff; {
DoCastRandomTarget(SPELL_NETHERBREATH, 0, 40.0f, true);
if (PortalPhase) // PORTAL PHASE context.Repeat(5s, 7s);
{ });
// Distribute beams and buffs Talk(EMOTE_PHASE_BANISH);
if (PortalTimer <= diff) for (uint8 i = 0; i < 3; ++i)
{ {
UpdatePortals(); me->RemoveAurasDueToSpell(NetherBuff[i]);
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;
}
}
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();
} }
}; }
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);
SwitchToPortalPhase();
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
{
BossAI::JustDied(killer);
HandleDoors(true);
DestroyPortals();
}
void UpdateAI(uint32 diff) override
{
if (!UpdateVictim())
return;
scheduler.Update(diff);
if (me->HasUnitState(UNIT_STATE_CASTING))
return;
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,218 +69,270 @@ 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 void Reset() override
{ {
boss_nightbaneAI(Creature* creature) : ScriptedAI(creature) BossAI::Reset();
_skeletonscheduler.CancelAll();
Phase = 1;
MovePhase = 0;
me->SetUnitFlag(UNIT_FLAG_NOT_SELECTABLE);
me->SetSpeed(MOVE_RUN, 2.0f);
me->SetDisableGravity(_intro);
me->SetWalk(false);
me->setActive(true);
if (instance)
{ {
instance = creature->GetInstanceScript(); if (instance->GetData(DATA_NIGHTBANE) == DONE)
Intro = true; me->DisappearAndDie();
else
instance->SetData(DATA_NIGHTBANE, NOT_STARTED);
} }
InstanceScript* instance; HandleTerraceDoors(true);
uint32 Phase; _flying = false;
_movement = false;
bool RainBones; if (!_intro)
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
{ {
BellowingRoarTimer = 30000; //when boss is reset and we're past the intro
CharredEarthTimer = 15000; //cannot despawn, but have to move to a location where he normally is
DistractingAshTimer = 20000; //me->SetHomePosition(IntroWay[7][0], IntroWay[7][1], IntroWay[7][2], 0);
SmolderingBreathTimer = 10000; Position preSpawnPosis = me->GetHomePosition();
TailSweepTimer = 12000; me->NearTeleportTo(preSpawnPosis);
RainofBonesTimer = 10000; instance->SetData(DATA_NIGHTBANE, NOT_STARTED);
SmokingBlastTimer = 20000; _intro = true;
FireballBarrageTimer = 13000;
SearingCindersTimer = 14000;
Phase = 1; Phase = 1;
FlyCount = 0;
MovePhase = 0; MovePhase = 0;
}
me->SetSpeed(MOVE_RUN, 2.0f); ScheduleHealthCheckEvent({25, 50, 70}, [&]{
me->SetDisableGravity(Intro); TakeOff();
me->SetWalk(false); });
me->setActive(true); }
if (instance) void HandleTerraceDoors(bool open)
{
if (instance)
{
instance->HandleGameObject(instance->GetGuidData(DATA_MASTERS_TERRACE_DOOR_1), open);
instance->HandleGameObject(instance->GetGuidData(DATA_MASTERS_TERRACE_DOOR_2), open);
}
}
void JustEngagedWith(Unit* /*who*/) override
{
_JustEngagedWith();
if (instance)
instance->SetData(DATA_NIGHTBANE, IN_PROGRESS);
HandleTerraceDoors(false);
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 (instance->GetData(DATA_NIGHTBANE) == DONE) if (!me->HasInArc(M_PI, target))
me->DisappearAndDie();
else
instance->SetData(DATA_NIGHTBANE, NOT_STARTED);
}
HandleTerraceDoors(true);
Flying = false;
Movement = false;
if (!Intro)
{
me->SetHomePosition(IntroWay[7][0], IntroWay[7][1], IntroWay[7][2], 0);
me->GetMotionMaster()->MoveTargetedHome();
}
}
void HandleTerraceDoors(bool open)
{
if (instance)
{
instance->HandleGameObject(instance->GetGuidData(DATA_MASTERS_TERRACE_DOOR_1), open);
instance->HandleGameObject(instance->GetGuidData(DATA_MASTERS_TERRACE_DOOR_2), open);
}
}
void JustEngagedWith(Unit* /*who*/) override
{
if (instance)
instance->SetData(DATA_NIGHTBANE, IN_PROGRESS);
HandleTerraceDoors(false);
Talk(YELL_AGGRO);
}
void AttackStart(Unit* who) override
{
if (!Intro && !Flying)
ScriptedAI::AttackStart(who);
}
void JustDied(Unit* /*killer*/) override
{
if (instance)
instance->SetData(DATA_NIGHTBANE, DONE);
HandleTerraceDoors(true);
}
void MoveInLineOfSight(Unit* who) override
{
if (!Intro && !Flying)
ScriptedAI::MoveInLineOfSight(who);
}
void MovementInform(uint32 type, uint32 id) override
{
if (type != POINT_MOTION_TYPE)
return;
if (Intro)
{
if (id >= 8)
{ {
Intro = false; DoCast(target, SPELL_TAIL_SWEEP);
me->SetHomePosition(IntroWay[7][0], IntroWay[7][1], IntroWay[7][2], 0);
return;
} }
}
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
{
if (!_intro && !_flying)
ScriptedAI::AttackStart(who);
}
void JustDied(Unit* /*killer*/) override
{
if (instance)
instance->SetData(DATA_NIGHTBANE, DONE);
HandleTerraceDoors(true);
}
void MoveInLineOfSight(Unit* who) override
{
if (!_intro && !_flying)
ScriptedAI::MoveInLineOfSight(who);
}
void MovementInform(uint32 type, uint32 id) override
{
if (type != POINT_MOTION_TYPE)
return;
if (_intro)
{
if (id >= 8)
{
_intro = false;
//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;
}
MovePhase = id + 1;
return;
}
if (_flying)
{
if (id == 0)
{
Talk(EMOTE_BREATH);
_flying = false;
Phase = 2;
return;
}
if (id < 8)
MovePhase = id + 1; MovePhase = id + 1;
else
{
Phase = 1;
_flying = false;
_movement = true;
return; return;
} }
}
}
if (Flying) void JustSummoned(Creature* summon) override
{
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 (id == 0) if (unit->IsPlayer() && !unit->IsWithinDist(me, tresholddistance, false))
{ {
Talk(EMOTE_BREATH); me->CastSpell(unit, spellid, triggered);
Flying = false;
Phase = 2;
return;
}
if (id < 8)
MovePhase = id + 1;
else
{
Phase = 1;
Flying = false;
Movement = true;
return;
} }
} }
} }
}
void JustSummoned(Creature* summoned) override void TakeOff()
{
Talk(YELL_FLY_PHASE);
scheduler.CancelGroup(GROUP_GROUND);
me->InterruptSpell(CURRENT_GENERIC_SPELL);
me->HandleEmoteCommand(EMOTE_ONESHOT_LIFTOFF);
me->SetDisableGravity(true);
me->GetMotionMaster()->Clear(false);
me->GetMotionMaster()->MovePoint(0, IntroWay[2][0], IntroWay[2][1], IntroWay[2][2]);
_flying = true;
ScheduleFly();
//handle landing again
scheduler.Schedule(45s, 60s, [this](TaskContext)
{ {
summoned->AI()->AttackStart(me->GetVictim()); Talk(YELL_LAND_PHASE);
}
void TakeOff()
{
Talk(YELL_FLY_PHASE);
me->InterruptSpell(CURRENT_GENERIC_SPELL);
me->HandleEmoteCommand(EMOTE_ONESHOT_LIFTOFF);
me->SetDisableGravity(true);
me->GetMotionMaster()->Clear(false); me->GetMotionMaster()->Clear(false);
me->GetMotionMaster()->MovePoint(0, IntroWay[2][0], IntroWay[2][1], IntroWay[2][2]); me->GetMotionMaster()->MovePoint(3, IntroWay[3][0], IntroWay[3][1], IntroWay[3][2]);
Flying = true; _flying = true;
scheduler.CancelGroup(GROUP_FLYING);
FlyTimer = urand(45000, 60000); //timer wrong between 45 and 60 seconds scheduler.Schedule(2s, [this](TaskContext)
++FlyCount;
RainofBonesTimer = 5000; //timer wrong (maybe)
RainBones = false;
Skeletons = false;
}
void UpdateAI(uint32 diff) override
{
if (Intro)
{ {
if (MovePhase) ScheduleGround();
{ });
if (MovePhase >= 7) });
{ }
me->SetDisableGravity(false);
me->HandleEmoteCommand(EMOTE_ONESHOT_LAND);
me->GetMotionMaster()->MovePoint(8, IntroWay[7][0], IntroWay[7][1], IntroWay[7][2]);
}
else
{
me->GetMotionMaster()->MovePoint(MovePhase, IntroWay[MovePhase][0], IntroWay[MovePhase][1], IntroWay[MovePhase][2]);
}
MovePhase = 0;
}
return;
}
if (Flying && MovePhase) void UpdateAI(uint32 diff) override
{
if (_intro)
{
if (MovePhase)
{ {
if (MovePhase >= 7) if (MovePhase >= 7)
{ {
@@ -282,158 +341,70 @@ public:
me->GetMotionMaster()->MovePoint(8, IntroWay[7][0], IntroWay[7][1], IntroWay[7][2]); me->GetMotionMaster()->MovePoint(8, IntroWay[7][0], IntroWay[7][1], IntroWay[7][2]);
} }
else else
{
me->GetMotionMaster()->MovePoint(MovePhase, IntroWay[MovePhase][0], IntroWay[MovePhase][1], IntroWay[MovePhase][2]); me->GetMotionMaster()->MovePoint(MovePhase, IntroWay[MovePhase][0], IntroWay[MovePhase][1], IntroWay[MovePhase][2]);
}
MovePhase = 0; MovePhase = 0;
} }
return;
if (!UpdateVictim())
return;
if (Flying)
return;
// Phase 1 "GROUND FIGHT"
if (Phase == 1)
{
if (Movement)
{
DoStartMovement(me->GetVictim());
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();
}
//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
{
DoCastVictim(SPELL_RAIN_OF_BONES);
RainBones = true;
SmokingBlastTimer = 20000;
}
else
RainofBonesTimer -= diff;
if (DistractingAshTimer <= diff)
{
if (Unit* target = SelectTarget(SelectTargetMethod::Random, 0, 100, true))
DoCast(target, SPELL_DISTRACTING_ASH);
DistractingAshTimer = 2000; //timer wrong
}
else
DistractingAshTimer -= diff;
}
if (RainBones)
{
if (SmokingBlastTimer <= diff)
{
DoCastVictim(SPELL_SMOKING_BLAST);
SmokingBlastTimer = 1500; //timer wrong
}
else
SmokingBlastTimer -= diff;
}
if (FireballBarrageTimer <= diff)
{
if (Unit* target = SelectTarget(SelectTargetMethod::MinDistance, 0))
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;
}
} }
};
};
if (_flying && MovePhase)
{
if (MovePhase >= 7)
{
me->SetDisableGravity(false);
me->HandleEmoteCommand(EMOTE_ONESHOT_LAND);
me->GetMotionMaster()->MovePoint(8, IntroWay[7][0], IntroWay[7][1], IntroWay[7][2]);
}
else
me->GetMotionMaster()->MovePoint(MovePhase, IntroWay[MovePhase][0], IntroWay[MovePhase][1], IntroWay[MovePhase][2]);
MovePhase = 0;
}
if (!UpdateVictim())
return;
if (_flying)
return;
scheduler.Update(diff);
_skeletonscheduler.Update(diff);
// Phase 1 "GROUND FIGHT"
if (Phase == 1)
{
if (_movement)
{
DoStartMovement(me->GetVictim());
_movement = false;
}
DoMeleeAttackIfReady();
}
}
private:
uint32 Phase;
TaskScheduler _skeletonscheduler;
bool _intro;
bool _flying;
bool _movement;
uint32 MovePhase;
uint8 _skeletonCount;
uint8 _skeletonSpawnCounter;
};
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);
} }