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
+250 -229
View File
@@ -30,6 +30,7 @@
# GAME SETTINGS # GAME SETTINGS
# GAME MASTER # GAME MASTER
# CHEAT # CHEAT
# ACCOUNT
# CHARACTER DATABASE # CHARACTER DATABASE
# CHARACTER DELETE # CHARACTER DELETE
# CHARACTER CREATION # CHARACTER CREATION
@@ -109,11 +110,10 @@ BindIP = "0.0.0.0"
# CharacterDatabaseInfo # CharacterDatabaseInfo
# Description: Database connection settings for the world server. # Description: Database connection settings for the world server.
# Example: "hostname;port;username;password;database" # Example: "hostname;port;username;password;database"
# ".;somenumber;username;password;database" - (Use named pipes on Windows # ".;somenumber;username;password;database"
# "enable-named-pipe" to [mysqld] # (Use named pipes on Windows "enable-named-pipe" to [mysqld] section my.ini)
# section my.ini) # ".;/path/to/unix_socket;username;password;database"
# ".;/path/to/unix_socket;username;password;database" - (use Unix sockets on # (Use Unix sockets on Unix/Linux)
# Unix/Linux)
# Default: "127.0.0.1;3306;acore;acore;acore_auth" - (LoginDatabaseInfo) # Default: "127.0.0.1;3306;acore;acore;acore_auth" - (LoginDatabaseInfo)
# "127.0.0.1;3306;acore;acore;acore_world" - (WorldDatabaseInfo) # "127.0.0.1;3306;acore;acore;acore_world" - (WorldDatabaseInfo)
# "127.0.0.1;3306;acore;acore;acore_characters" - (CharacterDatabaseInfo) # "127.0.0.1;3306;acore;acore;acore_characters" - (CharacterDatabaseInfo)
@@ -160,7 +160,6 @@ MaxPingTime = 30
# #
# Database.Reconnect.Seconds # Database.Reconnect.Seconds
# Database.Reconnect.Attempts # Database.Reconnect.Attempts
#
# Description: How many seconds between every reconnection attempt # Description: How many seconds between every reconnection attempt
# and how many attempts will be performed in total # and how many attempts will be performed in total
# Default: 20 attempts every 15 seconds # Default: 20 attempts every 15 seconds
@@ -287,12 +286,10 @@ FlashAtStart = 1
# #
# Updates.EnableDatabases # Updates.EnableDatabases
# Description: A mask that describes which databases should be updated. # Description: A mask that describes which databases should be updated.
# # Note: Following flags are available
# Following flags are available # DATABASE_LOGIN = 1, Auth database
# DATABASE_LOGIN = 1, // Auth database # DATABASE_CHARACTER = 2, Character database
# DATABASE_CHARACTER = 2, // Character database # DATABASE_WORLD = 4, World database
# DATABASE_WORLD = 4, // World database
#
# Default: 7 - (All enabled) # Default: 7 - (All enabled)
# 4 - (Enable world only) # 4 - (Enable world only)
# 0 - (All disabled) # 0 - (All disabled)
@@ -405,9 +402,7 @@ Network.EnableProxyProtocol = 0
# When enabled and the process is started by systemd socket activation, # When enabled and the process is started by systemd socket activation,
# the server will use the socket passed by systemd instead of # the server will use the socket passed by systemd instead of
# creating and binding its own listening socket. Disabled by default. # creating and binding its own listening socket. Disabled by default.
#
# When enabled the realm is not automatically set as offline on shutdown. # When enabled the realm is not automatically set as offline on shutdown.
#
# Example: 1 - (Enabled) # Example: 1 - (Enabled)
# Default: 0 - (Disabled) # Default: 0 - (Disabled)
@@ -479,10 +474,8 @@ SOAP.Port = 7878
# Description: The key used by authserver to decrypt TOTP secrets from database storage. # Description: The key used by authserver to decrypt TOTP secrets from database storage.
# You only need to set this here if you plan to use the in-game 2FA # You only need to set this here if you plan to use the in-game 2FA
# management commands (.account 2fa), otherwise this can be left blank. # management commands (.account 2fa), otherwise this can be left blank.
#
# The server will auto-detect if this does not match your authserver setting, # The server will auto-detect if this does not match your authserver setting,
# in which case any commands reliant on the secret will be disabled. # in which case any commands reliant on the secret will be disabled.
#
# Default: <blank> # Default: <blank>
# #
@@ -590,7 +583,6 @@ MinRecordUpdateTimeDiff = 100
# Example: "C:/acore/IP2LOCATION-LITE-DB1.CSV" # Example: "C:/acore/IP2LOCATION-LITE-DB1.CSV"
# "/home/acore/IP2LOCATION-LITE-DB1.CSV" # "/home/acore/IP2LOCATION-LITE-DB1.CSV"
# Default: "" - (Disabled) # Default: "" - (Disabled)
#
IPLocationFile = "" IPLocationFile = ""
@@ -599,7 +591,6 @@ IPLocationFile = ""
# Description: Specifies if IP addresses can be logged to the database # Description: Specifies if IP addresses can be logged to the database
# Default: 1 - (Enabled) # Default: 1 - (Enabled)
# 0 - (Disabled) # 0 - (Disabled)
#
AllowLoggingIPAddressesInDatabase = 1 AllowLoggingIPAddressesInDatabase = 1
@@ -608,7 +599,6 @@ AllowLoggingIPAddressesInDatabase = 1
# Description: Logs actions, e.g. account login and logout to name a few, based on IP of current session. # Description: Logs actions, e.g. account login and logout to name a few, based on IP of current session.
# Default: 0 - (Disabled) # Default: 0 - (Disabled)
# 1 - (Enabled) # 1 - (Enabled)
#
Allow.IP.Based.Action.Logging = 0 Allow.IP.Based.Action.Logging = 0
@@ -617,7 +607,6 @@ Allow.IP.Based.Action.Logging = 0
# Description: Allow logging spam reports from players in the chat, mail or calendar into the database. # Description: Allow logging spam reports from players in the chat, mail or calendar into the database.
# Default: 1 - (Enabled) # Default: 1 - (Enabled)
# 0 - (Disabled) # 0 - (Disabled)
#
LogSpamReports = 1 LogSpamReports = 1
@@ -626,7 +615,6 @@ LogSpamReports = 1
# Description: Enable or disable chat logging (chat_log.cpp). # Description: Enable or disable chat logging (chat_log.cpp).
# Default: 0 - (Disabled) # Default: 0 - (Disabled)
# 1 - (Enabled) # 1 - (Enabled)
#
ChatLog.Enable = 0 ChatLog.Enable = 0
@@ -635,24 +623,18 @@ ChatLog.Enable = 0
# Appender.name # Appender.name
# Description: Defines 'where to log'. # Description: Defines 'where to log'.
# Format: Type,LogLevel,Flags,optional1,optional2,optional3 # Format: Type,LogLevel,Flags,optional1,optional2,optional3
# # Type: 0 - (None)
# Type
# 0 - (None)
# 1 - (Console) # 1 - (Console)
# 2 - (File) # 2 - (File)
# 3 - (DB) # 3 - (DB)
# # LogLevel: 0 - (Disabled)
# LogLevel
# 0 - (Disabled)
# 1 - (Fatal) # 1 - (Fatal)
# 2 - (Error) # 2 - (Error)
# 3 - (Warning) # 3 - (Warning)
# 4 - (Info) # 4 - (Info)
# 5 - (Debug) # 5 - (Debug)
# 6 - (Trace) # 6 - (Trace)
# # Flags: 0 - None
# Flags:
# 0 - None
# 1 - Prefix Timestamp to the text # 1 - Prefix Timestamp to the text
# 2 - Prefix Log Level to the text # 2 - Prefix Log Level to the text
# 4 - Prefix Log Filter type to the text # 4 - Prefix Log Filter type to the text
@@ -660,8 +642,7 @@ ChatLog.Enable = 0
# (Only used with Type = 2) # (Only used with Type = 2)
# 16 - Make a backup of existing file before overwrite # 16 - Make a backup of existing file before overwrite
# (Only used with Mode = w) # (Only used with Mode = w)
# # Colors: (read as optional1 if Type = Console)
# Colors (read as optional1 if Type = Console)
# Format: "fatal error warn info debug trace" # Format: "fatal error warn info debug trace"
# 0 - BLACK # 0 - BLACK
# 1 - RED # 1 - RED
@@ -679,21 +660,17 @@ ChatLog.Enable = 0
# 13 - LCYAN # 13 - LCYAN
# 14 - WHITE # 14 - WHITE
# Example: "1 9 3 6 5 8" # Example: "1 9 3 6 5 8"
#
# File: Name of the file (read as optional1 if Type = File) # File: Name of the file (read as optional1 if Type = File)
# Allows to use one "%s" to create dynamic files # Allows to use one "%s" to create dynamic files
#
# Mode: Mode to open the file (read as optional2 if Type = File) # Mode: Mode to open the file (read as optional2 if Type = File)
# a - (Append) # a - (Append)
# w - (Overwrite) # w - (Overwrite)
#
# MaxFileSize: Maximum file size of the log file before creating a new log file # MaxFileSize: Maximum file size of the log file before creating a new log file
# (read as optional3 if Type = File) # (read as optional3 if Type = File)
# Size is measured in bytes expressed in a 64-bit unsigned integer. # Size is measured in bytes expressed in a 64-bit unsigned integer.
# Maximum value is 4294967295 (4 GB). Leave blank for no limit. # Maximum value is 4294967295 (4 GB). Leave blank for no limit.
# NOTE: Does not work with dynamic filenames. # NOTE: Does not work with dynamic filenames.
# Example: 536870912 (512 MB) # Example: 536870912 (512 MB)
#
Appender.Console=1,4,0,"1 9 3 6 5 8" Appender.Console=1,4,0,"1 9 3 6 5 8"
Appender.Server=2,5,0,Server.log,w Appender.Server=2,5,0,Server.log,w
@@ -707,23 +684,20 @@ Appender.Errors=2,2,0,Errors.log,w
# Logger.name # Logger.name
# Description: Defines 'What to log' # Description: Defines 'What to log'
# Format: LogLevel,AppenderList # Format: LogLevel,AppenderList
# # LogLevel: 0 - (Disabled)
# LogLevel
# 0 - (Disabled)
# 1 - (Fatal) # 1 - (Fatal)
# 2 - (Error) # 2 - (Error)
# 3 - (Warning) # 3 - (Warning)
# 4 - (Info) # 4 - (Info)
# 5 - (Debug) # 5 - (Debug)
# 6 - (Trace) # 6 - (Trace)
#
# AppenderList: List of appenders linked to logger # AppenderList: List of appenders linked to logger
# (Using spaces as separator). # (Using spaces as separator).
# #
Logger.root=2,Console Server Logger.root=2,Console Server
#Logger.metric=2,Console Server # Logger.metric=2,Console Server
#Logger.commands.gm=4,Console GM # Logger.commands.gm=4,Console GM
Logger.diff=3,Console Server Logger.diff=3,Console Server
Logger.mmaps=4,Server Logger.mmaps=4,Server
Logger.scripts.hotswap=4,Console Server Logger.scripts.hotswap=4,Console Server
@@ -735,105 +709,105 @@ Logger.time.update=4,Console Server
Logger.module=4,Console Server Logger.module=4,Console Server
Logger.spells.scripts=2,Console Errors Logger.spells.scripts=2,Console Errors
Logger.playerbots=5,Console Playerbots Logger.playerbots=5,Console Playerbots
#Logger.achievement=4,Console Server # Logger.achievement=4,Console Server
#Logger.addon=4,Console Server # Logger.addon=4,Console Server
#Logger.auctionHouse=4,Console Server # Logger.auctionHouse=4,Console Server
#Logger.autobroadcast=4, Console Server # Logger.autobroadcast=4, Console Server
#Logger.bg.arena=4,Console Server # Logger.bg.arena=4,Console Server
#Logger.bg.battlefield=4,Console Server # Logger.bg.battlefield=4,Console Server
#Logger.bg.battleground=4,Console Server # Logger.bg.battleground=4,Console Server
#Logger.bg.reportpvpafk=4,Console Server # Logger.bg.reportpvpafk=4,Console Server
#Logger.calendar=4,Console Server # Logger.calendar=4,Console Server
#Logger.chat.say=4,Console Chat # Logger.chat.say=4,Console Chat
#Logger.chat.emote=4,Console Chat # Logger.chat.emote=4,Console Chat
#Logger.chat.yell=4,Console Chat # Logger.chat.yell=4,Console Chat
#Logger.chat.whisper=4,Console Chat # Logger.chat.whisper=4,Console Chat
#Logger.chat.party=4,Console Chat # Logger.chat.party=4,Console Chat
#Logger.chat.raid=4,Console Chat # Logger.chat.raid=4,Console Chat
#Logger.chat.bg=4,Console Chat # Logger.chat.bg=4,Console Chat
#Logger.chat.guild=4,Console Chat # Logger.chat.guild=4,Console Chat
#Logger.chat.guild.officer=4,Console Chat # Logger.chat.guild.officer=4,Console Chat
#Logger.chat.channel=4,Console Chat # Logger.chat.channel=4,Console Chat
#Logger.chat.addon.msg=4,Console Chat # Logger.chat.addon.msg=4,Console Chat
#Logger.chat.addon.emote=4,Console Chat # Logger.chat.addon.emote=4,Console Chat
#Logger.chat.addon.yell=4,Console Chat # Logger.chat.addon.yell=4,Console Chat
#Logger.chat.addon.whisper=4,Console Chat # Logger.chat.addon.whisper=4,Console Chat
#Logger.chat.addon.party=4,Console Chat # Logger.chat.addon.party=4,Console Chat
#Logger.chat.addon.raid=4,Console Chat # Logger.chat.addon.raid=4,Console Chat
#Logger.chat.addon.bg=4,Console Chat # Logger.chat.addon.bg=4,Console Chat
#Logger.chat.addon.guild=4,Console Chat # Logger.chat.addon.guild=4,Console Chat
#Logger.chat.addon.guild.officer=4,Console Chat # Logger.chat.addon.guild.officer=4,Console Chat
#Logger.chat.addon.channel=4,Console Chat # Logger.chat.addon.channel=4,Console Chat
#Logger.chat.log=4,Console Server # Logger.chat.log=4,Console Server
#Logger.chat.log.addon=4,Console Server # Logger.chat.log.addon=4,Console Server
#Logger.chat.system=4,Console Server # Logger.chat.system=4,Console Server
#Logger.cheat=4,Console Server # Logger.cheat=4,Console Server
#Logger.commands.ra=4,Console Server # Logger.commands.ra=4,Console Server
#Logger.condition=4,Console Server # Logger.condition=4,Console Server
#Logger.dbc=4,Console Server # Logger.dbc=4,Console Server
#Logger.disable=4,Console Server # Logger.disable=4,Console Server
#Logger.entities.dyobject=4,Console Server # Logger.entities.dyobject=4,Console Server
#Logger.entities.faction=4,Console Server # Logger.entities.faction=4,Console Server
#Logger.entities.gameobject=4,Console Server # Logger.entities.gameobject=4,Console Server
#Logger.entities.object=4,Console Server # Logger.entities.object=4,Console Server
#Logger.entities.pet=4,Console Server # Logger.entities.pet=4,Console Server
#Logger.entities.player.auctionhouse=4,Console Server # Logger.entities.player.auctionhouse=4,Console Server
#Logger.entities.player.character=4,Console Server # Logger.entities.player.character=4,Console Server
#Logger.entities.player.dump=4,Console Server # Logger.entities.player.dump=4,Console Server
#Logger.entities.player.items=4,Console Server # Logger.entities.player.items=4,Console Server
#Logger.entities.player.loading=4,Console Server # Logger.entities.player.loading=4,Console Server
#Logger.entities.player.mail=4,Console Server # Logger.entities.player.mail=4,Console Server
#Logger.entities.player.skills=4,Console Server # Logger.entities.player.skills=4,Console Server
#Logger.entities.player.trade=4,Console Server # Logger.entities.player.trade=4,Console Server
#Logger.entities.player=4,Console Server # Logger.entities.player=4,Console Server
#Logger.entities.transport=4,Console Server # Logger.entities.transport=4,Console Server
#Logger.entities.unit.ai=4,Console Server # Logger.entities.unit.ai=4,Console Server
#Logger.entities.unit=4,Console Server # Logger.entities.unit=4,Console Server
#Logger.entities.vehicle=4,Console Server # Logger.entities.vehicle=4,Console Server
#Logger.gameevent=4,Console Server # Logger.gameevent=4,Console Server
#Logger.group=4,Console Server # Logger.group=4,Console Server
#Logger.guild=4,Console Server # Logger.guild=4,Console Server
#Logger.instance.save=4,Console Server # Logger.instance.save=4,Console Server
#Logger.instance.script=4,Console Server # Logger.instance.script=4,Console Server
#Logger.lfg=4,Console Server # Logger.lfg=4,Console Server
#Logger.loot=4,Console Server # Logger.loot=4,Console Server
#Logger.mail=4,Console Server # Logger.mail=4,Console Server
#Logger.maps.script=4,Console Server # Logger.maps.script=4,Console Server
#Logger.maps=4,Console Server # Logger.maps=4,Console Server
#Logger.misc=4,Console Server # Logger.misc=4,Console Server
#Logger.mmaps.tiles=4,Console Server # Logger.mmaps.tiles=4,Console Server
#Logger.movement.flightpath=4,Console Server # Logger.movement.flightpath=4,Console Server
#Logger.movement.motionmaster=4,Console Server # Logger.movement.motionmaster=4,Console Server
#Logger.movement.splinechain=4,Console Server # Logger.movement.splinechain=4,Console Server
#Logger.movement=4,Console Server # Logger.movement=4,Console Server
#Logger.network.kick=4,Console Server # Logger.network.kick=4,Console Server
#Logger.network.opcode=4,Console Server # Logger.network.opcode=4,Console Server
#Logger.network.soap=4,Console Server # Logger.network.soap=4,Console Server
#Logger.network=4,Console Server # Logger.network=4,Console Server
#Logger.outdoorpvp=4,Console Server # Logger.outdoorpvp=4,Console Server
#Logger.pool=4,Console Server # Logger.pool=4,Console Server
#Logger.rbac=4,Console Server # Logger.rbac=4,Console Server
#Logger.reputation=4,Console Server # Logger.reputation=4,Console Server
#Logger.scripts.ai.escortai=4,Console Server # Logger.scripts.ai.escortai=4,Console Server
#Logger.scripts.ai.followerai=4,Console Server # Logger.scripts.ai.followerai=4,Console Server
#Logger.scripts.ai.petai=4,Console Server # Logger.scripts.ai.petai=4,Console Server
#Logger.scripts.ai.sai=4,Console Server # Logger.scripts.ai.sai=4,Console Server
#Logger.scripts.ai=4,Console Server # Logger.scripts.ai=4,Console Server
#Logger.scripts.cos=4,Console Server # Logger.scripts.cos=4,Console Server
#Logger.scripts.midsummer=4,Console Server # Logger.scripts.midsummer=4,Console Server
#Logger.scripts=4,Console Server # Logger.scripts=4,Console Server
#Logger.server.authserver=4,Console Server # Logger.server.authserver=4,Console Server
#Logger.spells.aura.effect.nospell=4,Console Server # Logger.spells.aura.effect.nospell=4,Console Server
#Logger.spells.aura.effect=4,Console Server # Logger.spells.aura.effect=4,Console Server
#Logger.spells.effect.nospell=4,Console Server # Logger.spells.effect.nospell=4,Console Server
#Logger.spells.effect=4,Console Server # Logger.spells.effect=4,Console Server
#Logger.spells.scripts=4,Console Server # Logger.spells.scripts=4,Console Server
#Logger.spells=4,Console Server # Logger.spells=4,Console Server
#Logger.sql.dev=4,Console Server Dev # Logger.sql.dev=4,Console Server Dev
#Logger.sql.driver=4,Console Server # Logger.sql.driver=4,Console Server
#Logger.vehicles=4,Console Server # Logger.vehicles=4,Console Server
#Logger.warden=4,Console Server # Logger.warden=4,Console Server
#Logger.weather=4,Console Server # Logger.weather=4,Console Server
# #
# Log.Async.Enable # Log.Async.Enable
@@ -862,21 +836,12 @@ Metric.Enable = 0
# #
# Metric.InfluxDB # Metric.InfluxDB
# Description: Connection settings for InfluxDB. # Description: Connection settings for InfluxDB.
# # For InfluxDB v1: Only fill in Metric.InfluxDB.Connection.
# For InfluxDB v1: # Example: Metric.InfluxDB.Connection = "hostname;port;database"
# Only fill in Metric.InfluxDB.Connection. # For InfluxDB v2: Fill in every field.
#
# Example:
# Metric.InfluxDB.Connection = "hostname;port;database"
#
# For InfluxDB v2:
# Fill in every field.
#
# NOTE: Currently, Grafana will not work with the provided json files to visualize # NOTE: Currently, Grafana will not work with the provided json files to visualize
# data from InfluxDB v2. # data from InfluxDB v2.
# # Example: Metric.InfluxDB.Connection = "hostname;port"
# Example:
# Metric.InfluxDB.Connection = "hostname;port"
# Metric.InfluxDB.v2 = 0 - (Disabled) # Metric.InfluxDB.v2 = 0 - (Disabled)
# 1 - (Enabled) # 1 - (Enabled)
# Metric.InfluxDB.Org = "my-org" # Metric.InfluxDB.Org = "my-org"
@@ -896,7 +861,6 @@ Metric.InfluxDB.Token = ""
# Longer interval means larger batch of data. If the batch # Longer interval means larger batch of data. If the batch
# is too big, it might get rejected. # is too big, it might get rejected.
# Default: 1 second # Default: 1 second
#
Metric.Interval = 1 Metric.Interval = 1
@@ -904,7 +868,6 @@ Metric.Interval = 1
# Metric.OverallStatusInterval # Metric.OverallStatusInterval
# Description: Interval between every gathering of overall worldserver status data in seconds # Description: Interval between every gathering of overall worldserver status data in seconds
# Default: 1 second # Default: 1 second
#
Metric.OverallStatusInterval = 1 Metric.OverallStatusInterval = 1
@@ -970,7 +933,7 @@ GameType = 0
# #
# RealmZone # RealmZone
# Description: Server realm zone. Set allowed alphabet in character, etc. names. # Description: Server realm zone. Set allowed alphabet in character, etc. names.
# Default 1 - (Development - any language) # Default: 1 - (Development - any language)
# 2 - (United States - extended-Latin) # 2 - (United States - extended-Latin)
# 3 - (Oceanic - extended-Latin) # 3 - (Oceanic - extended-Latin)
# 4 - (Latin America - extended-Latin) # 4 - (Latin America - extended-Latin)
@@ -1424,7 +1387,6 @@ vmap.BlizzlikePvPLOS = 1
# Description: Check line of sight to see game objects in the open world. # Description: Check line of sight to see game objects in the open world.
# Default: 1 (Enabled, Players will be able to cast spells through tree stumps and other objects in the open world). # Default: 1 (Enabled, Players will be able to cast spells through tree stumps and other objects in the open world).
# 0 (Disabled, Players will not be able to cast spells through tree stumps and other objects in the open world). # 0 (Disabled, Players will not be able to cast spells through tree stumps and other objects in the open world).
#
vmap.BlizzlikeLOSInOpenWorld = 1 vmap.BlizzlikeLOSInOpenWorld = 1
@@ -1759,6 +1721,98 @@ InstantLogout = 1
# #
################################################################################################### ###################################################################################################
###################################################################################################
# ACCOUNT
#
# Trial.LevelCap
# Description: Maximum level a trial account can reach. XP gain stops once this level is reached.
# Default: 20
# 0 - (Disabled, no cap)
Trial.LevelCap = 20
#
# Trial.MoneyCap
# Description: Maximum money a trial account can hold, in copper.
# Default: 100000 - (10 gold)
# 0 - (Disabled, no cap)
Trial.MoneyCap = 100000
#
# Trial.TradeSkillCap
# Description: Maximum value any profession skill can reach on a trial account.
# Default: 100
# 0 - (Disabled, no cap)
Trial.TradeSkillCap = 100
#
# Trial.Restriction.Chat
# Description: Restrict trial accounts from using whisper/channel/guild chats.
# Whispers are only allowed when the trial player is on the recipients friendslist
# or when the trial account was messaged first.
# Default: 1 - (Enabled)
# 0 - (Disabled)
Trial.Restriction.Chat = 1
#
# Trial.Restriction.Mail
# Description: Restrict trial accounts from sending mail.
# Default: 1 - (Enabled)
# 0 - (Disabled)
Trial.Restriction.Mail = 1
#
# Trial.Restriction.Trade
# Description: Restrict trial accounts from initiating or being the target of in-game trade.
# Default: 1 - (Enabled)
# 0 - (Disabled)
Trial.Restriction.Trade = 1
#
# Trial.Restriction.Auction
# Description: Restrict trial accounts from selling, bidding, or cancelling auctions at the auction house.
# Default: 1 - (Enabled)
# 0 - (Disabled)
Trial.Restriction.Auction = 1
#
# Trial.Restriction.Party
# Description: Restrict trial accounts from inviting players to a party, and from accepting
# an invite to a party whose existing members are above Trial.LevelCap.
# The join check is skipped when Trial.LevelCap is 0.
# Default: 1 - (Enabled)
# 0 - (Disabled)
Trial.Restriction.Party = 1
#
# Trial.Restriction.Guild
# Description: Restrict trial accounts from joining a guild (accepting an invite) and from
# creating one (buying, signing, or turning in a guild charter).
# Default: 1 - (Enabled)
# 0 - (Disabled)
Trial.Restriction.Guild = 1
#
# Trial.Restriction.Queue
# Description: Force trial accounts through the world session queue even if their account
# flags would otherwise grant skip-queue priority. The RBAC_PERM_SKIP_QUEUE
# permission and recent-disconnect grace are still honored.
# Default: 1 - (Enabled)
# 0 - (Disabled)
Trial.Restriction.Queue = 1
#
###################################################################################################
################################################################################################### ###################################################################################################
# CHARACTER DATABASE # CHARACTER DATABASE
# #
@@ -1802,9 +1856,7 @@ CleanCharacterDB = 0
# CLEANING_FLAG_SPELLS = 0x4 # CLEANING_FLAG_SPELLS = 0x4
# CLEANING_FLAG_TALENTS = 0x8 # CLEANING_FLAG_TALENTS = 0x8
# CLEANING_FLAG_QUESTSTATUS = 0x10 # CLEANING_FLAG_QUESTSTATUS = 0x10
# # Note: Before use this feature, make a backup of your database.
# Before use this feature, make a backup of your database.
#
# Example: 14 - (CLEANING_FLAG_SKILLS + CLEANING_FLAG_SPELLS + CLEANING_FLAG_TALENTS # Example: 14 - (CLEANING_FLAG_SKILLS + CLEANING_FLAG_SPELLS + CLEANING_FLAG_TALENTS
# 2+4+8 => 14. This will clean up skills, talents and spells will # 2+4+8 => 14. This will clean up skills, talents and spells will
# remain enabled after the next cleanup) # remain enabled after the next cleanup)
@@ -1817,7 +1869,7 @@ PersistentCharacterCleanFlags = 0
# Description: If enabled, players will lose spells that are invalid for their race/class. # Description: If enabled, players will lose spells that are invalid for their race/class.
# Default: 1 - (Enabled, enforce valid spells) # Default: 1 - (Enabled, enforce valid spells)
# 0 - (Disabled, allow invalid spells) # 0 - (Disabled, allow invalid spells)
# Disabling this and then having your character learn spells which require DBC edits can result in the character not being saved in the database # Note: Disabling this and then having your character learn spells which require DBC edits can result in the character not being saved in the database
# Disable AT YOUR OWN RISK # Disable AT YOUR OWN RISK
ValidateSkillLearnedBySpells = 1 ValidateSkillLearnedBySpells = 1
@@ -2187,22 +2239,17 @@ Rate.Rest.MaxBonus = 1.5
# Rate.MissChanceMultiplier.Creature # Rate.MissChanceMultiplier.Creature
# Rate.MissChanceMultiplier.Player # Rate.MissChanceMultiplier.Player
# Rate.MissChanceMultiplier.OnlyAffectsPlayer # Rate.MissChanceMultiplier.OnlyAffectsPlayer
#
# Description: When the target is 3 or more level higher than the player, # Description: When the target is 3 or more level higher than the player,
# the chance to hit is determined by the formula: 94 - (levelDiff - 2) * Rate.MissChanceMultiplier # the chance to hit is determined by the formula: 94 - (levelDiff - 2) * Rate.MissChanceMultiplier
# The higher the Rate.MissChanceMultiplier constant, the higher is the chance to miss. # The higher the Rate.MissChanceMultiplier constant, the higher is the chance to miss.
#
# Note: this does not affect when the player is less than 3 levels different than the target, # Note: this does not affect when the player is less than 3 levels different than the target,
# where this (linear) formula is used instead to calculate the hit chance: 96 - levelDiff. # where this (linear) formula is used instead to calculate the hit chance: 96 - levelDiff.
# You can set Rate.MissChanceMultiplier.OnlyAffectsPlayer to 1 if you only want to affect the MissChance # You can set Rate.MissChanceMultiplier.OnlyAffectsPlayer to 1 if you only want to affect the MissChance
# for player casters only. This way you won't be affecting creature missing chance. # for player casters only. This way you won't be affecting creature missing chance.
#
# Example: if you want the chance to keep growing linearly, use 1. # Example: if you want the chance to keep growing linearly, use 1.
#
# Default: Rate.MissChanceMultiplier.TargetCreature = 11 # Default: Rate.MissChanceMultiplier.TargetCreature = 11
# Rate.MissChanceMultiplier.TargetPlayer = 7 # Rate.MissChanceMultiplier.TargetPlayer = 7
# Rate.MissChanceMultiplier.OnlyAffectsPlayer = 0 # Rate.MissChanceMultiplier.OnlyAffectsPlayer = 0
#
Rate.MissChanceMultiplier.TargetCreature = 11 Rate.MissChanceMultiplier.TargetCreature = 11
Rate.MissChanceMultiplier.TargetPlayer = 7 Rate.MissChanceMultiplier.TargetPlayer = 7
@@ -2482,7 +2529,6 @@ Rate.XP.BattlegroundBonus = 1
# Description: Modifies the amount of experience required to level up a pet. # Description: Modifies the amount of experience required to level up a pet.
# The lower the rate the less experience is required. # The lower the rate the less experience is required.
# Default: 0.05 # Default: 0.05
#
Rate.Pet.LevelXP = 0.05 Rate.Pet.LevelXP = 0.05
@@ -2676,7 +2722,7 @@ DurabilityLossChance.Block = 0.05
# Example: 11 - (Level 1-10 characters will not be affected, # Example: 11 - (Level 1-10 characters will not be affected,
# Level 11-19 characters will be affected for 1 minute, # Level 11-19 characters will be affected for 1 minute,
# Level 20-MaxPlayerLevel characters will be affected for 10 minutes) # Level 20-MaxPlayerLevel characters will be affected for 10 minutes)
# Default: 11 - (Enabled, See Example) # Default: 11 - (Enabled, see Example)
# MaxPlayerLevel+1 - (Disabled) # MaxPlayerLevel+1 - (Disabled)
# -10 - (Enabled, Level 1+ characters have 10 minute duration) # -10 - (Enabled, Level 1+ characters have 10 minute duration)
@@ -2735,26 +2781,22 @@ ItemDelete.Method = 0
# Description: Saving items into database when the player sells items to vendor # Description: Saving items into database when the player sells items to vendor
# Default: 0 (disabled) # Default: 0 (disabled)
# 1 (enabled) # 1 (enabled)
#
ItemDelete.Vendor = 0 ItemDelete.Vendor = 0
# #
# ItemDelete.Quality # ItemDelete.Quality
# Description: Saving items into database that have quality greater or equal to ItemDelete.Quality # Description: Saving items into database that have quality greater or equal to ItemDelete.Quality
# # | ID | Color | Quality |
# ID | Color | Quality # | 0 | Grey | Poor |
# 0 | Grey | Poor # | 1 | White | Common |
# 1 | White | Common # | 2 | Green | Uncommon |
# 2 | Green | Uncommon # | 3 | Blue | Rare |
# 3 | Blue | Rare # | 4 | Purple | Epic |
# 4 | Purple| Epic # | 5 | Orange | Legendary |
# 5 | Orange| Legendary # | 6 | Red | Artifact |
# 6 | Red | Artifact # | 7 | Gold | Bind to Account |
# 7 | Gold | Bind to Account
#
# Default: 3 # Default: 3
#
ItemDelete.Quality = 3 ItemDelete.Quality = 3
@@ -2762,7 +2804,6 @@ ItemDelete.Quality = 3
# ItemDelete.ItemLevel # ItemDelete.ItemLevel
# Description: Saving items into database that are Item Levels greater or equal to ItemDelete.ItemLevel # Description: Saving items into database that are Item Levels greater or equal to ItemDelete.ItemLevel
# Default: 80 # Default: 80
#
ItemDelete.ItemLevel = 80 ItemDelete.ItemLevel = 80
@@ -2781,7 +2822,7 @@ ItemDelete.KeepDays = 0
# ITEM # ITEM
# #
# DBC.EnforceItemAttributes # DBC.EnforceItemAttributes
# Disallow overriding item attributes stored in DBC files with values from the database # Description: Disallow overriding item attributes stored in DBC files with values from the database
# Default: 0 - Off, Use DB values # Default: 0 - Off, Use DB values
# 1 - On, Enforce DBC Values (default) # 1 - On, Enforce DBC Values (default)
@@ -2818,6 +2859,7 @@ Rate.Drop.Item.Artifact = 1
Rate.Drop.Item.Referenced = 1 Rate.Drop.Item.Referenced = 1
Rate.Drop.Money = 1 Rate.Drop.Money = 1
#
# Rate.Drop.Item.ReferencedAmount # Rate.Drop.Item.ReferencedAmount
# Description: Multiplier for referenced loot amount. Makes many raid bosses (and others) drop additional loot. # Description: Multiplier for referenced loot amount. Makes many raid bosses (and others) drop additional loot.
# Default: 1 # Default: 1
@@ -3254,7 +3296,7 @@ LeaveGroupOnLogout.Enabled = 0
# #
# Group.Raid.LevelRestriction # Group.Raid.LevelRestriction
# #
# The Group members need to the same, or higher level than the specified value. # Description: The Group members need to the same, or higher level than the specified value.
# Minimum level is 10. # Minimum level is 10.
# Default: 10 # Default: 10
# #
@@ -3264,7 +3306,7 @@ Group.Raid.LevelRestriction = 10
# #
# Group.RandomRollMaximum # Group.RandomRollMaximum
# #
# The maximum value for use with the client '/roll' command. # Description: The maximum value for use with the client '/roll' command.
# Blizzlike and maximum value is 1000000. (Based on Classic and 3.3.5a client testing respectively) # Blizzlike and maximum value is 1000000. (Based on Classic and 3.3.5a client testing respectively)
# Default: 1000000 # Default: 1000000
# #
@@ -3350,7 +3392,6 @@ AccountInstancesPerHour = 5
# Description: Forces ICC and RS Normal and Heroic to share lockouts. ToC is uneffected and Normal and Heroic will be separate. # Description: Forces ICC and RS Normal and Heroic to share lockouts. ToC is uneffected and Normal and Heroic will be separate.
# Default: 1 - Enable # Default: 1 - Enable
# 0 - Disable # 0 - Disable
#
Instance.SharedNormalHeroicId = 1 Instance.SharedNormalHeroicId = 1
@@ -3361,7 +3402,6 @@ Instance.SharedNormalHeroicId = 1
# Default: 1 - (Display only one requirement at a time (BlizzLike, like in the LFG interface)) # Default: 1 - (Display only one requirement at a time (BlizzLike, like in the LFG interface))
# 0 - (Display no extra information, only "Requirements not met") # 0 - (Display no extra information, only "Requirements not met")
# 2 - (Display detailed requirements, all at once, with clickable links) # 2 - (Display detailed requirements, all at once, with clickable links)
#
DungeonAccessRequirements.PrintMode = 1 DungeonAccessRequirements.PrintMode = 1
@@ -3421,10 +3461,9 @@ DungeonFinder.OptionsMask = 5
# #
# LFG.Location.All # LFG.Location.All
# #
# Includes satellite to search for work elsewhere LFG # Description: Includes satellite to search for work elsewhere LFG
# Default: 0 - Disable # Default: 0 - Disable
# 1 - Enable # 1 - Enable
#
LFG.Location.All = 0 LFG.Location.All = 0
@@ -3764,7 +3803,6 @@ Battleground.QueueAnnouncer.Limit.MinPlayers = 3
# Battleground.QueueAnnouncer.SpamProtection.Delay # Battleground.QueueAnnouncer.SpamProtection.Delay
# Description: Show announce if player rejoined in queue after sec # Description: Show announce if player rejoined in queue after sec
# Default: 30 # Default: 30
#
Battleground.QueueAnnouncer.SpamProtection.Delay = 30 Battleground.QueueAnnouncer.SpamProtection.Delay = 30
@@ -3781,7 +3819,6 @@ Battleground.QueueAnnouncer.PlayerOnly = 0
# Description: Enabled battleground queue announcements based on timer # Description: Enabled battleground queue announcements based on timer
# Default: 0 - (Disabled) # Default: 0 - (Disabled)
# 1 - (Enabled - Set Arena.QueueAnnouncer.Timer) # 1 - (Enabled - Set Arena.QueueAnnouncer.Timer)
#
Battleground.QueueAnnouncer.Timed = 0 Battleground.QueueAnnouncer.Timed = 0
@@ -3789,7 +3826,6 @@ Battleground.QueueAnnouncer.Timed = 0
# Battleground.QueueAnnouncer.Timer # Battleground.QueueAnnouncer.Timer
# Description: Set timer for queue announcements # Description: Set timer for queue announcements
# Default: 30000 (30 sec) # Default: 30000 (30 sec)
#
Battleground.QueueAnnouncer.Timer = 30000 Battleground.QueueAnnouncer.Timer = 30000
@@ -3896,7 +3932,6 @@ Battleground.DisableReadyCheckInBG = 0
# 0 - Battleground.RewardWinnerArenaLast # 0 - Battleground.RewardWinnerArenaLast
# 5 - Battleground.RewardLoserHonorFirst # 5 - Battleground.RewardLoserHonorFirst
# 5 - Battleground.RewardLoserHonorLast # 5 - Battleground.RewardLoserHonorLast
#
Battleground.RewardWinnerHonorFirst = 30 Battleground.RewardWinnerHonorFirst = 30
Battleground.RewardWinnerArenaFirst = 25 Battleground.RewardWinnerArenaFirst = 25
@@ -4057,7 +4092,6 @@ Arena.QueueAnnouncer.Enable = 0
# Description: Arena queue announcement type. # Description: Arena queue announcement type.
# Default: 0 - (System message, Anyone can see it) # Default: 0 - (System message, Anyone can see it)
# 1 - (Private, Only queued players can see it) # 1 - (Private, Only queued players can see it)
#
Arena.QueueAnnouncer.PlayerOnly = 0 Arena.QueueAnnouncer.PlayerOnly = 0
@@ -4068,7 +4102,6 @@ Arena.QueueAnnouncer.PlayerOnly = 0
# 2 - (Announce only the team's name) # 2 - (Announce only the team's name)
# 1 - (Announce only the team's rating) # 1 - (Announce only the team's rating)
# 0 - (Do not announce any information about the teams) # 0 - (Do not announce any information about the teams)
#
Arena.QueueAnnouncer.Detail = 3 Arena.QueueAnnouncer.Detail = 3
@@ -4182,8 +4215,6 @@ LevelReq.Mail = 1
# IsContinentTransport.Enabled # IsContinentTransport.Enabled
# Description: Controls the continent transport (ships, zeppelins etc..) # Description: Controls the continent transport (ships, zeppelins etc..)
# Default: 1 - (Enabled) # Default: 1 - (Enabled)
#
#
IsContinentTransport.Enabled = 1 IsContinentTransport.Enabled = 1
@@ -4192,10 +4223,8 @@ IsContinentTransport.Enabled = 1
# Description: Should we preload the transport? # Description: Should we preload the transport?
# (Not recommended on low-end servers as it consumes 100% more ram) # (Not recommended on low-end servers as it consumes 100% more ram)
# and it's not really necessary to be enabled. # and it's not really necessary to be enabled.
#
# Default: 0 - (Disabled) # Default: 0 - (Disabled)
# #
#
IsPreloadedContinentTransport.Enabled = 0 IsPreloadedContinentTransport.Enabled = 0
@@ -4235,7 +4264,6 @@ AddonChannel = 1
# "normal" chat messages for sending data to other clients. # "normal" chat messages for sending data to other clients.
# Default: 1 - (Enabled, Blizzlike) # Default: 1 - (Enabled, Blizzlike)
# 0 - (Disabled) # 0 - (Disabled)
#
ChatFakeMessagePreventing = 1 ChatFakeMessagePreventing = 1
@@ -4245,12 +4273,10 @@ ChatFakeMessagePreventing = 1
# -1 - (Only verify validity of link data, but permit use of custom colors) # -1 - (Only verify validity of link data, but permit use of custom colors)
# Default: 0 - (Only verify that link data and color are valid without checking text) # Default: 0 - (Only verify that link data and color are valid without checking text)
# 1 - (Additionally verifies that the link text matches the provided data) # 1 - (Additionally verifies that the link text matches the provided data)
#
# Note: If this is set to '1', you must additionally provide .dbc files for all # Note: If this is set to '1', you must additionally provide .dbc files for all
# client locales that are in use on your server. # client locales that are in use on your server.
# If any files are missing, messages with links from clients using those # If any files are missing, messages with links from clients using those
# locales will likely be blocked by the server. # locales will likely be blocked by the server.
#
ChatStrictLinkChecking.Severity = 0 ChatStrictLinkChecking.Severity = 0
@@ -4260,7 +4286,6 @@ ChatStrictLinkChecking.Severity = 0
# is received. # is received.
# Default: 0 - (Silently ignore message) # Default: 0 - (Silently ignore message)
# 1 - (Ignore message and kick player) # 1 - (Ignore message and kick player)
#
ChatStrictLinkChecking.Kick = 0 ChatStrictLinkChecking.Kick = 0
@@ -4334,7 +4359,7 @@ Channel.RestrictedLfg = 1
Channel.SilentlyGMJoin = 0 Channel.SilentlyGMJoin = 0
# Channel.ModerationGMLevel # Channel.ModerationGMLevel
# Min GM account security level required for executing moderator in-game commands in the channels # Description: Min GM account security level required for executing moderator in-game commands in the channels
# This also bypasses password prompts on joining channels which require password # This also bypasses password prompts on joining channels which require password
# 0 (in-game channel moderator privileges only) # 0 (in-game channel moderator privileges only)
# Default: 1 (enabled for moderators and above) # Default: 1 (enabled for moderators and above)
@@ -4493,7 +4518,7 @@ TalentsInspecting = 1
# ChangeFaction.MaxMoney # ChangeFaction.MaxMoney
# Description: Maximum amount of gold allowed on the character to perform a faction change. # Description: Maximum amount of gold allowed on the character to perform a faction change.
# Default: 0 - Disabled # Default: 0 - Disabled
# > 0 - Enabled (money in copper) # >0 - Enabled (money in copper)
# Example: If set to 10000, the maximum amount of money allowed on the character would be 1 gold. # Example: If set to 10000, the maximum amount of money allowed on the character would be 1 gold.
ChangeFaction.MaxMoney = 0 ChangeFaction.MaxMoney = 0
@@ -4637,28 +4662,28 @@ PlayerDump.DisallowOverwrite = 1
################################################################################################### ###################################################################################################
# CUSTOM # CUSTOM
# #
# ICC Buff # ICC.Buff.Alliance
# ICC.Buff.Horde
# Description: Specify ICC buff # Description: Specify ICC buff
# (It is necessary to restart the server after changing the values!) # Note: It is necessary to restart the server after changing the values!
# Default: ICC.Buff.Horde = 73822 # Example: | Spell IDs | Amount | Faction |
# ICC.Buff.Alliance = 73828 # | 73762 | 5% | Alliance |
# # | 73824 | 10% | Alliance |
# Spell IDs for the auras: # | 73825 | 15% | Alliance |
# 73816 - 5% buff Horde # | 73826 | 20% | Alliance |
# 73818 - 10% buff Horde # | 73827 | 25% | Alliance |
# 73819 - 15% buff Horde # | 73828 | 30% | Alliance |
# 73820 - 20% buff Horde # | 73816 | 5% | Horde |
# 73821 - 25% buff Horde # | 73818 | 10% | Horde |
# 73822 - 30% buff Horde # | 73819 | 15% | Horde |
# 73762 - 5% buff Alliance # | 73820 | 20% | Horde |
# 73824 - 10% buff Alliance # | 73821 | 25% | Horde |
# 73825 - 15% buff Alliance # | 73822 | 30% | Horde |
# 73826 - 20% buff Alliance # Default: ICC.Buff.Alliance = 73828
# 73827 - 25% buff Alliance # ICC.Buff.Horde = 73822
# 73828 - 30% buff Alliance
ICC.Buff.Horde = 73822
ICC.Buff.Alliance = 73828 ICC.Buff.Alliance = 73828
ICC.Buff.Horde = 73822
# #
# WipeGunshipBlizzlike.Enable # WipeGunshipBlizzlike.Enable
@@ -4679,7 +4704,6 @@ Minigob.Manabonk.Enable = 1
# Description: Calculate at loading creature zoneId / areaId and save in creature table # Description: Calculate at loading creature zoneId / areaId and save in creature table
# WARNING: SLOW WORLD SERVER STARTUP. Should only be used for debugging. # WARNING: SLOW WORLD SERVER STARTUP. Should only be used for debugging.
# Default: 0 - (Do not show) # Default: 0 - (Do not show)
#
Calculate.Creature.Zone.Area.Data = 0 Calculate.Creature.Zone.Area.Data = 0
@@ -4688,7 +4712,6 @@ Calculate.Creature.Zone.Area.Data = 0
# Description: Calculate at loading gameobject zoneId / areaId and save in gameobject table # Description: Calculate at loading gameobject zoneId / areaId and save in gameobject table
# WARNING: SLOW WORLD SERVER STARTUP. Should only be used for debugging. # WARNING: SLOW WORLD SERVER STARTUP. Should only be used for debugging.
# Default: 0 - (Do not show) # Default: 0 - (Do not show)
#
Calculate.Gameoject.Zone.Area.Data = 0 Calculate.Gameoject.Zone.Area.Data = 0
@@ -4801,7 +4824,6 @@ Respawn.DynamicMinimumGameObject = 10
# When enabled, escort NPCs in spawn groups flagged as ESCORTQUESTNPC # When enabled, escort NPCs in spawn groups flagged as ESCORTQUESTNPC
# will use special respawn handling. # will use special respawn handling.
# Default: 0 - (Disabled) # Default: 0 - (Disabled)
#
Respawn.DynamicEscortNPC = 0 Respawn.DynamicEscortNPC = 0
@@ -4812,7 +4834,6 @@ Respawn.DynamicEscortNPC = 0
# gameobjects respawn in-place as they always have in AzerothCore. # gameobjects respawn in-place as they always have in AzerothCore.
# Set to 1 to force legacy behavior for all spawns. # Set to 1 to force legacy behavior for all spawns.
# Default: 0 - (Disabled, spawn groups control respawn mode) # Default: 0 - (Disabled, spawn groups control respawn mode)
#
Respawn.ForceCompatibilityMode = 0 Respawn.ForceCompatibilityMode = 0
@@ -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,8 +1218,6 @@ 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_ALLIANCE_INTACT:
@@ -1236,7 +1233,6 @@ struct BfWGGameObjectBuilding
gobj->SetDestructibleState(GO_DESTRUCTIBLE_DAMAGED); gobj->SetDestructibleState(GO_DESTRUCTIBLE_DAMAGED);
break; break;
} }
}
int32 towerid = -1; int32 towerid = -1;
switch (gobj->GetEntry()) switch (gobj->GetEntry())
@@ -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;
+6 -6
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,15 +1795,18 @@ 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
if (!grp) if (!grp)
@@ -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);
+65 -68
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;
SendDirectMessage(questUpdateComplete.Write());
LOG_DEBUG("network", "WORLD: Sent SMSG_QUESTUPDATE_COMPLETE quest = {}", quest_id); 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)
SendDirectMessage(questGiverQuestFailed.Write());
LOG_DEBUG("network", "WORLD: Sent SMSG_QUESTGIVER_QUEST_FAILED"); 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;
SendDirectMessage(questUpdateFailedTimer.Write());
LOG_DEBUG("network", "WORLD: Sent SMSG_QUESTUPDATE_FAILEDTIMER"); 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;
SendDirectMessage(questPushResult.Write());
LOG_DEBUG("network", "WORLD: Sent MSG_QUEST_PUSH_RESULT"); 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)
+28 -17
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,9 +15802,8 @@ 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 });
else if (seatAddon->ExitParameter == VehicleExitParameters::VehicleExitParamDest)
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); 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());
@@ -15809,6 +15811,15 @@ void Unit::_ExitVehicle(Position const* exitPosition)
if (!isInLoS || pos.GetPositionZ() < floorZ) if (!isInLoS || pos.GetPositionZ() < floorZ)
pos = vehicleBase->GetPosition(); pos = vehicleBase->GetPosition();
} }
else if (seatAddon->ExitParameter == VehicleExitParameters::VehicleExitParamDest)
{
pos.Relocate({ seatAddon->ExitParameterX, seatAddon->ExitParameterY, seatAddon->ExitParameterZ, seatAddon->ExitParameterO });
float floorZ = GetMapHeight(pos.GetPositionX(), pos.GetPositionY(), pos.GetPositionZ());
if (pos.GetPositionZ() < floorZ)
pos.m_positionZ = floorZ + 0.1f;
}
}
} }
AddUnitState(UNIT_STATE_MOVE); AddUnitState(UNIT_STATE_MOVE);
+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)
{ {
@@ -4243,6 +4245,8 @@ void AuraEffect::HandleModPercentStat(AuraApplication const* aurApp, uint8 mode,
return; return;
for (int32 i = STAT_STRENGTH; i < MAX_STATS; ++i) for (int32 i = STAT_STRENGTH; i < MAX_STATS; ++i)
{
if (GetMiscValue() == i || GetMiscValue() == -1)
{ {
if (apply) if (apply)
target->ApplyStatPctModifier(UnitMods(UNIT_MOD_STAT_START + i), BASE_PCT, float(GetAmount())); target->ApplyStatPctModifier(UnitMods(UNIT_MOD_STAT_START + i), BASE_PCT, float(GetAmount()));
@@ -4255,6 +4259,7 @@ void AuraEffect::HandleModPercentStat(AuraApplication const* aurApp, uint8 mode,
target->SetStatPctModifier(UnitMods(UNIT_MOD_STAT_START + i), BASE_PCT, amount); target->SetStatPctModifier(UnitMods(UNIT_MOD_STAT_START + i), BASE_PCT, amount);
} }
} }
}
} }
void AuraEffect::HandleModSpellDamagePercentFromStat(AuraApplication const* aurApp, uint8 mode, bool /*apply*/) const void AuraEffect::HandleModSpellDamagePercentFromStat(AuraApplication const* aurApp, uint8 mode, bool /*apply*/) const
@@ -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,13 +202,17 @@ 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 (!IsAnyAssemblyMemberEngaged(pInstance, me))
{
RespawnAssemblyOfIron(pInstance, me);
if (pInstance) if (pInstance)
pInstance->SetBossState(BOSS_ASSEMBLY, NOT_STARTED); 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 (!IsAnyAssemblyMemberEngaged(pInstance, me))
{
RespawnAssemblyOfIron(pInstance, me);
if (pInstance) if (pInstance)
pInstance->SetBossState(BOSS_ASSEMBLY, NOT_STARTED); 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,9 +602,14 @@ 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 (!IsAnyAssemblyMemberEngaged(pInstance, me))
{
RespawnAssemblyOfIron(pInstance, me);
if (pInstance) if (pInstance)
pInstance->SetBossState(BOSS_ASSEMBLY, NOT_STARTED); pInstance->SetBossState(BOSS_ASSEMBLY, NOT_STARTED);
} }
}
void JustReachedHome() override void JustReachedHome() override
{ {
@@ -1445,21 +1445,38 @@ 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 (Vehicle* vehicle = seat->GetVehicleKit())
if (Unit* pyrite = vehicle->GetPassenger(1)) if (Unit* passenger = vehicle->GetPassenger(1))
pyrite->ExitVehicle(); {
// On the chopper the rear seat may carry a player; never eject them to grab.
if (passenger->IsPlayer())
return;
passenger->ExitVehicle();
}
if (Unit* parent = seat->GetVehicleBase()) 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); GetCaster()->CastSpell(parent, SPELL_ADD_PYRITE, true);
target->CastSpell(seat, GetEffectValue()); target->CastSpell(seat, GetEffectValue());
if (target->IsCreature()) if (target->IsCreature())
target->ToCreature()->DespawnOrUnsummon(1300ms); 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());
} }
} }
@@ -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