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

View File

@@ -23,7 +23,7 @@ jobs:
COMPILER: ${{ matrix.compiler }}
if: github.repository == 'azerothcore/azerothcore-wotlk' && !github.event.pull_request.draft
steps:
- uses: actions/checkout@v2
- uses: actions/checkout@v3
- name: Cache
uses: actions/cache@v3
env:

View File

@@ -6,6 +6,6 @@ jobs:
check-pending-sql:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- uses: actions/checkout@v3
- name: Check pending SQL
run: source ./apps/ci/ci-pending.sh

View File

@@ -11,6 +11,6 @@ jobs:
runs-on: ${{ matrix.os }}
name: check codestyle
steps:
- uses: actions/checkout@v2
- uses: actions/checkout@v3
- name: Check core codestyle
run: source ./apps/ci/ci-codestyle.sh

View File

@@ -23,9 +23,9 @@ jobs:
COMPILER: ${{ matrix.compiler }}
if: github.repository == 'azerothcore/azerothcore-wotlk' && !github.event.pull_request.draft
steps:
- uses: actions/checkout@v2
- uses: actions/checkout@v3
- name: Cache
uses: actions/cache@v2
uses: actions/cache@v3
with:
path: var/ccache
key: ccache:${{ matrix.os }}:${{ matrix.compiler }}:${{ github.ref }}:${{ github.sha }}

View File

@@ -40,9 +40,9 @@ jobs:
|| github.event.label.name == 'run-build')
)
steps:
- uses: actions/checkout@v2
- uses: actions/checkout@v3
- name: Cache
uses: actions/cache@v2
uses: actions/cache@v3
with:
path: var/ccache
key: ccache:${{ matrix.os }}:${{ matrix.compiler }}:${{ github.ref }}:${{ github.sha }}

View File

@@ -34,12 +34,12 @@ jobs:
|| github.event.label.name == 'run-build')
)
steps:
- uses: actions/checkout@v2
- uses: actions/checkout@v3
- name: Checkout modules
run: ./apps/ci/ci-install-modules.sh
if: matrix.modules == 'with'
- name: Cache
uses: actions/cache@v2
uses: actions/cache@v3
with:
path: var/ccache
key: ccache:${{ matrix.os }}:${{ matrix.compiler }}:${{ matrix.modules }}-modules:${{ github.ref }}:${{ github.sha }}

View File

@@ -20,7 +20,7 @@ jobs:
runs-on: ubuntu-22.04
name: cpp check
steps:
- uses: actions/checkout@v2
- uses: actions/checkout@v3
- name: cpp check
run: |
sudo apt update -y

View File

@@ -124,7 +124,7 @@ jobs:
password: ${{ secrets.DOCKERHUB_TOKEN }}
- name: Cache
uses: actions/cache@v2
uses: actions/cache@v3
with:
path: var/docker/ccache
key: ccache:${{ matrix.os }}:clang:without-modules:${{ github.ref }}:${{ github.sha }}

View File

@@ -11,7 +11,7 @@ jobs:
runs-on: ubuntu-20.04
if: github.repository == 'azerothcore/azerothcore-wotlk' && !github.event.pull_request.draft
steps:
- uses: actions/checkout@v2
- uses: actions/checkout@v3
with:
persist-credentials: false
- name: Extract branch name

View File

@@ -25,9 +25,9 @@ jobs:
&& !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')
steps:
- uses: actions/checkout@v2
- uses: actions/checkout@v3
- name: Cache
uses: actions/cache@v2
uses: actions/cache@v3
with:
path: ~/Library/Caches/ccache
key: ccache:${{ matrix.os }}:${{ github.ref }}:${{ github.sha }}

View File

@@ -23,7 +23,7 @@ jobs:
COMPILER: ${{ matrix.compiler }}
if: github.repository == 'azerothcore/azerothcore-wotlk' && !github.event.pull_request.draft
steps:
- uses: actions/checkout@v2
- uses: actions/checkout@v3
- name: Cache
uses: actions/cache@v3
env:

