Merge remote-tracking branch 'acore/master' into test-staging

This commit is contained in:
Keleborn
2026-06-19 09:51:14 -07:00
113 changed files with 2587 additions and 1173 deletions
+83
View File
@@ -0,0 +1,83 @@
# yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json
# CodeRabbit review config. Mirrors the hard rules in CLAUDE.md so the AI
# reviewer enforces project conventions on every PR.
language: en-US
reviews:
profile: chill
request_changes_workflow: false
high_level_summary: false
poem: false
review_status: false
collapse_walkthrough: true
sequence_diagrams: false
auto_review:
enabled: true
# Review on PR open only; don't re-post a walkthrough on every push.
auto_incremental_review: false
drafts: false
# Skip generated, vendored, and immutable content so reviews stay focused.
path_filters:
- "!deps/**"
- "!data/sql/updates/db_*/**"
path_instructions:
- path: "**/*.{cpp,h,hpp}"
instructions: |
AzerothCore C++ conventions (CI enforces these with -Werror; flag violations):
- C++20. 4-space indent, tabs forbidden. UTF-8, LF, max 120 columns, trailing newline.
- Allman braces. No braces around single-line statements. `if (x)`, never `if(x)` or `if ( x )`.
- `auto const&` (not `const auto&`); `Type const*` (not `const Type*`).
- Use fmt-style `{}` format specifiers, never printf-style `%u`/`%s`.
- Logging: `LOG_INFO("category.sub", "msg {}", arg)` (also LOG_WARN/ERROR/DEBUG/TRACE).
No printf, no `sLog->`, no `TC_LOG_*`.
- Random: use Random.h helpers (urand, irand, frand, rand32, rand_chance,
roll_chance_f/i). Never `std::rand` or `<random>` directly.
- Strings: `Acore::StringFormat(fmt, args...)`.
- Config: `sConfigMgr->GetOption<T>("Name", default)`.
- Namespace is `Acore::` — flag any leftover `Trinity::` from upstream ports.
- Use typed helpers instead of raw flag access: IsPlayer()/IsCreature()/IsItem();
GetNpcFlags()/HasNpcFlag()/SetNpcFlag()/RemoveNpcFlag()/ReplaceAllNpcFlags();
IsRefundable()/IsBOPTradable()/IsWrapped(); HasFlag(ItemFlag)/HasFlag2()/HasFlagCu();
ObjectGuid::ToString().c_str() instead of GetCounter().
- Never store a raw Player*/Creature*/Unit* past the current call/tick — store the
ObjectGuid and resolve at use time (ObjectAccessor::FindPlayer,
ObjectAccessor::GetCreature(*from, guid), Map::GetCreature, …).
- DB access: use PreparedStatement, not raw query strings. Non-blocking reads go through
the async path (_queryProcessor.AddCallback(db.AsyncQuery(...))). Multi-statement
writes wrap in SQLTransaction.
- Timed AI actions: use EventMap or TaskScheduler, not hand-rolled tick counters.
- Prefer SmartAI (DB) for new creature behaviour; reach for CreatureScript only when the
SmartAI vocabulary isn't enough. New creature AI prefers RegisterCreatureAI(ClassName).
- Script registration: spell/aura scripts use RegisterSpellScript(ClassName) or
RegisterSpellAndAuraScriptPair(...) inside AddSC_(); creature AI uses
RegisterCreatureAI(ClassName) (preferred) or new ClassName() (legacy). Declare and
call AddSC_() from the regional loader (e.g. Spells/spells_script_loader.cpp,
EasternKingdoms/eastern_kingdoms_script_loader.cpp). Module hooks inherit from
PlayerScript/WorldScript/etc. and register with new MyClass() in AddSC_().
- path: "data/sql/updates/pending_db_*/**/*.sql"
instructions: |
AzerothCore SQL update conventions (enforced by apps/codestyle/codestyle-sql.py):
- Every INSERT must be preceded by a matching DELETE for idempotency, and that DELETE must include a WHERE clause scoped precisely to the intended rows. A predicate that is too broad will remove unrelated data, so confirm it matches exactly what the INSERT will re-add.
- 4-space indent (no tabs), trailing newline, no double semicolons, no multiple blank lines.
- Tables must use the InnoDB engine.
- path: "data/sql/base/**"
instructions: |
This SQL directory is immutable. Changes here should not happen in a normal PR —
flag any modification. New SQL belongs in data/sql/updates/pending_db_*/.
- path: "data/sql/archive/**"
instructions: |
This SQL directory is immutable. Changes here should not happen in a normal PR —
flag any modification. New SQL belongs in data/sql/updates/pending_db_*/.
knowledge_base:
code_guidelines:
enabled: true
# CLAUDE.md is in CodeRabbit's defaults, but list it explicitly so the project's
# full guideline doc is always pulled into review context.
filePatterns:
- "CLAUDE.md"
chat:
auto_reply: true
+2 -2
View File
@@ -5,7 +5,7 @@ indent_size = 4
tab_width = 4 tab_width = 4
insert_final_newline = true insert_final_newline = true
trim_trailing_whitespace = true trim_trailing_whitespace = true
max_line_length = 80 max_line_length = 120
[*.{json,ts,js,yml,sh}] [*.{json,ts,js,yml,sh}]
charset = utf-8 charset = utf-8
@@ -14,4 +14,4 @@ indent_size = 2
tab_width = 2 tab_width = 2
insert_final_newline = true insert_final_newline = true
trim_trailing_whitespace = true trim_trailing_whitespace = true
max_line_length = 80 max_line_length = 120
+1 -1
View File
@@ -24,7 +24,7 @@ Based on CLAUDE.md, always verify:
- 4-space indentation for C++ (no tabs) - 4-space indentation for C++ (no tabs)
- 2-space indentation for JSON, YAML, shell scripts - 2-space indentation for JSON, YAML, shell scripts
- UTF-8 encoding, LF line endings - UTF-8 encoding, LF line endings
- Max 80 character line length - Max 120 character line length
- No braces around single-line statements - No braces around single-line statements
- Format variables in output using {} placeholders instead of printf-style format specifiers like %u - Format variables in output using {} placeholders instead of printf-style format specifiers like %u
+1 -1
View File
@@ -64,7 +64,7 @@ python apps/codestyle/codestyle-sql.py # SQL (compares to origin/master)
Hard rules (also enforced by CI with `-Werror`): Hard rules (also enforced by CI with `-Werror`):
- 4-space indent for C++ (tabs forbidden); 2-space for JSON/YAML/sh/ts/js. UTF-8, LF, max 80 cols, trailing newline. - 4-space indent for C++ (tabs forbidden); 2-space for JSON/YAML/sh/ts/js. UTF-8, LF, max 120 cols, trailing newline.
- Allman braces. No braces around single-line statements. `if (x)` — never `if(x)` or `if ( x )`. - Allman braces. No braces around single-line statements. `if (x)` — never `if(x)` or `if ( x )`.
- `auto const&` (not `const auto&`); `Type const*` (not `const Type*`). - `auto const&` (not `const auto&`); `Type const*` (not `const Type*`).
- Use `{}` format specifiers (`fmt`-style), not `%u`/`%s`. - Use `{}` format specifiers (`fmt`-style), not `%u`/`%s`.
@@ -0,0 +1,55 @@
-- DB update 2026_06_12_02 -> 2026_06_13_00
-- SMILES O'BYRON (Engineering(350 / Gnomish Engineer) - Ultrasafe Transporter: Toshley's Station)
-- Add NPC Text for learn Ultrasafe Transporter gossip concurrent to already learned schematic gossip
DELETE FROM `npc_text` WHERE (`ID` = 10369);
INSERT INTO `npc_text` (`ID`, `text0_0`, `text0_1`, `BroadcastTextID0`, `lang0`, `Probability0`, `em0_0`, `em0_1`, `em0_2`, `em0_3`, `em0_4`, `em0_5`, `text1_0`, `text1_1`, `BroadcastTextID1`, `lang1`, `Probability1`, `em1_0`, `em1_1`, `em1_2`, `em1_3`, `em1_4`, `em1_5`, `text2_0`, `text2_1`, `BroadcastTextID2`, `lang2`, `Probability2`, `em2_0`, `em2_1`, `em2_2`, `em2_3`, `em2_4`, `em2_5`, `text3_0`, `text3_1`, `BroadcastTextID3`, `lang3`, `Probability3`, `em3_0`, `em3_1`, `em3_2`, `em3_3`, `em3_4`, `em3_5`, `text4_0`, `text4_1`, `BroadcastTextID4`, `lang4`, `Probability4`, `em4_0`, `em4_1`, `em4_2`, `em4_3`, `em4_4`, `em4_5`, `text5_0`, `text5_1`, `BroadcastTextID5`, `lang5`, `Probability5`, `em5_0`, `em5_1`, `em5_2`, `em5_3`, `em5_4`, `em5_5`, `text6_0`, `text6_1`, `BroadcastTextID6`, `lang6`, `Probability6`, `em6_0`, `em6_1`, `em6_2`, `em6_3`, `em6_4`, `em6_5`, `text7_0`, `text7_1`, `BroadcastTextID7`, `lang7`, `Probability7`, `em7_0`, `em7_1`, `em7_2`, `em7_3`, `em7_4`, `em7_5`, `VerifiedBuild`) VALUES
(10369, 'Once you have built the device, you simply activate the device to be transported to Toshley''s Station! A few people have reported being transformed into chickens, but I am sure they were exaggerating!$B$BIf possible I would try and not use the device on a day when someone else has, but it will probably work out alright even if you do!', '', 19133, 0, 0, 0, 0, 0, 0, 0, 0, '', '', 0, 0, 0, 0, 0, 0, 0, 0, 0, '', '', 0, 0, 0, 0, 0, 0, 0, 0, 0, '', '', 0, 0, 0, 0, 0, 0, 0, 0, 0, '', '', 0, 0, 0, 0, 0, 0, 0, 0, 0, '', '', 0, 0, 0, 0, 0, 0, 0, 0, 0, '', '', 0, 0, 0, 0, 0, 0, 0, 0, 0, '', '', 0, 0, 0, 0, 0, 0, 0, 0, 0, 0);
-- Add learn Ultrasafe Transporter: Toshley's Station gossip concurrent to Smiles' base gossip menu (8306) with correct text
DELETE FROM `gossip_menu` WHERE (`MenuID` = 8307) AND (`TextID` IN (10369));
INSERT INTO `gossip_menu` (`MenuID`, `TextID`) VALUES
(8307, 10369);
-- Add gossip menu option (0)
DELETE FROM `gossip_menu_option` WHERE (`MenuID` = 8306) AND (`OptionID` IN (0));
INSERT INTO `gossip_menu_option` (`MenuID`, `OptionID`, `OptionIcon`, `OptionText`, `OptionBroadcastTextID`, `OptionType`, `OptionNpcFlag`, `ActionMenuID`, `ActionPoiID`, `BoxCoded`, `BoxMoney`, `BoxText`, `BoxBroadcastTextID`, `VerifiedBuild`) VALUES
(8306, 0, 0, 'I must build a beacon for this marvelous device!', 9997, 1, 1, 8307, 0, 0, 0, '', 0, 0);
-- Add secondary gossip text for players that have already learned Ultrasafe Transporter
DELETE FROM `gossip_menu` WHERE (`MenuID` = 8306) AND (`TextID` IN (10368));
INSERT INTO `gossip_menu` (`MenuID`, `TextID`) VALUES
(8306, 10368);
-- Add condition (Engineering >= 350) to show option 0
DELETE FROM `conditions` WHERE (`SourceTypeOrReferenceId` = 15) AND (`SourceGroup` = 8306) AND (`SourceEntry` = 0) AND (`SourceId` = 0) AND (`ElseGroup` = 0) AND (`ConditionTypeOrReference` = 7) AND (`ConditionTarget` = 0) AND (`ConditionValue1` = 202) AND (`ConditionValue2` = 350) AND (`ConditionValue3` = 0);
INSERT INTO `conditions` (`SourceTypeOrReferenceId`, `SourceGroup`, `SourceEntry`, `SourceId`, `ElseGroup`, `ConditionTypeOrReference`, `ConditionTarget`, `ConditionValue1`, `ConditionValue2`, `ConditionValue3`, `NegativeCondition`, `ErrorType`, `ErrorTextId`, `ScriptName`, `Comment`) VALUES
(15, 8306, 0, 0, 0, 7, 0, 202, 350, 0, 0, 0, 0, '', 'Show Ultrasafe Transporter gossip option if Engineering skill is >= 350');
-- Add condition (Has Gnomish Engineer) to show option 0
DELETE FROM `conditions` WHERE (`SourceTypeOrReferenceId` = 15) AND (`SourceGroup` = 8306) AND (`SourceEntry` = 0) AND (`SourceId` = 0) AND (`ElseGroup` = 0) AND (`ConditionTypeOrReference` = 25) AND (`ConditionTarget` = 0) AND (`ConditionValue1` = 20219) AND (`ConditionValue2` = 0) AND (`ConditionValue3` = 0);
INSERT INTO `conditions` (`SourceTypeOrReferenceId`, `SourceGroup`, `SourceEntry`, `SourceId`, `ElseGroup`, `ConditionTypeOrReference`, `ConditionTarget`, `ConditionValue1`, `ConditionValue2`, `ConditionValue3`, `NegativeCondition`, `ErrorType`, `ErrorTextId`, `ScriptName`, `Comment`) VALUES
(15, 8306, 0, 0, 0, 25, 0, 20219, 0, 0, 0, 0, 0, '', 'Show Ultrasafe Transporter gossip option if player has Gnomish Engineer');
-- Add condition (NOT Learned Ultrasafe Transporter) to show option 0
DELETE FROM `conditions` WHERE (`SourceTypeOrReferenceId` = 15) AND (`SourceGroup` = 8306) AND (`SourceEntry` = 0) AND (`SourceId` = 0) AND (`ElseGroup` = 0) AND (`ConditionTypeOrReference` = 25) AND (`ConditionTarget` = 0) AND (`ConditionValue1` = 36955) AND (`ConditionValue2` = 0) AND (`ConditionValue3` = 0);
INSERT INTO `conditions` (`SourceTypeOrReferenceId`, `SourceGroup`, `SourceEntry`, `SourceId`, `ElseGroup`, `ConditionTypeOrReference`, `ConditionTarget`, `ConditionValue1`, `ConditionValue2`, `ConditionValue3`, `NegativeCondition`, `ErrorType`, `ErrorTextId`, `ScriptName`, `Comment`) VALUES
(15, 8306, 0, 0, 0, 25, 0, 36955, 0, 0, 1, 0, 0, '', 'Show Ultrasafe Transporter gossip option if not already learned');
-- Add condition to show base gossip text when Ultrasafe Transporter has NOT been learned
DELETE FROM `conditions` WHERE (`SourceTypeOrReferenceId` = 14) AND (`SourceGroup` = 8306) AND (`SourceEntry` = 10410) AND (`SourceId` = 0) AND (`ElseGroup` = 0) AND (`ConditionTypeOrReference` = 25) AND (`ConditionTarget` = 0) AND (`ConditionValue1` = 36955) AND (`ConditionValue2` = 0) AND (`ConditionValue3` = 0);
INSERT INTO `conditions` (`SourceTypeOrReferenceId`, `SourceGroup`, `SourceEntry`, `SourceId`, `ElseGroup`, `ConditionTypeOrReference`, `ConditionTarget`, `ConditionValue1`, `ConditionValue2`, `ConditionValue3`, `NegativeCondition`, `ErrorType`, `ErrorTextId`, `ScriptName`, `Comment`) VALUES
(14, 8306, 10410, 0, 0, 25, 0, 36955, 0, 0, 1, 0, 0, '', 'Show gossip text regarding ''Ultrasafe Transporter: Toshley''s Station'' if it has NOT been learned');
-- Add condition to show gossip text when Ultrasafe Transporter has already been learned
DELETE FROM `conditions` WHERE (`SourceTypeOrReferenceId` = 14) AND (`SourceGroup` = 8306) AND (`SourceEntry` = 10368) AND (`SourceId` = 0) AND (`ElseGroup` = 0) AND (`ConditionTypeOrReference` = 25) AND (`ConditionTarget` = 0) AND (`ConditionValue1` = 36955) AND (`ConditionValue2` = 0) AND (`ConditionValue3` = 0);
INSERT INTO `conditions` (`SourceTypeOrReferenceId`, `SourceGroup`, `SourceEntry`, `SourceId`, `ElseGroup`, `ConditionTypeOrReference`, `ConditionTarget`, `ConditionValue1`, `ConditionValue2`, `ConditionValue3`, `NegativeCondition`, `ErrorType`, `ErrorTextId`, `ScriptName`, `Comment`) VALUES
(14, 8306, 10368, 0, 0, 25, 0, 36955, 0, 0, 0, 0, 0, '', 'Show gossip text regarding ''Ultrasafe Transporter: Toshley''s Station'' if it has already been learned');
-- Update Smiles to use SmartAI
UPDATE `creature_template` SET `AIName` = 'SmartAI', `ScriptName` = '' WHERE (`entry` = 21494) AND (`name` = 'Smiles O''Byron');
-- Add SmartAI to learn spell Ultrasafe Transporter: Toshley's Station to invoker(player) using triggered flag for spell (36957)
DELETE FROM `smart_scripts` WHERE (`entryorguid` = 21494) AND (`source_type` = 0) AND (`id` IN (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`, `event_param6`, `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
(21494, 0, 0, 0, 62, 0, 100, 0, 8306, 0, 0, 0, 0, 0, 134, 36957, 2, 0, 1, 0, 0, 7, 0, 0, 0, 0, 0, 0, 0, 0, 'Smiles O''Byron - On Gossip Option 0 Selected - Invoker Cast ''Ultrasafe Transporter - Toshley`s Station''');
@@ -0,0 +1,60 @@
-- DB update 2026_06_13_00 -> 2026_06_13_01
-- KABLAMM FARFLINGER (Engineering(350 / Goblin Engineer)) - Dimensional Ripper: Area 52
-- Add NPC Text for learn schematic gossip
DELETE FROM `npc_text` WHERE (`ID` = 10367);
INSERT INTO `npc_text` (`ID`, `text0_0`, `text0_1`, `BroadcastTextID0`, `lang0`, `Probability0`, `em0_0`, `em0_1`, `em0_2`, `em0_3`, `em0_4`, `em0_5`, `text1_0`, `text1_1`, `BroadcastTextID1`, `lang1`, `Probability1`, `em1_0`, `em1_1`, `em1_2`, `em1_3`, `em1_4`, `em1_5`, `text2_0`, `text2_1`, `BroadcastTextID2`, `lang2`, `Probability2`, `em2_0`, `em2_1`, `em2_2`, `em2_3`, `em2_4`, `em2_5`, `text3_0`, `text3_1`, `BroadcastTextID3`, `lang3`, `Probability3`, `em3_0`, `em3_1`, `em3_2`, `em3_3`, `em3_4`, `em3_5`, `text4_0`, `text4_1`, `BroadcastTextID4`, `lang4`, `Probability4`, `em4_0`, `em4_1`, `em4_2`, `em4_3`, `em4_4`, `em4_5`, `text5_0`, `text5_1`, `BroadcastTextID5`, `lang5`, `Probability5`, `em5_0`, `em5_1`, `em5_2`, `em5_3`, `em5_4`, `em5_5`, `text6_0`, `text6_1`, `BroadcastTextID6`, `lang6`, `Probability6`, `em6_0`, `em6_1`, `em6_2`, `em6_3`, `em6_4`, `em6_5`, `text7_0`, `text7_1`, `BroadcastTextID7`, `lang7`, `Probability7`, `em7_0`, `em7_1`, `em7_2`, `em7_3`, `em7_4`, `em7_5`, `VerifiedBuild`) VALUES
(10367, 'The theory behind it is that we completely destroy you with a massive explosion wherever you are and send those particles through a dimensional rip and then re-implode you at the machine here. Instant Transport! It might not work ALL the time, but what kind of goblin engineer are you! If survival was your first priority you could never be a real Goblin Engineer!$B$BHere is the recipe you will need to make the Dimensional Ripper and try it out!', '', 9995, 0, 0, 0, 0, 0, 0, 0, 0, '', '', 0, 0, 0, 0, 0, 0, 0, 0, 0, '', '', 0, 0, 0, 0, 0, 0, 0, 0, 0, '', '', 0, 0, 0, 0, 0, 0, 0, 0, 0, '', '', 0, 0, 0, 0, 0, 0, 0, 0, 0, '', '', 0, 0, 0, 0, 0, 0, 0, 0, 0, '', '', 0, 0, 0, 0, 0, 0, 0, 0, 0, '', '', 0, 0, 0, 0, 0, 0, 0, 0, 0, 0);
-- Add NPC Text for already learned schematic gossip text
DELETE FROM `npc_text` WHERE (`ID` = 10366);
INSERT INTO `npc_text` (`ID`, `text0_0`, `text0_1`, `BroadcastTextID0`, `lang0`, `Probability0`, `em0_0`, `em0_1`, `em0_2`, `em0_3`, `em0_4`, `em0_5`, `text1_0`, `text1_1`, `BroadcastTextID1`, `lang1`, `Probability1`, `em1_0`, `em1_1`, `em1_2`, `em1_3`, `em1_4`, `em1_5`, `text2_0`, `text2_1`, `BroadcastTextID2`, `lang2`, `Probability2`, `em2_0`, `em2_1`, `em2_2`, `em2_3`, `em2_4`, `em2_5`, `text3_0`, `text3_1`, `BroadcastTextID3`, `lang3`, `Probability3`, `em3_0`, `em3_1`, `em3_2`, `em3_3`, `em3_4`, `em3_5`, `text4_0`, `text4_1`, `BroadcastTextID4`, `lang4`, `Probability4`, `em4_0`, `em4_1`, `em4_2`, `em4_3`, `em4_4`, `em4_5`, `text5_0`, `text5_1`, `BroadcastTextID5`, `lang5`, `Probability5`, `em5_0`, `em5_1`, `em5_2`, `em5_3`, `em5_4`, `em5_5`, `text6_0`, `text6_1`, `BroadcastTextID6`, `lang6`, `Probability6`, `em6_0`, `em6_1`, `em6_2`, `em6_3`, `em6_4`, `em6_5`, `text7_0`, `text7_1`, `BroadcastTextID7`, `lang7`, `Probability7`, `em7_0`, `em7_1`, `em7_2`, `em7_3`, `em7_4`, `em7_5`, `VerifiedBuild`) VALUES
(10366, 'It''s good to see an engineer brave enough to make the device. We goblin engineers laugh at danger!', '', 10000, 0, 0, 0, 0, 0, 0, 0, 0, '', '', 0, 0, 0, 0, 0, 0, 0, 0, 0, '', '', 0, 0, 0, 0, 0, 0, 0, 0, 0, '', '', 0, 0, 0, 0, 0, 0, 0, 0, 0, '', '', 0, 0, 0, 0, 0, 0, 0, 0, 0, '', '', 0, 0, 0, 0, 0, 0, 0, 0, 0, '', '', 0, 0, 0, 0, 0, 0, 0, 0, 0, '', '', 0, 0, 0, 0, 0, 0, 0, 0, 0, 0);
-- Update gossip option (0) to be correct (This NPC shouldn't be a trainer)
DELETE FROM `gossip_menu_option` WHERE (`MenuID` = 8308) AND (`OptionID` IN (0));
INSERT INTO `gossip_menu_option` (`MenuID`, `OptionID`, `OptionIcon`, `OptionText`, `OptionBroadcastTextID`, `OptionType`, `OptionNpcFlag`, `ActionMenuID`, `ActionPoiID`, `BoxCoded`, `BoxMoney`, `BoxText`, `BoxBroadcastTextID`, `VerifiedBuild`) VALUES
(8308, 0, 0, 'This Dimensional Imploder sounds dangerous! How can I make one?', 9994, 1, 1, 8309, 0, 0, 0, '', 0, 0);
-- Add gossip menu for learn schematic
DELETE FROM `gossip_menu` WHERE (`MenuID` = 8309) AND (`TextID` IN (10367));
INSERT INTO `gossip_menu` (`MenuID`, `TextID`) VALUES
(8309, 10367);
-- Add gossip text for already learned schematic
DELETE FROM `gossip_menu` WHERE (`MenuID` = 8308) AND (`TextID` IN (10366));
INSERT INTO `gossip_menu` (`MenuID`, `TextID`) VALUES
(8308, 10366);
-- Add condition to show gossip option if engineering >= 350
DELETE FROM `conditions` WHERE (`SourceTypeOrReferenceId` = 15) AND (`SourceGroup` = 8308) AND (`SourceEntry` = 0) AND (`SourceId` = 0) AND (`ElseGroup` = 0) AND (`ConditionTypeOrReference` = 7) AND (`ConditionTarget` = 0) AND (`ConditionValue1` = 202) AND (`ConditionValue2` = 350) AND (`ConditionValue3` = 0);
INSERT INTO `conditions` (`SourceTypeOrReferenceId`, `SourceGroup`, `SourceEntry`, `SourceId`, `ElseGroup`, `ConditionTypeOrReference`, `ConditionTarget`, `ConditionValue1`, `ConditionValue2`, `ConditionValue3`, `NegativeCondition`, `ErrorType`, `ErrorTextId`, `ScriptName`, `Comment`) VALUES
(15, 8308, 0, 0, 0, 7, 0, 202, 350, 0, 0, 0, 0, '', 'Show option to learn Dimensional Ripper: Area 52 if engineering >= 350');
-- Add condition to show gossip option if player has Goblin Engineer
DELETE FROM `conditions` WHERE (`SourceTypeOrReferenceId` = 15) AND (`SourceGroup` = 8308) AND (`SourceEntry` = 0) AND (`SourceId` = 0) AND (`ElseGroup` = 0) AND (`ConditionTypeOrReference` = 25) AND (`ConditionTarget` = 0) AND (`ConditionValue1` = 20222) AND (`ConditionValue2` = 0) AND (`ConditionValue3` = 0);
INSERT INTO `conditions` (`SourceTypeOrReferenceId`, `SourceGroup`, `SourceEntry`, `SourceId`, `ElseGroup`, `ConditionTypeOrReference`, `ConditionTarget`, `ConditionValue1`, `ConditionValue2`, `ConditionValue3`, `NegativeCondition`, `ErrorType`, `ErrorTextId`, `ScriptName`, `Comment`) VALUES
(15, 8308, 0, 0, 0, 25, 0, 20222, 0, 0, 0, 0, 0, '', 'Show option to learn Dimensional Ripper: Area 52 if player has Goblin Engineer');
-- Add condition to show gossip option if player does NOT have schematic
DELETE FROM `conditions` WHERE (`SourceTypeOrReferenceId` = 15) AND (`SourceGroup` = 8308) AND (`SourceEntry` = 0) AND (`SourceId` = 0) AND (`ElseGroup` = 0) AND (`ConditionTypeOrReference` = 25) AND (`ConditionTarget` = 0) AND (`ConditionValue1` = 36954) AND (`ConditionValue2` = 0) AND (`ConditionValue3` = 0);
INSERT INTO `conditions` (`SourceTypeOrReferenceId`, `SourceGroup`, `SourceEntry`, `SourceId`, `ElseGroup`, `ConditionTypeOrReference`, `ConditionTarget`, `ConditionValue1`, `ConditionValue2`, `ConditionValue3`, `NegativeCondition`, `ErrorType`, `ErrorTextId`, `ScriptName`, `Comment`) VALUES
(15, 8308, 0, 0, 0, 25, 0, 36954, 0, 0, 1, 0, 0, '', 'Show option to learn Dimensional Ripper: Area 52 if player does NOT already have schematic');
-- Add condition to show alt gossip text if player has schematic
DELETE FROM `conditions` WHERE (`SourceTypeOrReferenceId` = 14) AND (`SourceGroup` = 8308) AND (`SourceEntry` = 10366) AND (`SourceId` = 0) AND (`ElseGroup` = 0) AND (`ConditionTypeOrReference` = 25) AND (`ConditionTarget` = 0) AND (`ConditionValue1` = 36954) AND (`ConditionValue2` = 0) AND (`ConditionValue3` = 0);
INSERT INTO `conditions` (`SourceTypeOrReferenceId`, `SourceGroup`, `SourceEntry`, `SourceId`, `ElseGroup`, `ConditionTypeOrReference`, `ConditionTarget`, `ConditionValue1`, `ConditionValue2`, `ConditionValue3`, `NegativeCondition`, `ErrorType`, `ErrorTextId`, `ScriptName`, `Comment`) VALUES
(14, 8308, 10366, 0, 0, 25, 0, 36954, 0, 0, 0, 0, 0, '', 'Show learned schematic gossip text if player already has Dimensional Ripper: Area 52 schematic');
-- Add condition to show base gossip text if player does NOT have schematic
DELETE FROM `conditions` WHERE (`SourceTypeOrReferenceId` = 14) AND (`SourceGroup` = 8308) AND (`SourceEntry` = 10365) AND (`SourceId` = 0) AND (`ElseGroup` = 0) AND (`ConditionTypeOrReference` = 25) AND (`ConditionTarget` = 0) AND (`ConditionValue1` = 36954) AND (`ConditionValue2` = 0) AND (`ConditionValue3` = 0);
INSERT INTO `conditions` (`SourceTypeOrReferenceId`, `SourceGroup`, `SourceEntry`, `SourceId`, `ElseGroup`, `ConditionTypeOrReference`, `ConditionTarget`, `ConditionValue1`, `ConditionValue2`, `ConditionValue3`, `NegativeCondition`, `ErrorType`, `ErrorTextId`, `ScriptName`, `Comment`) VALUES
(14, 8308, 10365, 0, 0, 25, 0, 36954, 0, 0, 1, 0, 0, '', 'Show base gossip text if player does not have Dimensional Ripper: Area 52 schematic');
-- Update Kablamm to use SMART-AI and FORCE-GOSSIP
UPDATE `creature_template` SET `AIName` = 'SmartAI', `ScriptName` = '', `type_flags` = 134217728 WHERE (`entry` = 21493) AND (`name` = 'Kablamm Farflinger');
-- Add Smart-AI to learn spell Dimensional Ripper: Area 52 to invoker(player)
DELETE FROM `smart_scripts` WHERE (`entryorguid` = 21493) AND (`source_type` = 0) AND (`id` IN (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`, `event_param6`, `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
(21493, 0, 0, 0, 62, 0, 100, 0, 8308, 0, 0, 0, 0, 0, 134, 36956, 2, 0, 1, 0, 0, 7, 0, 0, 0, 0, 0, 0, 0, 0, 'Kablamm Farflinger - On Gossip Option 0 Selected - Invoker Cast ''Dimensional Ripper: Area 52''');
@@ -0,0 +1,47 @@
-- DB update 2026_06_13_01 -> 2026_06_13_02
-- ZAP FARFLINGER (Engineering(260 / Goblin Engineer)) - Dimensional Ripper: Everlook
-- Dimensional Ripper gossip option already updated in Kablamm Farflinger update / Both brothers share post learn gossip texts
-- Add gossip option (0) to learn schematic
DELETE FROM `gossip_menu_option` WHERE (`MenuID` = 6092) AND (`OptionID` IN (0));
INSERT INTO `gossip_menu_option` (`MenuID`, `OptionID`, `OptionIcon`, `OptionText`, `OptionBroadcastTextID`, `OptionType`, `OptionNpcFlag`, `ActionMenuID`, `ActionPoiID`, `BoxCoded`, `BoxMoney`, `BoxText`, `BoxBroadcastTextID`, `VerifiedBuild`) VALUES
(6092, 0, 0, 'This Dimensional Imploder sounds dangerous! How can I make one?', 9994, 1, 1, 8309, 0, 0, 0, '', 0, 0);
-- Add gossip text for already learned schematic
DELETE FROM `gossip_menu` WHERE (`MenuID` = 6092) AND (`TextID` IN (10366));
INSERT INTO `gossip_menu` (`MenuID`, `TextID`) VALUES
(6092, 10366);
-- Add condition to show gossip option if engineering >= 260
DELETE FROM `conditions` WHERE (`SourceTypeOrReferenceId` = 15) AND (`SourceGroup` = 6092) AND (`SourceEntry` = 0) AND (`SourceId` = 0) AND (`ElseGroup` = 0) AND (`ConditionTypeOrReference` = 7) AND (`ConditionTarget` = 0) AND (`ConditionValue1` = 202) AND (`ConditionValue2` = 260) AND (`ConditionValue3` = 0);
INSERT INTO `conditions` (`SourceTypeOrReferenceId`, `SourceGroup`, `SourceEntry`, `SourceId`, `ElseGroup`, `ConditionTypeOrReference`, `ConditionTarget`, `ConditionValue1`, `ConditionValue2`, `ConditionValue3`, `NegativeCondition`, `ErrorType`, `ErrorTextId`, `ScriptName`, `Comment`) VALUES
(15, 6092, 0, 0, 0, 7, 0, 202, 260, 0, 0, 0, 0, '', 'Show option to learn Dimensional Ripper: Everlook if engineering >= 260');
-- Add condition to show gossip option if player has Goblin Engineer
DELETE FROM `conditions` WHERE (`SourceTypeOrReferenceId` = 15) AND (`SourceGroup` = 6092) AND (`SourceEntry` = 0) AND (`SourceId` = 0) AND (`ElseGroup` = 0) AND (`ConditionTypeOrReference` = 25) AND (`ConditionTarget` = 0) AND (`ConditionValue1` = 20222) AND (`ConditionValue2` = 0) AND (`ConditionValue3` = 0);
INSERT INTO `conditions` (`SourceTypeOrReferenceId`, `SourceGroup`, `SourceEntry`, `SourceId`, `ElseGroup`, `ConditionTypeOrReference`, `ConditionTarget`, `ConditionValue1`, `ConditionValue2`, `ConditionValue3`, `NegativeCondition`, `ErrorType`, `ErrorTextId`, `ScriptName`, `Comment`) VALUES
(15, 6092, 0, 0, 0, 25, 0, 20222, 0, 0, 0, 0, 0, '', 'Show option to learn Dimensional Ripper: Everlook if player has Goblin Engineer');
-- Add condition to show gossip option if player does NOT have schematic
DELETE FROM `conditions` WHERE (`SourceTypeOrReferenceId` = 15) AND (`SourceGroup` = 6092) AND (`SourceEntry` = 0) AND (`SourceId` = 0) AND (`ElseGroup` = 0) AND (`ConditionTypeOrReference` = 25) AND (`ConditionTarget` = 0) AND (`ConditionValue1` = 23486) AND (`ConditionValue2` = 0) AND (`ConditionValue3` = 0);
INSERT INTO `conditions` (`SourceTypeOrReferenceId`, `SourceGroup`, `SourceEntry`, `SourceId`, `ElseGroup`, `ConditionTypeOrReference`, `ConditionTarget`, `ConditionValue1`, `ConditionValue2`, `ConditionValue3`, `NegativeCondition`, `ErrorType`, `ErrorTextId`, `ScriptName`, `Comment`) VALUES
(15, 6092, 0, 0, 0, 25, 0, 23486, 0, 0, 1, 0, 0, '', 'Show option to learn Dimensional Ripper: Everlook if player does NOT already have schematic');
-- Add condition to show alt gossip text if player has schematic
DELETE FROM `conditions` WHERE (`SourceTypeOrReferenceId` = 14) AND (`SourceGroup` = 6092) AND (`SourceEntry` = 10366) AND (`SourceId` = 0) AND (`ElseGroup` = 0) AND (`ConditionTypeOrReference` = 25) AND (`ConditionTarget` = 0) AND (`ConditionValue1` = 23486) AND (`ConditionValue2` = 0) AND (`ConditionValue3` = 0);
INSERT INTO `conditions` (`SourceTypeOrReferenceId`, `SourceGroup`, `SourceEntry`, `SourceId`, `ElseGroup`, `ConditionTypeOrReference`, `ConditionTarget`, `ConditionValue1`, `ConditionValue2`, `ConditionValue3`, `NegativeCondition`, `ErrorType`, `ErrorTextId`, `ScriptName`, `Comment`) VALUES
(14, 6092, 10366, 0, 0, 25, 0, 23486, 0, 0, 0, 0, 0, '', 'Show learned schematic gossip text if player already has Dimensional Ripper: Everlook schematic');
-- Add condition to show base gossip text if player does NOT have schematic
DELETE FROM `conditions` WHERE (`SourceTypeOrReferenceId` = 14) AND (`SourceGroup` = 6092) AND (`SourceEntry` = 7249) AND (`SourceId` = 0) AND (`ElseGroup` = 0) AND (`ConditionTypeOrReference` = 25) AND (`ConditionTarget` = 0) AND (`ConditionValue1` = 23486) AND (`ConditionValue2` = 0) AND (`ConditionValue3` = 0);
INSERT INTO `conditions` (`SourceTypeOrReferenceId`, `SourceGroup`, `SourceEntry`, `SourceId`, `ElseGroup`, `ConditionTypeOrReference`, `ConditionTarget`, `ConditionValue1`, `ConditionValue2`, `ConditionValue3`, `NegativeCondition`, `ErrorType`, `ErrorTextId`, `ScriptName`, `Comment`) VALUES
(14, 6092, 7249, 0, 0, 25, 0, 23486, 0, 0, 1, 0, 0, '', 'Show base gossip text if player does not have Dimensional Ripper: Everlook schematic');
-- Update Zap to use SMART-AI and FORCE-GOSSIP
UPDATE `creature_template` SET `type_flags` = 134217728, `AIName` = 'SmartAI', `ScriptName` = '' WHERE (`entry` = 14742) AND (`name` = 'Zap Farflinger');
-- Add Smart-AI to learn spell Dimensional Ripper: Everlook to invoker(player)
DELETE FROM `smart_scripts` WHERE (`entryorguid` = 14742) AND (`source_type` = 0) AND (`id` IN (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`, `event_param6`, `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
(14742, 0, 0, 0, 62, 0, 100, 0, 6092, 0, 0, 0, 0, 0, 134, 23490, 2, 0, 1, 0, 0, 7, 0, 0, 0, 0, 0, 0, 0, 0, 'Zap Farflinger - On Gossip Option 0 Selected - Invoker Cast \'Dimension Ripper - Everlook\'');
@@ -0,0 +1,50 @@
-- DB update 2026_06_13_02 -> 2026_06_13_03
-- JHORDY LAPFORGE (Engineering(260 / Gnomish Engineer)) - Ultrasafe Transporter: Gadgetzan
-- Add gossip option (0) to learn schematic
DELETE FROM `gossip_menu_option` WHERE (`MenuID` = 6094) AND (`OptionID` IN (0));
INSERT INTO `gossip_menu_option` (`MenuID`, `OptionID`, `OptionIcon`, `OptionText`, `OptionBroadcastTextID`, `OptionType`, `OptionNpcFlag`, `ActionMenuID`, `ActionPoiID`, `BoxCoded`, `BoxMoney`, `BoxText`, `BoxBroadcastTextID`, `VerifiedBuild`) VALUES
(6094, 0, 0, 'I must build a beacon for this marvelous device!', 9997, 1, 1, 6095, 0, 0, 0, '', 0, 0);
-- Add gossip menu for learn schematic
DELETE FROM `gossip_menu` WHERE (`MenuID` = 6095) AND (`TextID` IN (7252));
INSERT INTO `gossip_menu` (`MenuID`, `TextID`) VALUES
(6095, 7252);
-- Add gossip text for already learned schematic
DELETE FROM `gossip_menu` WHERE (`MenuID` = 6094) AND (`TextID` IN (7253));
INSERT INTO `gossip_menu` (`MenuID`, `TextID`) VALUES
(6094, 7253);
-- Add condition to show gossip option if engineering >= 260
DELETE FROM `conditions` WHERE (`SourceTypeOrReferenceId` = 15) AND (`SourceGroup` = 6094) AND (`SourceEntry` = 0) AND (`SourceId` = 0) AND (`ElseGroup` = 0) AND (`ConditionTypeOrReference` = 7) AND (`ConditionTarget` = 0) AND (`ConditionValue1` = 202) AND (`ConditionValue2` = 260) AND (`ConditionValue3` = 0);
INSERT INTO `conditions` (`SourceTypeOrReferenceId`, `SourceGroup`, `SourceEntry`, `SourceId`, `ElseGroup`, `ConditionTypeOrReference`, `ConditionTarget`, `ConditionValue1`, `ConditionValue2`, `ConditionValue3`, `NegativeCondition`, `ErrorType`, `ErrorTextId`, `ScriptName`, `Comment`) VALUES
(15, 6094, 0, 0, 0, 7, 0, 202, 260, 0, 0, 0, 0, '', 'Show option to learn Ultrasafe Transporter: Gadgetzan if engineering >= 260');
-- Add condition to show gossip option if player has Gnomish Engineer
DELETE FROM `conditions` WHERE (`SourceTypeOrReferenceId` = 15) AND (`SourceGroup` = 6094) AND (`SourceEntry` = 0) AND (`SourceId` = 0) AND (`ElseGroup` = 0) AND (`ConditionTypeOrReference` = 25) AND (`ConditionTarget` = 0) AND (`ConditionValue1` = 20219) AND (`ConditionValue2` = 0) AND (`ConditionValue3` = 0);
INSERT INTO `conditions` (`SourceTypeOrReferenceId`, `SourceGroup`, `SourceEntry`, `SourceId`, `ElseGroup`, `ConditionTypeOrReference`, `ConditionTarget`, `ConditionValue1`, `ConditionValue2`, `ConditionValue3`, `NegativeCondition`, `ErrorType`, `ErrorTextId`, `ScriptName`, `Comment`) VALUES
(15, 6094, 0, 0, 0, 25, 0, 20219, 0, 0, 0, 0, 0, '', 'Show option to learn Ultrasafe Transporter: Gadgetzan if player has Gnomish Engineer');
-- Add condition to show gossip option if player does NOT have schematic
DELETE FROM `conditions` WHERE (`SourceTypeOrReferenceId` = 15) AND (`SourceGroup` = 6094) AND (`SourceEntry` = 0) AND (`SourceId` = 0) AND (`ElseGroup` = 0) AND (`ConditionTypeOrReference` = 25) AND (`ConditionTarget` = 0) AND (`ConditionValue1` = 23489) AND (`ConditionValue2` = 0) AND (`ConditionValue3` = 0);
INSERT INTO `conditions` (`SourceTypeOrReferenceId`, `SourceGroup`, `SourceEntry`, `SourceId`, `ElseGroup`, `ConditionTypeOrReference`, `ConditionTarget`, `ConditionValue1`, `ConditionValue2`, `ConditionValue3`, `NegativeCondition`, `ErrorType`, `ErrorTextId`, `ScriptName`, `Comment`) VALUES
(15, 6094, 0, 0, 0, 25, 0, 23489, 0, 0, 1, 0, 0, '', 'Show option to learn Ultrasafe Transporter: Gadgetzan if player does NOT already have schematic');
-- Add condition to show alt gossip text if player has schematic
DELETE FROM `conditions` WHERE (`SourceTypeOrReferenceId` = 14) AND (`SourceGroup` = 6094) AND (`SourceEntry` = 7253) AND (`SourceId` = 0) AND (`ElseGroup` = 0) AND (`ConditionTypeOrReference` = 25) AND (`ConditionTarget` = 0) AND (`ConditionValue1` = 23489) AND (`ConditionValue2` = 0) AND (`ConditionValue3` = 0);
INSERT INTO `conditions` (`SourceTypeOrReferenceId`, `SourceGroup`, `SourceEntry`, `SourceId`, `ElseGroup`, `ConditionTypeOrReference`, `ConditionTarget`, `ConditionValue1`, `ConditionValue2`, `ConditionValue3`, `NegativeCondition`, `ErrorType`, `ErrorTextId`, `ScriptName`, `Comment`) VALUES
(14, 6094, 7253, 0, 0, 25, 0, 23489, 0, 0, 0, 0, 0, '', 'Show learned schematic gossip text if player already has Ultrasafe Transporter: Gadgetzan schematic');
-- Add condition to show base gossip text if player does NOT have schematic
DELETE FROM `conditions` WHERE (`SourceTypeOrReferenceId` = 14) AND (`SourceGroup` = 6094) AND (`SourceEntry` = 7251) AND (`SourceId` = 0) AND (`ElseGroup` = 0) AND (`ConditionTypeOrReference` = 25) AND (`ConditionTarget` = 0) AND (`ConditionValue1` = 23489) AND (`ConditionValue2` = 0) AND (`ConditionValue3` = 0);
INSERT INTO `conditions` (`SourceTypeOrReferenceId`, `SourceGroup`, `SourceEntry`, `SourceId`, `ElseGroup`, `ConditionTypeOrReference`, `ConditionTarget`, `ConditionValue1`, `ConditionValue2`, `ConditionValue3`, `NegativeCondition`, `ErrorType`, `ErrorTextId`, `ScriptName`, `Comment`) VALUES
(14, 6094, 7251, 0, 0, 25, 0, 23489, 0, 0, 1, 0, 0, '', 'Show base gossip text if player does not have Ultrasafe Transporter: Gadgetzan schematic');
-- Update Jhordy to use SMART-AI and FORCE-GOSSIP
UPDATE `creature_template` SET `type_flags` = 134217728, `AIName` = 'SmartAI', `ScriptName` = '' WHERE (`entry` = 14743) AND (`name` = 'Jhordy Lapforge');
-- Add Smart-AI to learn spell Ultrasafe Transporter: Gadgetzan to invoker(player)
DELETE FROM `smart_scripts` WHERE (`entryorguid` = 14743) AND (`source_type` = 0) AND (`id` IN (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`, `event_param6`, `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
(14743, 0, 0, 0, 62, 0, 100, 0, 6094, 0, 0, 0, 0, 0, 134, 23491, 2, 0, 1, 0, 0, 7, 0, 0, 0, 0, 0, 0, 0, 0, 'Jhordy Lapforge - On Gossip Option 0 Selected - Invoker Cast \'Ultrasafe Transporter: Gadgetzan\'');
@@ -0,0 +1,8 @@
-- DB update 2026_06_13_03 -> 2026_06_13_04
UPDATE `smart_scripts` SET
`event_param1` = 10000, -- InitialMin: 10s
`event_param2` = 20000, -- InitialMax: 20s
`event_param3` = 10000, -- RepeatMin: 10s
`event_param4` = 45000, -- RepeatMax: 45s
`comment` = 'Surveyor Candress - Combat - Cast Fireball (Range 0-40, 10-45s cd)'
WHERE `entryorguid` = 16522 AND `id` = 5 AND `source_type` = 0;
@@ -0,0 +1,3 @@
-- DB update 2026_06_13_04 -> 2026_06_13_05
--
UPDATE `spell_cone` SET `ConeDegrees`=82 WHERE `ID` IN (55696, 55697, 50155, 15847, 23364, 25653, 50155);
@@ -0,0 +1,59 @@
-- DB update 2026_06_13_05 -> 2026_06_14_00
-- Add SAI (High General Abbendis)
UPDATE `creature_template` SET `AIName` = 'SmartAI' WHERE `entry` = 28548;
DELETE FROM `smart_scripts` WHERE (`source_type` = 0 AND `entryorguid` = 28548);
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`, `event_param6`, `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
(28548, 0, 0, 0, 25, 0, 100, 0, 0, 0, 0, 0, 0, 0, 22, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'High General Abbendis - On Reset - Set Event Phase 1'),
(28548, 0, 1, 0, 1, 1, 100, 0, 15000, 35000, 15000, 35000, 0, 0, 88, 2854800, 2854803, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'High General Abbendis - OOC - Call Random Actionlist (Phase 1)');
-- Set Action Lists
DELETE FROM `smart_scripts` WHERE (`source_type` = 9) AND (`entryorguid` IN (2854800, 2854801, 2854802, 2854803));
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`, `event_param6`, `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
(2854800, 9, 0, 0, 0, 0, 100, 0, 0, 0, 0, 0, 0, 0, 22, 2, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'High General Abbendis - On Script - Set Event Phase 2'),
(2854800, 9, 1, 0, 0, 0, 100, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 10, 129476, 28660, 0, 0, 0, 0, 0, 0, 'High General Abbendis - On Script - Say Line 0 (Citizen of Havenshire)'),
(2854800, 9, 2, 0, 0, 0, 100, 0, 6000, 6000, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'High General Abbendis - On Script - Say Line 0'),
(2854800, 9, 3, 0, 0, 0, 100, 0, 3000, 3000, 0, 0, 0, 0, 5, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'High General Abbendis - On Script - Play Emote \'Talk\''),
(2854800, 9, 4, 0, 0, 0, 100, 0, 5000, 5000, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 10, 129476, 28660, 0, 0, 0, 0, 0, 0, 'High General Abbendis - On Script - Say Line 1 (Citizen of Havenshire)'),
(2854800, 9, 5, 0, 0, 0, 100, 0, 3000, 3000, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 19, 28558, 20, 0, 0, 0, 0, 0, 0, 'High General Abbendis - On Script - Say Line 0 (High Abbot Landgren)'),
(2854800, 9, 6, 0, 0, 0, 100, 0, 0, 0, 0, 0, 0, 0, 22, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'High General Abbendis - On Script - Set Event Phase 1'),
(2854800, 9, 7, 0, 0, 0, 100, 0, 2000, 2000, 0, 0, 0, 0, 5, 6, 0, 0, 0, 0, 0, 19, 28558, 20, 0, 0, 0, 0, 0, 0, 'High General Abbendis - On Script - Play Emote \'Question\' (High Abbot Landgren)'),
(2854801, 9, 0, 0, 0, 0, 100, 0, 0, 0, 0, 0, 0, 0, 22, 2, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'High General Abbendis - On Script - Set Event Phase 2'),
(2854801, 9, 1, 0, 0, 0, 100, 0, 0, 0, 0, 0, 0, 0, 1, 2, 0, 0, 0, 0, 0, 10, 129478, 28660, 0, 0, 0, 0, 0, 0, 'High General Abbendis - On Script - Say Line 2 (Citizen of Havenshire)'),
(2854801, 9, 2, 0, 0, 0, 100, 0, 5000, 5000, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 10, 129489, 28662, 0, 0, 0, 0, 0, 0, 'High General Abbendis - On Script - Say Line 0 (Citizen of Havenshire)'),
(2854801, 9, 3, 0, 0, 0, 100, 0, 6000, 6000, 0, 0, 0, 0, 1, 3, 0, 0, 0, 0, 0, 10, 129474, 28660, 0, 0, 0, 0, 0, 0, 'High General Abbendis - On Script - Say Line 3 (Citizen of Havenshire)'),
(2854801, 9, 4, 0, 0, 0, 100, 0, 5000, 5000, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 10, 129490, 28662, 0, 0, 0, 0, 0, 0, 'High General Abbendis - On Script - Say Line 1 (Citizen of Havenshire)'),
(2854801, 9, 5, 0, 0, 0, 100, 0, 3000, 3000, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'High General Abbendis - On Script - Say Line 1'),
(2854801, 9, 6, 0, 0, 0, 100, 0, 0, 0, 0, 0, 0, 0, 22, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'High General Abbendis - On Script - Set Event Phase 1'),
(2854801, 9, 7, 0, 0, 0, 100, 0, 3000, 3000, 0, 0, 0, 0, 5, 6, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'High General Abbendis - On Script - Play Emote \'Question\''),
(2854801, 9, 8, 0, 0, 0, 100, 0, 2000, 2000, 0, 0, 0, 0, 5, 5, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'High General Abbendis - On Script - Play Emote \'Point\''),
(2854802, 9, 0, 0, 0, 0, 100, 0, 0, 0, 0, 0, 0, 0, 22, 2, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'High General Abbendis - On Script - Set Event Phase 2'),
(2854802, 9, 1, 0, 0, 0, 100, 0, 0, 0, 0, 0, 0, 0, 1, 4, 0, 0, 0, 0, 0, 10, 129475, 28660, 0, 0, 0, 0, 0, 0, 'High General Abbendis - On Script - Say Line 4 (Citizen of Havenshire)'),
(2854802, 9, 2, 0, 0, 0, 100, 0, 6000, 6000, 0, 0, 0, 0, 1, 2, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'High General Abbendis - On Script - Say Line 2'),
(2854802, 9, 3, 0, 0, 0, 100, 0, 0, 0, 0, 0, 0, 0, 22, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'High General Abbendis - On Script - Set Event Phase 1'),
(2854802, 9, 4, 0, 0, 0, 100, 0, 3000, 3000, 0, 0, 0, 0, 5, 6, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'High General Abbendis - On Script - Play Emote \'Question\''),
(2854803, 9, 0, 0, 0, 0, 100, 0, 0, 0, 0, 0, 0, 0, 22, 2, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'High General Abbendis - On Script - Set Event Phase 2'),
(2854803, 9, 1, 0, 0, 0, 100, 0, 0, 0, 0, 0, 0, 0, 1, 2, 0, 0, 0, 0, 0, 10, 129487, 28662, 0, 0, 0, 0, 0, 0, 'High General Abbendis - On Script - Say Line 2 (Citizen of Havenshire)'),
(2854803, 9, 2, 0, 0, 0, 100, 0, 5000, 5000, 0, 0, 0, 0, 1, 3, 0, 0, 0, 0, 0, 10, 129483, 28662, 0, 0, 0, 0, 0, 0, 'High General Abbendis - On Script - Say Line 3 (Citizen of Havenshire)'),
(2854803, 9, 3, 0, 0, 0, 100, 0, 6000, 6000, 0, 0, 0, 0, 1, 3, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'High General Abbendis - On Script - Say Line 3'),
(2854803, 9, 4, 0, 0, 0, 100, 0, 0, 0, 0, 0, 0, 0, 22, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'High General Abbendis - On Script - Set Event Phase 1'),
(2854803, 9, 5, 0, 0, 0, 100, 0, 2000, 2000, 0, 0, 0, 0, 5, 15, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'High General Abbendis - On Script - Play Emote \'Roar\'');
-- Add Creature Texts
DELETE FROM `creature_text` WHERE `CreatureID` IN (28548, 28558, 28660, 28662);
INSERT INTO `creature_text` (`CreatureID`, `GroupID`, `ID`, `Text`, `Type`, `Language`, `Probability`, `Emote`, `Duration`, `Sound`, `BroadcastTextId`, `TextRange`, `comment`) VALUES
(28548, 0, 0, "The pure of heart will be allowed to remain in New Avalon. Those that the inquisitors find bereft of the holy Light will be turned away.", 12, 0, 100, 1, 0, 0, 28580, 0, "High General Abbendis"),
(28548, 1, 0, "SILENCE! The Light has not abandoned us! The Light is stronger than ever among the pure!", 12, 0, 100, 5, 0, 0, 28575, 0, "High General Abbendis"),
(28548, 2, 0, "We fight! We push them back, just as we have always done!", 12, 0, 100, 5, 0, 0, 28568, 0, "High General Abbendis"),
(28548, 3, 0, "We are sending crusaders to reclaim what is rightfully ours! Any Scourge that stand in our way will be turned to ashes.", 12, 0, 100, 1, 0, 0, 28578, 0, "High General Abbendis"),
(28558, 0, 0, "But none of you could possibly have anything to worry about, right?", 12, 0, 100, 25, 0, 0, 28582, 0, "High Abbot Landgren"),
(28660, 0, 0, "Where do you expect us to stay? New Avalon cannot hold all of us!", 12, 0, 100, 1, 0, 0, 28579, 0, "Citizen of Havenshire"),
(28660, 1, 0, "The crowd gasps.", 16, 0, 100, 0, 0, 0, 28581, 0, "Citizen of Havenshire"),
(28660, 2, 0, "What does the Light say to you now, Abbendis!", 12, 0, 100, 25, 0, 0, 28570, 0, "Citizen of Havenshire"),
(28660, 3, 0, "Nor my husband and brothers!", 12, 0, 100, 1, 0, 0, 28572, 0, "Citizen of Havenshire"),
(28660, 4, 0, "What do we do? We've lost everything!", 12, 0, 100, 5, 0, 0, 0, 0, "Citizen of Havenshire"),
(28662, 0, 0, "I didn't see the Light step in to save my wife and children!", 12, 0, 100, 1, 0, 0, 28571, 0, "Citizen of Havenshire"),
(28662, 1, 0, "The Light has abandoned us!", 12, 0, 100, 1, 0, 0, 28574, 0, "Citizen of Havenshire"),
(28662, 2, 0, "Havenshire is lost!", 12, 0, 100, 1, 0, 0, 28576, 0, "Citizen of Havenshire"),
(28662, 3, 0, "The stables and mill are left abandoned! What will happen to our horses?", 12, 0, 100, 1, 0, 0, 28577, 0, "Citizen of Havenshire");
@@ -0,0 +1,5 @@
-- DB update 2026_06_14_00 -> 2026_06_14_01
--
-- Fix Deep Freeze: only proc trigger spell 71757 on permanently stun-immune creatures
DELETE FROM `spell_script_names` WHERE `spell_id`=71761;
INSERT INTO `spell_script_names` (`spell_id`, `ScriptName`) VALUES (71761, 'spell_mage_deep_freeze_immunity_state');
@@ -0,0 +1,3 @@
-- DB update 2026_06_14_01 -> 2026_06_14_02
-- Freya: Detonating Lasher (32918/33399) CC-able like TC/retail (was -273).
UPDATE `creature_template` SET `CreatureImmunitiesId` = 0 WHERE `entry` IN (32918, 33399);
@@ -0,0 +1,3 @@
-- DB update 2026_06_14_02 -> 2026_06_15_00
-- Auriaya (33515/34175): HARD_RESET - despawn/respawn at spawn on evade.
UPDATE `creature_template` SET `flags_extra` = `flags_extra` | 0x80000000 WHERE `entry` IN (33515, 34175);
@@ -0,0 +1,7 @@
-- DB update 2026_06_15_00 -> 2026_06_15_01
--
-- Ony 25man difficulty Tail Sweep
DELETE FROM `spell_cone` WHERE `ID` = 69286;
INSERT INTO `spell_cone` (`ID`, `ConeDegrees`) VALUES (69286, 82);
-- Ony 10man difficulty Tail Sweep
UPDATE `spell_cone` SET `ConeDegrees`=82 WHERE `ID`=68867;
@@ -0,0 +1,26 @@
-- DB update 2026_06_15_01 -> 2026_06_16_00
-- Create creature_multispawn table for multi-ID spawning
CREATE TABLE IF NOT EXISTS `creature_multispawn` (
`spawnId` int unsigned NOT NULL COMMENT 'creature.guid',
`entry` int unsigned NOT NULL COMMENT 'creature_template.entry',
PRIMARY KEY (`spawnId`, `entry`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='Additional creature entries for multi-ID spawning';
-- Migrate id2 and id3 entries
DELETE FROM `creature_multispawn`;
INSERT IGNORE INTO `creature_multispawn` (`spawnId`, `entry`)
SELECT `guid`, `id2` FROM `creature` WHERE `id2` != 0
UNION ALL
SELECT `guid`, `id3` FROM `creature` WHERE `id3` != 0;
-- Drop old index before column rename
ALTER TABLE `creature` DROP INDEX `idx_id`;
-- Rename id1 -> id
ALTER TABLE `creature` CHANGE COLUMN `id1` `id` int unsigned NOT NULL DEFAULT 0 COMMENT 'Creature Identifier';
-- Drop id2, id3
ALTER TABLE `creature` DROP COLUMN `id2`, DROP COLUMN `id3`;
-- Recreate index on renamed column
ALTER TABLE `creature` ADD INDEX `idx_id` (`id`);
@@ -0,0 +1,12 @@
-- DB update 2026_06_16_00 -> 2026_06_16_01
-- Troll Patrol dailies 12563 and 12587 were missing the spells that gate the
-- "Congratulations!" (12604) follow-up. 12604 requires both On Patrol (51573)
-- and On Patrol Heartbeat Script (53707); the time limit is enforced by 51573's
-- 20-minute duration. 12501 already grants both and works, so match it:
-- 51573 on accept (SourceSpellID), 53707 on turn-in (RewardSpell).
-- On Patrol (51573) on accept of Troll Patrol 12563
UPDATE `quest_template_addon` SET `SourceSpellID` = 51573 WHERE `ID` = 12563;
-- On Patrol Heartbeat Script (53707) on turn-in of Troll Patrol 12563 and 12587
UPDATE `quest_template` SET `RewardSpell` = 53707 WHERE `ID` IN (12563, 12587);
@@ -0,0 +1,33 @@
-- DB update 2026_06_16_01 -> 2026_06_16_02
-- Fix for issue #23784: Rhydian should spawn portal to Shattrath when quest 13081 "The Will of the Naaru" is accepted
-- Enable SmartAI for Rhydian and Tirion
UPDATE `creature_template` SET `AIName` = 'SmartAI' WHERE `entry` IN (30656, 31044);
-- Add Rhydian's text
DELETE FROM `creature_text` WHERE `CreatureID` = 30656 AND `GroupID` = 0;
INSERT INTO `creature_text` (`CreatureID`, `GroupID`, `ID`, `Text`, `Type`, `Language`, `Probability`, `Emote`, `Duration`, `Sound`, `BroadcastTextId`, `TextRange`, `comment`) VALUES
(30656, 0, 0, 'Hail. I could not help but overhear your conversation. Please allow me to lend some assistance.', 12, 0, 100, 0, 0, 0, 31380, 0, 'Rhydian - Quest 13081');
-- Tirion: On quest 13081 accepted, SetData on Rhydian
DELETE FROM `smart_scripts` WHERE `entryorguid` = 31044 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`, `event_param6`, `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
(31044, 0, 0, 0, 19, 0, 100, 0, 13081, 0, 0, 0, 0, 0, 223, 1, 0, 0, 0, 0, 0, 19, 30656, 50, 0, 0, 0, 0, 0, 0, 'Highlord Tirion Fordring - On Quest 13081 Accepted - Do Action 1 on Rhydian');
-- Rhydian: On DataSet, run first action list (walk to point)
-- On MovementInform PointID 1, run second action list (say line, cast portal, walk back)
DELETE FROM `smart_scripts` WHERE `entryorguid` = 30656 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`, `event_param6`, `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
(30656, 0, 0, 0, 72, 0, 100, 0, 1, 0, 0, 0, 0, 0, 80, 3065600, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Rhydian - On Do Action 1 - Run Timed Action List'),
(30656, 0, 1, 0, 34, 0, 100, 0, 8, 1, 0, 0, 0, 0, 80, 3065601, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Rhydian - On MovementInform Point 1 - Run Timed Action List');
-- Rhydian action list 1: set walk and move to point
DELETE FROM `smart_scripts` WHERE `entryorguid` IN (3065600, 3065601) AND `source_type` = 9;
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`, `event_param6`, `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
(3065600, 9, 0, 0, 0, 0, 100, 0, 0, 0, 0, 0, 0, 0, 59, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Rhydian - Timed - Set Walk'),
(3065600, 9, 1, 0, 0, 0, 100, 0, 0, 0, 0, 0, 0, 0, 69, 1, 0, 0, 0, 0, 0, 8, 0, 0, 0, 0, 6417.89, 431.21, 511.33, 0, 'Rhydian - Timed - Move To Point'),
-- Rhydian action list 2: say line, cast portal, walk back
(3065601, 9, 0, 0, 0, 0, 100, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Rhydian - Timed - Say Text'),
(3065601, 9, 1, 0, 0, 0, 100, 0, 5000, 5000, 0, 0, 0, 0, 11, 57676, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Rhydian - Timed - Cast Portal to Shattrath'),
(3065601, 9, 2, 0, 0, 0, 100, 0, 18000, 18000, 0, 0, 0, 0, 69, 2, 0, 0, 0, 0, 0, 8, 0, 0, 0, 0, 6409.12, 422.382, 511.348, 0.628319, 'Rhydian - Timed - Walk Back To Home'),
(3065601, 9, 3, 0, 0, 0, 100, 0, 5000, 5000, 0, 0, 0, 0, 59, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Rhydian - Timed - Set Run');
@@ -0,0 +1,3 @@
-- DB update 2026_06_16_02 -> 2026_06_16_03
-- Remove creature_multispawn rows orphaned by deleted creature spawns
DELETE FROM `creature_multispawn` WHERE `spawnId` NOT IN (SELECT `guid` FROM `creature`);
@@ -0,0 +1,2 @@
-- DB update 2026_06_16_03 -> 2026_06_16_04
UPDATE `quest_poi_points` SET `X` = -6, `Y` = -927 WHERE `QuestID` = 7321 AND `Idx1` = 4 AND `Idx2` = 0;
@@ -0,0 +1,3 @@
-- DB update 2026_06_16_04 -> 2026_06_16_05
-- Image of Loken
UPDATE `creature_template` SET `unit_flags` = `unit_flags` | 256 | 512 | 33554432 WHERE (`entry` = 27212);
@@ -0,0 +1,5 @@
-- DB update 2026_06_16_05 -> 2026_06_16_06
-- Dust Cloud (54404) - remove on the affected unit's first missed melee swing
DELETE FROM `spell_proc` WHERE `SpellId` = 54404;
INSERT INTO `spell_proc` (`SpellId`, `ProcFlags`, `HitMask`, `Chance`, `Charges`)
VALUES (54404, 0x00000004, 0x00000004, 100, 1);
@@ -0,0 +1,29 @@
-- DB update 2026_06_16_06 -> 2026_06_16_07
--
UPDATE `spell_dbc` SET `RangeIndex`= 12, `ProcChance` = 101, `Effect_1` = 140, `ImplicitTargetA_1` = 25, `EffectTriggerSpell_1` = 47681 WHERE `Id` = 47680;
DELETE FROM `conditions` WHERE `SourceTypeOrReferenceId` = 13 AND `SourceEntry` = 47681;
INSERT INTO `conditions` (`SourceTypeOrReferenceId`,`SourceGroup`,`SourceEntry`,`SourceId`,`ElseGroup`,`ConditionTypeOrReference`,`ConditionTarget`,`ConditionValue1`,`ConditionValue2`,`ConditionValue3`,`NegativeCondition`,`ErrorType`,`ErrorTextId`,`ScriptName`,`Comment`) VALUES
(13,1,47681,0,0,31,0,3,26811,0,0,0,0,'','Group 0: Spell \'Aggro Ancient Drakkari\' (Effect 0) targets creature \'Ancient Drakkari Warmonger\''),
(13,1,47681,0,1,31,0,3,26812,0,0,0,0,'','Group 1: Spell \'Aggro Ancient Drakkari\' (Effect 0) targets creature \'Ancient Drakkari Soothsayer\'');
DELETE FROM `creature_text` WHERE `CreatureID` IN (26811,26812) AND `GroupID` = 1;
INSERT INTO `creature_text` (`CreatureID`, `GroupID`, `ID`, `Text`, `Type`, `Language`, `Probability`, `Emote`, `Duration`, `Sound`, `BroadcastTextId`, `TextRange`, `comment`) VALUES
(26811,1,0,'You take my heart, now I take yours!',12,0,100,0,0,0,26085,0,'Ancient Drakkari Warmonger'),
(26811,1,1,'Why ya wanna mess wit me innards, mon?',12,0,100,0,0,0,26086,0,'Ancient Drakkari Warmonger'),
(26811,1,2,'Dat me liver you be squeezin, mon!',12,0,100,0,0,0,26087,0,'Ancient Drakkari Warmonger'),
(26811,1,3,'Come back here with me guts, $r!',12,0,100,0,0,0,26088,0,'Ancient Drakkari Warmonger'),
(26812,1,0,'You take my heart, now I take yours!',12,0,100,0,0,0,26085,0,'Ancient Drakkari Soothsayer'),
(26812,1,1,'Why ya wanna mess wit me innards, mon?',12,0,100,0,0,0,26086,0,'Ancient Drakkari Soothsayer'),
(26812,1,2,'Dat me liver you be squeezin, mon!',12,0,100,0,0,0,26087,0,'Ancient Drakkari Soothsayer'),
(26812,1,3,'Come back here with me guts, $r!',12,0,100,0,0,0,26088,0,'Ancient Drakkari Soothsayer');
DELETE FROM `smart_scripts` WHERE `entryorguid` = 26811 AND `source_type` = 0 AND `id` IN (12,13);
DELETE FROM `smart_scripts` WHERE `entryorguid` = 26812 AND `source_type` = 0 AND `id` IN (21,22);
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
(26811,0,12,0,8,0,100,0,47681,0,0,0,0,1,1,0,0,0,0,0,7,0,0,0,0,0,0,0,0,'Ancient Drakkari Warmonger - On Spellhit \'Aggro Ancient Drakkari\' - Say Line 1'),
(26811,0,13,0,8,0,100,0,47681,0,0,0,0,49,0,0,0,0,0,0,7,0,0,0,0,0,0,0,0,'Ancient Drakkari Warmonger - On Spellhit \'Aggro Ancient Drakkari\' - Start Attacking'),
(26812,0,21,0,8,0,100,0,47681,0,0,0,0,1,1,0,0,0,0,0,7,0,0,0,0,0,0,0,0,'Ancient Drakkari Soothsayer - On Spellhit \'Aggro Ancient Drakkari\' - Say Line 1'),
(26812,0,22,0,8,0,100,0,47681,0,0,0,0,49,0,0,0,0,0,0,7,0,0,0,0,0,0,0,0,'Ancient Drakkari Soothsayer - On Spellhit \'Aggro Ancient Drakkari\' - Start Attacking');
@@ -0,0 +1,7 @@
-- DB update 2026_06_16_07 -> 2026_06_17_00
-- Garwal
UPDATE `creature_template` SET `faction` = 1971 WHERE (`entry` = 24277);
-- 43062 - Garwal's Invisibility
UPDATE `spell_dbc` SET `Effect_1` = 6, `EffectBasePoints_1` = 999, `EffectAura_1` = 18, `EffectMiscValue_1` = 8, `ImplicitTargetA_1` = 1
WHERE (`ID` = 43062);
@@ -0,0 +1,9 @@
-- DB update 2026_06_17_00 -> 2026_06_18_00
--
-- Ignis the Furnace Master: bind the Brittle aura proc script (spell_ignis_brittle_aura)
-- to both raid difficulty variants so Iron Constructs shatter at the correct,
-- tooltip-accurate damage threshold (62382 = 10m/5000, 67114 = 25m/3000).
DELETE FROM `spell_script_names` WHERE `spell_id` IN (62382, 67114);
INSERT INTO `spell_script_names` (`spell_id`, `ScriptName`) VALUES
(62382, 'spell_ignis_brittle_aura'),
(67114, 'spell_ignis_brittle_aura');
@@ -0,0 +1,13 @@
-- DB update 2026_06_18_00 -> 2026_06_18_01
-- Ulduar: Flame Leviathan - give the Salvaged Chopper its "Grab Pyrite" hook ability (spell 67372 -> 67387).
-- Mirrors the Salvaged Demolisher's "Grab Crate" (62479 -> 62482).
-- Add "Grab Pyrite" (67372) to the Salvaged Chopper (33062) vehicle action bar.
DELETE FROM `creature_template_spell` WHERE `CreatureID`=33062 AND `Index`=4;
INSERT INTO `creature_template_spell` (`CreatureID`, `Index`, `Spell`, `VerifiedBuild`) VALUES
(33062, 4, 67372, 0);
-- Bind the chopper's "Grab Crate" (67387, triggered by 67372) to the existing grab-pyrite script.
DELETE FROM `spell_script_names` WHERE `spell_id`=67387 AND `ScriptName`='spell_vehicle_grab_pyrite';
INSERT INTO `spell_script_names` (`spell_id`, `ScriptName`) VALUES
(67387, 'spell_vehicle_grab_pyrite');
@@ -0,0 +1,10 @@
-- DB update 2026_06_18_01 -> 2026_06_18_02
-- DB update 2026_06_11_02 -> 2026_06_15_00
--
-- Fix Brann Bronzebeard (33235) SAI condition for Assembly of Iron completion check.
-- ConditionValue3=0 (INSTANCE_INFO_DATA) calls GetData() which has no handler for
-- BOSS_ASSEMBLY_OF_IRON (4) in Ulduar, always returning 0. The correct type is
-- ConditionValue3=2 (INSTANCE_INFO_BOSS_STATE) which calls GetBossState(4) == DONE(3).
DELETE FROM `conditions` WHERE `SourceTypeOrReferenceId` = 22 AND `SourceGroup` = 13 AND `SourceEntry` = 33235;
INSERT INTO `conditions` (`SourceTypeOrReferenceId`, `SourceGroup`, `SourceEntry`, `SourceId`, `ElseGroup`, `ConditionTypeOrReference`, `ConditionTarget`, `ConditionValue1`, `ConditionValue2`, `ConditionValue3`, `NegativeCondition`, `ErrorType`, `ErrorTextId`, `ScriptName`, `Comment`) VALUES
(22, 13, 33235, 0, 0, 13, 1, 4, 3, 2, 0, 0, 0, '', 'Execute SAI only if Assembly of Iron Done');
@@ -0,0 +1,5 @@
-- DB update 2026_06_18_02 -> 2026_06_18_03
--
-- Hodir: Flash Freeze should not be Bleed-immune (let Warriors apply Rend).
-- Move to preset -361 (identical to -369 but without the BLEED mechanic).
UPDATE `creature_template` SET `CreatureImmunitiesId` = -361 WHERE `entry` IN (32926, 32938, 33352, 33353);
+1 -1
View File
@@ -67,7 +67,7 @@ enum AccountFlag
ACCOUNT_FLAG_GM = 0x1, // Account is GM ACCOUNT_FLAG_GM = 0x1, // Account is GM
ACCOUNT_FLAG_NOKICK = 0x2, // NYI UNK ACCOUNT_FLAG_NOKICK = 0x2, // NYI UNK
ACCOUNT_FLAG_COLLECTOR = 0x4, // NYI Collector's Edition ACCOUNT_FLAG_COLLECTOR = 0x4, // NYI Collector's Edition
ACCOUNT_FLAG_TRIAL = 0x8, // NYI Trial account ACCOUNT_FLAG_TRIAL = 0x8, // Trial account
ACCOUNT_FLAG_CANCELLED = 0x10, // NYI UNK ACCOUNT_FLAG_CANCELLED = 0x10, // NYI UNK
ACCOUNT_FLAG_IGR = 0x20, // NYI Internet Game Room (Internet cafe?) ACCOUNT_FLAG_IGR = 0x20, // NYI Internet Game Room (Internet cafe?)
ACCOUNT_FLAG_WHOLESALER = 0x40, // NYI UNK ACCOUNT_FLAG_WHOLESALER = 0x40, // NYI UNK
File diff suppressed because it is too large Load Diff
@@ -76,19 +76,20 @@ void WorldDatabaseConnection::DoPrepareStatements()
PrepareStatement(WORLD_UPD_WAYPOINT_SCRIPT_O, "UPDATE waypoint_scripts SET o = ? WHERE guid = ?", CONNECTION_ASYNC); PrepareStatement(WORLD_UPD_WAYPOINT_SCRIPT_O, "UPDATE waypoint_scripts SET o = ? WHERE guid = ?", CONNECTION_ASYNC);
PrepareStatement(WORLD_SEL_WAYPOINT_SCRIPT_ID_BY_GUID, "SELECT id FROM waypoint_scripts WHERE guid = ?", CONNECTION_SYNCH); PrepareStatement(WORLD_SEL_WAYPOINT_SCRIPT_ID_BY_GUID, "SELECT id FROM waypoint_scripts WHERE guid = ?", CONNECTION_SYNCH);
PrepareStatement(WORLD_DEL_CREATURE, "DELETE FROM creature WHERE guid = ?", CONNECTION_ASYNC); PrepareStatement(WORLD_DEL_CREATURE, "DELETE FROM creature WHERE guid = ?", CONNECTION_ASYNC);
PrepareStatement(WORLD_DEL_CREATURE_MULTISPAWN, "DELETE FROM creature_multispawn WHERE spawnId = ?", CONNECTION_ASYNC);
PrepareStatement(WORLD_SEL_COMMANDS, "SELECT name, security, help FROM command", CONNECTION_SYNCH); PrepareStatement(WORLD_SEL_COMMANDS, "SELECT name, security, help FROM command", CONNECTION_SYNCH);
PrepareStatement(WORLD_SEL_CREATURE_TEMPLATE, "SELECT entry, difficulty_entry_1, difficulty_entry_2, difficulty_entry_3, KillCredit1, KillCredit2, name, subname, IconName, gossip_menu_id, minlevel, maxlevel, exp, faction, npcflag, speed_walk, speed_run, speed_swim, speed_flight, detection_range, `rank`, dmgschool, DamageModifier, BaseAttackTime, RangeAttackTime, BaseVariance, RangeVariance, unit_class, unit_flags, unit_flags2, dynamicflags, family, type, type_flags, lootid, pickpocketloot, skinloot, PetSpellDataId, VehicleId, mingold, maxgold, AIName, MovementType, ctm.Ground, ctm.Swim, ctm.Flight, ctm.Rooted, ctm.Chase, ctm.Random, ctm.InteractionPauseTimer, HoverHeight, HealthModifier, ManaModifier, ArmorModifier, ExperienceModifier, RacialLeader, movementId, RegenHealth, CreatureImmunitiesId, flags_extra, ScriptName FROM creature_template ct LEFT JOIN creature_template_movement ctm ON ct.entry = ctm.CreatureId WHERE entry = ?", CONNECTION_SYNCH); PrepareStatement(WORLD_SEL_CREATURE_TEMPLATE, "SELECT entry, difficulty_entry_1, difficulty_entry_2, difficulty_entry_3, KillCredit1, KillCredit2, name, subname, IconName, gossip_menu_id, minlevel, maxlevel, exp, faction, npcflag, speed_walk, speed_run, speed_swim, speed_flight, detection_range, `rank`, dmgschool, DamageModifier, BaseAttackTime, RangeAttackTime, BaseVariance, RangeVariance, unit_class, unit_flags, unit_flags2, dynamicflags, family, type, type_flags, lootid, pickpocketloot, skinloot, PetSpellDataId, VehicleId, mingold, maxgold, AIName, MovementType, ctm.Ground, ctm.Swim, ctm.Flight, ctm.Rooted, ctm.Chase, ctm.Random, ctm.InteractionPauseTimer, HoverHeight, HealthModifier, ManaModifier, ArmorModifier, ExperienceModifier, RacialLeader, movementId, RegenHealth, CreatureImmunitiesId, flags_extra, ScriptName FROM creature_template ct LEFT JOIN creature_template_movement ctm ON ct.entry = ctm.CreatureId WHERE entry = ?", CONNECTION_SYNCH);
PrepareStatement(WORLD_SEL_WAYPOINT_SCRIPT_BY_ID, "SELECT guid, delay, command, datalong, datalong2, dataint, x, y, z, o FROM waypoint_scripts WHERE id = ?", CONNECTION_SYNCH); PrepareStatement(WORLD_SEL_WAYPOINT_SCRIPT_BY_ID, "SELECT guid, delay, command, datalong, datalong2, dataint, x, y, z, o FROM waypoint_scripts WHERE id = ?", CONNECTION_SYNCH);
PrepareStatement(WORLD_SEL_ITEM_TEMPLATE_BY_NAME, "SELECT entry FROM item_template WHERE name = ?", CONNECTION_SYNCH); PrepareStatement(WORLD_SEL_ITEM_TEMPLATE_BY_NAME, "SELECT entry FROM item_template WHERE name = ?", CONNECTION_SYNCH);
PrepareStatement(WORLD_SEL_CREATURE_BY_ID, "SELECT guid FROM creature WHERE id1 = ? OR id2 = ? OR id3 = ?", CONNECTION_SYNCH); PrepareStatement(WORLD_SEL_CREATURE_BY_ID, "SELECT guid FROM creature WHERE id = ? UNION SELECT spawnId AS guid FROM creature_multispawn WHERE entry = ?", CONNECTION_SYNCH);
PrepareStatement(WORLD_SEL_GAMEOBJECT_NEAREST, "SELECT guid, id, position_x, position_y, position_z, map, (POW(position_x - ?, 2) + POW(position_y - ?, 2) + POW(position_z - ?, 2)) AS order_ FROM gameobject WHERE map = ? AND (POW(position_x - ?, 2) + POW(position_y - ?, 2) + POW(position_z - ?, 2)) <= ? AND (phaseMask & ?) <> 0 ORDER BY order_", CONNECTION_SYNCH); PrepareStatement(WORLD_SEL_GAMEOBJECT_NEAREST, "SELECT guid, id, position_x, position_y, position_z, map, (POW(position_x - ?, 2) + POW(position_y - ?, 2) + POW(position_z - ?, 2)) AS order_ FROM gameobject WHERE map = ? AND (POW(position_x - ?, 2) + POW(position_y - ?, 2) + POW(position_z - ?, 2)) <= ? AND (phaseMask & ?) <> 0 ORDER BY order_", CONNECTION_SYNCH);
PrepareStatement(WORLD_SEL_CREATURE_NEAREST, "SELECT guid, id1, id2, id3, position_x, position_y, position_z, map, (POW(position_x - ?, 2) + POW(position_y - ?, 2) + POW(position_z - ?, 2)) AS order_ FROM creature WHERE map = ? AND (POW(position_x - ?, 2) + POW(position_y - ?, 2) + POW(position_z - ?, 2)) <= ? AND (phaseMask & ?) <> 0 ORDER BY order_", CONNECTION_SYNCH); PrepareStatement(WORLD_SEL_CREATURE_NEAREST, "SELECT guid, id, position_x, position_y, position_z, map, (POW(position_x - ?, 2) + POW(position_y - ?, 2) + POW(position_z - ?, 2)) AS order_ FROM creature WHERE map = ? AND (POW(position_x - ?, 2) + POW(position_y - ?, 2) + POW(position_z - ?, 2)) <= ? AND (phaseMask & ?) <> 0 ORDER BY order_", CONNECTION_SYNCH);
PrepareStatement(WORLD_INS_CREATURE, "INSERT INTO creature (guid, id1, id2, id3, map, spawnMask, phaseMask, equipment_id, position_x, position_y, position_z, orientation, spawntimesecs, wander_distance, currentwaypoint, curhealth, curmana, MovementType, npcflag, unit_flags, dynamicflags) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)", CONNECTION_ASYNC); PrepareStatement(WORLD_INS_CREATURE, "INSERT INTO creature (guid, id, map, spawnMask, phaseMask, equipment_id, position_x, position_y, position_z, orientation, spawntimesecs, wander_distance, currentwaypoint, curhealth, curmana, MovementType, npcflag, unit_flags, dynamicflags) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)", CONNECTION_ASYNC);
PrepareStatement(WORLD_SEL_GAME_EVENTS, "SELECT eventEntry, UNIX_TIMESTAMP(start_time), UNIX_TIMESTAMP(end_time), occurence, length, holiday, holidayStage, description, world_event, announce FROM game_event", CONNECTION_SYNCH); PrepareStatement(WORLD_SEL_GAME_EVENTS, "SELECT eventEntry, UNIX_TIMESTAMP(start_time), UNIX_TIMESTAMP(end_time), occurence, length, holiday, holidayStage, description, world_event, announce FROM game_event", CONNECTION_SYNCH);
PrepareStatement(WORLD_SEL_GAME_EVENT_PREREQUISITE_DATA, "SELECT eventEntry, prerequisite_event FROM game_event_prerequisite", CONNECTION_SYNCH); PrepareStatement(WORLD_SEL_GAME_EVENT_PREREQUISITE_DATA, "SELECT eventEntry, prerequisite_event FROM game_event_prerequisite", CONNECTION_SYNCH);
PrepareStatement(WORLD_SEL_GAME_EVENT_CREATURE_DATA, "SELECT guid, eventEntry FROM game_event_creature", CONNECTION_SYNCH); PrepareStatement(WORLD_SEL_GAME_EVENT_CREATURE_DATA, "SELECT guid, eventEntry FROM game_event_creature", CONNECTION_SYNCH);
PrepareStatement(WORLD_SEL_GAME_EVENT_GAMEOBJECT_DATA, "SELECT guid, eventEntry FROM game_event_gameobject", CONNECTION_SYNCH); PrepareStatement(WORLD_SEL_GAME_EVENT_GAMEOBJECT_DATA, "SELECT guid, eventEntry FROM game_event_gameobject", CONNECTION_SYNCH);
PrepareStatement(WORLD_SEL_GAME_EVENT_MODEL_EQUIPMENT_DATA, "SELECT creature.guid, creature.id1, creature.id2, creature.id3, game_event_model_equip.eventEntry, game_event_model_equip.modelid, game_event_model_equip.equipment_id FROM creature JOIN game_event_model_equip ON creature.guid=game_event_model_equip.guid", CONNECTION_SYNCH); PrepareStatement(WORLD_SEL_GAME_EVENT_MODEL_EQUIPMENT_DATA, "SELECT creature.guid, creature.id, game_event_model_equip.eventEntry, game_event_model_equip.modelid, game_event_model_equip.equipment_id FROM creature JOIN game_event_model_equip ON creature.guid=game_event_model_equip.guid", CONNECTION_SYNCH);
PrepareStatement(WORLD_SEL_GAME_EVENT_QUEST_DATA, "SELECT id, quest, eventEntry FROM game_event_creature_quest", CONNECTION_SYNCH); PrepareStatement(WORLD_SEL_GAME_EVENT_QUEST_DATA, "SELECT id, quest, eventEntry FROM game_event_creature_quest", CONNECTION_SYNCH);
PrepareStatement(WORLD_SEL_GAME_EVENT_GAMEOBJECT_QUEST_DATA, "SELECT id, quest, eventEntry FROM game_event_gameobject_quest", CONNECTION_SYNCH); PrepareStatement(WORLD_SEL_GAME_EVENT_GAMEOBJECT_QUEST_DATA, "SELECT id, quest, eventEntry FROM game_event_gameobject_quest", CONNECTION_SYNCH);
PrepareStatement(WORLD_SEL_GAME_EVENT_QUEST_CONDITION_DATA, "SELECT quest, eventEntry, condition_id, num FROM game_event_quest_condition", CONNECTION_SYNCH); PrepareStatement(WORLD_SEL_GAME_EVENT_QUEST_CONDITION_DATA, "SELECT quest, eventEntry, condition_id, num FROM game_event_quest_condition", CONNECTION_SYNCH);
@@ -81,6 +81,7 @@ enum WorldDatabaseStatements : uint32
WORLD_UPD_WAYPOINT_SCRIPT_O, WORLD_UPD_WAYPOINT_SCRIPT_O,
WORLD_SEL_WAYPOINT_SCRIPT_ID_BY_GUID, WORLD_SEL_WAYPOINT_SCRIPT_ID_BY_GUID,
WORLD_DEL_CREATURE, WORLD_DEL_CREATURE,
WORLD_DEL_CREATURE_MULTISPAWN,
WORLD_SEL_COMMANDS, WORLD_SEL_COMMANDS,
WORLD_SEL_CREATURE_TEMPLATE, WORLD_SEL_CREATURE_TEMPLATE,
WORLD_SEL_WAYPOINT_SCRIPT_BY_ID, WORLD_SEL_WAYPOINT_SCRIPT_BY_ID,
+3
View File
@@ -42,6 +42,7 @@ void GuardAI::EnterEvadeMode(EvadeReason /*why*/)
me->GetMotionMaster()->MoveIdle(); me->GetMotionMaster()->MoveIdle();
me->CombatStop(true); me->CombatStop(true);
me->GetThreatMgr().ClearAllThreat(); me->GetThreatMgr().ClearAllThreat();
EngagementOver();
return; return;
} }
@@ -51,6 +52,8 @@ void GuardAI::EnterEvadeMode(EvadeReason /*why*/)
me->GetThreatMgr().ClearAllThreat(); me->GetThreatMgr().ClearAllThreat();
me->CombatStop(true); me->CombatStop(true);
EngagementOver();
// Remove ChaseMovementGenerator from MotionMaster stack list, and add HomeMovementGenerator instead // Remove ChaseMovementGenerator from MotionMaster stack list, and add HomeMovementGenerator instead
if (me->GetMotionMaster()->GetCurrentMovementGeneratorType() == CHASE_MOTION_TYPE) if (me->GetMotionMaster()->GetCurrentMovementGeneratorType() == CHASE_MOTION_TYPE)
me->GetMotionMaster()->MoveTargetedHome(); me->GetMotionMaster()->MoveTargetedHome();
+6 -2
View File
@@ -292,10 +292,14 @@ SpellCastResult UnitAI::DoCastAOE(uint32 spellId, bool triggered)
/** /**
* @brief Cast the spell on a random unit from the threat list * @brief Cast the spell on a random unit from the threat list
*
* @param aura Optional aura filter forwarded to SelectTarget: a positive value
* requires the aura on the target, a negative value excludes targets
* that already have it.
*/ */
SpellCastResult UnitAI::DoCastRandomTarget(uint32 spellId, uint32 threatTablePosition, float dist, bool playerOnly, bool triggered, bool withTank) SpellCastResult UnitAI::DoCastRandomTarget(uint32 spellId, uint32 threatTablePosition, float dist, bool playerOnly, bool triggered, bool withTank, int32 aura)
{ {
if (Unit* target = SelectTarget(SelectTargetMethod::Random, threatTablePosition, dist, playerOnly, withTank)) if (Unit* target = SelectTarget(SelectTargetMethod::Random, threatTablePosition, dist, playerOnly, withTank, aura))
{ {
return DoCast(target, spellId, triggered); return DoCast(target, spellId, triggered);
} }
+2 -1
View File
@@ -395,7 +395,8 @@ public:
SpellCastResult DoCastToAllHostilePlayers(uint32 spellid, bool triggered = false); SpellCastResult DoCastToAllHostilePlayers(uint32 spellid, bool triggered = false);
SpellCastResult DoCastVictim(uint32 spellId, bool triggered = false); SpellCastResult DoCastVictim(uint32 spellId, bool triggered = false);
SpellCastResult DoCastAOE(uint32 spellId, bool triggered = false); SpellCastResult DoCastAOE(uint32 spellId, bool triggered = false);
SpellCastResult DoCastRandomTarget(uint32 spellId, uint32 threatTablePosition = 0, float dist = 0.0f, bool playerOnly = true, bool triggered = false, bool withTank = true); // aura: forwarded to SelectTarget - if 0: ignored, if > 0: the target shall have the aura, if < 0: the target shall NOT have the aura
SpellCastResult DoCastRandomTarget(uint32 spellId, uint32 threatTablePosition = 0, float dist = 0.0f, bool playerOnly = true, bool triggered = false, bool withTank = true, int32 aura = 0);
/// @brief Cast spell on the top threat target, which may not be the current victim. /// @brief Cast spell on the top threat target, which may not be the current victim.
SpellCastResult DoCastMaxThreat(uint32 spellId, uint32 threatTablePosition = 0, float dist = 0.0f, bool playerOnly = true, bool triggered = false); SpellCastResult DoCastMaxThreat(uint32 spellId, uint32 threatTablePosition = 0, float dist = 0.0f, bool playerOnly = true, bool triggered = false);
+4 -1
View File
@@ -24,6 +24,7 @@
#include "ObjectDefines.h" #include "ObjectDefines.h"
#include "ObjectMgr.h" #include "ObjectMgr.h"
#include "ScriptedCreature.h" #include "ScriptedCreature.h"
#include "ScriptMgr.h"
#include "SpellMgr.h" #include "SpellMgr.h"
#include "Vehicle.h" #include "Vehicle.h"
@@ -711,7 +712,7 @@ void SmartAI::JustExitedCombat()
CreatureAI::JustExitedCombat(); CreatureAI::JustExitedCombat();
} }
void SmartAI::EnterEvadeMode(EvadeReason /*why*/) void SmartAI::EnterEvadeMode(EvadeReason why)
{ {
if (mSuppressEvade) if (mSuppressEvade)
return; return;
@@ -763,6 +764,8 @@ void SmartAI::EnterEvadeMode(EvadeReason /*why*/)
if (!me->HasUnitState(UNIT_STATE_EVADE)) if (!me->HasUnitState(UNIT_STATE_EVADE))
GetScript()->OnReset(); GetScript()->OnReset();
} }
sScriptMgr->OnUnitEnterEvadeMode(me, why);
} }
void SmartAI::MoveInLineOfSight(Unit* who) void SmartAI::MoveInLineOfSight(Unit* who)
@@ -330,7 +330,7 @@ void SmartAIMgr::CheckIfSmartAIInDatabaseExists()
// check GUID SAI // check GUID SAI
for (auto const& pair : sObjectMgr->GetAllCreatureData()) for (auto const& pair : sObjectMgr->GetAllCreatureData())
{ {
if (pair.second.id1 != creatureTemplate.Entry) if (pair.second.id != creatureTemplate.Entry)
continue; continue;
if (mEventMap[uint32(SmartScriptType::SMART_SCRIPT_TYPE_CREATURE)].find((-1) * pair.first) != mEventMap[uint32(SmartScriptType::SMART_SCRIPT_TYPE_CREATURE)].end()) if (mEventMap[uint32(SmartScriptType::SMART_SCRIPT_TYPE_CREATURE)].find((-1) * pair.first) != mEventMap[uint32(SmartScriptType::SMART_SCRIPT_TYPE_CREATURE)].end())
@@ -2100,7 +2100,7 @@ bool SmartAIMgr::IsTextValid(SmartScriptHolder const& e, uint32 id)
return false; return false;
} }
else else
entry = data->id1; entry = data->id;
} }
else else
entry = uint32(e.entryOrGuid); entry = uint32(e.entryOrGuid);
@@ -216,9 +216,11 @@ void AuctionHouseMgr::SendAuctionSuccessfulMail(AuctionEntry* auction, Character
.AddMoney(profit) .AddMoney(profit)
.SendMailTo(trans, MailReceiver(owner, auction->owner.GetCounter()), auction, MAIL_CHECK_MASK_COPIED, sWorld->getIntConfig(CONFIG_MAIL_DELIVERY_DELAY)); .SendMailTo(trans, MailReceiver(owner, auction->owner.GetCounter()), auction, MAIL_CHECK_MASK_COPIED, sWorld->getIntConfig(CONFIG_MAIL_DELIVERY_DELAY));
LOG_INFO("entities.player.auctionhouse", "AuctionHouse: Auction #{} sold: Seller {} (GUID: {}), Buyer: {} (GUID: {}), Item (Entry: {}) x{}, Sale Price: {} copper, Profit: {} copper (cut: {} copper)", CharacterCacheEntry const* sellerCache = sCharacterCache->GetCharacterCacheByGuid(auction->owner);
auction->Id, owner ? owner->GetName() : "offline", auction->owner.GetCounter(), CharacterCacheEntry const* bidderCache = sCharacterCache->GetCharacterCacheByGuid(auction->bidder);
auction->bidder.GetCounter(), auction->item_template, auction->itemCount, LOG_INFO("entities.player.auctionhouse", "AuctionHouse: Auction #{} sold: Seller {} (AccountID: {}, GUID: {}), Buyer: {} (AccountID: {}, GUID: {}), Item (Entry: {}) x{}, Sale Price: {} copper, Profit: {} copper (cut: {} copper)",
auction->Id, sellerCache ? sellerCache->Name : (owner ? owner->GetName() : "offline"), sellerCache ? sellerCache->AccountId : owner_accId, auction->owner.GetCounter(),
bidderCache ? bidderCache->Name : "unknown", bidderCache ? bidderCache->AccountId : 0, auction->bidder.GetCounter(), auction->item_template, auction->itemCount,
auction->bid, profit, auction->GetAuctionCut()); auction->bid, profit, auction->GetAuctionCut());
if (auction->bid >= 500 * GOLD) if (auction->bid >= 500 * GOLD)
@@ -36,7 +36,6 @@ using GameObjectSet = std::set<GameObject*>;
using GameObjectBuilding = std::set<BfWGGameObjectBuilding*>; using GameObjectBuilding = std::set<BfWGGameObjectBuilding*>;
using Workshop = std::set<WGWorkshop*>; using Workshop = std::set<WGWorkshop*>;
using GroupSet = std::set<Group*>; using GroupSet = std::set<Group*>;
//using CapturePointSet = std::set<WintergraspCapturePoint*>; unused ?
uint32 const VehNumWorldState[2] = { WORLD_STATE_BATTLEFIELD_WG_VEHICLE_A, WORLD_STATE_BATTLEFIELD_WG_VEHICLE_H }; uint32 const VehNumWorldState[2] = { WORLD_STATE_BATTLEFIELD_WG_VEHICLE_A, WORLD_STATE_BATTLEFIELD_WG_VEHICLE_H };
uint32 const MaxVehNumWorldState[2] = { WORLD_STATE_BATTLEFIELD_WG_MAX_VEHICLE_A, WORLD_STATE_BATTLEFIELD_WG_MAX_VEHICLE_H }; uint32 const MaxVehNumWorldState[2] = { WORLD_STATE_BATTLEFIELD_WG_MAX_VEHICLE_A, WORLD_STATE_BATTLEFIELD_WG_MAX_VEHICLE_H };
@@ -495,8 +494,8 @@ enum WintergraspWorkshopIds
BATTLEFIELD_WG_WORKSHOP_NW, BATTLEFIELD_WG_WORKSHOP_NW,
BATTLEFIELD_WG_WORKSHOP_SE, BATTLEFIELD_WG_WORKSHOP_SE,
BATTLEFIELD_WG_WORKSHOP_SW, BATTLEFIELD_WG_WORKSHOP_SW,
BATTLEFIELD_WG_WORKSHOP_KEEP_WEST,
BATTLEFIELD_WG_WORKSHOP_KEEP_EAST, BATTLEFIELD_WG_WORKSHOP_KEEP_EAST,
BATTLEFIELD_WG_WORKSHOP_KEEP_WEST,
}; };
/// @todo: Handle this with creature_text ? /// @todo: Handle this with creature_text ?
@@ -1022,9 +1021,11 @@ WintergraspTowerCannonData const TowerCannon[WG_MAX_TOWER_CANNON] =
// Workshop data and elements // Workshop data and elements
uint8 const WG_MAX_WORKSHOP = 6; uint8 const WG_MAX_WORKSHOP = 6;
// IDs below this are the capturable workshops (NE/NW/SE/SW) with graveyards.
uint8 const WG_CAPTURE_WORKSHOP_COUNT = 4;
struct WGWorkshopData struct WGWorkshopData
{ {
uint8 id;
uint32 worldstate; uint32 worldstate;
uint8 attackText; uint8 attackText;
uint8 takenText; uint8 takenText;
@@ -1033,17 +1034,17 @@ struct WGWorkshopData
WGWorkshopData const WorkshopsData[WG_MAX_WORKSHOP] = WGWorkshopData const WorkshopsData[WG_MAX_WORKSHOP] =
{ {
// NE // NE
{BATTLEFIELD_WG_WORKSHOP_NE, WORLD_STATE_BATTLEFIELD_WG_WORKSHOP_NE, BATTLEFIELD_WG_TEXT_WORKSHOP_NE_ATTACK, BATTLEFIELD_WG_TEXT_WORKSHOP_NE_TAKEN}, {WORLD_STATE_BATTLEFIELD_WG_WORKSHOP_NE, BATTLEFIELD_WG_TEXT_WORKSHOP_NE_ATTACK, BATTLEFIELD_WG_TEXT_WORKSHOP_NE_TAKEN},
// NW // NW
{BATTLEFIELD_WG_WORKSHOP_NW, WORLD_STATE_BATTLEFIELD_WG_WORKSHOP_NW, BATTLEFIELD_WG_TEXT_WORKSHOP_NW_ATTACK, BATTLEFIELD_WG_TEXT_WORKSHOP_NW_TAKEN}, {WORLD_STATE_BATTLEFIELD_WG_WORKSHOP_NW, BATTLEFIELD_WG_TEXT_WORKSHOP_NW_ATTACK, BATTLEFIELD_WG_TEXT_WORKSHOP_NW_TAKEN},
// SE // SE
{BATTLEFIELD_WG_WORKSHOP_SE, WORLD_STATE_BATTLEFIELD_WG_WORKSHOP_SE, BATTLEFIELD_WG_TEXT_WORKSHOP_SE_ATTACK, BATTLEFIELD_WG_TEXT_WORKSHOP_SE_TAKEN}, {WORLD_STATE_BATTLEFIELD_WG_WORKSHOP_SE, BATTLEFIELD_WG_TEXT_WORKSHOP_SE_ATTACK, BATTLEFIELD_WG_TEXT_WORKSHOP_SE_TAKEN},
// SW // SW
{BATTLEFIELD_WG_WORKSHOP_SW, WORLD_STATE_BATTLEFIELD_WG_WORKSHOP_SW, BATTLEFIELD_WG_TEXT_WORKSHOP_SW_ATTACK, BATTLEFIELD_WG_TEXT_WORKSHOP_SW_TAKEN}, {WORLD_STATE_BATTLEFIELD_WG_WORKSHOP_SW, BATTLEFIELD_WG_TEXT_WORKSHOP_SW_ATTACK, BATTLEFIELD_WG_TEXT_WORKSHOP_SW_TAKEN},
// KEEP WEST - It can't be taken
{BATTLEFIELD_WG_WORKSHOP_KEEP_WEST, WORLD_STATE_BATTLEFIELD_WG_WORKSHOP_K_W, 0, BATTLEFIELD_WG_TEXT_WORKSHOP_NE_TAKEN},
// KEEP EAST - It can't be taken // KEEP EAST - It can't be taken
{BATTLEFIELD_WG_WORKSHOP_KEEP_EAST, WORLD_STATE_BATTLEFIELD_WG_WORKSHOP_K_E, 0, BATTLEFIELD_WG_TEXT_WORKSHOP_NE_TAKEN} {WORLD_STATE_BATTLEFIELD_WG_WORKSHOP_K_E, 0, BATTLEFIELD_WG_TEXT_WORKSHOP_NE_TAKEN},
// KEEP WEST - It can't be taken
{WORLD_STATE_BATTLEFIELD_WG_WORKSHOP_K_W, 0, BATTLEFIELD_WG_TEXT_WORKSHOP_NE_TAKEN}
}; };
// Structs for Building, Graveyard, and Workshop runtime objects // Structs for Building, Graveyard, and Workshop runtime objects
@@ -1185,9 +1186,7 @@ struct BfWGGameObjectBuilding
void Init(GameObject* gobj, uint32 type, uint32 worldstate, uint8 damageText, uint8 destroyText) void Init(GameObject* gobj, uint32 type, uint32 worldstate, uint8 damageText, uint8 destroyText)
{ {
if (!gobj) if (!gobj)
{
return; return;
}
// GameObject associated to object // GameObject associated to object
m_Build = gobj->GetGUID(); m_Build = gobj->GetGUID();
@@ -1219,23 +1218,20 @@ struct BfWGGameObjectBuilding
} }
m_State = sWorldState->getWorldState(m_WorldState); m_State = sWorldState->getWorldState(m_WorldState);
if (gobj) switch (m_State)
{ {
switch (m_State) case BATTLEFIELD_WG_OBJECTSTATE_ALLIANCE_INTACT:
{ case BATTLEFIELD_WG_OBJECTSTATE_HORDE_INTACT:
case BATTLEFIELD_WG_OBJECTSTATE_ALLIANCE_INTACT: gobj->SetDestructibleState(GO_DESTRUCTIBLE_REBUILDING, nullptr, true);
case BATTLEFIELD_WG_OBJECTSTATE_HORDE_INTACT: break;
gobj->SetDestructibleState(GO_DESTRUCTIBLE_REBUILDING, nullptr, true); case BATTLEFIELD_WG_OBJECTSTATE_ALLIANCE_DESTROY:
break; case BATTLEFIELD_WG_OBJECTSTATE_HORDE_DESTROY:
case BATTLEFIELD_WG_OBJECTSTATE_ALLIANCE_DESTROY: gobj->SetDestructibleState(GO_DESTRUCTIBLE_DESTROYED);
case BATTLEFIELD_WG_OBJECTSTATE_HORDE_DESTROY: break;
gobj->SetDestructibleState(GO_DESTRUCTIBLE_DESTROYED); case BATTLEFIELD_WG_OBJECTSTATE_ALLIANCE_DAMAGE:
break; case BATTLEFIELD_WG_OBJECTSTATE_HORDE_DAMAGE:
case BATTLEFIELD_WG_OBJECTSTATE_ALLIANCE_DAMAGE: gobj->SetDestructibleState(GO_DESTRUCTIBLE_DAMAGED);
case BATTLEFIELD_WG_OBJECTSTATE_HORDE_DAMAGE: break;
gobj->SetDestructibleState(GO_DESTRUCTIBLE_DAMAGED);
break;
}
} }
int32 towerid = -1; int32 towerid = -1;
@@ -1415,7 +1411,7 @@ struct WGWorkshop
WGWorkshop(BattlefieldWG* _bf, uint8 _workshopId) WGWorkshop(BattlefieldWG* _bf, uint8 _workshopId)
{ {
ASSERT(_bf || _workshopId < WG_MAX_WORKSHOP); ASSERT(_bf && _workshopId < WG_MAX_WORKSHOP);
bf = _bf; bf = _bf;
workshopId = _workshopId; workshopId = _workshopId;
@@ -1423,6 +1419,9 @@ struct WGWorkshop
state = BATTLEFIELD_WG_OBJECTSTATE_NONE; state = BATTLEFIELD_WG_OBJECTSTATE_NONE;
} }
// True for the four capturable workshops.
bool IsCapturable() const { return workshopId < WG_CAPTURE_WORKSHOP_COUNT; }
void GiveControlTo(TeamId team, bool init /* for first call in setup*/) void GiveControlTo(TeamId team, bool init /* for first call in setup*/)
{ {
switch (team) switch (team)
@@ -1438,7 +1437,7 @@ struct WGWorkshop
bf->SendUpdateWorldState(WorkshopsData[workshopId].worldstate, state); bf->SendUpdateWorldState(WorkshopsData[workshopId].worldstate, state);
// Found associate graveyard and update it // Found associate graveyard and update it
if (workshopId < BATTLEFIELD_WG_WORKSHOP_KEEP_WEST) if (IsCapturable())
if (bf->GetGraveyardById(workshopId)) if (bf->GetGraveyardById(workshopId))
bf->GetGraveyardById(workshopId)->GiveControlTo(team); bf->GetGraveyardById(workshopId)->GiveControlTo(team);
@@ -1457,7 +1456,7 @@ struct WGWorkshop
bf->SendWarning(team == TEAM_ALLIANCE ? WorkshopsData[workshopId].takenText : (WorkshopsData[workshopId].takenText + 2)); bf->SendWarning(team == TEAM_ALLIANCE ? WorkshopsData[workshopId].takenText : (WorkshopsData[workshopId].takenText + 2));
// Found associate graveyard and update it // Found associate graveyard and update it
if (workshopId < BATTLEFIELD_WG_WORKSHOP_KEEP_WEST) if (IsCapturable())
if (bf->GetGraveyardById(workshopId)) if (bf->GetGraveyardById(workshopId))
bf->GetGraveyardById(workshopId)->GiveControlTo(team); bf->GetGraveyardById(workshopId)->GiveControlTo(team);
@@ -1475,7 +1474,7 @@ struct WGWorkshop
void UpdateGraveyardAndWorkshop() void UpdateGraveyardAndWorkshop()
{ {
if (workshopId < BATTLEFIELD_WG_WORKSHOP_KEEP_WEST) if (IsCapturable())
bf->GetGraveyardById(workshopId)->GiveControlTo(TeamId(teamControl)); bf->GetGraveyardById(workshopId)->GiveControlTo(TeamId(teamControl));
else else
GiveControlTo(bf->GetDefenderTeam(), true); GiveControlTo(bf->GetDefenderTeam(), true);
@@ -310,9 +310,24 @@ void BattlegroundQueue::RemovePlayer(ObjectGuid guid, bool decreaseInvitedCount)
// if invited to bg, and should decrease invited count, then do it // if invited to bg, and should decrease invited count, then do it
if (decreaseInvitedCount && groupInfo->IsInvitedToBGInstanceGUID) if (decreaseInvitedCount && groupInfo->IsInvitedToBGInstanceGUID)
{
if (Battleground* bg = sBattlegroundMgr->GetBattleground(groupInfo->IsInvitedToBGInstanceGUID, groupInfo->BgTypeId)) if (Battleground* bg = sBattlegroundMgr->GetBattleground(groupInfo->IsInvitedToBGInstanceGUID, groupInfo->BgTypeId))
{
bg->DecreaseInvitedCount(groupInfo->teamId); bg->DecreaseInvitedCount(groupInfo->teamId);
// re-enqueue BG if free slots reopened due to invite expiration
if (bg->HasFreeSlots())
{
bg->AddToBGFreeSlotQueue();
BattlegroundQueueTypeId queueTypeId =
BattlegroundMgr::BGQueueTypeId(bg->GetBgTypeID(), bg->GetArenaType());
sBattlegroundMgr->ScheduleQueueUpdate(0, 0, queueTypeId, bg->GetBgTypeID(), bg->GetBracketId());
}
}
}
// remove player queue info // remove player queue info
m_QueuedPlayers.erase(itr); m_QueuedPlayers.erase(itr);
+2 -1
View File
@@ -59,7 +59,8 @@
// ... both units must be allowed to enter combat // ... both units must be allowed to enter combat
if (a->IsCombatDisallowed() || b->IsCombatDisallowed()) if (a->IsCombatDisallowed() || b->IsCombatDisallowed())
return false; return false;
if (a->IsFriendlyTo(b) || b->IsFriendlyTo(a)) // ...not friendly, unless one side is hostile (asymmetric aggressor wins)
if ((a->IsFriendlyTo(b) || b->IsFriendlyTo(a)) && !a->IsHostileTo(b) && !b->IsHostileTo(a))
return false; return false;
Player const* playerA = a->GetCharmerOrOwnerPlayerOrPlayerItself(); Player const* playerA = a->GetCharmerOrOwnerPlayerOrPlayerItself();
Player const* playerB = b->GetCharmerOrOwnerPlayerOrPlayerItself(); Player const* playerB = b->GetCharmerOrOwnerPlayerOrPlayerItself();
+2 -2
View File
@@ -1958,7 +1958,7 @@ bool ConditionMgr::isSourceTypeValid(Condition* cond)
return false; return false;
} }
if (data->id1 != uint32(cond->SourceEntry)) if (data->id != uint32(cond->SourceEntry))
{ {
LOG_ERROR("sql.sql", "CONDITION_SOURCE_TYPE_OBJECT_VISIBILITY has creature guid {} that does not match SourceEntry {}, skipped.", cond->SourceId, cond->SourceEntry); LOG_ERROR("sql.sql", "CONDITION_SOURCE_TYPE_OBJECT_VISIBILITY has creature guid {} that does not match SourceEntry {}, skipped.", cond->SourceId, cond->SourceEntry);
return false; return false;
@@ -2322,7 +2322,7 @@ bool ConditionMgr::isConditionTypeValid(Condition* cond)
{ {
if (CreatureData const* creatureData = sObjectMgr->GetCreatureData(cond->ConditionValue3)) if (CreatureData const* creatureData = sObjectMgr->GetCreatureData(cond->ConditionValue3))
{ {
if (cond->ConditionValue2 && creatureData->id1 != cond->ConditionValue2) if (cond->ConditionValue2 && creatureData->id != cond->ConditionValue2)
{ {
LOG_ERROR("sql.sql", "ObjectEntryGuid condition has guid {} set but does not match creature entry ({}), skipped", cond->ConditionValue3, cond->ConditionValue2); LOG_ERROR("sql.sql", "ObjectEntryGuid condition has guid {} set but does not match creature entry ({}), skipped", cond->ConditionValue3, cond->ConditionValue2);
return false; return false;
+7 -7
View File
@@ -1504,7 +1504,6 @@ namespace lfg
if (!gguid) if (!gguid)
return; return;
LfgRolesMap check_roles;
LfgRoleCheckContainer::iterator itRoleCheck = RoleChecksStore.find(gguid); LfgRoleCheckContainer::iterator itRoleCheck = RoleChecksStore.find(gguid);
if (itRoleCheck == RoleChecksStore.end()) if (itRoleCheck == RoleChecksStore.end())
return; return;
@@ -1527,9 +1526,7 @@ namespace lfg
if (itRoles == roleCheck.roles.end()) if (itRoles == roleCheck.roles.end())
{ {
// use temporal var to check roles, CheckGroupRoles modifies the roles roleCheck.state = CheckGroupRoles(roleCheck.roles) ? LFG_ROLECHECK_FINISHED : LFG_ROLECHECK_WRONG_ROLES;
check_roles = roleCheck.roles;
roleCheck.state = CheckGroupRoles(check_roles) ? LFG_ROLECHECK_FINISHED : LFG_ROLECHECK_WRONG_ROLES;
} }
} }
@@ -1798,14 +1795,17 @@ namespace lfg
ObjectGuid gguid = grp->GetGUID(); ObjectGuid gguid = grp->GetGUID();
SetState(gguid, LFG_STATE_PROPOSAL); SetState(gguid, LFG_STATE_PROPOSAL);
sGroupMgr->AddGroup(grp); sGroupMgr->AddGroup(grp);
grp->SetLfgRoles(pguid, proposal.players.find(pguid)->second.role);
} }
else if (group != grp) else if (group != grp)
{ {
if (!grp->IsFull()) if (!grp->IsFull())
grp->AddMember(player); grp->AddMember(player, proposal.players.find(pguid)->second.role);
}
else
{
grp->SetLfgRoles(pguid, proposal.players.find(pguid)->second.role);
} }
grp->SetLfgRoles(pguid, proposal.players.find(pguid)->second.role);
} }
// pussywizard: crashfix, group wasn't created when iterating players (no player found by guid), proposal is deleted by the calling function // pussywizard: crashfix, group wasn't created when iterating players (no player found by guid), proposal is deleted by the calling function
@@ -1423,7 +1423,7 @@ void Creature::SaveToDB(uint32 mapid, uint8 spawnMask, uint32 phaseMask)
dynamicflags = 0; dynamicflags = 0;
} }
data.id1 = GetEntry(); data.id = GetEntry();
data.mapid = mapid; data.mapid = mapid;
data.phaseMask = phaseMask; data.phaseMask = phaseMask;
data.displayid = displayId; data.displayid = displayId;
@@ -1469,8 +1469,6 @@ void Creature::SaveToDB(uint32 mapid, uint8 spawnMask, uint32 phaseMask)
stmt = WorldDatabase.GetPreparedStatement(WORLD_INS_CREATURE); stmt = WorldDatabase.GetPreparedStatement(WORLD_INS_CREATURE);
stmt->SetData(index++, m_spawnId); stmt->SetData(index++, m_spawnId);
stmt->SetData(index++, GetEntry()); stmt->SetData(index++, GetEntry());
stmt->SetData(index++, 0);
stmt->SetData(index++, 0);
stmt->SetData(index++, uint16(mapid)); stmt->SetData(index++, uint16(mapid));
stmt->SetData(index++, spawnMask); stmt->SetData(index++, spawnMask);
stmt->SetData(index++, GetPhaseMask()); stmt->SetData(index++, GetPhaseMask());
@@ -1725,7 +1723,7 @@ bool Creature::LoadCreatureFromDB(ObjectGuid::LowType spawnId, Map* map, bool ad
|| !groupData || (groupData->flags & SPAWNGROUP_FLAG_COMPATIBILITY_MODE); || !groupData || (groupData->flags & SPAWNGROUP_FLAG_COMPATIBILITY_MODE);
// Add to world // Add to world
uint32 entry = GetRandomId(data->id1, data->id2, data->id3); uint32 entry = GetRandomId(data->id, data->id2, data->id3);
if (!Create(map->GenerateLowGuid<HighGuid::Unit>(), map, data->phaseMask, entry, 0, data->posX, data->posY, data->posZ, data->orientation, data)) if (!Create(map->GenerateLowGuid<HighGuid::Unit>(), map, data->phaseMask, entry, 0, data->posX, data->posY, data->posZ, data->orientation, data))
return false; return false;
@@ -1852,6 +1850,10 @@ void Creature::DeleteFromDB()
stmt->SetData(0, m_spawnId); stmt->SetData(0, m_spawnId);
trans->Append(stmt); trans->Append(stmt);
stmt = WorldDatabase.GetPreparedStatement(WORLD_DEL_CREATURE_MULTISPAWN);
stmt->SetData(0, m_spawnId);
trans->Append(stmt);
stmt = WorldDatabase.GetPreparedStatement(WORLD_DEL_GAME_EVENT_CREATURE); stmt = WorldDatabase.GetPreparedStatement(WORLD_DEL_GAME_EVENT_CREATURE);
stmt->SetData(0, m_spawnId); stmt->SetData(0, m_spawnId);
trans->Append(stmt); trans->Append(stmt);
@@ -2041,7 +2043,7 @@ void Creature::Respawn(bool force)
if (!allowed && !force) // Will be rechecked on next Update call if (!allowed && !force) // Will be rechecked on next Update call
return; return;
ObjectGuid dbtableHighGuid = ObjectGuid::Create<HighGuid::Unit>(m_creatureData ? m_creatureData->id1 : GetEntry(), m_spawnId); ObjectGuid dbtableHighGuid = ObjectGuid::Create<HighGuid::Unit>(m_creatureData ? m_creatureData->id : GetEntry(), m_spawnId);
time_t linkedRespawntime = GetMap()->GetLinkedRespawnTime(dbtableHighGuid); time_t linkedRespawntime = GetMap()->GetLinkedRespawnTime(dbtableHighGuid);
CreatureTemplate const* cInfo = sObjectMgr->GetCreatureTemplate(GetEntry()); CreatureTemplate const* cInfo = sObjectMgr->GetCreatureTemplate(GetEntry());
@@ -2061,7 +2063,7 @@ void Creature::Respawn(bool force)
// Respawn check if spawn has 2 entries // Respawn check if spawn has 2 entries
if (data->id2) if (data->id2)
{ {
uint32 entry = GetRandomId(data->id1, data->id2, data->id3); uint32 entry = GetRandomId(data->id, data->id2, data->id3);
UpdateEntry(entry, data, true); // Select Random Entry UpdateEntry(entry, data, true); // Select Random Entry
m_defaultMovementType = MovementGeneratorType(data->movementType); // Reload Movement Type m_defaultMovementType = MovementGeneratorType(data->movementType); // Reload Movement Type
LoadEquipment(data->equipmentId); // Reload Equipment LoadEquipment(data->equipmentId); // Reload Equipment
@@ -3169,7 +3171,7 @@ uint32 Creature::GetScriptId() const
if (CreatureData const* creatureData = GetCreatureData()) if (CreatureData const* creatureData = GetCreatureData())
{ {
uint32 scriptId = creatureData->ScriptId; uint32 scriptId = creatureData->ScriptId;
if (scriptId && GetEntry() == creatureData->id1) if (scriptId && GetEntry() == creatureData->id)
return scriptId; return scriptId;
} }
@@ -369,10 +369,9 @@ typedef std::unordered_map<uint32, EquipmentInfoContainerInternal> EquipmentInfo
struct CreatureData : public SpawnData struct CreatureData : public SpawnData
{ {
CreatureData() : SpawnData(SPAWN_TYPE_CREATURE) {} CreatureData() : SpawnData(SPAWN_TYPE_CREATURE) {}
ObjectGuid::LowType spawnId{ 0 }; // mod_playerbots uint32 id{0}; // entry in creature_template
uint32 id1{0}; // entry in creature_template uint32 id2{0}; // entry in creature_template (from creature_multispawn)
uint32 id2{0}; // entry in creature_template uint32 id3{0}; // entry in creature_template (from creature_multispawn)
uint32 id3{0}; // entry in creature_template
uint32 displayid{0}; uint32 displayid{0};
int8 equipmentId{0}; int8 equipmentId{0};
uint32 spawntimesecs{0}; uint32 spawntimesecs{0};
+21 -3
View File
@@ -2388,7 +2388,14 @@ void Player::GiveXP(uint32 xp, Unit* victim, float group_rate, bool isLFGReward)
// Favored experience increase END // Favored experience increase END
// XP to money conversion processed in Player::RewardQuest // XP to money conversion processed in Player::RewardQuest
if (level >= sWorld->getIntConfig(CONFIG_MAX_PLAYER_LEVEL)) uint32 maxLevel = sWorld->getIntConfig(CONFIG_MAX_PLAYER_LEVEL);
// Trial account level cap (0 disables the cap)
if (uint32 trialLevelCap = sWorld->getIntConfig(CONFIG_TRIAL_LEVEL_CAP))
if (GetSession()->IsTrialAccount())
maxLevel = std::min(maxLevel, trialLevelCap);
if (level >= maxLevel)
return; return;
uint32 bonus_xp = 0; uint32 bonus_xp = 0;
@@ -2413,11 +2420,11 @@ void Player::GiveXP(uint32 xp, Unit* victim, float group_rate, bool isLFGReward)
uint32 nextLvlXP = GetUInt32Value(PLAYER_NEXT_LEVEL_XP); uint32 nextLvlXP = GetUInt32Value(PLAYER_NEXT_LEVEL_XP);
uint32 newXP = curXP + xp + bonus_xp; uint32 newXP = curXP + xp + bonus_xp;
while (newXP >= nextLvlXP && level < sWorld->getIntConfig(CONFIG_MAX_PLAYER_LEVEL)) while (newXP >= nextLvlXP && level < maxLevel)
{ {
newXP -= nextLvlXP; newXP -= nextLvlXP;
if (level < sWorld->getIntConfig(CONFIG_MAX_PLAYER_LEVEL)) if (level < maxLevel)
GiveLevel(level + 1); GiveLevel(level + 1);
level = GetLevel(); level = GetLevel();
@@ -11509,6 +11516,17 @@ bool Player::ModifyMoney(int32 amount, bool sendError /*= true*/)
SetMoney (GetMoney() > uint32(-amount) ? GetMoney() + amount : 0); SetMoney (GetMoney() > uint32(-amount) ? GetMoney() + amount : 0);
else else
{ {
// Trial account money cap (0 disables the cap)
if (uint32 trialMoneyCap = sWorld->getIntConfig(CONFIG_TRIAL_MONEY_CAP))
{
if (GetSession()->IsTrialAccount() && GetMoney() + uint32(amount) > trialMoneyCap)
{
if (sendError)
SendEquipError(EQUIP_ERR_TOO_MUCH_GOLD, nullptr, nullptr);
return false;
}
}
if (GetMoney() < uint32(MAX_MONEY_AMOUNT - amount)) if (GetMoney() < uint32(MAX_MONEY_AMOUNT - amount))
SetMoney(GetMoney() + amount); SetMoney(GetMoney() + amount);
else else
+2 -2
View File
@@ -1560,9 +1560,9 @@ public:
void SendQuestReward(Quest const* quest, uint32 XP); void SendQuestReward(Quest const* quest, uint32 XP);
void SendQuestFailed(uint32 questId, InventoryResult reason = EQUIP_ERR_OK); void SendQuestFailed(uint32 questId, InventoryResult reason = EQUIP_ERR_OK);
void SendQuestTimerFailed(uint32 quest_id); void SendQuestTimerFailed(uint32 quest_id);
void SendCanTakeQuestResponse(uint32 msg) const; void SendCanTakeQuestResponse(QuestFailedReason msg) const;
void SendQuestConfirmAccept(Quest const* quest, Player* pReceiver); void SendQuestConfirmAccept(Quest const* quest, Player* pReceiver);
void SendPushToPartyResponse(Player const* player, uint8 msg) const; void SendPushToPartyResponse(Player const* player, QuestShareMessages msg) const;
void SendQuestUpdateAddItem(Quest const* quest, uint32 item_idx, uint16 count); void SendQuestUpdateAddItem(Quest const* quest, uint32 item_idx, uint16 count);
void SendQuestUpdateAddCreatureOrGo(Quest const* quest, ObjectGuid guid, uint32 creatureOrGO_idx, uint16 old_count, uint16 add_count); void SendQuestUpdateAddCreatureOrGo(Quest const* quest, ObjectGuid guid, uint32 creatureOrGO_idx, uint16 old_count, uint16 add_count);
void SendQuestUpdateAddPlayer(Quest const* quest, uint16 old_count, uint16 add_count); void SendQuestUpdateAddPlayer(Quest const* quest, uint16 old_count, uint16 add_count);
+69 -72
View File
@@ -26,6 +26,7 @@
#include "MapMgr.h" #include "MapMgr.h"
#include "Player.h" #include "Player.h"
#include "PoolMgr.h" #include "PoolMgr.h"
#include "QuestPackets.h"
#include "ReputationMgr.h" #include "ReputationMgr.h"
#include "ScriptMgr.h" #include "ScriptMgr.h"
#include "SpellAuraEffects.h" #include "SpellAuraEffects.h"
@@ -991,8 +992,7 @@ bool Player::SatisfyQuestLog(bool msg)
if (msg) if (msg)
{ {
WorldPacket data(SMSG_QUESTLOG_FULL, 0); SendDirectMessage(WorldPackets::Quest::QuestLogFull().Write());
SendDirectMessage(&data);
LOG_DEBUG("network", "WORLD: Sent SMSG_QUESTLOG_FULL"); LOG_DEBUG("network", "WORLD: Sent SMSG_QUESTLOG_FULL");
} }
return false; return false;
@@ -2381,13 +2381,13 @@ bool Player::HasQuestForItem(uint32 itemid, uint32 excludeQuestId /* 0 */, bool
void Player::SendQuestComplete(uint32 quest_id) void Player::SendQuestComplete(uint32 quest_id)
{ {
if (quest_id) if (!quest_id)
{ return;
WorldPacket data(SMSG_QUESTUPDATE_COMPLETE, 4);
data << uint32(quest_id); WorldPackets::Quest::QuestUpdateComplete questUpdateComplete;
SendDirectMessage(&data); questUpdateComplete.QuestId = quest_id;
LOG_DEBUG("network", "WORLD: Sent SMSG_QUESTUPDATE_COMPLETE quest = {}", quest_id); SendDirectMessage(questUpdateComplete.Write());
} LOG_DEBUG("network", "WORLD: Sent SMSG_QUESTUPDATE_COMPLETE quest = {}", quest_id);
} }
void Player::SendQuestReward(Quest const* quest, uint32 XP) void Player::SendQuestReward(Quest const* quest, uint32 XP)
@@ -2395,54 +2395,50 @@ void Player::SendQuestReward(Quest const* quest, uint32 XP)
uint32 questid = quest->GetQuestId(); uint32 questid = quest->GetQuestId();
LOG_DEBUG("network", "WORLD: Sent SMSG_QUESTGIVER_QUEST_COMPLETE quest = {}", questid); LOG_DEBUG("network", "WORLD: Sent SMSG_QUESTGIVER_QUEST_COMPLETE quest = {}", questid);
sGameEventMgr->HandleQuestComplete(questid); sGameEventMgr->HandleQuestComplete(questid);
WorldPacket data(SMSG_QUESTGIVER_QUEST_COMPLETE, (4 + 4 + 4 + 4 + 4)); WorldPackets::Quest::QuestGiverQuestComplete questGiverQuestComplete;
data << uint32(questid); questGiverQuestComplete.QuestId = questid;
uint32 rewardMoney = quest->GetRewOrReqMoney(GetLevel());
if (!IsMaxLevel()) if (!IsMaxLevel())
{ questGiverQuestComplete.Experience = XP;
data << uint32(XP);
data << uint32(quest->GetRewOrReqMoney(GetLevel()));
}
else else
{ rewardMoney += quest->GetRewMoneyMaxLevel();
data << uint32(0);
data << uint32(quest->GetRewOrReqMoney(GetLevel()) + quest->GetRewMoneyMaxLevel());
}
data << uint32(10 * quest->CalculateHonorGain(GetQuestLevel(quest))); questGiverQuestComplete.RewardMoney = rewardMoney;
data << uint32(quest->GetBonusTalents()); // bonus talents questGiverQuestComplete.RewardHonor = 10 * quest->CalculateHonorGain(GetQuestLevel(quest));
data << uint32(quest->GetRewArenaPoints()); questGiverQuestComplete.RewardTalents = quest->GetBonusTalents();
SendDirectMessage(&data); questGiverQuestComplete.RewardArena = quest->GetRewArenaPoints();
SendDirectMessage(questGiverQuestComplete.Write());
} }
void Player::SendQuestFailed(uint32 questId, InventoryResult reason) void Player::SendQuestFailed(uint32 questId, InventoryResult reason)
{ {
if (questId) if (!questId)
{ return;
WorldPacket data(SMSG_QUESTGIVER_QUEST_FAILED, 4 + 4);
data << uint32(questId); WorldPackets::Quest::QuestGiverQuestFailed questGiverQuestFailed;
data << uint32(reason); // failed reason (valid reasons: 4, 16, 50, 17, 74, other values show default message) questGiverQuestFailed.QuestId = questId;
SendDirectMessage(&data); questGiverQuestFailed.FailureReason = reason; // failed reason (valid reasons: 4, 16, 50, 17, 74, other values show default message)
LOG_DEBUG("network", "WORLD: Sent SMSG_QUESTGIVER_QUEST_FAILED"); SendDirectMessage(questGiverQuestFailed.Write());
} LOG_DEBUG("network", "WORLD: Sent SMSG_QUESTGIVER_QUEST_FAILED");
} }
void Player::SendQuestTimerFailed(uint32 quest_id) void Player::SendQuestTimerFailed(uint32 quest_id)
{ {
if (quest_id) if (!quest_id)
{ return;
WorldPacket data(SMSG_QUESTUPDATE_FAILEDTIMER, 4);
data << uint32(quest_id); WorldPackets::Quest::QuestUpdateFailedTimer questUpdateFailedTimer;
SendDirectMessage(&data); questUpdateFailedTimer.QuestId = quest_id;
LOG_DEBUG("network", "WORLD: Sent SMSG_QUESTUPDATE_FAILEDTIMER"); SendDirectMessage(questUpdateFailedTimer.Write());
} LOG_DEBUG("network", "WORLD: Sent SMSG_QUESTUPDATE_FAILEDTIMER");
} }
void Player::SendCanTakeQuestResponse(uint32 msg) const void Player::SendCanTakeQuestResponse(QuestFailedReason msg) const
{ {
WorldPacket data(SMSG_QUESTGIVER_QUEST_INVALID, 4); WorldPackets::Quest::QuestGiverQuestInvalid questGiverQuestInvalid;
data << uint32(msg); questGiverQuestInvalid.FailureReason = msg;
SendDirectMessage(&data); SendDirectMessage(questGiverQuestInvalid.Write());
LOG_DEBUG("network", "WORLD: Sent SMSG_QUESTGIVER_QUEST_INVALID"); LOG_DEBUG("network", "WORLD: Sent SMSG_QUESTGIVER_QUEST_INVALID");
} }
@@ -2458,35 +2454,36 @@ void Player::SendQuestConfirmAccept(const Quest* quest, Player* pReceiver)
if (const QuestLocale* pLocale = sObjectMgr->GetQuestLocale(quest->GetQuestId())) if (const QuestLocale* pLocale = sObjectMgr->GetQuestLocale(quest->GetQuestId()))
ObjectMgr::GetLocaleString(pLocale->Title, loc_idx, strTitle); ObjectMgr::GetLocaleString(pLocale->Title, loc_idx, strTitle);
WorldPacket data(SMSG_QUEST_CONFIRM_ACCEPT, (4 + quest->GetTitle().size() + 8)); WorldPackets::Quest::QuestConfirmAccept questConfirmAccept;
data << uint32(quest->GetQuestId()); questConfirmAccept.QuestId = quest->GetQuestId();
data << quest->GetTitle(); questConfirmAccept.QuestTitle = quest->GetTitle();
data << GetGUID(); questConfirmAccept.PlayerGuid = GetGUID();
pReceiver->SendDirectMessage(&data); pReceiver->SendDirectMessage(questConfirmAccept.Write());
LOG_DEBUG("network", "WORLD: Sent SMSG_QUEST_CONFIRM_ACCEPT"); LOG_DEBUG("network", "WORLD: Sent SMSG_QUEST_CONFIRM_ACCEPT");
} }
} }
void Player::SendPushToPartyResponse(Player const* player, uint8 msg) const void Player::SendPushToPartyResponse(Player const* player, QuestShareMessages msg) const
{ {
if (player) if (!player)
{ return;
WorldPacket data(MSG_QUEST_PUSH_RESULT, (8 + 1));
data << player->GetGUID(); WorldPackets::Quest::QuestPushResult questPushResult;
data << uint8(msg); // valid values: 0-8 questPushResult.PlayerGuid = player->GetGUID();
SendDirectMessage(&data); questPushResult.QuestShareMessage = msg;
LOG_DEBUG("network", "WORLD: Sent MSG_QUEST_PUSH_RESULT"); SendDirectMessage(questPushResult.Write());
} LOG_DEBUG("network", "WORLD: Sent MSG_QUEST_PUSH_RESULT");
} }
void Player::SendQuestUpdateAddItem(Quest const* /*quest*/, uint32 /*item_idx*/, uint16 /*count*/) void Player::SendQuestUpdateAddItem(Quest const* /*quest*/, uint32 /*item_idx*/, uint16 /*count*/)
{ {
WorldPacket data(SMSG_QUESTUPDATE_ADD_ITEM, 0); // Packet is intentionally sent empty; the optional payload (item id and
// count) is not required by the 3.3.5 client:
//questUpdateAddItem.ItemId = quest->RequiredItemId[item_idx];
//questUpdateAddItem.Count = count;
SendDirectMessage(WorldPackets::Quest::QuestUpdateAddItem().Write());
LOG_DEBUG("network", "WORLD: Sent SMSG_QUESTUPDATE_ADD_ITEM"); LOG_DEBUG("network", "WORLD: Sent SMSG_QUESTUPDATE_ADD_ITEM");
//data << quest->RequiredItemId[item_idx];
//data << count;
SendDirectMessage(&data);
} }
void Player::SendQuestUpdateAddCreatureOrGo(Quest const* quest, ObjectGuid guid, uint32 creatureOrGO_idx, uint16 old_count, uint16 add_count) void Player::SendQuestUpdateAddCreatureOrGo(Quest const* quest, ObjectGuid guid, uint32 creatureOrGO_idx, uint16 old_count, uint16 add_count)
@@ -2498,14 +2495,14 @@ void Player::SendQuestUpdateAddCreatureOrGo(Quest const* quest, ObjectGuid guid,
// client expected gameobject template id in form (id|0x80000000) // client expected gameobject template id in form (id|0x80000000)
entry = (-entry) | 0x80000000; entry = (-entry) | 0x80000000;
WorldPacket data(SMSG_QUESTUPDATE_ADD_KILL, (4 * 4 + 8)); WorldPackets::Quest::QuestUpdateAddKill questUpdateAddKill;
questUpdateAddKill.QuestId = quest->GetQuestId();
questUpdateAddKill.CreatureEntry = entry;
questUpdateAddKill.CurrentCount = old_count + add_count;
questUpdateAddKill.RequiredCount = quest->RequiredNpcOrGoCount[creatureOrGO_idx];
questUpdateAddKill.ObjectiveGuid = guid;
SendDirectMessage(questUpdateAddKill.Write());
LOG_DEBUG("network", "WORLD: Sent SMSG_QUESTUPDATE_ADD_KILL"); LOG_DEBUG("network", "WORLD: Sent SMSG_QUESTUPDATE_ADD_KILL");
data << uint32(quest->GetQuestId());
data << uint32(entry);
data << uint32(old_count + add_count);
data << uint32(quest->RequiredNpcOrGoCount[ creatureOrGO_idx ]);
data << guid;
SendDirectMessage(&data);
uint16 log_slot = FindQuestSlot(quest->GetQuestId()); uint16 log_slot = FindQuestSlot(quest->GetQuestId());
if (log_slot < MAX_QUEST_LOG_SIZE) if (log_slot < MAX_QUEST_LOG_SIZE)
@@ -2516,12 +2513,12 @@ void Player::SendQuestUpdateAddPlayer(Quest const* quest, uint16 old_count, uint
{ {
ASSERT(old_count + add_count < 65536 && "player count store in 16 bits"); ASSERT(old_count + add_count < 65536 && "player count store in 16 bits");
WorldPacket data(SMSG_QUESTUPDATE_ADD_PVP_KILL, (3 * 4)); WorldPackets::Quest::QuestUpdateAddPvPKill questUpdateAddPvPKill;
questUpdateAddPvPKill.QuestId = quest->GetQuestId();
questUpdateAddPvPKill.CurrentCount = old_count + add_count;
questUpdateAddPvPKill.RequiredCount = quest->GetPlayersSlain();
SendDirectMessage(questUpdateAddPvPKill.Write());
LOG_DEBUG("network", "WORLD: Sent SMSG_QUESTUPDATE_ADD_PVP_KILL"); LOG_DEBUG("network", "WORLD: Sent SMSG_QUESTUPDATE_ADD_PVP_KILL");
data << uint32(quest->GetQuestId());
data << uint32(old_count + add_count);
data << uint32(quest->GetPlayersSlain());
SendDirectMessage(&data);
uint16 log_slot = FindQuestSlot(quest->GetQuestId()); uint16 log_slot = FindQuestSlot(quest->GetQuestId());
if (log_slot < MAX_QUEST_LOG_SIZE) if (log_slot < MAX_QUEST_LOG_SIZE)
@@ -4726,7 +4726,7 @@ void Player::ApplyEnchantment(Item* item, EnchantmentSlot slot, bool apply, bool
{ {
WeaponAttackType const attackType = Player::GetAttackBySlot(item->GetSlot()); WeaponAttackType const attackType = Player::GetAttackBySlot(item->GetSlot());
if (attackType != MAX_ATTACK) if (attackType != MAX_ATTACK)
UpdateDamageDoneMods(attackType); UpdateDamageDoneMods(attackType, apply ? -1 : slot);
break; break;
} }
case ITEM_ENCHANTMENT_TYPE_USE_SPELL: case ITEM_ENCHANTMENT_TYPE_USE_SPELL:
@@ -940,6 +940,11 @@ bool Player::UpdateSkillPro(uint16 SkillId, int32 Chance, uint32 step)
if (!MaxValue || !SkillValue || SkillValue >= MaxValue) if (!MaxValue || !SkillValue || SkillValue >= MaxValue)
return false; return false;
// Trial account trade-skill cap (0 disables the cap)
if (uint32 trialSkillCap = sWorld->getIntConfig(CONFIG_TRIAL_TRADE_SKILL_CAP))
if (GetSession()->IsTrialAccount() && SkillValue >= trialSkillCap)
return false;
int32 Roll = irand(1, 1000); int32 Roll = irand(1, 1000);
if (Roll <= Chance) if (Roll <= Chance)
+31 -20
View File
@@ -1555,28 +1555,32 @@ void Unit::CalculateSpellDamageTaken(SpellNonMeleeDamage* damageInfo, int32 dama
{ {
damageInfo->HitInfo |= SPELL_HIT_TYPE_CRIT; damageInfo->HitInfo |= SPELL_HIT_TYPE_CRIT;
// Calculate crit bonus // Calculate crit bonus (100% for melee/ranged spells)
uint32 crit_bonus = damage; int32 crit_bonus = damage;
// Apply crit_damage bonus for melee spells crit_bonus += damage;
if (Player* modOwner = GetSpellModOwner())
modOwner->ApplySpellMod(spellInfo->Id, SPELLMOD_CRIT_DAMAGE_BONUS, crit_bonus);
damage += crit_bonus;
// Apply SPELL_AURA_MOD_ATTACKER_RANGED_CRIT_DAMAGE or SPELL_AURA_MOD_ATTACKER_MELEE_CRIT_DAMAGE // Apply SPELL_AURA_MOD_ATTACKER_RANGED_CRIT_DAMAGE or SPELL_AURA_MOD_ATTACKER_MELEE_CRIT_DAMAGE
float critPctDamageMod = 0.0f; float crit_mod = 0.0f;
if (attackType == RANGED_ATTACK) if (attackType == RANGED_ATTACK)
critPctDamageMod += victim->GetTotalAuraModifier(SPELL_AURA_MOD_ATTACKER_RANGED_CRIT_DAMAGE); crit_mod += victim->GetTotalAuraModifier(SPELL_AURA_MOD_ATTACKER_RANGED_CRIT_DAMAGE);
else else
critPctDamageMod += victim->GetTotalAuraModifier(SPELL_AURA_MOD_ATTACKER_MELEE_CRIT_DAMAGE); crit_mod += victim->GetTotalAuraModifier(SPELL_AURA_MOD_ATTACKER_MELEE_CRIT_DAMAGE);
// Increase crit damage from SPELL_AURA_MOD_CRIT_DAMAGE_BONUS // Increase crit damage from SPELL_AURA_MOD_CRIT_DAMAGE_BONUS
critPctDamageMod += GetTotalAuraModifierByMiscMask(SPELL_AURA_MOD_CRIT_DAMAGE_BONUS, spellInfo->GetSchoolMask()); crit_mod += GetTotalAuraModifierByMiscMask(SPELL_AURA_MOD_CRIT_DAMAGE_BONUS, spellInfo->GetSchoolMask());
// Increase crit damage from SPELL_AURA_MOD_CRIT_PERCENT_VERSUS // Increase crit damage from SPELL_AURA_MOD_CRIT_PERCENT_VERSUS
critPctDamageMod += GetTotalAuraModifierByMiscMask(SPELL_AURA_MOD_CRIT_PERCENT_VERSUS, crTypeMask); crit_mod += GetTotalAuraModifierByMiscMask(SPELL_AURA_MOD_CRIT_PERCENT_VERSUS, crTypeMask);
if (critPctDamageMod != 0) if (crit_bonus != 0 && crit_mod != 0.0f)
AddPct(damage, critPctDamageMod); AddPct(crit_bonus, crit_mod);
crit_bonus -= damage;
// adds additional damage to critBonus (from talents)
if (Player* modOwner = GetSpellModOwner())
modOwner->ApplySpellMod(spellInfo->Id, SPELLMOD_CRIT_DAMAGE_BONUS, crit_bonus);
damage = crit_bonus + damage;
} }
// Spell weapon based damage CAN BE crit & blocked at same time // Spell weapon based damage CAN BE crit & blocked at same time
@@ -9390,7 +9394,6 @@ uint32 Unit::SpellCriticalDamageBonus(Unit const* caster, SpellInfo const* spell
{ {
case SPELL_DAMAGE_CLASS_MELEE: // for melee based spells is 100% case SPELL_DAMAGE_CLASS_MELEE: // for melee based spells is 100%
case SPELL_DAMAGE_CLASS_RANGED: case SPELL_DAMAGE_CLASS_RANGED:
/// @todo: write here full calculation for melee/ranged spells
crit_bonus += damage; crit_bonus += damage;
break; break;
default: default:
@@ -15799,15 +15802,23 @@ void Unit::_ExitVehicle(Position const* exitPosition)
if (seatAddon) if (seatAddon)
{ {
if (seatAddon->ExitParameter == VehicleExitParameters::VehicleExitParamOffset) if (seatAddon->ExitParameter == VehicleExitParameters::VehicleExitParamOffset)
{
pos.RelocateOffset({ seatAddon->ExitParameterX, seatAddon->ExitParameterY, seatAddon->ExitParameterZ, seatAddon->ExitParameterO }); pos.RelocateOffset({ seatAddon->ExitParameterX, seatAddon->ExitParameterY, seatAddon->ExitParameterZ, seatAddon->ExitParameterO });
bool isInLoS = GetMap()->isInLineOfSight(GetPositionX(), GetPositionY(), GetPositionZ(), pos.GetPositionX(), pos.GetPositionY(), pos.GetPositionZ(), GetPhaseMask(), LINEOFSIGHT_ALL_CHECKS, VMAP::ModelIgnoreFlags::Nothing);
float floorZ = GetMapHeight(pos.GetPositionX(), pos.GetPositionY(), pos.GetPositionZ());
if (!isInLoS || pos.GetPositionZ() < floorZ)
pos = vehicleBase->GetPosition();
}
else if (seatAddon->ExitParameter == VehicleExitParameters::VehicleExitParamDest) else if (seatAddon->ExitParameter == VehicleExitParameters::VehicleExitParamDest)
{
pos.Relocate({ seatAddon->ExitParameterX, seatAddon->ExitParameterY, seatAddon->ExitParameterZ, seatAddon->ExitParameterO }); pos.Relocate({ seatAddon->ExitParameterX, seatAddon->ExitParameterY, seatAddon->ExitParameterZ, seatAddon->ExitParameterO });
bool isInLoS = GetMap()->isInLineOfSight(GetPositionX(), GetPositionY(), GetPositionZ(), pos.GetPositionX(), pos.GetPositionY(), pos.GetPositionZ(), GetPhaseMask(), LINEOFSIGHT_ALL_CHECKS, VMAP::ModelIgnoreFlags::Nothing); float floorZ = GetMapHeight(pos.GetPositionX(), pos.GetPositionY(), pos.GetPositionZ());
float floorZ = GetMapHeight(pos.GetPositionX(), pos.GetPositionY(), pos.GetPositionZ()); if (pos.GetPositionZ() < floorZ)
pos.m_positionZ = floorZ + 0.1f;
if (!isInLoS || pos.GetPositionZ() < floorZ) }
pos = vehicleBase->GetPosition();
} }
} }
+1 -6
View File
@@ -359,12 +359,7 @@ bool Vehicle::AddPassenger(Unit* unit, int8 seatId)
return false; return false;
if (!seat->second.IsEmpty()) if (!seat->second.IsEmpty())
{ return false;
if (Unit* passenger = ObjectAccessor::GetUnit(*GetBase(), seat->second.Passenger.Guid))
passenger->ExitVehicle();
seat->second.Passenger.Guid.Clear();
}
ASSERT(seat->second.IsEmpty()); ASSERT(seat->second.IsEmpty());
} }
+5 -7
View File
@@ -300,7 +300,7 @@ void GameEventMgr::LoadEventVendors()
// Get creature entry // Get creature entry
newEntry.Entry = 0; newEntry.Entry = 0;
if (CreatureData const* data = sObjectMgr->GetCreatureData(guid)) if (CreatureData const* data = sObjectMgr->GetCreatureData(guid))
newEntry.Entry = data->id1; newEntry.Entry = data->id;
// Validate vendor item // Validate vendor item
if (!sObjectMgr->IsVendorItemValid(newEntry.Entry, newEntry.Item, newEntry.MaxCount, newEntry.Incrtime, newEntry.ExtendedCost, nullptr, nullptr, event_npc_flag)) if (!sObjectMgr->IsVendorItemValid(newEntry.Entry, newEntry.Item, newEntry.MaxCount, newEntry.Incrtime, newEntry.ExtendedCost, nullptr, nullptr, event_npc_flag))
@@ -614,9 +614,7 @@ void GameEventMgr::LoadEventModelEquipmentChangeData()
ObjectGuid::LowType guid = fields[0].Get<uint32>(); ObjectGuid::LowType guid = fields[0].Get<uint32>();
uint32 entry = fields[1].Get<uint32>(); uint32 entry = fields[1].Get<uint32>();
uint32 entry2 = fields[2].Get<uint32>(); uint16 eventId = fields[2].Get<uint8>();
uint32 entry3 = fields[3].Get<uint32>();
uint16 eventId = fields[4].Get<uint8>();
if (eventId >= _gameEventModelEquip.size()) if (eventId >= _gameEventModelEquip.size())
{ {
@@ -626,15 +624,15 @@ void GameEventMgr::LoadEventModelEquipmentChangeData()
ModelEquipList& equiplist = _gameEventModelEquip[eventId]; ModelEquipList& equiplist = _gameEventModelEquip[eventId];
ModelEquip newModelEquipSet; ModelEquip newModelEquipSet;
newModelEquipSet.ModelId = fields[5].Get<uint32>(); newModelEquipSet.ModelId = fields[3].Get<uint32>();
newModelEquipSet.EquipmentId = fields[6].Get<uint8>(); newModelEquipSet.EquipmentId = fields[4].Get<uint8>();
newModelEquipSet.EquipementIdPrev = 0; newModelEquipSet.EquipementIdPrev = 0;
newModelEquipSet.ModelIdPrev = 0; newModelEquipSet.ModelIdPrev = 0;
if (newModelEquipSet.EquipmentId > 0) if (newModelEquipSet.EquipmentId > 0)
{ {
int8 equipId = static_cast<int8>(newModelEquipSet.EquipmentId); int8 equipId = static_cast<int8>(newModelEquipSet.EquipmentId);
if ((!sObjectMgr->GetEquipmentInfo(entry, equipId)) || (entry2 && !sObjectMgr->GetEquipmentInfo(entry2, equipId)) || (entry3 && !sObjectMgr->GetEquipmentInfo(entry3, equipId))) if (!sObjectMgr->GetEquipmentInfo(entry, equipId))
{ {
LOG_ERROR("sql.sql", "Table `game_event_model_equip` have creature (Guid: {}) with equipment_id {} not found in table `creature_equip_template`, set to no equipment.", LOG_ERROR("sql.sql", "Table `game_event_model_equip` have creature (Guid: {}) with equipment_id {} not found in table `creature_equip_template`, set to no equipment.",
guid, newModelEquipSet.EquipmentId); guid, newModelEquipSet.EquipmentId);
+199 -137
View File
@@ -1572,7 +1572,7 @@ void ObjectMgr::LoadCreatureMovementOverrides()
"COALESCE(cmo.InteractionPauseTimer, ctm.InteractionPauseTimer) " "COALESCE(cmo.InteractionPauseTimer, ctm.InteractionPauseTimer) "
"FROM creature_movement_override AS cmo " "FROM creature_movement_override AS cmo "
"LEFT JOIN creature AS c ON c.guid = cmo.SpawnId " "LEFT JOIN creature AS c ON c.guid = cmo.SpawnId "
"LEFT JOIN creature_template_movement AS ctm ON ctm.CreatureId = c.id1"); "LEFT JOIN creature_template_movement AS ctm ON ctm.CreatureId = c.id");
if (!result) if (!result)
{ {
LOG_WARN("server.loading", ">> Loaded 0 creature movement overrides. DB table `creature_movement_override` is empty!"); LOG_WARN("server.loading", ">> Loaded 0 creature movement overrides. DB table `creature_movement_override` is empty!");
@@ -1978,8 +1978,8 @@ void ObjectMgr::LoadLinkedRespawn()
break; break;
} }
guid = ObjectGuid::Create<HighGuid::Unit>(slave->id1, guidLow); guid = ObjectGuid::Create<HighGuid::Unit>(slave->id, guidLow);
linkedGuid = ObjectGuid::Create<HighGuid::Unit>(master->id1, linkedGuidLow); linkedGuid = ObjectGuid::Create<HighGuid::Unit>(master->id, linkedGuidLow);
break; break;
} }
case CREATURE_TO_GO: case CREATURE_TO_GO:
@@ -2015,7 +2015,7 @@ void ObjectMgr::LoadLinkedRespawn()
break; break;
} }
guid = ObjectGuid::Create<HighGuid::Unit>(slave->id1, guidLow); guid = ObjectGuid::Create<HighGuid::Unit>(slave->id, guidLow);
linkedGuid = ObjectGuid::Create<HighGuid::GameObject>(master->id, linkedGuidLow); linkedGuid = ObjectGuid::Create<HighGuid::GameObject>(master->id, linkedGuidLow);
break; break;
} }
@@ -2090,7 +2090,7 @@ void ObjectMgr::LoadLinkedRespawn()
} }
guid = ObjectGuid::Create<HighGuid::GameObject>(slave->id, guidLow); guid = ObjectGuid::Create<HighGuid::GameObject>(slave->id, guidLow);
linkedGuid = ObjectGuid::Create<HighGuid::Unit>(master->id1, linkedGuidLow); linkedGuid = ObjectGuid::Create<HighGuid::Unit>(master->id, linkedGuidLow);
break; break;
} }
} }
@@ -2109,7 +2109,7 @@ bool ObjectMgr::SetCreatureLinkedRespawn(ObjectGuid::LowType guidLow, ObjectGuid
return false; return false;
CreatureData const* master = GetCreatureData(guidLow); CreatureData const* master = GetCreatureData(guidLow);
ObjectGuid guid = ObjectGuid::Create<HighGuid::Unit>(master->id1, guidLow); ObjectGuid guid = ObjectGuid::Create<HighGuid::Unit>(master->id, guidLow);
if (!linkedGuidLow) // we're removing the linking if (!linkedGuidLow) // we're removing the linking
{ {
@@ -2140,7 +2140,7 @@ bool ObjectMgr::SetCreatureLinkedRespawn(ObjectGuid::LowType guidLow, ObjectGuid
return false; return false;
} }
ObjectGuid linkedGuid = ObjectGuid::Create<HighGuid::Unit>(slave->id1, linkedGuidLow); ObjectGuid linkedGuid = ObjectGuid::Create<HighGuid::Unit>(slave->id, linkedGuidLow);
_linkedRespawnStore[guid] = linkedGuid; _linkedRespawnStore[guid] = linkedGuid;
WorldDatabasePreparedStatement* stmt = WorldDatabase.GetPreparedStatement(WORLD_REP_CREATURE_LINKED_RESPAWN); WorldDatabasePreparedStatement* stmt = WorldDatabase.GetPreparedStatement(WORLD_REP_CREATURE_LINKED_RESPAWN);
@@ -2321,11 +2321,11 @@ void ObjectMgr::LoadCreatures()
{ {
uint32 oldMSTime = getMSTime(); uint32 oldMSTime = getMSTime();
// 0 1 2 3 4 5 6 7 8 9 10 11 // 0 1 2 3 4 5 6 7 8 9
QueryResult result = WorldDatabase.Query("SELECT creature.guid, id1, id2, id3, map, equipment_id, position_x, position_y, position_z, orientation, spawntimesecs, wander_distance, " QueryResult result = WorldDatabase.Query("SELECT creature.guid, id, map, equipment_id, position_x, position_y, position_z, orientation, spawntimesecs, wander_distance, "
// 12 13 14 15 16 17 18 19 20 21 22 // 10 11 12 13 14 15 16 17 18 19 20
"currentwaypoint, curhealth, curmana, MovementType, spawnMask, phaseMask, eventEntry, pool_entry, creature.npcflag, creature.unit_flags, creature.dynamicflags, " "currentwaypoint, curhealth, curmana, MovementType, spawnMask, phaseMask, eventEntry, pool_entry, creature.npcflag, creature.unit_flags, creature.dynamicflags, "
// 23 // 21
"creature.ScriptName " "creature.ScriptName "
"FROM creature " "FROM creature "
"LEFT OUTER JOIN game_event_creature ON creature.guid = game_event_creature.guid " "LEFT OUTER JOIN game_event_creature ON creature.guid = game_event_creature.guid "
@@ -2356,58 +2356,37 @@ void ObjectMgr::LoadCreatures()
Field* fields = result->Fetch(); Field* fields = result->Fetch();
ObjectGuid::LowType spawnId = fields[0].Get<uint32>(); ObjectGuid::LowType spawnId = fields[0].Get<uint32>();
uint32 id1 = fields[1].Get<uint32>(); uint32 creatureId = fields[1].Get<uint32>();
uint32 id2 = fields[2].Get<uint32>();
uint32 id3 = fields[3].Get<uint32>();
CreatureTemplate const* cInfo = GetCreatureTemplate(id1); CreatureTemplate const* cInfo = GetCreatureTemplate(creatureId);
if (!cInfo) if (!cInfo)
{ {
LOG_ERROR("sql.sql", "Table `creature` has creature (SpawnId: {}) with non existing creature entry {} in id1 field, skipped.", spawnId, id1); LOG_ERROR("sql.sql", "Table `creature` has creature (SpawnId: {}) with non existing creature entry {} in `id` field, skipped.", spawnId, creatureId);
continue;
}
CreatureTemplate const* cInfo2 = GetCreatureTemplate(id2);
if (!cInfo2 && id2)
{
LOG_ERROR("sql.sql", "Table `creature` has creature (SpawnId: {}) with non existing creature entry {} in id2 field, skipped.", spawnId, id2);
continue;
}
CreatureTemplate const* cInfo3 = GetCreatureTemplate(id3);
if (!cInfo3 && id3)
{
LOG_ERROR("sql.sql", "Table `creature` has creature (SpawnId: {}) with non existing creature entry {} in id3 field, skipped.", spawnId, id3);
continue;
}
if (!id2 && id3)
{
LOG_ERROR("sql.sql", "Table `creature` has creature (SpawnId: {}) with creature entry {} in id3 field but no entry in id2 field, skipped.", spawnId, id3);
continue; continue;
} }
CreatureData& data = _creatureDataStore[spawnId]; CreatureData& data = _creatureDataStore[spawnId];
data.spawnId = spawnId; data.spawnId = spawnId;
data.id1 = id1; data.id = creatureId;
data.id2 = id2; data.mapid = fields[2].Get<uint16>();
data.id3 = id3; data.equipmentId = fields[3].Get<int8>();
data.mapid = fields[4].Get<uint16>(); data.posX = fields[4].Get<float>();
data.equipmentId = fields[5].Get<int8>(); data.posY = fields[5].Get<float>();
data.posX = fields[6].Get<float>(); data.posZ = fields[6].Get<float>();
data.posY = fields[7].Get<float>(); data.orientation = fields[7].Get<float>();
data.posZ = fields[8].Get<float>(); data.spawntimesecs = fields[8].Get<uint32>();
data.orientation = fields[9].Get<float>(); data.wander_distance = fields[9].Get<float>();
data.spawntimesecs = fields[10].Get<uint32>(); data.currentwaypoint = fields[10].Get<uint32>();
data.wander_distance = fields[11].Get<float>(); data.curhealth = fields[11].Get<uint32>();
data.currentwaypoint = fields[12].Get<uint32>(); data.curmana = fields[12].Get<uint32>();
data.curhealth = fields[13].Get<uint32>(); data.movementType = fields[13].Get<uint8>();
data.curmana = fields[14].Get<uint32>(); data.spawnMask = fields[14].Get<uint8>();
data.movementType = fields[15].Get<uint8>(); data.phaseMask = fields[15].Get<uint32>();
data.spawnMask = fields[16].Get<uint8>(); int16 gameEvent = fields[16].Get<int16>();
data.phaseMask = fields[17].Get<uint32>(); uint32 PoolId = fields[17].Get<uint32>();
int16 gameEvent = fields[18].Get<int16>(); data.npcflag = fields[18].Get<uint32>();
uint32 PoolId = fields[19].Get<uint32>(); data.unit_flags = fields[19].Get<uint32>();
data.npcflag = fields[20].Get<uint32>(); data.dynamicflags = fields[20].Get<uint32>();
data.unit_flags = fields[21].Get<uint32>(); data.ScriptId = GetScriptId(fields[21].Get<std::string>());
data.dynamicflags = fields[22].Get<uint32>();
data.ScriptId = GetScriptId(fields[23].Get<std::string>());
data.spawnGroupId = 0; data.spawnGroupId = 0;
if (!data.ScriptId) if (!data.ScriptId)
@@ -2437,10 +2416,10 @@ void ObjectMgr::LoadCreatures()
bool ok = true; bool ok = true;
for (uint32 diff = 0; diff < MAX_DIFFICULTY - 1 && ok; ++diff) for (uint32 diff = 0; diff < MAX_DIFFICULTY - 1 && ok; ++diff)
{ {
if ((_difficultyEntries[diff].find(data.id1) != _difficultyEntries[diff].end()) || (_difficultyEntries[diff].find(data.id2) != _difficultyEntries[diff].end()) || (_difficultyEntries[diff].find(data.id3) != _difficultyEntries[diff].end())) if (_difficultyEntries[diff].find(data.id) != _difficultyEntries[diff].end())
{ {
LOG_ERROR("sql.sql", "Table `creature` have creature (SpawnId: {}) that listed as difficulty {} template (Entries: {}, {}, {}) in `creature_template`, skipped.", LOG_ERROR("sql.sql", "Table `creature` have creature (SpawnId: {}) that listed as difficulty {} template (Entry: {}) in `creature_template`, skipped.",
spawnId, diff + 1, data.id1, data.id2, data.id3); spawnId, diff + 1, data.id);
ok = false; ok = false;
} }
} }
@@ -2450,35 +2429,35 @@ void ObjectMgr::LoadCreatures()
// -1 random, 0 no equipment, // -1 random, 0 no equipment,
if (data.equipmentId != 0) if (data.equipmentId != 0)
{ {
if ((!GetEquipmentInfo(data.id1, data.equipmentId)) || (data.id2 && !GetEquipmentInfo(data.id2, data.equipmentId)) || (data.id3 && !GetEquipmentInfo(data.id3, data.equipmentId))) if (!GetEquipmentInfo(data.id, data.equipmentId))
{ {
LOG_ERROR("sql.sql", "Table `creature` have creature (Entries: {}, {}, {}) one or more with equipment_id {} not found in table `creature_equip_template`, set to no equipment.", LOG_ERROR("sql.sql", "Table `creature` have creature (Entry: {}) with equipment_id {} not found in table `creature_equip_template`, set to no equipment.",
data.id1, data.id2, data.id3, data.equipmentId); data.id, data.equipmentId);
data.equipmentId = 0; data.equipmentId = 0;
} }
} }
if (cInfo->HasFlagsExtra(CREATURE_FLAG_EXTRA_INSTANCE_BIND) || (data.id2 && cInfo2->HasFlagsExtra(CREATURE_FLAG_EXTRA_INSTANCE_BIND)) || (data.id3 && cInfo3->HasFlagsExtra(CREATURE_FLAG_EXTRA_INSTANCE_BIND))) if (cInfo->HasFlagsExtra(CREATURE_FLAG_EXTRA_INSTANCE_BIND))
{ {
if (!mapEntry->IsDungeon()) if (!mapEntry->IsDungeon())
LOG_ERROR("sql.sql", "Table `creature` have creature (SpawnId: {} Entries: {}, {}, {}) with a `creature_template`.`flags_extra` in one or more entries including CREATURE_FLAG_EXTRA_INSTANCE_BIND but creature are not in instance.", LOG_ERROR("sql.sql", "Table `creature` have creature (SpawnId: {} Entry: {}) with `creature_template`.`flags_extra` including CREATURE_FLAG_EXTRA_INSTANCE_BIND but creature are not in instance.",
spawnId, data.id1, data.id2, data.id3); spawnId, data.id);
} }
if (data.movementType >= MAX_DB_MOTION_TYPE) if (data.movementType >= MAX_DB_MOTION_TYPE)
{ {
LOG_ERROR("sql.sql", "Table `creature` has creature (SpawnId: {} Entries: {}, {}, {}) with wrong movement generator type ({}), ignored and set to IDLE.", spawnId, data.id1, data.id2, data.id3, data.movementType); LOG_ERROR("sql.sql", "Table `creature` has creature (SpawnId: {} Entry: {}) with wrong movement generator type ({}), ignored and set to IDLE.", spawnId, data.id, data.movementType);
data.movementType = IDLE_MOTION_TYPE; data.movementType = IDLE_MOTION_TYPE;
} }
if (data.wander_distance < 0.0f) if (data.wander_distance < 0.0f)
{ {
LOG_ERROR("sql.sql", "Table `creature` have creature (SpawnId: {} Entries: {}, {}, {}) with `wander_distance`< 0, set to 0.", spawnId, data.id1, data.id2, data.id3); LOG_ERROR("sql.sql", "Table `creature` have creature (SpawnId: {} Entry: {}) with `wander_distance`< 0, set to 0.", spawnId, data.id);
data.wander_distance = 0.0f; data.wander_distance = 0.0f;
} }
else if (data.movementType == RANDOM_MOTION_TYPE) else if (data.movementType == RANDOM_MOTION_TYPE)
{ {
if (data.wander_distance == 0.0f) if (data.wander_distance == 0.0f)
{ {
LOG_ERROR("sql.sql", "Table `creature` have creature (SpawnId: {} Entries: {}, {}, {}) with `MovementType`=1 (random movement) but with `wander_distance`=0, replace by idle movement type (0).", LOG_ERROR("sql.sql", "Table `creature` have creature (SpawnId: {} Entry: {}) with `MovementType`=1 (random movement) but with `wander_distance`=0, replace by idle movement type (0).",
spawnId, data.id1, data.id2, data.id3); spawnId, data.id);
data.movementType = IDLE_MOTION_TYPE; data.movementType = IDLE_MOTION_TYPE;
} }
} }
@@ -2486,14 +2465,14 @@ void ObjectMgr::LoadCreatures()
{ {
if (data.wander_distance != 0.0f) if (data.wander_distance != 0.0f)
{ {
LOG_ERROR("sql.sql", "Table `creature` have creature (SpawnId: {} Entries: {}, {}, {}) with `MovementType`=0 (idle) have `wander_distance`<>0, set to 0.", spawnId, data.id1, data.id2, data.id3); LOG_ERROR("sql.sql", "Table `creature` have creature (SpawnId: {} Entry: {}) with `MovementType`=0 (idle) have `wander_distance`<>0, set to 0.", spawnId, data.id);
data.wander_distance = 0.0f; data.wander_distance = 0.0f;
} }
} }
if (data.phaseMask == 0) if (data.phaseMask == 0)
{ {
LOG_ERROR("sql.sql", "Table `creature` have creature (SpawnId: {} Entries: {}, {}, {}) with `phaseMask`=0 (not visible for anyone), set to 1.", spawnId, data.id1, data.id2, data.id3); LOG_ERROR("sql.sql", "Table `creature` have creature (SpawnId: {} Entry: {}) with `phaseMask`=0 (not visible for anyone), set to 1.", spawnId, data.id);
data.phaseMask = 1; data.phaseMask = 1;
} }
@@ -2518,6 +2497,72 @@ void ObjectMgr::LoadCreatures()
++count; ++count;
} while (result->NextRow()); } while (result->NextRow());
// Load alternate entries from creature_multispawn
QueryResult variantResult = WorldDatabase.Query("SELECT spawnId, entry FROM creature_multispawn ORDER BY spawnId");
if (variantResult)
{
uint32 variantCount = 0;
do
{
Field* fields = variantResult->Fetch();
ObjectGuid::LowType spawnId = fields[0].Get<uint32>();
uint32 entry = fields[1].Get<uint32>();
auto creatureDataIt = _creatureDataStore.find(spawnId);
if (creatureDataIt == _creatureDataStore.end())
{
LOG_ERROR("sql.sql", "Table `creature_multispawn` has entry for non-existing creature spawn (SpawnId: {}), skipped.", spawnId);
continue;
}
CreatureData* data = &creatureDataIt->second;
CreatureTemplate const* variantInfo = GetCreatureTemplate(entry);
if (!variantInfo)
{
LOG_ERROR("sql.sql", "Table `creature_multispawn` has creature (SpawnId: {}) with non-existing creature entry {}, skipped.", spawnId, entry);
continue;
}
// Check difficulty entries for variant
bool diffOk = true;
for (uint32 diff = 0; diff < MAX_DIFFICULTY - 1 && diffOk; ++diff)
{
if (_difficultyEntries[diff].find(entry) != _difficultyEntries[diff].end())
{
LOG_ERROR("sql.sql", "Table `creature_multispawn` has creature (SpawnId: {}) with entry {} listed as difficulty template in `creature_template`, skipped.",
spawnId, entry);
diffOk = false;
}
}
if (!diffOk)
continue;
// Validate equipment for variant entry
if (data->equipmentId != 0 && !GetEquipmentInfo(entry, data->equipmentId))
{
LOG_ERROR("sql.sql", "Table `creature_multispawn` has creature (SpawnId: {}) with entry {} where equipment_id {} not found in `creature_equip_template`.",
spawnId, entry, data->equipmentId);
}
// Populate id2/id3 fields
if (!data->id2)
{
data->id2 = entry;
++variantCount;
}
else if (!data->id3)
{
data->id3 = entry;
++variantCount;
}
else
LOG_ERROR("sql.sql", "Table `creature_multispawn` has more than 2 variant entries for creature (SpawnId: {}), extra entry {} skipped.", spawnId, entry);
} while (variantResult->NextRow());
LOG_INFO("server.loading", ">> Loaded {} creature spawn variants", variantCount);
}
LOG_INFO("server.loading", ">> Loaded {} Creatures in {} ms", count, GetMSTimeDiffToNow(oldMSTime)); LOG_INFO("server.loading", ">> Loaded {} Creatures in {} ms", count, GetMSTimeDiffToNow(oldMSTime));
LOG_INFO("server.loading", " "); LOG_INFO("server.loading", " ");
} }
@@ -2531,7 +2576,7 @@ CreatureData const* ObjectMgr::LoadCreatureDataFromDB(ObjectGuid::LowType spawnI
if (data) if (data)
return data; return data;
QueryResult result = WorldDatabase.Query("SELECT creature.guid, id1, id2, id3, map, equipment_id, " QueryResult result = WorldDatabase.Query("SELECT creature.guid, id, map, equipment_id, "
"position_x, position_y, position_z, orientation, spawntimesecs, wander_distance, " "position_x, position_y, position_z, orientation, spawntimesecs, wander_distance, "
"currentwaypoint, curhealth, curmana, MovementType, spawnMask, phaseMask, " "currentwaypoint, curhealth, curmana, MovementType, spawnMask, phaseMask, "
"creature.npcflag, creature.unit_flags, creature.dynamicflags, creature.ScriptName " "creature.npcflag, creature.unit_flags, creature.dynamicflags, creature.ScriptName "
@@ -2541,62 +2586,83 @@ CreatureData const* ObjectMgr::LoadCreatureDataFromDB(ObjectGuid::LowType spawnI
return nullptr; return nullptr;
Field* fields = result->Fetch(); Field* fields = result->Fetch();
uint32 id1 = fields[1].Get<uint32>(); uint32 creatureId = fields[1].Get<uint32>();
uint32 id2 = fields[2].Get<uint32>();
uint32 id3 = fields[3].Get<uint32>();
CreatureTemplate const* cInfo = GetCreatureTemplate(id1); CreatureTemplate const* cInfo = GetCreatureTemplate(creatureId);
if (!cInfo) if (!cInfo)
{ {
LOG_ERROR("sql.sql", "Table `creature` has creature (SpawnId: {}) with non-existing creature entry {} in id1 field, skipped.", spawnId, id1); LOG_ERROR("sql.sql", "Table `creature` has creature (SpawnId: {}) with non-existing creature entry {} in `id` field, skipped.", spawnId, creatureId);
return nullptr;
}
if (id2 && !GetCreatureTemplate(id2))
{
LOG_ERROR("sql.sql", "Table `creature` has creature (SpawnId: {}) with non-existing creature entry {} in id2 field, skipped.", spawnId, id2);
return nullptr;
}
if (id3 && !GetCreatureTemplate(id3))
{
LOG_ERROR("sql.sql", "Table `creature` has creature (SpawnId: {}) with non-existing creature entry {} in id3 field, skipped.", spawnId, id3);
return nullptr;
}
if (!id2 && id3)
{
LOG_ERROR("sql.sql", "Table `creature` has creature (SpawnId: {}) with creature entry {} in id3 field but no entry in id2 field, skipped.", spawnId, id3);
return nullptr; return nullptr;
} }
CreatureData& creatureData = _creatureDataStore[spawnId]; CreatureData& creatureData = _creatureDataStore[spawnId];
creatureData.id1 = id1; creatureData.id = creatureId;
creatureData.id2 = id2; creatureData.mapid = fields[2].Get<uint16>();
creatureData.id3 = id3; creatureData.equipmentId = fields[3].Get<int8>();
creatureData.mapid = fields[4].Get<uint16>(); creatureData.posX = fields[4].Get<float>();
creatureData.equipmentId = fields[5].Get<int8>(); creatureData.posY = fields[5].Get<float>();
creatureData.posX = fields[6].Get<float>(); creatureData.posZ = fields[6].Get<float>();
creatureData.posY = fields[7].Get<float>(); creatureData.orientation = fields[7].Get<float>();
creatureData.posZ = fields[8].Get<float>(); creatureData.spawntimesecs = fields[8].Get<uint32>();
creatureData.orientation = fields[9].Get<float>(); creatureData.wander_distance = fields[9].Get<float>();
creatureData.spawntimesecs = fields[10].Get<uint32>(); creatureData.currentwaypoint = fields[10].Get<uint32>();
creatureData.wander_distance = fields[11].Get<float>(); creatureData.curhealth = fields[11].Get<uint32>();
creatureData.currentwaypoint = fields[12].Get<uint32>(); creatureData.curmana = fields[12].Get<uint32>();
creatureData.curhealth = fields[13].Get<uint32>(); creatureData.movementType = fields[13].Get<uint8>();
creatureData.curmana = fields[14].Get<uint32>(); creatureData.spawnMask = fields[14].Get<uint8>();
creatureData.movementType = fields[15].Get<uint8>(); creatureData.phaseMask = fields[15].Get<uint32>();
creatureData.spawnMask = fields[16].Get<uint8>(); creatureData.npcflag = fields[16].Get<uint32>();
creatureData.phaseMask = fields[17].Get<uint32>(); creatureData.unit_flags = fields[17].Get<uint32>();
creatureData.npcflag = fields[18].Get<uint32>(); creatureData.dynamicflags = fields[18].Get<uint32>();
creatureData.unit_flags = fields[19].Get<uint32>(); creatureData.ScriptId = GetScriptId(fields[19].Get<std::string>());
creatureData.dynamicflags = fields[20].Get<uint32>();
creatureData.ScriptId = GetScriptId(fields[21].Get<std::string>());
creatureData.spawnGroupId = 0; creatureData.spawnGroupId = 0;
if (!creatureData.ScriptId) if (!creatureData.ScriptId)
creatureData.ScriptId = cInfo->ScriptID; creatureData.ScriptId = cInfo->ScriptID;
// Load alternate entries from creature_multispawn
QueryResult variantResult = WorldDatabase.Query("SELECT entry FROM creature_multispawn WHERE spawnId = {} ORDER BY entry", spawnId);
if (variantResult)
{
do
{
uint32 variantEntry = variantResult->Fetch()[0].Get<uint32>();
if (!GetCreatureTemplate(variantEntry))
{
LOG_ERROR("sql.sql", "Table `creature_multispawn` has creature (SpawnId: {}) with non-existing entry {}, skipped.", spawnId, variantEntry);
continue;
}
// Check difficulty entries for variant
bool diffOk = true;
for (uint32 diff = 0; diff < MAX_DIFFICULTY - 1 && diffOk; ++diff)
{
if (_difficultyEntries[diff].find(variantEntry) != _difficultyEntries[diff].end())
{
LOG_ERROR("sql.sql", "Table `creature_multispawn` has creature (SpawnId: {}) with entry {} listed as difficulty template in `creature_template`, skipped.",
spawnId, variantEntry);
diffOk = false;
}
}
if (!diffOk)
continue;
// Validate equipment for variant entry
if (creatureData.equipmentId != 0 && !GetEquipmentInfo(variantEntry, creatureData.equipmentId))
{
LOG_ERROR("sql.sql", "Table `creature_multispawn` has creature (SpawnId: {}) with entry {} where equipment_id {} not found in `creature_equip_template`.",
spawnId, variantEntry, creatureData.equipmentId);
}
if (!creatureData.id2)
creatureData.id2 = variantEntry;
else if (!creatureData.id3)
creatureData.id3 = variantEntry;
else
LOG_ERROR("sql.sql", "Table `creature_multispawn` has more than 2 variant entries for creature (SpawnId: {}), extra entry {} skipped.", spawnId, variantEntry);
} while (variantResult->NextRow());
}
MapEntry const* mapEntry = sMapStore.LookupEntry(creatureData.mapid); MapEntry const* mapEntry = sMapStore.LookupEntry(creatureData.mapid);
if (!mapEntry) if (!mapEntry)
{ {
@@ -2611,12 +2677,10 @@ CreatureData const* ObjectMgr::LoadCreatureDataFromDB(ObjectGuid::LowType spawnI
bool ok = true; bool ok = true;
for (uint32 diff = 0; diff < MAX_DIFFICULTY - 1 && ok; ++diff) for (uint32 diff = 0; diff < MAX_DIFFICULTY - 1 && ok; ++diff)
{ {
if (_difficultyEntries[diff].find(id1) != _difficultyEntries[diff].end() || if (_difficultyEntries[diff].find(creatureId) != _difficultyEntries[diff].end())
_difficultyEntries[diff].find(id2) != _difficultyEntries[diff].end() ||
_difficultyEntries[diff].find(id3) != _difficultyEntries[diff].end())
{ {
LOG_ERROR("sql.sql", "Table `creature` has creature (SpawnId: {}) that is listed as difficulty {} template (Entries: {}, {}, {}) in `creature_template`, skipped.", LOG_ERROR("sql.sql", "Table `creature` has creature (SpawnId: {}) that is listed as difficulty {} template (Entry: {}) in `creature_template`, skipped.",
spawnId, diff + 1, id1, id2, id3); spawnId, diff + 1, creatureId);
ok = false; ok = false;
} }
} }
@@ -2629,35 +2693,33 @@ CreatureData const* ObjectMgr::LoadCreatureDataFromDB(ObjectGuid::LowType spawnI
if (creatureData.equipmentId != 0) if (creatureData.equipmentId != 0)
{ {
if (!GetEquipmentInfo(id1, creatureData.equipmentId) || if (!GetEquipmentInfo(creatureId, creatureData.equipmentId))
(id2 && !GetEquipmentInfo(id2, creatureData.equipmentId)) ||
(id3 && !GetEquipmentInfo(id3, creatureData.equipmentId)))
{ {
LOG_ERROR("sql.sql", "Table `creature` has creature (Entries: {}, {}, {}) with equipment_id {} not found in table `creature_equip_template`, set to no equipment.", LOG_ERROR("sql.sql", "Table `creature` has creature (Entry: {}) with equipment_id {} not found in table `creature_equip_template`, set to no equipment.",
id1, id2, id3, creatureData.equipmentId); creatureId, creatureData.equipmentId);
creatureData.equipmentId = 0; creatureData.equipmentId = 0;
} }
} }
if (creatureData.movementType >= MAX_DB_MOTION_TYPE) if (creatureData.movementType >= MAX_DB_MOTION_TYPE)
{ {
LOG_ERROR("sql.sql", "Table `creature` has creature (SpawnId: {} Entries: {}, {}, {}) with wrong movement generator type ({}), set to IDLE.", LOG_ERROR("sql.sql", "Table `creature` has creature (SpawnId: {} Entry: {}) with wrong movement generator type ({}), set to IDLE.",
spawnId, id1, id2, id3, creatureData.movementType); spawnId, creatureId, creatureData.movementType);
creatureData.movementType = IDLE_MOTION_TYPE; creatureData.movementType = IDLE_MOTION_TYPE;
} }
if (creatureData.wander_distance < 0.0f) if (creatureData.wander_distance < 0.0f)
{ {
LOG_ERROR("sql.sql", "Table `creature` has creature (SpawnId: {} Entries: {}, {}, {}) with `wander_distance`< 0, set to 0.", LOG_ERROR("sql.sql", "Table `creature` has creature (SpawnId: {} Entry: {}) with `wander_distance`< 0, set to 0.",
spawnId, id1, id2, id3); spawnId, creatureId);
creatureData.wander_distance = 0.0f; creatureData.wander_distance = 0.0f;
} }
else if (creatureData.movementType == RANDOM_MOTION_TYPE) else if (creatureData.movementType == RANDOM_MOTION_TYPE)
{ {
if (creatureData.wander_distance == 0.0f) if (creatureData.wander_distance == 0.0f)
{ {
LOG_ERROR("sql.sql", "Table `creature` has creature (SpawnId: {} Entries: {}, {}, {}) with `MovementType`=1 (random movement) but with `wander_distance`=0, replace by idle movement type (0).", LOG_ERROR("sql.sql", "Table `creature` has creature (SpawnId: {} Entry: {}) with `MovementType`=1 (random movement) but with `wander_distance`=0, replace by idle movement type (0).",
spawnId, id1, id2, id3); spawnId, creatureId);
creatureData.movementType = IDLE_MOTION_TYPE; creatureData.movementType = IDLE_MOTION_TYPE;
} }
} }
@@ -2665,16 +2727,16 @@ CreatureData const* ObjectMgr::LoadCreatureDataFromDB(ObjectGuid::LowType spawnI
{ {
if (creatureData.wander_distance != 0.0f) if (creatureData.wander_distance != 0.0f)
{ {
LOG_ERROR("sql.sql", "Table `creature` has creature (SpawnId: {} Entries: {}, {}, {}) with `MovementType`=0 (idle) have `wander_distance`<>0, set to 0.", LOG_ERROR("sql.sql", "Table `creature` has creature (SpawnId: {} Entry: {}) with `MovementType`=0 (idle) have `wander_distance`<>0, set to 0.",
spawnId, id1, id2, id3); spawnId, creatureId);
creatureData.wander_distance = 0.0f; creatureData.wander_distance = 0.0f;
} }
} }
if (creatureData.phaseMask == 0) if (creatureData.phaseMask == 0)
{ {
LOG_ERROR("sql.sql", "Table `creature` has creature (SpawnId: {} Entries: {}, {}, {}) with `phaseMask`=0 (not visible for anyone), set to 1.", LOG_ERROR("sql.sql", "Table `creature` has creature (SpawnId: {} Entry: {}) with `phaseMask`=0 (not visible for anyone), set to 1.",
spawnId, id1, id2, id3); spawnId, creatureId);
creatureData.phaseMask = 1; creatureData.phaseMask = 1;
} }
@@ -2814,7 +2876,7 @@ ObjectGuid::LowType ObjectMgr::AddCreData(uint32 entry, uint32 mapId, float x, f
CreatureData& data = NewOrExistCreatureData(spawnId); CreatureData& data = NewOrExistCreatureData(spawnId);
data.spawnId = spawnId; data.spawnId = spawnId;
data.spawnMask = spawnId; data.spawnMask = spawnId;
data.id1 = entry; data.id = entry;
data.id2 = 0; data.id2 = 0;
data.id3 = 0; data.id3 = 0;
data.mapid = mapId; data.mapid = mapId;
+2 -2
View File
@@ -428,7 +428,7 @@ Player* Group::GetInvited(const std::string& name) const
return nullptr; return nullptr;
} }
bool Group::AddMember(Player* player) bool Group::AddMember(Player* player, uint8 roles /* = 0 */)
{ {
if (!player) if (!player)
return false; return false;
@@ -456,7 +456,7 @@ bool Group::AddMember(Player* player)
member.name = player->GetName(); member.name = player->GetName();
member.group = subGroup; member.group = subGroup;
member.flags = 0; member.flags = 0;
member.roles = 0; member.roles = roles;
m_memberSlots.push_back(member); m_memberSlots.push_back(member);
if (!isBGGroup() && !isBFGroup()) if (!isBGGroup() && !isBFGroup())
+1 -1
View File
@@ -204,7 +204,7 @@ public:
void RemoveInvite(Player* player); void RemoveInvite(Player* player);
void RemoveAllInvites(); void RemoveAllInvites();
bool AddLeaderInvite(Player* player); bool AddLeaderInvite(Player* player);
bool AddMember(Player* player); bool AddMember(Player* player, uint8 roles = 0);
bool RemoveMember(ObjectGuid guid, const RemoveMethod& method = GROUP_REMOVEMETHOD_DEFAULT, ObjectGuid kicker = ObjectGuid::Empty, const char* reason = nullptr); bool RemoveMember(ObjectGuid guid, const RemoveMethod& method = GROUP_REMOVEMETHOD_DEFAULT, ObjectGuid kicker = ObjectGuid::Empty, const char* reason = nullptr);
void ChangeLeader(ObjectGuid guid); void ChangeLeader(ObjectGuid guid);
void SetLootMethod(LootMethod method); void SetLootMethod(LootMethod method);
@@ -132,6 +132,13 @@ void WorldSession::HandleAuctionSellItem(WorldPacket& recvData)
return; return;
} }
if (sWorld->getBoolConfig(CONFIG_TRIAL_RESTRICTION_AUCTION) && IsTrialAccount())
{
SendAuctionCommandResult(0, AUCTION_SELL_ITEM, ERR_AUCTION_RESTRICTED_ACCOUNT);
recvData.rfinish();
return;
}
for (uint32 i = 0; i < itemsCount; ++i) for (uint32 i = 0; i < itemsCount; ++i)
{ {
recvData >> itemGUIDs[i]; recvData >> itemGUIDs[i];
@@ -279,7 +286,7 @@ void WorldSession::HandleAuctionSellItem(WorldPacket& recvData)
return; return;
} }
CreatureTemplate const* auctioneerInfo = sObjectMgr->GetCreatureTemplate(auctioneerData->id1); CreatureTemplate const* auctioneerInfo = sObjectMgr->GetCreatureTemplate(auctioneerData->id);
if (!auctioneerInfo) if (!auctioneerInfo)
{ {
LOG_ERROR("network.opcode", "Non existing auctioneer ({})", auctioneer.ToString()); LOG_ERROR("network.opcode", "Non existing auctioneer ({})", auctioneer.ToString());
@@ -426,6 +433,12 @@ void WorldSession::HandleAuctionPlaceBid(WorldPacket& recvData)
if (!auctionId || !price) if (!auctionId || !price)
return; //check for cheaters return; //check for cheaters
if (sWorld->getBoolConfig(CONFIG_TRIAL_RESTRICTION_AUCTION) && IsTrialAccount())
{
SendAuctionCommandResult(0, AUCTION_PLACE_BID, ERR_AUCTION_RESTRICTED_ACCOUNT);
return;
}
Creature* creature = GetPlayer()->GetNPCIfCanInteractWith(auctioneer, UNIT_NPC_FLAG_AUCTIONEER); Creature* creature = GetPlayer()->GetNPCIfCanInteractWith(auctioneer, UNIT_NPC_FLAG_AUCTIONEER);
if (!creature) if (!creature)
{ {
@@ -578,6 +591,12 @@ void WorldSession::HandleAuctionRemoveItem(WorldPacket& recvData)
recvData >> auctioneer; recvData >> auctioneer;
recvData >> auctionId; recvData >> auctionId;
if (sWorld->getBoolConfig(CONFIG_TRIAL_RESTRICTION_AUCTION) && IsTrialAccount())
{
SendAuctionCommandResult(0, AUCTION_CANCEL, ERR_AUCTION_RESTRICTED_ACCOUNT);
return;
}
Creature* creature = GetPlayer()->GetNPCIfCanInteractWith(auctioneer, UNIT_NPC_FLAG_AUCTIONEER); Creature* creature = GetPlayer()->GetNPCIfCanInteractWith(auctioneer, UNIT_NPC_FLAG_AUCTIONEER);
if (!creature) if (!creature)
{ {
+28
View File
@@ -34,6 +34,7 @@
#include "Opcodes.h" #include "Opcodes.h"
#include "Player.h" #include "Player.h"
#include "ScriptMgr.h" #include "ScriptMgr.h"
#include "SocialMgr.h"
#include "SpellAuraEffects.h" #include "SpellAuraEffects.h"
#include "SpellAuras.h" #include "SpellAuras.h"
#include "Util.h" #include "Util.h"
@@ -112,6 +113,18 @@ void WorldSession::HandleMessagechatOpcode(WorldPacket& recvData)
} }
} }
// Trial accounts cannot speak in chat channels or guild/officer chat. Whisper is handled later.
// Addon traffic (LANG_ADDON) is excluded; it carries the Warden Lua check response over guild chat.
if (sWorld->getBoolConfig(CONFIG_TRIAL_RESTRICTION_CHAT) && IsTrialAccount() && lang != LANG_ADDON
&& (type == CHAT_MSG_CHANNEL || type == CHAT_MSG_GUILD || type == CHAT_MSG_OFFICER))
{
WorldPacket data;
ChatHandler::BuildChatPacket(data, CHAT_MSG_RESTRICTED, LANG_UNIVERSAL, sender, sender, "");
SendPacket(&data);
recvData.rfinish();
return;
}
// pussywizard: chatting on most chat types requires 2 hours played to prevent spam/abuse // pussywizard: chatting on most chat types requires 2 hours played to prevent spam/abuse
if (!HasPermission(rbac::RBAC_PERM_SKIP_CHECK_CHAT_CHANNEL_REQ)) if (!HasPermission(rbac::RBAC_PERM_SKIP_CHECK_CHAT_CHANNEL_REQ))
{ {
@@ -401,6 +414,17 @@ void WorldSession::HandleMessagechatOpcode(WorldPacket& recvData)
return; return;
} }
// Trial accounts can only whisper players who have them on their friend list, or players who whispered them first.
if (sWorld->getBoolConfig(CONFIG_TRIAL_RESTRICTION_CHAT) && IsTrialAccount() && receiver != sender
&& !receiver->GetSocial()->HasFriend(sender->GetGUID())
&& !sender->IsInWhisperWhiteList(receiver->GetGUID()))
{
WorldPacket data;
ChatHandler::BuildChatPacket(data, CHAT_MSG_RESTRICTED, LANG_UNIVERSAL, sender, sender, "");
SendPacket(&data);
return;
}
if (!sWorld->getBoolConfig(CONFIG_ALLOW_TWO_SIDE_INTERACTION_CHAT) && senderIsPlayer && receiverIsPlayer) if (!sWorld->getBoolConfig(CONFIG_ALLOW_TWO_SIDE_INTERACTION_CHAT) && senderIsPlayer && receiverIsPlayer)
if (GetPlayer()->GetTeamId() != receiver->GetTeamId()) if (GetPlayer()->GetTeamId() != receiver->GetTeamId())
{ {
@@ -420,6 +444,10 @@ void WorldSession::HandleMessagechatOpcode(WorldPacket& recvData)
(HasPermission(rbac::RBAC_PERM_CAN_FILTER_WHISPERS) && !sender->isAcceptWhispers() && !sender->IsInWhisperWhiteList(receiver->GetGUID()))) (HasPermission(rbac::RBAC_PERM_CAN_FILTER_WHISPERS) && !sender->isAcceptWhispers() && !sender->IsInWhisperWhiteList(receiver->GetGUID())))
sender->AddWhisperWhiteList(receiver->GetGUID()); sender->AddWhisperWhiteList(receiver->GetGUID());
// Allow a trial-account recipient to whisper back without being on the sender's friend list.
if (receiver->GetSession()->IsTrialAccount() && !receiver->IsInWhisperWhiteList(sender->GetGUID()))
receiver->AddWhisperWhiteList(sender->GetGUID());
GetPlayer()->Whisper(msg, Language(lang), receiver); GetPlayer()->Whisper(msg, Language(lang), receiver);
} }
break; break;
+40 -2
View File
@@ -89,6 +89,12 @@ void WorldSession::HandleGroupInviteOpcode(WorldPacket& recvData)
if (!sScriptMgr->OnPlayerCanGroupInvite(invitingPlayer, membername)) if (!sScriptMgr->OnPlayerCanGroupInvite(invitingPlayer, membername))
return; return;
if (sWorld->getBoolConfig(CONFIG_TRIAL_RESTRICTION_PARTY) && IsTrialAccount())
{
SendPartyResult(PARTY_OP_INVITE, membername, ERR_INVITE_RESTRICTED);
return;
}
if (invitingPlayer->IsSpectator() || invitedPlayer->IsSpectator()) if (invitingPlayer->IsSpectator() || invitedPlayer->IsSpectator())
{ {
SendPartyResult(PARTY_OP_INVITE, membername, ERR_INVITE_RESTRICTED); SendPartyResult(PARTY_OP_INVITE, membername, ERR_INVITE_RESTRICTED);
@@ -244,6 +250,40 @@ void WorldSession::HandleGroupAcceptOpcode(WorldPacket& recvData)
if (!sScriptMgr->OnPlayerCanGroupAccept(GetPlayer(), group)) if (!sScriptMgr->OnPlayerCanGroupAccept(GetPlayer(), group))
return; return;
Player* leader = ObjectAccessor::FindConnectedPlayer(group->GetLeaderGUID());
// Trial accounts cannot join a group whose existing members are above the trial level cap.
if (sWorld->getBoolConfig(CONFIG_TRIAL_RESTRICTION_PARTY) && IsTrialAccount())
{
if (uint32 trialLevelCap = sWorld->getIntConfig(CONFIG_TRIAL_LEVEL_CAP))
{
uint8 leaderLevel = leader ? leader->GetLevel() : sCharacterCache->GetCharacterLevelByGuid(group->GetLeaderGUID());
if (leaderLevel > trialLevelCap)
{
SendPartyResult(PARTY_OP_INVITE, "", ERR_INVITE_RESTRICTED);
return;
}
for (auto const& slot : group->GetMemberSlots())
{
if (slot.guid == GetPlayer()->GetGUID())
continue;
uint8 memberLevel = 0;
if (Player* member = ObjectAccessor::FindConnectedPlayer(slot.guid))
memberLevel = member->GetLevel();
else
memberLevel = sCharacterCache->GetCharacterLevelByGuid(slot.guid);
if (memberLevel > trialLevelCap)
{
SendPartyResult(PARTY_OP_INVITE, "", ERR_INVITE_RESTRICTED);
return;
}
}
}
}
if (group->GetLeaderGUID() == GetPlayer()->GetGUID()) if (group->GetLeaderGUID() == GetPlayer()->GetGUID())
{ {
LOG_ERROR("network.opcode", "HandleGroupAcceptOpcode: player {} ({}) tried to accept an invite to his own group", LOG_ERROR("network.opcode", "HandleGroupAcceptOpcode: player {} ({}) tried to accept an invite to his own group",
@@ -258,8 +298,6 @@ void WorldSession::HandleGroupAcceptOpcode(WorldPacket& recvData)
return; return;
} }
Player* leader = ObjectAccessor::FindConnectedPlayer(group->GetLeaderGUID());
// Forming a new group, create it // Forming a new group, create it
if (!group->IsCreated()) if (!group->IsCreated())
{ {
@@ -21,6 +21,7 @@
#include "Log.h" #include "Log.h"
#include "ObjectMgr.h" #include "ObjectMgr.h"
#include "SocialMgr.h" #include "SocialMgr.h"
#include "World.h"
#include "WorldSession.h" #include "WorldSession.h"
void WorldSession::HandleGuildQueryOpcode(WorldPackets::Guild::QueryGuildInfo& query) void WorldSession::HandleGuildQueryOpcode(WorldPackets::Guild::QueryGuildInfo& query)
@@ -59,6 +60,14 @@ void WorldSession::HandleGuildAcceptOpcode(WorldPackets::Guild::AcceptGuildInvit
{ {
LOG_DEBUG("guild", "CMSG_GUILD_ACCEPT [{}]", GetPlayer()->GetName()); LOG_DEBUG("guild", "CMSG_GUILD_ACCEPT [{}]", GetPlayer()->GetName());
if (sWorld->getBoolConfig(CONFIG_TRIAL_RESTRICTION_GUILD) && IsTrialAccount())
{
Guild::SendCommandResult(this, GUILD_COMMAND_INVITE, ERR_GUILD_PERMISSIONS);
GetPlayer()->SetGuildIdInvited(0);
GetPlayer()->SetInGuild(0);
return;
}
if (!GetPlayer()->GetGuildId()) if (!GetPlayer()->GetGuildId())
if (Guild* guild = sGuildMgr->GetGuildById(GetPlayer()->GetGuildIdInvited())) if (Guild* guild = sGuildMgr->GetGuildById(GetPlayer()->GetGuildIdInvited()))
guild->HandleAcceptMember(this); guild->HandleAcceptMember(this);
+6
View File
@@ -117,6 +117,12 @@ void WorldSession::HandleSendMail(WorldPacket& recvData)
Player* player = _player; Player* player = _player;
if (sWorld->getBoolConfig(CONFIG_TRIAL_RESTRICTION_MAIL) && IsTrialAccount())
{
player->SendMailResult(0, MAIL_SEND, MAIL_ERR_DISABLED_FOR_TRIAL_ACC);
return;
}
if (player->GetLevel() < sWorld->getIntConfig(CONFIG_MAIL_LEVEL_REQ)) if (player->GetLevel() < sWorld->getIntConfig(CONFIG_MAIL_LEVEL_REQ))
{ {
ChatHandler(this).SendNotification(LANG_MAIL_SENDER_REQ, sWorld->getIntConfig(CONFIG_MAIL_LEVEL_REQ)); ChatHandler(this).SendNotification(LANG_MAIL_SENDER_REQ, sWorld->getIntConfig(CONFIG_MAIL_LEVEL_REQ));
@@ -84,6 +84,12 @@ void WorldSession::HandlePetitionBuyOpcode(WorldPacket& recvData)
if (_player->GetGuildId()) if (_player->GetGuildId())
return; return;
if (sWorld->getBoolConfig(CONFIG_TRIAL_RESTRICTION_GUILD) && IsTrialAccount())
{
Guild::SendCommandResult(this, GUILD_COMMAND_CREATE, ERR_GUILD_PERMISSIONS);
return;
}
charterid = GUILD_CHARTER; charterid = GUILD_CHARTER;
cost = sWorld->getIntConfig(CONFIG_CHARTER_COST_GUILD); cost = sWorld->getIntConfig(CONFIG_CHARTER_COST_GUILD);
type = GUILD_CHARTER_TYPE; type = GUILD_CHARTER_TYPE;
@@ -455,6 +461,12 @@ void WorldSession::HandlePetitionSignOpcode(WorldPacket& recvData)
return; return;
} }
if (sWorld->getBoolConfig(CONFIG_TRIAL_RESTRICTION_GUILD) && IsTrialAccount())
{
Guild::SendCommandResult(this, GUILD_COMMAND_INVITE, ERR_GUILD_PERMISSIONS);
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());
@@ -680,6 +692,12 @@ void WorldSession::HandleTurnInPetitionOpcode(WorldPacket& recvData)
// Petition type (guild/arena) specific checks // Petition type (guild/arena) specific checks
if (type == GUILD_CHARTER_TYPE) if (type == GUILD_CHARTER_TYPE)
{ {
if (sWorld->getBoolConfig(CONFIG_TRIAL_RESTRICTION_GUILD) && IsTrialAccount())
{
Guild::SendCommandResult(this, GUILD_COMMAND_CREATE, ERR_GUILD_PERMISSIONS);
return;
}
// Check if player is already in a guild // Check if player is already in a guild
if (_player->GetGuildId()) if (_player->GetGuildId())
{ {
+25 -41
View File
@@ -27,6 +27,7 @@
#include "Opcodes.h" #include "Opcodes.h"
#include "Player.h" #include "Player.h"
#include "QuestDef.h" #include "QuestDef.h"
#include "QuestPackets.h"
#include "ScriptMgr.h" #include "ScriptMgr.h"
#include "World.h" #include "World.h"
#include "WorldPacket.h" #include "WorldPacket.h"
@@ -382,29 +383,23 @@ void WorldSession::HandleQuestgiverCancel(WorldPacket& /*recvData*/)
_player->PlayerTalkClass->SendCloseGossip(); _player->PlayerTalkClass->SendCloseGossip();
} }
void WorldSession::HandleQuestLogSwapQuest(WorldPacket& recvData) void WorldSession::HandleQuestLogSwapQuest(WorldPackets::Quest::QuestLogSwapQuest& packet)
{ {
uint8 slot1, slot2; if (packet.Slot1 == packet.Slot2 || packet.Slot1 >= MAX_QUEST_LOG_SIZE || packet.Slot2 >= MAX_QUEST_LOG_SIZE)
recvData >> slot1 >> slot2;
if (slot1 == slot2 || slot1 >= MAX_QUEST_LOG_SIZE || slot2 >= MAX_QUEST_LOG_SIZE)
return; return;
LOG_DEBUG("network", "WORLD: Received CMSG_QUESTLOG_SWAP_QUEST slot 1 = {}, slot 2 = {}", slot1, slot2); LOG_DEBUG("network", "WORLD: Received CMSG_QUESTLOG_SWAP_QUEST slot 1 = {}, slot 2 = {}", packet.Slot1, packet.Slot2);
GetPlayer()->SwapQuestSlot(slot1, slot2); GetPlayer()->SwapQuestSlot(packet.Slot1, packet.Slot2);
} }
void WorldSession::HandleQuestLogRemoveQuest(WorldPacket& recvData) void WorldSession::HandleQuestLogRemoveQuest(WorldPackets::Quest::QuestLogRemoveQuest& packet)
{ {
uint8 slot; LOG_DEBUG("network", "WORLD: Received CMSG_QUESTLOG_REMOVE_QUEST slot = {}", packet.Slot);
recvData >> slot;
LOG_DEBUG("network", "WORLD: Received CMSG_QUESTLOG_REMOVE_QUEST slot = {}", slot); if (packet.Slot < MAX_QUEST_LOG_SIZE)
if (slot < MAX_QUEST_LOG_SIZE)
{ {
if (uint32 questId = _player->GetQuestSlotQuestId(slot)) if (uint32 questId = _player->GetQuestSlotQuestId(packet.Slot))
{ {
if (!_player->TakeQuestSourceItem(questId, true)) if (!_player->TakeQuestSourceItem(questId, true))
return; // can't un-equip some items, reject quest cancel return; // can't un-equip some items, reject quest cancel
@@ -442,20 +437,17 @@ void WorldSession::HandleQuestLogRemoveQuest(WorldPacket& recvData)
} }
} }
_player->SetQuestSlot(slot, 0); _player->SetQuestSlot(packet.Slot, 0);
_player->UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_QUEST_ABANDONED, 1); _player->UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_QUEST_ABANDONED, 1);
} }
} }
void WorldSession::HandleQuestConfirmAccept(WorldPacket& recvData) void WorldSession::HandleQuestConfirmAccept(WorldPackets::Quest::QuestConfirmAcceptClient& packet)
{ {
uint32 questId; LOG_DEBUG("network", "WORLD: Received CMSG_QUEST_CONFIRM_ACCEPT quest = {}", packet.QuestId);
recvData >> questId;
LOG_DEBUG("network", "WORLD: Received CMSG_QUEST_CONFIRM_ACCEPT quest = {}", questId); if (Quest const* quest = sObjectMgr->GetQuestTemplate(packet.QuestId))
if (Quest const* quest = sObjectMgr->GetQuestTemplate(questId))
{ {
if (!quest->HasFlag(QUEST_FLAGS_PARTY_ACCEPT)) if (!quest->HasFlag(QUEST_FLAGS_PARTY_ACCEPT))
return; return;
@@ -530,21 +522,18 @@ void WorldSession::HandleQuestgiverCompleteQuest(WorldPacket& recvData)
} }
} }
void WorldSession::HandleQuestgiverQuestAutoLaunch(WorldPacket& /*recvPacket*/) void WorldSession::HandleQuestgiverQuestAutoLaunch(WorldPackets::Quest::QuestGiverQuestAutoLaunch& /*packet*/)
{ {
} }
void WorldSession::HandlePushQuestToParty(WorldPacket& recvPacket) void WorldSession::HandlePushQuestToParty(WorldPackets::Quest::PushQuestToParty& packet)
{ {
uint32 questId; if (!_player->CanShareQuest(packet.QuestId))
recvPacket >> questId;
if (!_player->CanShareQuest(questId))
return; return;
LOG_DEBUG("network", "WORLD: Received CMSG_PUSHQUESTTOPARTY quest = {}", questId); LOG_DEBUG("network", "WORLD: Received CMSG_PUSHQUESTTOPARTY quest = {}", packet.QuestId);
if (Quest const* quest = sObjectMgr->GetQuestTemplate(questId)) if (Quest const* quest = sObjectMgr->GetQuestTemplate(packet.QuestId))
{ {
if (Group* group = _player->GetGroup()) if (Group* group = _player->GetGroup())
{ {
@@ -561,7 +550,7 @@ void WorldSession::HandlePushQuestToParty(WorldPacket& recvPacket)
continue; continue;
} }
if (player->GetQuestStatus(questId) == QUEST_STATUS_COMPLETE) if (player->GetQuestStatus(packet.QuestId) == QUEST_STATUS_COMPLETE)
{ {
_player->SendPushToPartyResponse(player, QUEST_PARTY_MSG_FINISH_QUEST); _player->SendPushToPartyResponse(player, QUEST_PARTY_MSG_FINISH_QUEST);
continue; continue;
@@ -613,21 +602,16 @@ void WorldSession::HandlePushQuestToParty(WorldPacket& recvPacket)
} }
} }
void WorldSession::HandleQuestPushResult(WorldPacket& recvPacket) void WorldSession::HandleQuestPushResult(WorldPackets::Quest::QuestPushResultClient& packet)
{ {
ObjectGuid guid; if (_player->GetDivider() && _player->GetDivider() == packet.PlayerGuid)
uint32 questId;
uint8 msg;
recvPacket >> guid >> questId >> msg;
if (_player->GetDivider() && _player->GetDivider() == guid)
{ {
if (Player* player = ObjectAccessor::GetPlayer(*_player, _player->GetDivider())) if (Player* player = ObjectAccessor::GetPlayer(*_player, _player->GetDivider()))
{ {
WorldPacket data(MSG_QUEST_PUSH_RESULT, 8 + 4 + 1); WorldPackets::Quest::QuestPushResult questPushResult;
data << _player->GetGUID(); questPushResult.PlayerGuid = _player->GetGUID();
data << uint8(msg); // valid values: 0-8 questPushResult.QuestShareMessage = packet.QuestShareMessage;
player->SendDirectMessage(&data); player->SendDirectMessage(questPushResult.Write());
_player->SetDivider(); _player->SetDivider();
} }
} }
+14
View File
@@ -675,6 +675,13 @@ void WorldSession::HandleInitiateTradeOpcode(WorldPacket& recvPacket)
return; return;
} }
if (sWorld->getBoolConfig(CONFIG_TRIAL_RESTRICTION_TRADE) && IsTrialAccount())
{
info.Status = TRADE_STATUS_TRIAL_ACCOUNT;
SendTradeStatus(info);
return;
}
if (GetPlayer()->IsSpectator()) if (GetPlayer()->IsSpectator())
return; return;
@@ -722,6 +729,13 @@ void WorldSession::HandleInitiateTradeOpcode(WorldPacket& recvPacket)
return; return;
} }
if (sWorld->getBoolConfig(CONFIG_TRIAL_RESTRICTION_TRADE) && pOther->GetSession()->IsTrialAccount())
{
info.Status = TRADE_STATUS_TRIAL_ACCOUNT;
SendTradeStatus(info);
return;
}
if (pOther->GetTeamId() != _player->GetTeamId() && if (pOther->GetTeamId() != _player->GetTeamId() &&
!GetPlayer()->GetSession()->HasPermission(rbac::RBAC_PERM_ALLOW_TWO_SIDE_TRADE)) !GetPlayer()->GetSession()->HasPermission(rbac::RBAC_PERM_ALLOW_TWO_SIDE_TRADE))
{ {
+2 -2
View File
@@ -307,7 +307,7 @@ bool Map::AddToMap(T* obj, bool checkTransport)
if (obj->IsInWorld()) if (obj->IsInWorld())
{ {
ASSERT(obj->IsInGrid()); ASSERT(obj->IsInGrid());
obj->UpdateObjectVisibilityOnCreate(); obj->UpdateObjectVisibility(true);
return true; return true;
} }
@@ -344,7 +344,7 @@ bool Map::AddToMap(T* obj, bool checkTransport)
//something, such as vehicle, needs to be update immediately //something, such as vehicle, needs to be update immediately
//also, trigger needs to cast spell, if not update, cannot see visual //also, trigger needs to cast spell, if not update, cannot see visual
obj->UpdateObjectVisibility(true); obj->UpdateObjectVisibilityOnCreate();
// Post-visibility so accessories seat after the vehicle's create packet reaches clients. // Post-visibility so accessories seat after the vehicle's create packet reaches clients.
if (obj->IsCreature()) if (obj->IsCreature())
+1 -1
View File
@@ -28,7 +28,7 @@ public:
ZoneScript() {} ZoneScript() {}
virtual ~ZoneScript() {} virtual ~ZoneScript() {}
virtual uint32 GetCreatureEntry(ObjectGuid::LowType /*guidlow*/, CreatureData const* data) { return data->id1; } virtual uint32 GetCreatureEntry(ObjectGuid::LowType /*guidlow*/, CreatureData const* data) { return data->id; }
virtual uint32 GetGameObjectEntry(ObjectGuid::LowType /*guidlow*/, uint32 entry) { return entry; } virtual uint32 GetGameObjectEntry(ObjectGuid::LowType /*guidlow*/, uint32 entry) { return entry; }
virtual void OnCreatureCreate(Creature*) { } virtual void OnCreatureCreate(Creature*) { }
+1 -1
View File
@@ -96,7 +96,7 @@ void OPvPCapturePoint::AddCre(uint32 type, ObjectGuid::LowType guid, uint32 entr
return; return;
} }
entry = data->id1; entry = data->id;
} }
_creatures[type] = guid; _creatures[type] = guid;
@@ -32,6 +32,7 @@
#include "NPCPackets.h" #include "NPCPackets.h"
#include "PetPackets.h" #include "PetPackets.h"
#include "QueryPackets.h" #include "QueryPackets.h"
#include "QuestPackets.h"
#include "TotemPackets.h" #include "TotemPackets.h"
#include "WorldStatePackets.h" #include "WorldStatePackets.h"
@@ -0,0 +1,126 @@
/*
* This file is part of the AzerothCore Project. See AUTHORS file for Copyright information
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License along
* with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "QuestPackets.h"
WorldPacket const* WorldPackets::Quest::QuestUpdateComplete::Write()
{
_worldPacket << QuestId;
return &_worldPacket;
}
WorldPacket const* WorldPackets::Quest::QuestGiverQuestComplete::Write()
{
_worldPacket << QuestId;
_worldPacket << Experience;
_worldPacket << RewardMoney;
_worldPacket << RewardHonor;
_worldPacket << RewardTalents;
_worldPacket << RewardArena;
return &_worldPacket;
}
WorldPacket const* WorldPackets::Quest::QuestGiverQuestFailed::Write()
{
_worldPacket << QuestId;
_worldPacket << FailureReason;
return &_worldPacket;
}
WorldPacket const* WorldPackets::Quest::QuestUpdateFailedTimer::Write()
{
_worldPacket << QuestId;
return &_worldPacket;
}
WorldPacket const* WorldPackets::Quest::QuestGiverQuestInvalid::Write()
{
_worldPacket << FailureReason;
return &_worldPacket;
}
WorldPacket const* WorldPackets::Quest::QuestConfirmAccept::Write()
{
_worldPacket << QuestId;
_worldPacket << QuestTitle;
_worldPacket << PlayerGuid;
return &_worldPacket;
}
WorldPacket const* WorldPackets::Quest::QuestPushResult::Write()
{
_worldPacket << PlayerGuid;
_worldPacket << QuestShareMessage;
return &_worldPacket;
}
WorldPacket const* WorldPackets::Quest::QuestUpdateAddKill::Write()
{
_worldPacket << QuestId;
_worldPacket << CreatureEntry;
_worldPacket << CurrentCount;
_worldPacket << RequiredCount;
_worldPacket << ObjectiveGuid;
return &_worldPacket;
}
WorldPacket const* WorldPackets::Quest::QuestUpdateAddPvPKill::Write()
{
_worldPacket << QuestId;
_worldPacket << CurrentCount;
_worldPacket << RequiredCount;
return &_worldPacket;
}
void WorldPackets::Quest::QuestPushResultClient::Read()
{
_worldPacket >> PlayerGuid;
_worldPacket >> QuestId;
uint8 Message;
_worldPacket >> Message;
QuestShareMessage = static_cast<QuestShareMessages>(Message);
}
void WorldPackets::Quest::QuestLogSwapQuest::Read()
{
_worldPacket >> Slot1;
_worldPacket >> Slot2;
}
void WorldPackets::Quest::QuestLogRemoveQuest::Read()
{
_worldPacket >> Slot;
}
void WorldPackets::Quest::QuestConfirmAcceptClient::Read()
{
_worldPacket >> QuestId;
}
void WorldPackets::Quest::PushQuestToParty::Read()
{
_worldPacket >> QuestId;
}
@@ -0,0 +1,214 @@
/*
* This file is part of the AzerothCore Project. See AUTHORS file for Copyright information
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License along
* with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef QuestPackets_h__
#define QuestPackets_h__
#include "ObjectGuid.h"
#include "Packet.h"
#include "QuestDef.h"
namespace WorldPackets
{
namespace Quest
{
class QuestLogFull final : public ServerPacket
{
public:
QuestLogFull() : ServerPacket(SMSG_QUESTLOG_FULL, 0) {}
WorldPacket const* Write() override { return &_worldPacket; }
};
class QuestUpdateComplete final : public ServerPacket
{
public:
QuestUpdateComplete() : ServerPacket(SMSG_QUESTUPDATE_COMPLETE, 4) {}
WorldPacket const* Write() override;
uint32 QuestId = 0;
};
class QuestGiverQuestComplete final : public ServerPacket
{
public:
QuestGiverQuestComplete() : ServerPacket(SMSG_QUESTGIVER_QUEST_COMPLETE, 4 + 4 + 4 + 4 + 4 + 4) {}
WorldPacket const* Write() override;
uint32 QuestId = 0;
uint32 Experience = 0;
uint32 RewardMoney = 0;
uint32 RewardHonor = 0;
uint32 RewardTalents = 0;
uint32 RewardArena = 0;
};
class QuestGiverQuestFailed final : public ServerPacket
{
public:
QuestGiverQuestFailed() : ServerPacket(SMSG_QUESTGIVER_QUEST_FAILED, 4 + 4) {}
WorldPacket const* Write() override;
uint32 QuestId = 0;
uint32 FailureReason = 0;
};
class QuestUpdateFailedTimer final : public ServerPacket
{
public:
QuestUpdateFailedTimer() : ServerPacket(SMSG_QUESTUPDATE_FAILEDTIMER, 4) {}
WorldPacket const* Write() override;
uint32 QuestId = 0;
};
class QuestGiverQuestInvalid final : public ServerPacket
{
public:
QuestGiverQuestInvalid() : ServerPacket(SMSG_QUESTGIVER_QUEST_INVALID, 4) {}
WorldPacket const* Write() override;
QuestFailedReason FailureReason = INVALIDREASON_DONT_HAVE_REQ;
};
class QuestConfirmAccept final : public ServerPacket
{
public:
// 4 (QuestId) + title (variable, 16 reserved estimate) + 8 (GUID)
QuestConfirmAccept() : ServerPacket(SMSG_QUEST_CONFIRM_ACCEPT, 4 + 16 + 8) {}
WorldPacket const* Write() override;
uint32 QuestId = 0;
std::string_view QuestTitle = "";
ObjectGuid PlayerGuid;
};
class QuestPushResult final : public ServerPacket
{
public:
QuestPushResult() : ServerPacket(MSG_QUEST_PUSH_RESULT, 8 + 1) {}
WorldPacket const* Write() override;
ObjectGuid PlayerGuid;
QuestShareMessages QuestShareMessage = QUEST_PARTY_MSG_SHARING_QUEST; // valid values: 0-10
};
class QuestUpdateAddItem final : public ServerPacket
{
public:
QuestUpdateAddItem() : ServerPacket(SMSG_QUESTUPDATE_ADD_ITEM, 0) {}
WorldPacket const* Write() override { return &_worldPacket; }
};
class QuestUpdateAddKill final : public ServerPacket
{
public:
QuestUpdateAddKill() : ServerPacket(SMSG_QUESTUPDATE_ADD_KILL, 4 * 4 + 8) {}
WorldPacket const* Write() override;
uint32 QuestId = 0;
uint32 CreatureEntry = 0;
uint32 CurrentCount = 0;
uint32 RequiredCount = 0;
ObjectGuid ObjectiveGuid;
};
class QuestUpdateAddPvPKill final : public ServerPacket
{
public:
QuestUpdateAddPvPKill() : ServerPacket(SMSG_QUESTUPDATE_ADD_PVP_KILL, 3 * 4) {}
WorldPacket const* Write() override;
uint32 QuestId = 0;
uint32 CurrentCount = 0;
uint32 RequiredCount = 0;
};
class QuestPushResultClient final : public ClientPacket
{
public:
QuestPushResultClient(WorldPacket&& packet) : ClientPacket(MSG_QUEST_PUSH_RESULT, std::move(packet)) {}
void Read() override;
ObjectGuid PlayerGuid;
uint32 QuestId = 0;
QuestShareMessages QuestShareMessage = QUEST_PARTY_MSG_SHARING_QUEST;
};
class QuestGiverQuestAutoLaunch final : public ClientPacket
{
public:
QuestGiverQuestAutoLaunch(WorldPacket&& packet) : ClientPacket(CMSG_QUESTGIVER_QUEST_AUTOLAUNCH, std::move(packet)) {}
void Read() override {}
};
class QuestLogSwapQuest final : public ClientPacket
{
public:
QuestLogSwapQuest(WorldPacket&& packet) : ClientPacket(CMSG_QUESTLOG_SWAP_QUEST, std::move(packet)) {}
void Read() override;
uint8 Slot1 = 0;
uint8 Slot2 = 0;
};
class QuestLogRemoveQuest final : public ClientPacket
{
public:
QuestLogRemoveQuest(WorldPacket&& packet) : ClientPacket(CMSG_QUESTLOG_REMOVE_QUEST, std::move(packet)) {}
void Read() override;
uint8 Slot = 0;
};
class QuestConfirmAcceptClient final : public ClientPacket
{
public:
QuestConfirmAcceptClient(WorldPacket&& packet) : ClientPacket(CMSG_QUEST_CONFIRM_ACCEPT, std::move(packet)) {}
void Read() override;
uint32 QuestId = 0;
};
class PushQuestToParty final : public ClientPacket
{
public:
PushQuestToParty(WorldPacket&& packet) : ClientPacket(CMSG_PUSHQUESTTOPARTY, std::move(packet)) {}
void Read() override;
uint32 QuestId = 0;
};
}
}
#endif // QuestPackets_h__
+16 -6
View File
@@ -192,6 +192,16 @@ namespace WorldPackets
class ItemRefund; class ItemRefund;
} }
namespace Quest
{
class QuestPushResultClient;
class QuestGiverQuestAutoLaunch;
class QuestLogSwapQuest;
class QuestLogRemoveQuest;
class QuestConfirmAcceptClient;
class PushQuestToParty;
}
namespace Calendar namespace Calendar
{ {
class GetEvent; class GetEvent;
@@ -934,13 +944,13 @@ public: // opcodes handlers
void HandleQuestgiverRequestRewardOpcode(WorldPacket& recvPacket); void HandleQuestgiverRequestRewardOpcode(WorldPacket& recvPacket);
void HandleQuestQueryOpcode(WorldPacket& recvPacket); void HandleQuestQueryOpcode(WorldPacket& recvPacket);
void HandleQuestgiverCancel(WorldPacket& recvData); void HandleQuestgiverCancel(WorldPacket& recvData);
void HandleQuestLogSwapQuest(WorldPacket& recvData); void HandleQuestLogSwapQuest(WorldPackets::Quest::QuestLogSwapQuest& packet);
void HandleQuestLogRemoveQuest(WorldPacket& recvData); void HandleQuestLogRemoveQuest(WorldPackets::Quest::QuestLogRemoveQuest& packet);
void HandleQuestConfirmAccept(WorldPacket& recvData); void HandleQuestConfirmAccept(WorldPackets::Quest::QuestConfirmAcceptClient& packet);
void HandleQuestgiverCompleteQuest(WorldPacket& recvData); void HandleQuestgiverCompleteQuest(WorldPacket& recvData);
void HandleQuestgiverQuestAutoLaunch(WorldPacket& recvPacket); void HandleQuestgiverQuestAutoLaunch(WorldPackets::Quest::QuestGiverQuestAutoLaunch& packet);
void HandlePushQuestToParty(WorldPacket& recvPacket); void HandlePushQuestToParty(WorldPackets::Quest::PushQuestToParty& packet);
void HandleQuestPushResult(WorldPacket& recvPacket); void HandleQuestPushResult(WorldPackets::Quest::QuestPushResultClient& packet);
void HandleMessagechatOpcode(WorldPacket& recvPacket); void HandleMessagechatOpcode(WorldPacket& recvPacket);
void SendPlayerNotFoundNotice(std::string const& name); void SendPlayerNotFoundNotice(std::string const& name);
+6 -1
View File
@@ -329,7 +329,12 @@ void WorldSessionMgr::AddSession_(WorldSession* session)
// don't count this session when checking player limit // don't count this session when checking player limit
--Sessions; --Sessions;
if (pLimit > 0 && Sessions >= pLimit && !session->HasPermission(rbac::RBAC_PERM_SKIP_QUEUE) && !session->CanSkipQueue() && !HasRecentlyDisconnected(session)) // Trial accounts do not get account-flag queue priority. RBAC_PERM_SKIP_QUEUE is still honored
// since it can be granted intentionally to specific accounts.
bool trialQueueRestricted = sWorld->getBoolConfig(CONFIG_TRIAL_RESTRICTION_QUEUE) && session->IsTrialAccount();
bool canSkipQueue = session->HasPermission(rbac::RBAC_PERM_SKIP_QUEUE) || (!trialQueueRestricted && session->CanSkipQueue());
if (pLimit > 0 && Sessions >= pLimit && !canSkipQueue && !HasRecentlyDisconnected(session))
{ {
AddQueuedPlayer(session); AddQueuedPlayer(session);
UpdateMaxSessionCounters(); UpdateMaxSessionCounters();
@@ -184,12 +184,7 @@ uint32 GetExplicitDiscoverySpell(uint32 spellId, Player* player)
continue; continue;
if (item_iter->chance > roll) if (item_iter->chance > roll)
{
// Update skill, not Book of Glyph Mastery
if (spellId != 64323)
player->UpdateGatherSkill(SKILL_INSCRIPTION, player->GetPureSkillValue(SKILL_INSCRIPTION), item_iter->reqSkillValue);
return item_iter->spellId; return item_iter->spellId;
}
roll -= item_iter->chance; roll -= item_iter->chance;
} }
@@ -847,7 +847,9 @@ void AuraEffect::ApplySpellMod(Unit* target, bool apply)
Aura* aura = iter->second->GetBase(); Aura* aura = iter->second->GetBase();
// only passive and permament auras-active auras should have amount set on spellcast and not be affected // only passive and permament auras-active auras should have amount set on spellcast and not be affected
// if aura is casted by others, it will not be affected // if aura is casted by others, it will not be affected
if ((aura->IsPassive() || aura->IsPermanent()) && aura->GetCasterGUID() == guid && aura->GetSpellInfo()->IsAffectedBySpellMod(m_spellmod)) if ((aura->IsPassive() || aura->IsPermanent()) && aura->GetCasterGUID() == guid &&
aura->GetSpellInfo()->CheckShapeshift(target->GetShapeshiftForm()) == SPELL_CAST_OK &&
aura->GetSpellInfo()->IsAffectedBySpellMod(m_spellmod))
{ {
if (GetMiscValue() == SPELLMOD_ALL_EFFECTS) if (GetMiscValue() == SPELLMOD_ALL_EFFECTS)
{ {
@@ -4244,15 +4246,18 @@ void AuraEffect::HandleModPercentStat(AuraApplication const* aurApp, uint8 mode,
for (int32 i = STAT_STRENGTH; i < MAX_STATS; ++i) for (int32 i = STAT_STRENGTH; i < MAX_STATS; ++i)
{ {
if (apply) if (GetMiscValue() == i || GetMiscValue() == -1)
target->ApplyStatPctModifier(UnitMods(UNIT_MOD_STAT_START + i), BASE_PCT, float(GetAmount()));
else
{ {
float amount = target->GetTotalAuraMultiplier(SPELL_AURA_MOD_PERCENT_STAT, [i](AuraEffect const* aurEff) if (apply)
target->ApplyStatPctModifier(UnitMods(UNIT_MOD_STAT_START + i), BASE_PCT, float(GetAmount()));
else
{ {
return (aurEff->GetMiscValue() == i || aurEff->GetMiscValue() == -1); float amount = target->GetTotalAuraMultiplier(SPELL_AURA_MOD_PERCENT_STAT, [i](AuraEffect const* aurEff)
}); {
target->SetStatPctModifier(UnitMods(UNIT_MOD_STAT_START + i), BASE_PCT, amount); return (aurEff->GetMiscValue() == i || aurEff->GetMiscValue() == -1);
});
target->SetStatPctModifier(UnitMods(UNIT_MOD_STAT_START + i), BASE_PCT, amount);
}
} }
} }
} }
@@ -1068,6 +1068,8 @@ void SpellMgr::LoadSpellInfoCorrections()
{ {
spellInfo->AttributesEx3 |= SPELL_ATTR3_SUPPRESS_TARGET_PROCS; spellInfo->AttributesEx3 |= SPELL_ATTR3_SUPPRESS_TARGET_PROCS;
spellInfo->AttributesEx4 |= SPELL_ATTR4_DAMAGE_DOESNT_BREAK_AURAS; spellInfo->AttributesEx4 |= SPELL_ATTR4_DAMAGE_DOESNT_BREAK_AURAS;
// Explosion is AoE triggered on aura expiry - cannot be reflected (retail: Spell Reflection only works on single-target spells)
spellInfo->AttributesEx |= SPELL_ATTR1_NO_REFLECTION;
}); });
// Evocation // Evocation
+21
View File
@@ -33,6 +33,8 @@
#include "Tokenize.h" #include "Tokenize.h"
#include "World.h" #include "World.h"
#include <algorithm>
bool IsPrimaryProfessionSkill(uint32 skill) bool IsPrimaryProfessionSkill(uint32 skill)
{ {
SkillLineEntry const* pSkill = sSkillLineStore.LookupEntry(skill); SkillLineEntry const* pSkill = sSkillLineStore.LookupEntry(skill);
@@ -687,6 +689,24 @@ SpellLearnSkillNode const* SpellMgr::GetSpellLearnSkill(uint32 spell_id) const
return nullptr; return nullptr;
} }
std::vector<uint32> SpellMgr::GetSkillRankSpells(uint32 skillId) const
{
// Returns every spell that grants this skill via SPELL_EFFECT_SKILL,
// i.e. the profession rank/proficiency spells (Apprentice -> Grand Master),
// ordered by step. Not strictly limited to the six ranks: any skill-granting
// spell for the line is included.
std::vector<uint32> result;
for (auto const& [spellId, node] : mSpellLearnSkills)
if (node.skill == skillId)
result.push_back(spellId);
std::ranges::sort(result, [this](uint32 a, uint32 b)
{
return mSpellLearnSkills.at(a).step < mSpellLearnSkills.at(b).step;
});
return result;
}
SpellTargetPosition const* SpellMgr::GetSpellTargetPosition(uint32 spell_id, SpellEffIndex effIndex) const SpellTargetPosition const* SpellMgr::GetSpellTargetPosition(uint32 spell_id, SpellEffIndex effIndex) const
{ {
SpellTargetPositionMap::const_iterator itr = mSpellTargetPositions.find(std::make_pair(spell_id, effIndex)); SpellTargetPositionMap::const_iterator itr = mSpellTargetPositions.find(std::make_pair(spell_id, effIndex));
@@ -3257,6 +3277,7 @@ void SpellMgr::LoadSpellInfoCustomAttributes()
case 44801: // Spectral Invisibility (Kalecgos, SWP) case 44801: // Spectral Invisibility (Kalecgos, SWP)
case 46021: // Spectral Realm (SWP) case 46021: // Spectral Realm (SWP)
case 52951: // Chapel Invisibility (DK starting zone) case 52951: // Chapel Invisibility (DK starting zone)
case 43062: // Alpha Worg: Garwal's Invisibility
break; break;
default: default:
spellInfo->AuraInterruptFlags |= AURA_INTERRUPT_FLAG_CAST; spellInfo->AuraInterruptFlags |= AURA_INTERRUPT_FLAG_CAST;
+1
View File
@@ -687,6 +687,7 @@ public:
// Spell learning // Spell learning
[[nodiscard]] SpellLearnSkillNode const* GetSpellLearnSkill(uint32 spell_id) const; [[nodiscard]] SpellLearnSkillNode const* GetSpellLearnSkill(uint32 spell_id) const;
[[nodiscard]] std::vector<uint32> GetSkillRankSpells(uint32 skillId) const;
// Spell target coordinates // Spell target coordinates
[[nodiscard]] SpellTargetPosition const* GetSpellTargetPosition(uint32 spell_id, SpellEffIndex effIndex) const; [[nodiscard]] SpellTargetPosition const* GetSpellTargetPosition(uint32 spell_id, SpellEffIndex effIndex) const;
+11
View File
@@ -348,6 +348,17 @@ void WorldConfig::BuildConfigCache()
SetConfigValue<bool>(CONFIG_CHAT_MUTE_FIRST_LOGIN, "Chat.MuteFirstLogin", false); SetConfigValue<bool>(CONFIG_CHAT_MUTE_FIRST_LOGIN, "Chat.MuteFirstLogin", false);
SetConfigValue<uint32>(CONFIG_CHAT_TIME_MUTE_FIRST_LOGIN, "Chat.MuteTimeFirstLogin", 120); SetConfigValue<uint32>(CONFIG_CHAT_TIME_MUTE_FIRST_LOGIN, "Chat.MuteTimeFirstLogin", 120);
SetConfigValue<bool>(CONFIG_TRIAL_RESTRICTION_CHAT, "Trial.Restriction.Chat", true);
SetConfigValue<bool>(CONFIG_TRIAL_RESTRICTION_MAIL, "Trial.Restriction.Mail", true);
SetConfigValue<bool>(CONFIG_TRIAL_RESTRICTION_TRADE, "Trial.Restriction.Trade", true);
SetConfigValue<bool>(CONFIG_TRIAL_RESTRICTION_AUCTION, "Trial.Restriction.Auction", true);
SetConfigValue<bool>(CONFIG_TRIAL_RESTRICTION_PARTY, "Trial.Restriction.Party", true);
SetConfigValue<bool>(CONFIG_TRIAL_RESTRICTION_GUILD, "Trial.Restriction.Guild", true);
SetConfigValue<bool>(CONFIG_TRIAL_RESTRICTION_QUEUE, "Trial.Restriction.Queue", true);
SetConfigValue<uint32>(CONFIG_TRIAL_LEVEL_CAP, "Trial.LevelCap", 20);
SetConfigValue<uint32>(CONFIG_TRIAL_MONEY_CAP, "Trial.MoneyCap", 100000); // copper, 10 gold
SetConfigValue<uint32>(CONFIG_TRIAL_TRADE_SKILL_CAP, "Trial.TradeSkillCap", 100);
SetConfigValue<uint32>(CONFIG_EVENT_ANNOUNCE, "Event.Announce", 0); SetConfigValue<uint32>(CONFIG_EVENT_ANNOUNCE, "Event.Announce", 0);
SetConfigValue<float>(CONFIG_CREATURE_LEASH_RADIUS, "CreatureLeashRadius", 30.0f); SetConfigValue<float>(CONFIG_CREATURE_LEASH_RADIUS, "CreatureLeashRadius", 30.0f);
+10
View File
@@ -501,6 +501,16 @@ enum ServerConfigs
CONFIG_ACHIEVEMENT_REALM_FIRST_KILL_WINDOW, CONFIG_ACHIEVEMENT_REALM_FIRST_KILL_WINDOW,
CONFIG_ACHIEVEMENT_REALM_FIRST_RACE_LIMIT_ONE_PER_CHARACTER, CONFIG_ACHIEVEMENT_REALM_FIRST_RACE_LIMIT_ONE_PER_CHARACTER,
CONFIG_CHATLOG_ENABLED, CONFIG_CHATLOG_ENABLED,
CONFIG_TRIAL_RESTRICTION_CHAT,
CONFIG_TRIAL_RESTRICTION_MAIL,
CONFIG_TRIAL_RESTRICTION_TRADE,
CONFIG_TRIAL_RESTRICTION_AUCTION,
CONFIG_TRIAL_RESTRICTION_PARTY,
CONFIG_TRIAL_RESTRICTION_GUILD,
CONFIG_TRIAL_RESTRICTION_QUEUE,
CONFIG_TRIAL_LEVEL_CAP,
CONFIG_TRIAL_MONEY_CAP,
CONFIG_TRIAL_TRADE_SKILL_CAP,
MAX_NUM_SERVER_CONFIGS MAX_NUM_SERVER_CONFIGS
}; };
+2 -2
View File
@@ -609,7 +609,7 @@ public:
CreatureData const* spawnpoint = nullptr; CreatureData const* spawnpoint = nullptr;
for (auto const& pair : sObjectMgr->GetAllCreatureData()) for (auto const& pair : sObjectMgr->GetAllCreatureData())
{ {
if (pair.second.id1 != entry) if (pair.second.id != entry)
{ {
continue; continue;
} }
@@ -633,7 +633,7 @@ public:
std::vector<CreatureData const*> spawnpoints; std::vector<CreatureData const*> spawnpoints;
for (auto const& pair : sObjectMgr->GetAllCreatureData()) for (auto const& pair : sObjectMgr->GetAllCreatureData())
{ {
if (pair.second.id1 != entry) if (pair.second.id != entry)
{ {
continue; continue;
} }
+15 -1
View File
@@ -309,6 +309,8 @@ public:
static bool HandleLearnAllCraftsCommand(ChatHandler* handler) static bool HandleLearnAllCraftsCommand(ChatHandler* handler)
{ {
Player* target = handler->GetSession()->GetPlayer();
for (uint32 i = 0; i < sSkillLineStore.GetNumRows(); ++i) for (uint32 i = 0; i < sSkillLineStore.GetNumRows(); ++i)
{ {
SkillLineEntry const* skillInfo = sSkillLineStore.LookupEntry(i); SkillLineEntry const* skillInfo = sSkillLineStore.LookupEntry(i);
@@ -318,7 +320,10 @@ public:
if ((skillInfo->categoryId == SKILL_CATEGORY_PROFESSION || skillInfo->categoryId == SKILL_CATEGORY_SECONDARY) && if ((skillInfo->categoryId == SKILL_CATEGORY_PROFESSION || skillInfo->categoryId == SKILL_CATEGORY_SECONDARY) &&
skillInfo->canLink) // only prof. with recipes have skillInfo->canLink) // only prof. with recipes have
{ {
HandleLearnSkillRecipesHelper(handler->GetSession()->GetPlayer(), skillInfo->id); HandleLearnSkillRecipesHelper(target, skillInfo->id);
uint16 const maxLevel = target->GetPureMaxSkillValue(skillInfo->id);
target->SetSkill(skillInfo->id, target->GetSkillStep(skillInfo->id), maxLevel, maxLevel);
} }
} }
@@ -388,6 +393,15 @@ public:
static void HandleLearnSkillRecipesHelper(Player* player, uint32 skillId) static void HandleLearnSkillRecipesHelper(Player* player, uint32 skillId)
{ {
// Rank spells (Apprentice -> Grand Master) must be learned so that the
// skill-cleanup loop in Player::SetSkill (which calls removeSpell on the
// first spell in each chain) can walk forward and strip every rank on
// profession unlearn. Without the first rank in the spellbook that loop
// bails out and the leftover rank spells re-grant the skill after relog
// (issue #2330).
for (uint32 rankSpell : sSpellMgr->GetSkillRankSpells(skillId))
player->learnSpell(rankSpell);
uint32 classmask = player->getClassMask(); uint32 classmask = player->getClassMask();
for (SkillLineAbilityEntry const* skillLine : GetSkillLineAbilitiesBySkillLine(skillId)) for (SkillLineAbilityEntry const* skillLine : GetSkillLineAbilitiesBySkillLine(skillId))
+8 -8
View File
@@ -77,19 +77,19 @@ public:
QueryResult result; QueryResult result;
uint32 creatureCount = 0; uint32 creatureCount = 0;
result = WorldDatabase.Query("SELECT COUNT(guid) FROM creature WHERE id1='{}' OR id2='{}' OR id3='{}'", uint32(creatureId), uint32(creatureId), uint32(creatureId)); result = WorldDatabase.Query("SELECT COUNT(guid) FROM creature WHERE id = '{}' OR guid IN (SELECT spawnId FROM creature_multispawn WHERE entry = '{}')", uint32(creatureId), uint32(creatureId));
if (result) if (result)
creatureCount = (*result)[0].Get<uint64>(); creatureCount = (*result)[0].Get<uint64>();
if (handler->GetSession()) if (handler->GetSession())
{ {
Player* player = handler->GetSession()->GetPlayer(); Player* player = handler->GetSession()->GetPlayer();
result = WorldDatabase.Query("SELECT guid, position_x, position_y, position_z, map, (POW(position_x - '{}', 2) + POW(position_y - '{}', 2) + POW(position_z - '{}', 2)) AS order_ FROM creature WHERE id1='{}' OR id2='{}' OR id3='{}' ORDER BY order_ ASC LIMIT {}", result = WorldDatabase.Query("SELECT guid, position_x, position_y, position_z, map, (POW(position_x - '{}', 2) + POW(position_y - '{}', 2) + POW(position_z - '{}', 2)) AS order_ FROM creature WHERE id = '{}' OR guid IN (SELECT spawnId FROM creature_multispawn WHERE entry = '{}') ORDER BY order_ ASC LIMIT {}",
player->GetPositionX(), player->GetPositionY(), player->GetPositionZ(), uint32(creatureId), uint32(creatureId), uint32(creatureId), count); player->GetPositionX(), player->GetPositionY(), player->GetPositionZ(), uint32(creatureId), uint32(creatureId), count);
} }
else else
result = WorldDatabase.Query("SELECT guid, position_x, position_y, position_z, map FROM creature WHERE id1='{}' OR id2='{}' OR id3='{}' LIMIT {}", result = WorldDatabase.Query("SELECT guid, position_x, position_y, position_z, map FROM creature WHERE id = '{}' OR guid IN (SELECT spawnId FROM creature_multispawn WHERE entry = '{}') LIMIT {}",
uint32(creatureId), uint32(creatureId), uint32(creatureId), count); uint32(creatureId), uint32(creatureId), count);
if (result) if (result)
{ {
@@ -557,13 +557,13 @@ public:
for (auto const& pair : map->GetCreatureRespawnTimes()) for (auto const& pair : map->GetCreatureRespawnTimes())
{ {
CreatureData const* data = sObjectMgr->GetCreatureData(pair.first); CreatureData const* data = sObjectMgr->GetCreatureData(pair.first);
if (!data || (entryFilter && data->id1 != *entryFilter)) if (!data || (entryFilter && data->id != *entryFilter))
continue; continue;
CreatureTemplate const* cTemplate = sObjectMgr->GetCreatureTemplate(data->id1); CreatureTemplate const* cTemplate = sObjectMgr->GetCreatureTemplate(data->id);
std::string name = cTemplate ? cTemplate->Name : "Unknown"; std::string name = cTemplate ? cTemplate->Name : "Unknown";
time_t remaining = pair.second > now ? pair.second - now : 0; time_t remaining = pair.second > now ? pair.second - now : 0;
handler->PSendSysMessage(LANG_LIST_RESPAWNS_CREATURE_ENTRY, pair.first, name, data->id1, remaining); handler->PSendSysMessage(LANG_LIST_RESPAWNS_CREATURE_ENTRY, pair.first, name, data->id, remaining);
++count; ++count;
if (count >= 50) if (count >= 50)
{ {
+4 -4
View File
@@ -2492,7 +2492,7 @@ public:
if (isAlive) if (isAlive)
{ {
handler->PSendSysMessage(LANG_RESPAWN_GUID_CREATURE_ALIVE, spawnId, creData->id1); handler->PSendSysMessage(LANG_RESPAWN_GUID_CREATURE_ALIVE, spawnId, creData->id);
return true; return true;
} }
@@ -2508,7 +2508,7 @@ public:
time_t now = GameTime::GetGameTime().count(); time_t now = GameTime::GetGameTime().count();
map->SaveCreatureRespawnTime(spawnId, now); map->SaveCreatureRespawnTime(spawnId, now);
} }
handler->PSendSysMessage(LANG_RESPAWN_GUID_CREATURE_QUEUED, spawnId, creData->id1); handler->PSendSysMessage(LANG_RESPAWN_GUID_CREATURE_QUEUED, spawnId, creData->id);
return true; return true;
} }
@@ -2608,7 +2608,7 @@ public:
for (auto const& [spawnId, creature] : map->GetCreatureBySpawnIdStore()) for (auto const& [spawnId, creature] : map->GetCreatureBySpawnIdStore())
{ {
CreatureData const* data = sObjectMgr->GetCreatureData(spawnId); CreatureData const* data = sObjectMgr->GetCreatureData(spawnId);
if (!data || data->id1 != entry) if (!data || data->id != entry)
continue; continue;
if (creature->isDead()) if (creature->isDead())
deadCreatures.push_back(creature); deadCreatures.push_back(creature);
@@ -2624,7 +2624,7 @@ public:
for (auto const& [spawnId, respawnTime] : map->GetCreatureRespawnTimes()) for (auto const& [spawnId, respawnTime] : map->GetCreatureRespawnTimes())
{ {
CreatureData const* data = sObjectMgr->GetCreatureData(spawnId); CreatureData const* data = sObjectMgr->GetCreatureData(spawnId);
if (!data || data->id1 != entry) if (!data || data->id != entry)
continue; continue;
if (sPoolMgr->IsPartOfAPool<Creature>(spawnId)) if (sPoolMgr->IsPartOfAPool<Creature>(spawnId))
continue; continue;
+10 -12
View File
@@ -229,7 +229,7 @@ public:
{ {
ObjectGuid::LowType guid = sObjectMgr->GenerateCreatureSpawnId(); ObjectGuid::LowType guid = sObjectMgr->GenerateCreatureSpawnId();
CreatureData& data = sObjectMgr->NewOrExistCreatureData(guid); CreatureData& data = sObjectMgr->NewOrExistCreatureData(guid);
data.id1 = id; data.id = id;
data.phaseMask = chr->GetPhaseMaskForSpawn(); data.phaseMask = chr->GetPhaseMaskForSpawn();
data.posX = chr->GetTransOffsetX(); data.posX = chr->GetTransOffsetX();
data.posY = chr->GetTransOffsetY(); data.posY = chr->GetTransOffsetY();
@@ -677,7 +677,7 @@ public:
uint32 id3 = 0; uint32 id3 = 0;
if (CreatureData const* cData = target->GetCreatureData()) if (CreatureData const* cData = target->GetCreatureData())
{ {
id1 = cData->id1; id1 = cData->id;
id2 = cData->id2; id2 = cData->id2;
id3 = cData->id3; id3 = cData->id3;
} }
@@ -721,7 +721,7 @@ public:
static bool HandleNpcInfoCommandShowFromDB(ChatHandler* handler, ObjectGuid::LowType lowGuid, CreatureData const* cData) static bool HandleNpcInfoCommandShowFromDB(ChatHandler* handler, ObjectGuid::LowType lowGuid, CreatureData const* cData)
{ {
CreatureTemplate const* cInfo = sObjectMgr->GetCreatureTemplate(cData->id1); CreatureTemplate const* cInfo = sObjectMgr->GetCreatureTemplate(cData->id);
if (!cInfo) if (!cInfo)
{ {
handler->SendErrorMessage(LANG_COMMAND_CREATGUIDNOTFOUND, lowGuid); handler->SendErrorMessage(LANG_COMMAND_CREATGUIDNOTFOUND, lowGuid);
@@ -730,8 +730,8 @@ public:
handler->PSendSysMessage("(Not in world - showing DB data)"); handler->PSendSysMessage("(Not in world - showing DB data)");
uint32 scriptId = cData->ScriptId ? cData->ScriptId : cInfo->ScriptID; uint32 scriptId = cData->ScriptId ? cData->ScriptId : cInfo->ScriptID;
handler->PSendSysMessage(LANG_NPCINFO_CHAR, lowGuid, ObjectGuid::Create<HighGuid::Unit>(cData->id1, lowGuid).ToString(), cData->id1, handler->PSendSysMessage(LANG_NPCINFO_CHAR, lowGuid, ObjectGuid::Create<HighGuid::Unit>(cData->id, lowGuid).ToString(), cData->id,
cData->id2, cData->id3, cData->displayid, cData->displayid, cInfo->faction, cData->id, cData->id2, cData->id3, cData->displayid, cData->displayid, cInfo->faction,
cData->npcflag); cData->npcflag);
handler->PSendSysMessage(LANG_NPCINFO_PHASEMASK, cData->phaseMask); handler->PSendSysMessage(LANG_NPCINFO_PHASEMASK, cData->phaseMask);
handler->PSendSysMessage(LANG_NPCINFO_POSITION, cData->posX, cData->posY, cData->posZ); handler->PSendSysMessage(LANG_NPCINFO_POSITION, cData->posX, cData->posY, cData->posZ);
@@ -763,7 +763,7 @@ public:
uint32 id3 = 0; uint32 id3 = 0;
if (CreatureData const* cData = target->GetCreatureData()) if (CreatureData const* cData = target->GetCreatureData())
{ {
id1 = cData->id1; id1 = cData->id;
id2 = cData->id2; id2 = cData->id2;
id3 = cData->id3; id3 = cData->id3;
} }
@@ -835,12 +835,10 @@ public:
continue; continue;
uint32 entry = fields[1].Get<uint32>(); uint32 entry = fields[1].Get<uint32>();
//uint32 entry2 = fields[2].Get<uint32>(); float x = fields[2].Get<float>();
//uint32 entry3 = fields[3].Get<uint32>(); float y = fields[3].Get<float>();
float x = fields[4].Get<float>(); float z = fields[4].Get<float>();
float y = fields[5].Get<float>(); uint16 mapId = fields[5].Get<uint16>();
float z = fields[6].Get<float>();
uint16 mapId = fields[7].Get<uint16>();
CreatureTemplate const* creatureTemplate = sObjectMgr->GetCreatureTemplate(entry); CreatureTemplate const* creatureTemplate = sObjectMgr->GetCreatureTemplate(entry);
if (!creatureTemplate) if (!creatureTemplate)
+1 -1
View File
@@ -78,7 +78,7 @@ private:
{ {
if (CreatureData const* data = sObjectMgr->GetCreatureData(obj.guid)) if (CreatureData const* data = sObjectMgr->GetCreatureData(obj.guid))
{ {
entry = data->id1; entry = data->id;
mapId = data->mapid; mapId = data->mapid;
x = data->posX; x = data->posX;
y = data->posY; y = data->posY;
+3 -3
View File
@@ -309,7 +309,7 @@ public:
CreatureData const* spawnpoint = nullptr; CreatureData const* spawnpoint = nullptr;
for (auto const& pair : sObjectMgr->GetAllCreatureData()) for (auto const& pair : sObjectMgr->GetAllCreatureData())
{ {
if (pair.second.id1 != *creatureId) if (pair.second.id != *creatureId)
continue; continue;
if (!spawnpoint) if (!spawnpoint)
@@ -341,7 +341,7 @@ public:
return false; return false;
} }
CreatureTemplate const* creatureTemplate = ASSERT_NOTNULL(sObjectMgr->GetCreatureTemplate(spawnpoint->id1)); CreatureTemplate const* creatureTemplate = ASSERT_NOTNULL(sObjectMgr->GetCreatureTemplate(spawnpoint->id));
return DoNameTeleport(handler, player, spawnpoint->mapid, { spawnpoint->posX, spawnpoint->posY, spawnpoint->posZ }, creatureTemplate->Name); return DoNameTeleport(handler, player, spawnpoint->mapid, { spawnpoint->posX, spawnpoint->posY, spawnpoint->posZ }, creatureTemplate->Name);
} }
@@ -352,7 +352,7 @@ public:
WorldDatabase.EscapeString(normalizedName); WorldDatabase.EscapeString(normalizedName);
// May need work //PussyWizardEliteMalcrom // May need work //PussyWizardEliteMalcrom
QueryResult result = WorldDatabase.Query("SELECT c.position_x, c.position_y, c.position_z, c.orientation, c.map, ct.name FROM creature c INNER JOIN creature_template ct ON c.id1 = ct.entry WHERE ct.name LIKE '{}'", normalizedName); QueryResult result = WorldDatabase.Query("SELECT c.position_x, c.position_y, c.position_z, c.orientation, c.map, ct.name FROM creature c INNER JOIN creature_template ct ON c.id = ct.entry WHERE ct.name LIKE '{}'", normalizedName);
if (!result) if (!result)
{ {
handler->SendErrorMessage(LANG_COMMAND_GOCREATNOTFOUND); handler->SendErrorMessage(LANG_COMMAND_GOCREATNOTFOUND);
+1 -1
View File
@@ -1037,7 +1037,7 @@ public:
if (show == "off") if (show == "off")
{ {
WorldDatabasePreparedStatement* stmt = WorldDatabase.GetPreparedStatement(WORLD_SEL_CREATURE_BY_ID); WorldDatabasePreparedStatement* stmt = WorldDatabase.GetPreparedStatement(WORLD_SEL_CREATURE_BY_ID);
stmt->SetArguments(1, 1, 1); stmt->SetArguments(1, 1);
PreparedQueryResult result = WorldDatabase.Query(stmt); PreparedQueryResult result = WorldDatabase.Query(stmt);
if (!result) if (!result)
@@ -38,10 +38,10 @@ struct SHostageInfo
static SHostageInfo HostageInfo[] = static SHostageInfo HostageInfo[] =
{ {
{23790, 186648, { -57.0f, 1343.0f, 40.77f, 3.2f } }, // bear {23999, 187021, { 400.0f, 1414.0f, 74.36f, 3.3f } }, // Harkor (Akil'zon)
{23999, 187021, { 400.0f, 1414.0f, 74.36f, 3.3f } }, // eagle {23790, 186648, { -57.0f, 1343.0f, 40.77f, 3.2f } }, // Tanzar (Nalorakk)
{24001, 186672, { -35.0f, 1134.0f, 18.71f, 1.9f } }, // dragonhawk {24024, 186667, { -35.0f, 1134.0f, 18.71f, 1.9f } }, // Kraz (Jan'alai)
{24024, 186667, { 413.0f, 1117.0f, 6.32f, 3.1f } } // lynx {24001, 186672, { 413.0f, 1117.0f, 6.32f, 3.1f } } // Ashli (Halazzi)
}; };
Position const HarrisonJonesLoc = {120.687f, 1674.0f, 42.0217f, 1.59044f}; Position const HarrisonJonesLoc = {120.687f, 1674.0f, 42.0217f, 1.59044f};
@@ -242,8 +242,8 @@ struct npc_forest_frog : public ScriptedAI
#define GOSSIP_HOSTAGE1 "I am glad to help you." #define GOSSIP_HOSTAGE1 "I am glad to help you."
static uint32 HostageEntry[] = {23790, 23999, 24024, 24001}; static uint32 HostageEntry[] = {23999, 23790, 24024, 24001};
static uint32 ChestEntry[] = {186648, 187021, 186667, 186672}; static uint32 ChestEntry[] = {187021, 186648, 186667, 186672};
class npc_zulaman_hostage : public CreatureScript class npc_zulaman_hostage : public CreatureScript
{ {
@@ -25,8 +25,8 @@
enum DataTypes enum DataTypes
{ {
DATA_NALORAKK = 0, DATA_AKILZON = 0,
DATA_AKILZON = 1, DATA_NALORAKK = 1,
DATA_JANALAI = 2, DATA_JANALAI = 2,
DATA_HALAZZI = 3, DATA_HALAZZI = 3,
DATA_HEXLORD = 4, DATA_HEXLORD = 4,
@@ -428,7 +428,7 @@ struct npc_ahnqiraji_critter : public ScriptedAI
// Don't attack nearby players randomly if they are the Twin's pet bugs. // Don't attack nearby players randomly if they are the Twin's pet bugs.
if (CreatureData const* crData = me->GetCreatureData()) if (CreatureData const* crData = me->GetCreatureData())
{ {
ObjectGuid dbtableHighGuid = ObjectGuid::Create<HighGuid::Unit>(crData->id1, me->GetSpawnId()); ObjectGuid dbtableHighGuid = ObjectGuid::Create<HighGuid::Unit>(crData->id, me->GetSpawnId());
ObjectGuid targetGuid = sObjectMgr->GetLinkedRespawnGuid(dbtableHighGuid); ObjectGuid targetGuid = sObjectMgr->GetLinkedRespawnGuid(dbtableHighGuid);
if (targetGuid.GetEntry() == NPC_VEKLOR) if (targetGuid.GetEntry() == NPC_VEKLOR)
@@ -585,9 +585,9 @@ enum ShadowPriestSezzizEnum
std::array<std::vector<std::pair<uint32, Position>>, 4> shadowpriestSezzizAdds = std::array<std::vector<std::pair<uint32, Position>>, 4> shadowpriestSezzizAdds =
{ { { {
{ { NPC_SANDFURY_ZEALOT, { 1874.12f, 1198.90f, 8.87f } }, { NPC_SANDFURY_ACOLYTE, { 1874.12f, 1198.90f, 8.87f } } }, { { NPC_SANDFURY_ZEALOT, { 1874.12f, 1198.90f, 8.87f } }, { NPC_SANDFURY_ACOLYTE, { 1874.12f, 1198.90f, 8.87f } } },
{ { NPC_SANDFURY_ACOLYTE, { 895.26f, 1199.09f, 8.87f } }, { NPC_SANDFURY_ACOLYTE, { 895.26f, 1199.088f, 8.87f } } }, { { NPC_SANDFURY_ACOLYTE, { 1895.26f, 1199.09f, 8.87f } }, { NPC_SANDFURY_ACOLYTE, { 1895.26f, 1199.088f, 8.87f } } },
{ { NPC_SANDFURY_ZEALOT, { 1874.12f, 1198.90f, 8.87f } }, { NPC_SANDFURY_ACOLYTE, { 895.26f, 1199.09f, 8.87f } }, { NPC_SANDFURY_ACOLYTE, { 895.26f, 1199.09f, 8.87f } } }, { { NPC_SANDFURY_ZEALOT, { 1874.12f, 1198.90f, 8.87f } }, { NPC_SANDFURY_ACOLYTE, { 1895.26f, 1199.09f, 8.87f } }, { NPC_SANDFURY_ACOLYTE, { 1895.26f, 1199.09f, 8.87f } } },
{ { NPC_SANDFURY_ZEALOT, { 895.26f, 1199.09f, 8.87f } }, { NPC_SANDFURY_ZEALOT, { 1874.12f, 1198.90f, 8.87f } }, { NPC_SANDFURY_ACOLYTE, { 1874.12f, 1198.90f } }, { NPC_SANDFURY_ACOLYTE, { 895.26f, 1199.09f, 8.87f } } } { { NPC_SANDFURY_ZEALOT, { 1895.26f, 1199.09f, 8.87f } }, { NPC_SANDFURY_ZEALOT, { 1874.12f, 1198.90f, 8.87f } }, { NPC_SANDFURY_ACOLYTE, { 1874.12f, 1198.90f } }, { NPC_SANDFURY_ACOLYTE, { 1895.26f, 1199.09f, 8.87f } } }
} }; } };
class npc_shadowpriest_sezziz : public CreatureScript class npc_shadowpriest_sezziz : public CreatureScript
@@ -103,6 +103,15 @@ struct npc_taldaram_flamesphere : public NullCreatureAI
} }
} }
void MovementInform(uint32 type, uint32 id) override
{
if (type == POINT_MOTION_TYPE && id == POINT_ORB)
{
me->DespawnOrUnsummon(1s);
DoCastSelf(SPELL_FLAME_SPHERE_DEATH_EFFECT, true);
}
}
void IsSummonedBy(WorldObject* /*summoner*/) override void IsSummonedBy(WorldObject* /*summoner*/) override
{ {
// Replace sphere instantly if sphere is summoned after prince death // Replace sphere instantly if sphere is summoned after prince death
@@ -116,11 +125,6 @@ struct npc_taldaram_flamesphere : public NullCreatureAI
DoCastSelf(SPELL_FLAME_SPHERE_VISUAL); DoCastSelf(SPELL_FLAME_SPHERE_VISUAL);
} }
void JustDied(Unit* /*who*/) override
{
DoCastSelf(SPELL_FLAME_SPHERE_DEATH_EFFECT);
}
void UpdateAI(uint32 diff) override void UpdateAI(uint32 diff) override
{ {
if (moveTimer) if (moveTimer)
@@ -147,7 +151,7 @@ struct npc_taldaram_flamesphere : public NullCreatureAI
float angle = me->GetAngle(&victimPos) + angleOffset; float angle = me->GetAngle(&victimPos) + angleOffset;
float x = me->GetPositionX() + DATA_SPHERE_DISTANCE * cos(angle); float x = me->GetPositionX() + DATA_SPHERE_DISTANCE * cos(angle);
float y = me->GetPositionY() + DATA_SPHERE_DISTANCE * std::sin(angle); float y = me->GetPositionY() + DATA_SPHERE_DISTANCE * std::sin(angle);
me->GetMotionMaster()->MovePoint(POINT_ORB, x, y, me->GetPositionZ()); me->GetMotionMaster()->MovePoint(POINT_ORB, x, y, me->GetPositionZ(), FORCED_MOVEMENT_WALK);
moveTimer = 0; moveTimer = 0;
} }
@@ -84,7 +84,7 @@ public:
uint32 GetCreatureEntry(ObjectGuid::LowType /*guidLow*/, CreatureData const* data) override uint32 GetCreatureEntry(ObjectGuid::LowType /*guidLow*/, CreatureData const* data) override
{ {
uint32 entry = data->id1; uint32 entry = data->id;
switch (entry) switch (entry)
{ {
case NPC_RESCUED_ALLIANCE_SLAVE: case NPC_RESCUED_ALLIANCE_SLAVE:
@@ -1045,7 +1045,7 @@ public:
if (Creature* crusader = ObjectAccessor::GetCreature(*me, instance->GetGuidData(DATA_CAPTAIN_ARNATH + i))) if (Creature* crusader = ObjectAccessor::GetCreature(*me, instance->GetGuidData(DATA_CAPTAIN_ARNATH + i)))
if (crusader->IsAlive()) if (crusader->IsAlive())
{ {
if (crusader->GetEntry() == crusader->GetCreatureData()->id1) if (crusader->GetEntry() == crusader->GetCreatureData()->id)
{ {
crusader->m_Events.AddEventAtOffset(new CaptainSurviveTalk(*crusader), delay); crusader->m_Events.AddEventAtOffset(new CaptainSurviveTalk(*crusader), delay);
delay += 6s; delay += 6s;
@@ -1235,7 +1235,7 @@ public:
void Reset() override void Reset() override
{ {
me->SetCorpseDelay(DAY); // leave corpse for a long time so svalna can resurrect me->SetCorpseDelay(DAY); // leave corpse for a long time so svalna can resurrect
IsUndead = (me->GetCreatureData() && me->GetCreatureData()->id1 != me->GetEntry()); IsUndead = (me->GetCreatureData() && me->GetCreatureData()->id != me->GetEntry());
} }
void JustDied(Unit* /*killer*/) override void JustDied(Unit* /*killer*/) override
@@ -539,7 +539,7 @@ public:
uint32 GetCreatureEntry(ObjectGuid::LowType /*guidLow*/, CreatureData const* data) override uint32 GetCreatureEntry(ObjectGuid::LowType /*guidLow*/, CreatureData const* data) override
{ {
uint32 entry = data->id1; uint32 entry = data->id;
switch (entry) switch (entry)
{ {
case NPC_HORDE_GUNSHIP_CANNON: case NPC_HORDE_GUNSHIP_CANNON:
@@ -135,6 +135,22 @@ static uint8 CountAliveBosses(InstanceScript* instance)
return count; return count;
} }
// True while any council member other than `me` is still alive and engaged.
// IsEngaged() (not IsInCombat()) survives Brundir's ~16s CombatStop during
// Lightning Tendrils, so a lone survivor's reset cannot revive dead members.
static bool IsAnyAssemblyMemberEngaged(InstanceScript* pInstance, Creature* me)
{
if (!pInstance || !me)
return false;
for (uint8 i = 0; i < 3; ++i)
if (Creature* boss = pInstance->GetCreature(DATA_STEELBREAKER + i))
if (boss != me && boss->IsAlive() && boss->IsEngaged())
return true;
return false;
}
bool IsEncounterComplete(InstanceScript* pInstance, Creature* me) bool IsEncounterComplete(InstanceScript* pInstance, Creature* me)
{ {
if (!pInstance || !me) if (!pInstance || !me)
@@ -186,12 +202,16 @@ struct boss_steelbreaker : public ScriptedAI
void Reset() override void Reset() override
{ {
me->SetLootMode(0); me->SetLootMode(0);
RespawnAssemblyOfIron(pInstance, me);
_phase = 0; _phase = 0;
events.Reset(); events.Reset();
if (pInstance)
pInstance->SetBossState(BOSS_ASSEMBLY, NOT_STARTED); if (!IsAnyAssemblyMemberEngaged(pInstance, me))
{
RespawnAssemblyOfIron(pInstance, me);
if (pInstance)
pInstance->SetBossState(BOSS_ASSEMBLY, NOT_STARTED);
}
} }
void JustReachedHome() override void JustReachedHome() override
@@ -316,11 +336,20 @@ struct boss_steelbreaker : public ScriptedAI
events.Repeat(15s, 20s); events.Repeat(15s, 20s);
break; break;
case EVENT_STATIC_DISRUPTION: case EVENT_STATIC_DISRUPTION:
if (Unit* pTarget = SelectTarget(SelectTargetMethod::MinDistance, 0, 0, true)) {
me->CastSpell(pTarget, SPELL_STATIC_DISRUPTION, false); // Prefer a random player out of melee range so the nature damage debuff
// (and its 5y splash) stays off the melee/tank cluster; if everyone is in
// melee, fall back to a random player regardless of range.
Unit* target = SelectTarget(SelectTargetMethod::Random, 0, -10.0f, true);
if (!target)
target = SelectTarget(SelectTargetMethod::Random, 0, 0.0f, true);
if (target)
me->CastSpell(target, SPELL_STATIC_DISRUPTION, false);
events.Repeat(20s, 40s); events.Repeat(20s, 40s);
break; break;
}
case EVENT_OVERWHELMING_POWER: case EVENT_OVERWHELMING_POWER:
Talk(SAY_STEELBREAKER_POWER); Talk(SAY_STEELBREAKER_POWER);
me->CastSpell(me->GetVictim(), SPELL_OVERWHELMING_POWER, true); me->CastSpell(me->GetVictim(), SPELL_OVERWHELMING_POWER, true);
@@ -367,14 +396,17 @@ struct boss_runemaster_molgeim : public ScriptedAI
void Reset() override void Reset() override
{ {
me->SetLootMode(0); me->SetLootMode(0);
RespawnAssemblyOfIron(pInstance, me);
_phase = 0; _phase = 0;
events.Reset(); events.Reset();
summons.DespawnAll(); summons.DespawnAll();
if (pInstance) if (!IsAnyAssemblyMemberEngaged(pInstance, me))
pInstance->SetBossState(BOSS_ASSEMBLY, NOT_STARTED); {
RespawnAssemblyOfIron(pInstance, me);
if (pInstance)
pInstance->SetBossState(BOSS_ASSEMBLY, NOT_STARTED);
}
me->m_Events.AddEventAtOffset(new CastRunesEvent(*me), 8s); me->m_Events.AddEventAtOffset(new CastRunesEvent(*me), 8s);
} }
@@ -559,7 +591,6 @@ struct boss_stormcaller_brundir : public ScriptedAI
{ {
SetInvincibility(false); SetInvincibility(false);
me->SetLootMode(0); me->SetLootMode(0);
RespawnAssemblyOfIron(pInstance, me);
_channelTimer = 0; _channelTimer = 0;
_phase = 0; _phase = 0;
@@ -571,8 +602,13 @@ struct boss_stormcaller_brundir : public ScriptedAI
me->SetDisableGravity(false); me->SetDisableGravity(false);
me->SetRegeneratingHealth(true); me->SetRegeneratingHealth(true);
me->SetReactState(REACT_AGGRESSIVE); me->SetReactState(REACT_AGGRESSIVE);
if (pInstance)
pInstance->SetBossState(BOSS_ASSEMBLY, NOT_STARTED); if (!IsAnyAssemblyMemberEngaged(pInstance, me))
{
RespawnAssemblyOfIron(pInstance, me);
if (pInstance)
pInstance->SetBossState(BOSS_ASSEMBLY, NOT_STARTED);
}
} }
void JustReachedHome() override void JustReachedHome() override
@@ -1445,22 +1445,39 @@ class spell_vehicle_grab_pyrite : public SpellScript
void HandleScript(SpellEffIndex /*effIndex*/) void HandleScript(SpellEffIndex /*effIndex*/)
{ {
if (Unit* target = GetHitUnit()) Unit* target = GetHitUnit();
if (Unit* seat = GetCaster()->GetVehicleBase()) if (!target)
return;
// The grabbing vehicle: the demolisher's mechanic seat, or the chopper itself.
Unit* seat = GetCaster()->GetVehicleBase();
if (!seat)
return;
if (Vehicle* vehicle = seat->GetVehicleKit())
if (Unit* passenger = vehicle->GetPassenger(1))
{ {
if (Vehicle* vehicle = seat->GetVehicleKit()) // On the chopper the rear seat may carry a player; never eject them to grab.
if (Unit* pyrite = vehicle->GetPassenger(1)) if (passenger->IsPlayer())
pyrite->ExitVehicle(); return;
if (Unit* parent = seat->GetVehicleBase()) passenger->ExitVehicle();
{
GetCaster()->CastSpell(parent, SPELL_ADD_PYRITE, true);
target->CastSpell(seat, GetEffectValue());
if (target->IsCreature())
target->ToCreature()->DespawnOrUnsummon(1300ms);
}
} }
if (Unit* parent = seat->GetVehicleBase())
{
// Demolisher: the seat is mounted on a parent vehicle that the pyrite fuels.
GetCaster()->CastSpell(parent, SPELL_ADD_PYRITE, true);
target->CastSpell(seat, GetEffectValue());
if (target->IsCreature())
target->ToCreature()->DespawnOrUnsummon(1300ms);
}
else
{
// Chopper: load the crate into the rear seat so it can be ferried to other vehicles.
target->CastSpell(seat, GetEffectValue());
}
} }
void Register() override void Register() override
@@ -1058,7 +1058,7 @@ struct boss_freya_summons : public ScriptedAI
switch (events.ExecuteEvent()) switch (events.ExecuteEvent())
{ {
case EVENT_ANCIENT_CONSERVATOR_NATURE_FURY: case EVENT_ANCIENT_CONSERVATOR_NATURE_FURY:
me->CastSpell(me->GetVictim(), SPELL_NATURE_FURY, false); DoCastRandomTarget(SPELL_NATURE_FURY, 0, 100.0f, true, false, true, -SPELL_NATURE_FURY);
events.Repeat(14s); events.Repeat(14s);
break; break;
case EVENT_ANCIENT_CONSERVATOR_GRIP: case EVENT_ANCIENT_CONSERVATOR_GRIP:
@@ -77,6 +77,16 @@ enum eEvents
EVENT_GRAB, EVENT_GRAB,
}; };
enum IgnisActions
{
ACTION_CONSTRUCT_SHATTERED = 0,
};
enum IgnisData
{
DATA_SHATTERED = 0,
};
struct npc_ulduar_iron_construct : public ScriptedAI struct npc_ulduar_iron_construct : public ScriptedAI
{ {
npc_ulduar_iron_construct(Creature* pCreature) : ScriptedAI(pCreature) npc_ulduar_iron_construct(Creature* pCreature) : ScriptedAI(pCreature)
@@ -130,19 +140,6 @@ struct npc_ulduar_iron_construct : public ScriptedAI
} }
} }
void DamageTaken(Unit* attacker, uint32& damage, DamageEffectType, SpellSchoolMask) override
{
if (damage >= RAID_MODE(3000U, 5000U) && me->GetAura(sSpellMgr->GetSpellIdForDifficulty(SPELL_BRITTLE, me)))
{
me->CastSpell(me, SPELL_SHATTER, true);
Unit::Kill(attacker, me);
if (InstanceScript* instance = me->GetInstanceScript())
if (Creature* ignis = instance->GetCreature(BOSS_IGNIS))
ignis->AI()->SetData(1337, 0);
}
}
void JustDied(Unit* /*killer*/) override void JustDied(Unit* /*killer*/) override
{ {
if (InstanceScript* instance = me->GetInstanceScript()) if (InstanceScript* instance = me->GetInstanceScript())
@@ -233,9 +230,9 @@ struct boss_ignis : public BossAI
} }
} }
void SetData(uint32 id, uint32 /*value*/) override void DoAction(int32 action) override
{ {
if (id == 1337) if (action == ACTION_CONSTRUCT_SHATTERED)
{ {
if (lastShatterMSTime) if (lastShatterMSTime)
if (getMSTimeDiff(lastShatterMSTime, GameTime::GetGameTimeMS().count()) <= 5000) if (getMSTimeDiff(lastShatterMSTime, GameTime::GetGameTimeMS().count()) <= 5000)
@@ -247,7 +244,7 @@ struct boss_ignis : public BossAI
uint32 GetData(uint32 id) const override uint32 GetData(uint32 id) const override
{ {
if (id == 1337) if (id == DATA_SHATTERED)
return (bShattered ? 1 : 0); return (bShattered ? 1 : 0);
return 0; return 0;
} }
@@ -498,6 +495,42 @@ class spell_ignis_slag_pot_aura : public AuraScript
} }
}; };
// 62382, 67114 - Brittle
class spell_ignis_brittle_aura : public AuraScript
{
PrepareAuraScript(spell_ignis_brittle_aura);
bool Validate(SpellInfo const* /*spellInfo*/) override
{
return ValidateSpellInfo({ SPELL_SHATTER });
}
bool CheckProc(ProcEventInfo& eventInfo)
{
DamageInfo* damageInfo = eventInfo.GetDamageInfo();
return damageInfo && damageInfo->GetDamage() >= (GetId() == SPELL_BRITTLE ? 5000u : 3000u);
}
void HandleProc(ProcEventInfo& eventInfo)
{
Unit* construct = GetTarget();
construct->CastSpell(construct, SPELL_SHATTER, true);
Unit* attacker = eventInfo.GetActor();
Unit::Kill(attacker ? attacker : construct, construct);
if (InstanceScript* instance = construct->GetInstanceScript())
if (Creature* ignis = instance->GetCreature(BOSS_IGNIS))
ignis->AI()->DoAction(ACTION_CONSTRUCT_SHATTERED);
}
void Register() override
{
DoCheckProc += AuraCheckProcFn(spell_ignis_brittle_aura::CheckProc);
OnProc += AuraProcFn(spell_ignis_brittle_aura::HandleProc);
}
};
class achievement_ignis_shattered : public AchievementCriteriaScript class achievement_ignis_shattered : public AchievementCriteriaScript
{ {
public: public:
@@ -507,7 +540,7 @@ public:
{ {
if (!target || !target->IsCreature()) if (!target || !target->IsCreature())
return false; return false;
return !!target->ToCreature()->AI()->GetData(1337); return !!target->ToCreature()->AI()->GetData(DATA_SHATTERED);
} }
}; };
@@ -518,5 +551,6 @@ void AddSC_boss_ignis()
RegisterSpellScript(spell_ignis_scorch_aura); RegisterSpellScript(spell_ignis_scorch_aura);
RegisterSpellScript(spell_ignis_grab_initial); RegisterSpellScript(spell_ignis_grab_initial);
RegisterSpellScript(spell_ignis_slag_pot_aura); RegisterSpellScript(spell_ignis_slag_pot_aura);
RegisterSpellScript(spell_ignis_brittle_aura);
new achievement_ignis_shattered(); new achievement_ignis_shattered();
} }

Some files were not shown because too many files have changed in this diff Show More