View File

@@ -25,7 +25,9 @@ jobs:
&& !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')
steps:
- uses: actions/checkout@v2
- uses: actions/checkout@v3
- name: ccache
uses: hendrikmuhs/ccache-action@v1.2.9
- name: Configure OS
shell: bash
env:
@@ -40,6 +42,10 @@ jobs:
run: |
export CTOOLS_BUILD=all
./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
shell: bash
run: |

View File

@@ -1,6 +1,6 @@
#!/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_MAXSIZE='500M'

View File

@@ -26,4 +26,4 @@ if ! command -v cmake &>/dev/null ; then
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

View File

@@ -0,0 +1,2 @@
-- DB update 2023_07_24_00 -> 2023_07_26_00
UPDATE `spell_proc_event` SET `procFlags` = 4 WHERE `entry` = 23689;

View File

@@ -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));

View File

@@ -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);

View File

@@ -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);

View File

@@ -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);

View File

@@ -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, '');

View File

@@ -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;

View File

@@ -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");

View File

@@ -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;

View File

@@ -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)');

View File

@@ -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;

View File

@@ -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;

View File

@@ -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');

View File

@@ -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;

View File

@@ -1563,6 +1563,14 @@ AllowTwoSide.Interaction.Group = 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
# Description: Allow auctions between factions.

View File

@@ -17,6 +17,7 @@
#include "Arena.h"
#include "ArenaTeamMgr.h"
#include "GroupMgr.h"
#include "Log.h"
#include "ObjectAccessor.h"
#include "Player.h"
@@ -93,6 +94,47 @@ void Arena::AddPlayer(Player* player)
}
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*/)

View File

@@ -133,7 +133,7 @@ void WorldSession::HandleArenaTeamInviteOpcode(WorldPacket& recvData)
if (player->GetSocial()->HasIgnore(GetPlayer()->GetGUID()))
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);
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
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);
return;

View File

@@ -414,18 +414,14 @@ void WorldSession::HandlePetitionSignOpcode(WorldPacket& recvData)
if (!signatures)
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 (!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))
{
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
{
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())
{
Guild::SendCommandResult(this, GUILD_COMMAND_INVITE, ERR_ALREADY_IN_GUILD_S, _player->GetName());
@@ -567,17 +569,14 @@ void WorldSession::HandleOfferPetitionOpcode(WorldPacket& recvData)
if (!petition)
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 (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))
{
// player is too low level to join an arena team
@@ -604,6 +603,12 @@ void WorldSession::HandleOfferPetitionOpcode(WorldPacket& recvData)
}
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())
{
Guild::SendCommandResult(this, GUILD_COMMAND_INVITE, ERR_ALREADY_IN_GUILD_S, _player->GetName());

View File

@@ -79,6 +79,7 @@ enum WorldBoolConfigs
CONFIG_ALLOW_TWO_SIDE_INTERACTION_CHANNEL,
CONFIG_ALLOW_TWO_SIDE_INTERACTION_GROUP,
CONFIG_ALLOW_TWO_SIDE_INTERACTION_GUILD,
CONFIG_ALLOW_TWO_SIDE_INTERACTION_ARENA,
CONFIG_ALLOW_TWO_SIDE_INTERACTION_AUCTION,
CONFIG_ALLOW_TWO_SIDE_INTERACTION_MAIL,
CONFIG_ALLOW_TWO_SIDE_WHO_LIST,

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_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_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_MAIL] = sConfigMgr->GetOption<bool>("AllowTwoSide.Interaction.Mail", false);
_bool_configs[CONFIG_ALLOW_TWO_SIDE_WHO_LIST] = sConfigMgr->GetOption<bool>("AllowTwoSide.WhoList", false);

View File

@@ -121,8 +121,6 @@ struct boss_moroes : public BossAI
context.Repeat(5s);
}).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);
context.Repeat(1min, 2min);
});

View File

@@ -15,13 +15,6 @@
* 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 "ScriptMgr.h"
#include "ScriptedCreature.h"
@@ -29,19 +22,35 @@ EndScriptData */
#include "SpellAuraEffects.h"
#include "SpellScript.h"
enum Netherspite
enum Emotes
{
EMOTE_PHASE_PORTAL = 0,
EMOTE_PHASE_BANISH = 1,
EMOTE_PHASE_PORTAL = 0,
EMOTE_PHASE_BANISH = 1
};
SPELL_NETHERBURN_AURA = 30522,
SPELL_VOIDZONE = 37063,
SPELL_NETHER_INFUSION = 38688,
SPELL_NETHERBREATH = 38523,
SPELL_BANISH_VISUAL = 39833,
SPELL_BANISH_ROOT = 42716,
SPELL_EMPOWERMENT = 38549,
SPELL_NETHERSPITE_ROAR = 38684,
enum Spells
{
SPELL_NETHERBURN_AURA = 30522,
SPELL_VOIDZONE = 37063,
SPELL_NETHER_INFUSION = 38688,
SPELL_NETHERBREATH = 38523,
SPELL_BANISH_VISUAL = 39833,
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] =
@@ -51,302 +60,255 @@ const float PortalCoord[3][3] =
{-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 PortalBeam[3] = {30465, 30464, 30463};
const uint32 PlayerBuff[3] = {30421, 30422, 30423};
const uint32 NetherBuff[3] = {30466, 30467, 30468};
const uint32 PortalBeam[3] = {30465, 30464, 30463};
const uint32 PlayerBuff[3] = {30421, 30422, 30423};
const uint32 NetherBuff[3] = {30466, 30467, 30468};
const uint32 PlayerDebuff[3] = {38637, 38638, 38639};
class boss_netherspite : public CreatureScript
struct boss_netherspite : public BossAI
{
public:
boss_netherspite() : CreatureScript("boss_netherspite") { }
boss_netherspite(Creature* creature) : BossAI(creature, DATA_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();
}
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 = me->SummonCreature(PortalID[i], PortalCoord[pos[i]][0], PortalCoord[pos[i]][1], PortalCoord[pos[i]][2], 0, TEMPSUMMON_TIMED_DESPAWN, 60000))
{
if (Creature* portal = ObjectAccessor::GetCreature(*me, PortalGUID[i]))
portal->DisappearAndDie();
if (Creature* portal = ObjectAccessor::GetCreature(*me, BeamerGUID[i]))
portal->DisappearAndDie();
PortalGUID[i].Clear();
BeamTarget[i].Clear();
PortalGUID[i] = portal->GetGUID();
portal->AddAura(PortalVisual[i], portal);
}
}
}
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[j]))
if (Creature* portal = ObjectAccessor::GetCreature(*me, PortalGUID[i]))
{
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
Unit* current = ObjectAccessor::GetUnit(*portal, BeamTarget[j]);
// temporary store for the best suitable beam reciever
Unit* target = me;
Map::PlayerList const& players = map->GetPlayers();
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();
// get the best suitable target
for (Map::PlayerList::const_iterator i = players.begin(); i != players.end(); ++i)
{
Player* p = i->GetSource();
if (p && p->IsAlive() // alive
&& (!target || target->GetDistance2d(portal) > p->GetDistance2d(portal)) // closer than current best
&& !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;
}
Player* p = i->GetSource();
if (p && p->IsAlive() // alive
&& (!target || target->GetDistance2d(portal) > p->GetDistance2d(portal)) // closer than current best
&& !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);
me->RemoveAurasDueToSpell(SPELL_BANISH_VISUAL);
SummonPortals();
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())
if (!me->IsNonMeleeSpellCast(false))
{
SwitchToBanishPhase();
return;
// Void Zone
if (VoidZoneTimer <= diff)
{
DoCast(SelectTarget(SelectTargetMethod::Random, 1, 45, true), SPELL_VOIDZONE, true);
VoidZoneTimer = 15000;
}
else
VoidZoneTimer -= diff;
}).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);
}
// NetherInfusion Berserk
if (!Berserk && NetherInfusionTimer <= diff)
void SwitchToBanishPhase()
{
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);
DoCast(me, SPELL_NETHERSPITE_ROAR);
Berserk = true;
SwitchToPortalPhase();
return;
}
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;
}
}
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();
}).Schedule(10s, VANISH_PHASE, [this](TaskContext context)
{
DoCastRandomTarget(SPELL_NETHERBREATH, 0, 40.0f, true);
context.Repeat(5s, 7s);
});
Talk(EMOTE_PHASE_BANISH);
for (uint8 i = 0; i < 3; ++i)
{
me->RemoveAurasDueToSpell(NetherBuff[i]);
}
};
}
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
@@ -366,6 +328,6 @@ class spell_nether_portal_perseverence : public AuraScript
void AddSC_boss_netherspite()
{
new boss_netherspite();
RegisterKarazhanCreatureAI(boss_netherspite);
RegisterSpellScript(spell_nether_portal_perseverence);
}

View File

@@ -25,6 +25,7 @@ EndScriptData */
#include "Player.h"
#include "ScriptMgr.h"
#include "ScriptedCreature.h"
#include "TaskScheduler.h"
#include "karazhan.h"
enum Spells
@@ -52,6 +53,12 @@ enum Says
EMOTE_BREATH = 4
};
enum Groups
{
GROUP_GROUND = 0,
GROUP_FLYING = 1
};
float IntroWay[8][3] =
{
{-11053.37f, -1794.48f, 149.00f},
@@ -62,218 +69,270 @@ float IntroWay[8][3] =
{-11128.73f, -1929.75f, 125.00f},
{-11140.00f, -1915.00f, 122.00f},
{-11163.00f, -1903.00f, 91.473f}
};
}; //TODO: move to table
class boss_nightbane : public CreatureScript
struct boss_nightbane : public BossAI
{
public:
boss_nightbane() : CreatureScript("boss_nightbane") { }
CreatureAI* GetAI(Creature* creature) const override
boss_nightbane(Creature* creature) : BossAI(creature, DATA_NIGHTBANE)
{
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();
Intro = true;
if (instance->GetData(DATA_NIGHTBANE) == DONE)
me->DisappearAndDie();
else
instance->SetData(DATA_NIGHTBANE, NOT_STARTED);
}
InstanceScript* instance;
HandleTerraceDoors(true);
uint32 Phase;
_flying = false;
_movement = false;
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
if (!_intro)
{
BellowingRoarTimer = 30000;
CharredEarthTimer = 15000;
DistractingAshTimer = 20000;
SmolderingBreathTimer = 10000;
TailSweepTimer = 12000;
RainofBonesTimer = 10000;
SmokingBlastTimer = 20000;
FireballBarrageTimer = 13000;
SearingCindersTimer = 14000;
//when boss is reset and we're past the intro
//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;
FlyCount = 0;
MovePhase = 0;
}
me->SetSpeed(MOVE_RUN, 2.0f);
me->SetDisableGravity(Intro);
me->SetWalk(false);
me->setActive(true);
ScheduleHealthCheckEvent({25, 50, 70}, [&]{
TakeOff();
});
}
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)
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)
if (!me->HasInArc(M_PI, target))
{
Intro = false;
me->SetHomePosition(IntroWay[7][0], IntroWay[7][1], IntroWay[7][2], 0);
return;
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
{
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;
else
{
Phase = 1;
_flying = false;
_movement = true;
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);
Flying = false;
Phase = 2;
return;
}
if (id < 8)
MovePhase = id + 1;
else
{
Phase = 1;
Flying = false;
Movement = true;
return;
me->CastSpell(unit, spellid, triggered);
}
}
}
}
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()->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;
FlyTimer = urand(45000, 60000); //timer wrong between 45 and 60 seconds
++FlyCount;
RainofBonesTimer = 5000; //timer wrong (maybe)
RainBones = false;
Skeletons = false;
}
void UpdateAI(uint32 diff) override
{
if (Intro)
_flying = true;
scheduler.CancelGroup(GROUP_FLYING);
scheduler.Schedule(2s, [this](TaskContext)
{
if (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;
}
return;
}
ScheduleGround();
});
});
}
if (Flying && MovePhase)
void UpdateAI(uint32 diff) override
{
if (_intro)
{
if (MovePhase)
{
if (MovePhase >= 7)
{
@@ -282,158 +341,70 @@ public:
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;
// 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;
}
return;
}
};
};
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
{
public:
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
{
if (InstanceScript* pInstance = go->GetInstanceScript())
@@ -449,6 +420,6 @@ public:
void AddSC_boss_nightbane()
{
new boss_nightbane();
RegisterKarazhanCreatureAI(boss_nightbane);
new go_blackened_urn();
}

View File

@@ -292,6 +292,7 @@ public:
{
instance->SetData(DATA_ARCHIMONDEEVENT, NOT_STARTED);
me->SetReactState(REACT_AGGRESSIVE);
DoomfireSpiritGUID.Clear();
WorldTreeGUID.Clear();
WispCount = 0;
@@ -618,6 +619,7 @@ public:
break;
}
case EVENT_BELOW_10_PERCENT_HP:
me->SetReactState(REACT_PASSIVE);
DoCastProtection(); // Protection of Elune against Finger and Hand of Death
BelowTenPercent = true;
me->GetMotionMaster()->Clear(false);

View File

@@ -218,9 +218,9 @@ struct boss_magtheridon : public BossAI
Talk(SAY_EMOTE_FREE);
Talk(SAY_FREE);
scheduler.CancelGroup(GROUP_EARLY_RELEASE_CHECK); //cancel regular countdown
_magReleased = true;
scheduler.Schedule(3s, [this](TaskContext)
{
_magReleased = true; //redundancy
ScheduleCombatEvents();
});
}
@@ -239,6 +239,7 @@ struct boss_magtheridon : public BossAI
{
Talk(SAY_EMOTE_FREE);
Talk(SAY_FREE);
_magReleased = true;
}).Schedule(123s, GROUP_EARLY_RELEASE_CHECK, [this](TaskContext /*context*/)
{
ScheduleCombatEvents();

View File

@@ -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()
{
RegisterSpellScript(spell_silithyst);
@@ -5029,4 +5062,5 @@ void AddSC_generic_spell_scripts()
RegisterSpellScript(spell_freezing_circle);
RegisterSpellScript(spell_gen_threshalisk_charge);
RegisterSpellScript(spell_gen_shriveling_gaze);
RegisterSpellScript(spell_gen_curse_of_pain);
}

View File

@@ -974,6 +974,9 @@ class spell_q6124_6129_apply_salve : public SpellScript
if (newEntry)
{
creatureTarget->UpdateEntry(newEntry);
creatureTarget->GetMotionMaster()->Clear();
creatureTarget->GetMotionMaster()->MoveFleeing(caster);
creatureTarget->SetUnitFlag(UNIT_FLAG_NOT_ATTACKABLE_1);
creatureTarget->DespawnOrUnsummon(DESPAWN_TIME);
caster->KilledMonsterCredit(newEntry);
}

View File

@@ -486,7 +486,7 @@ public:
if (creature->IsTrainer())
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))
{

View File

@@ -32,6 +32,7 @@
#include "ScriptedGossip.h"
#include "SmartAI.h"
#include "SpellAuras.h"
#include "TaskScheduler.h"
#include "WaypointMgr.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()
{
// Ours
@@ -2680,4 +2707,5 @@ void AddSC_npcs_special()
new npc_spring_rabbit();
new npc_stable_master();
RegisterCreatureAI(npc_arcanite_dragonling);
RegisterCreatureAI(npc_crashin_thrashin_robot);
}