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
insert_final_newline = true
trim_trailing_whitespace = true
max_line_length = 80
max_line_length = 120
[*.{json,ts,js,yml,sh}]
charset = utf-8
@@ -14,4 +14,4 @@ indent_size = 2
tab_width = 2
insert_final_newline = 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)
- 2-space indentation for JSON, YAML, shell scripts
- UTF-8 encoding, LF line endings
- Max 80 character line length
- Max 120 character line length
- No braces around single-line statements
- 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`):
- 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 )`.
- `auto const&` (not `const auto&`); `Type const*` (not `const Type*`).
- 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_NOKICK = 0x2, // NYI UNK
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_IGR = 0x20, // NYI Internet Game Room (Internet cafe?)
ACCOUNT_FLAG_WHOLESALER = 0x40, // NYI UNK
+250 -229
View File
@@ -30,6 +30,7 @@
# GAME SETTINGS
# GAME MASTER
# CHEAT
# ACCOUNT
# CHARACTER DATABASE
# CHARACTER DELETE
# CHARACTER CREATION
@@ -109,11 +110,10 @@ BindIP = "0.0.0.0"
# CharacterDatabaseInfo
# Description: Database connection settings for the world server.
# Example: "hostname;port;username;password;database"
# ".;somenumber;username;password;database" - (Use named pipes on Windows
# "enable-named-pipe" to [mysqld]
# section my.ini)
# ".;/path/to/unix_socket;username;password;database" - (use Unix sockets on
# Unix/Linux)
# ".;somenumber;username;password;database"
# (Use named pipes on Windows "enable-named-pipe" to [mysqld] section my.ini)
# ".;/path/to/unix_socket;username;password;database"
# (Use Unix sockets on Unix/Linux)
# 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_characters" - (CharacterDatabaseInfo)
@@ -160,7 +160,6 @@ MaxPingTime = 30
#
# Database.Reconnect.Seconds
# Database.Reconnect.Attempts
#
# Description: How many seconds between every reconnection attempt
# and how many attempts will be performed in total
# Default: 20 attempts every 15 seconds
@@ -287,12 +286,10 @@ FlashAtStart = 1
#
# Updates.EnableDatabases
# Description: A mask that describes which databases should be updated.
#
# Following flags are available
# DATABASE_LOGIN = 1, // Auth database
# DATABASE_CHARACTER = 2, // Character database
# DATABASE_WORLD = 4, // World database
#
# Note: Following flags are available
# DATABASE_LOGIN = 1, Auth database
# DATABASE_CHARACTER = 2, Character database
# DATABASE_WORLD = 4, World database
# Default: 7 - (All enabled)
# 4 - (Enable world only)
# 0 - (All disabled)
@@ -405,9 +402,7 @@ Network.EnableProxyProtocol = 0
# When enabled and the process is started by systemd socket activation,
# the server will use the socket passed by systemd instead of
# creating and binding its own listening socket. Disabled by default.
#
# When enabled the realm is not automatically set as offline on shutdown.
#
# Example: 1 - (Enabled)
# Default: 0 - (Disabled)
@@ -479,10 +474,8 @@ SOAP.Port = 7878
# 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
# management commands (.account 2fa), otherwise this can be left blank.
#
# 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.
#
# Default: <blank>
#
@@ -590,7 +583,6 @@ MinRecordUpdateTimeDiff = 100
# Example: "C:/acore/IP2LOCATION-LITE-DB1.CSV"
# "/home/acore/IP2LOCATION-LITE-DB1.CSV"
# Default: "" - (Disabled)
#
IPLocationFile = ""
@@ -599,7 +591,6 @@ IPLocationFile = ""
# Description: Specifies if IP addresses can be logged to the database
# Default: 1 - (Enabled)
# 0 - (Disabled)
#
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.
# Default: 0 - (Disabled)
# 1 - (Enabled)
#
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.
# Default: 1 - (Enabled)
# 0 - (Disabled)
#
LogSpamReports = 1
@@ -626,7 +615,6 @@ LogSpamReports = 1
# Description: Enable or disable chat logging (chat_log.cpp).
# Default: 0 - (Disabled)
# 1 - (Enabled)
#
ChatLog.Enable = 0
@@ -635,24 +623,18 @@ ChatLog.Enable = 0
# Appender.name
# Description: Defines 'where to log'.
# Format: Type,LogLevel,Flags,optional1,optional2,optional3
#
# Type
# 0 - (None)
# Type: 0 - (None)
# 1 - (Console)
# 2 - (File)
# 3 - (DB)
#
# LogLevel
# 0 - (Disabled)
# LogLevel: 0 - (Disabled)
# 1 - (Fatal)
# 2 - (Error)
# 3 - (Warning)
# 4 - (Info)
# 5 - (Debug)
# 6 - (Trace)
#
# Flags:
# 0 - None
# Flags: 0 - None
# 1 - Prefix Timestamp to the text
# 2 - Prefix Log Level to the text
# 4 - Prefix Log Filter type to the text
@@ -660,8 +642,7 @@ ChatLog.Enable = 0
# (Only used with Type = 2)
# 16 - Make a backup of existing file before overwrite
# (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"
# 0 - BLACK
# 1 - RED
@@ -679,21 +660,17 @@ ChatLog.Enable = 0
# 13 - LCYAN
# 14 - WHITE
# Example: "1 9 3 6 5 8"
#
# File: Name of the file (read as optional1 if Type = File)
# Allows to use one "%s" to create dynamic files
#
# Mode: Mode to open the file (read as optional2 if Type = File)
# a - (Append)
# w - (Overwrite)
#
# MaxFileSize: Maximum file size of the log file before creating a new log file
# (read as optional3 if Type = File)
# Size is measured in bytes expressed in a 64-bit unsigned integer.
# Maximum value is 4294967295 (4 GB). Leave blank for no limit.
# NOTE: Does not work with dynamic filenames.
# Example: 536870912 (512 MB)
#
Appender.Console=1,4,0,"1 9 3 6 5 8"
Appender.Server=2,5,0,Server.log,w
@@ -707,23 +684,20 @@ Appender.Errors=2,2,0,Errors.log,w
# Logger.name
# Description: Defines 'What to log'
# Format: LogLevel,AppenderList
#
# LogLevel
# 0 - (Disabled)
# LogLevel: 0 - (Disabled)
# 1 - (Fatal)
# 2 - (Error)
# 3 - (Warning)
# 4 - (Info)
# 5 - (Debug)
# 6 - (Trace)
#
# AppenderList: List of appenders linked to logger
# (Using spaces as separator).
#
Logger.root=2,Console Server
#Logger.metric=2,Console Server
#Logger.commands.gm=4,Console GM
# Logger.metric=2,Console Server
# Logger.commands.gm=4,Console GM
Logger.diff=3,Console Server
Logger.mmaps=4,Server
Logger.scripts.hotswap=4,Console Server
@@ -735,105 +709,105 @@ Logger.time.update=4,Console Server
Logger.module=4,Console Server
Logger.spells.scripts=2,Console Errors
Logger.playerbots=5,Console Playerbots
#Logger.achievement=4,Console Server
#Logger.addon=4,Console Server
#Logger.auctionHouse=4,Console Server
#Logger.autobroadcast=4, Console Server
#Logger.bg.arena=4,Console Server
#Logger.bg.battlefield=4,Console Server
#Logger.bg.battleground=4,Console Server
#Logger.bg.reportpvpafk=4,Console Server
#Logger.calendar=4,Console Server
#Logger.chat.say=4,Console Chat
#Logger.chat.emote=4,Console Chat
#Logger.chat.yell=4,Console Chat
#Logger.chat.whisper=4,Console Chat
#Logger.chat.party=4,Console Chat
#Logger.chat.raid=4,Console Chat
#Logger.chat.bg=4,Console Chat
#Logger.chat.guild=4,Console Chat
#Logger.chat.guild.officer=4,Console Chat
#Logger.chat.channel=4,Console Chat
#Logger.chat.addon.msg=4,Console Chat
#Logger.chat.addon.emote=4,Console Chat
#Logger.chat.addon.yell=4,Console Chat
#Logger.chat.addon.whisper=4,Console Chat
#Logger.chat.addon.party=4,Console Chat
#Logger.chat.addon.raid=4,Console Chat
#Logger.chat.addon.bg=4,Console Chat
#Logger.chat.addon.guild=4,Console Chat
#Logger.chat.addon.guild.officer=4,Console Chat
#Logger.chat.addon.channel=4,Console Chat
#Logger.chat.log=4,Console Server
#Logger.chat.log.addon=4,Console Server
#Logger.chat.system=4,Console Server
#Logger.cheat=4,Console Server
#Logger.commands.ra=4,Console Server
#Logger.condition=4,Console Server
#Logger.dbc=4,Console Server
#Logger.disable=4,Console Server
#Logger.entities.dyobject=4,Console Server
#Logger.entities.faction=4,Console Server
#Logger.entities.gameobject=4,Console Server
#Logger.entities.object=4,Console Server
#Logger.entities.pet=4,Console Server
#Logger.entities.player.auctionhouse=4,Console Server
#Logger.entities.player.character=4,Console Server
#Logger.entities.player.dump=4,Console Server
#Logger.entities.player.items=4,Console Server
#Logger.entities.player.loading=4,Console Server
#Logger.entities.player.mail=4,Console Server
#Logger.entities.player.skills=4,Console Server
#Logger.entities.player.trade=4,Console Server
#Logger.entities.player=4,Console Server
#Logger.entities.transport=4,Console Server
#Logger.entities.unit.ai=4,Console Server
#Logger.entities.unit=4,Console Server
#Logger.entities.vehicle=4,Console Server
#Logger.gameevent=4,Console Server
#Logger.group=4,Console Server
#Logger.guild=4,Console Server
#Logger.instance.save=4,Console Server
#Logger.instance.script=4,Console Server
#Logger.lfg=4,Console Server
#Logger.loot=4,Console Server
#Logger.mail=4,Console Server
#Logger.maps.script=4,Console Server
#Logger.maps=4,Console Server
#Logger.misc=4,Console Server
#Logger.mmaps.tiles=4,Console Server
#Logger.movement.flightpath=4,Console Server
#Logger.movement.motionmaster=4,Console Server
#Logger.movement.splinechain=4,Console Server
#Logger.movement=4,Console Server
#Logger.network.kick=4,Console Server
#Logger.network.opcode=4,Console Server
#Logger.network.soap=4,Console Server
#Logger.network=4,Console Server
#Logger.outdoorpvp=4,Console Server
#Logger.pool=4,Console Server
#Logger.rbac=4,Console Server
#Logger.reputation=4,Console Server
#Logger.scripts.ai.escortai=4,Console Server
#Logger.scripts.ai.followerai=4,Console Server
#Logger.scripts.ai.petai=4,Console Server
#Logger.scripts.ai.sai=4,Console Server
#Logger.scripts.ai=4,Console Server
#Logger.scripts.cos=4,Console Server
#Logger.scripts.midsummer=4,Console Server
#Logger.scripts=4,Console Server
#Logger.server.authserver=4,Console Server
#Logger.spells.aura.effect.nospell=4,Console Server
#Logger.spells.aura.effect=4,Console Server
#Logger.spells.effect.nospell=4,Console Server
#Logger.spells.effect=4,Console Server
#Logger.spells.scripts=4,Console Server
#Logger.spells=4,Console Server
#Logger.sql.dev=4,Console Server Dev
#Logger.sql.driver=4,Console Server
#Logger.vehicles=4,Console Server
#Logger.warden=4,Console Server
#Logger.weather=4,Console Server
# Logger.achievement=4,Console Server
# Logger.addon=4,Console Server
# Logger.auctionHouse=4,Console Server
# Logger.autobroadcast=4, Console Server
# Logger.bg.arena=4,Console Server
# Logger.bg.battlefield=4,Console Server
# Logger.bg.battleground=4,Console Server
# Logger.bg.reportpvpafk=4,Console Server
# Logger.calendar=4,Console Server
# Logger.chat.say=4,Console Chat
# Logger.chat.emote=4,Console Chat
# Logger.chat.yell=4,Console Chat
# Logger.chat.whisper=4,Console Chat
# Logger.chat.party=4,Console Chat
# Logger.chat.raid=4,Console Chat
# Logger.chat.bg=4,Console Chat
# Logger.chat.guild=4,Console Chat
# Logger.chat.guild.officer=4,Console Chat
# Logger.chat.channel=4,Console Chat
# Logger.chat.addon.msg=4,Console Chat
# Logger.chat.addon.emote=4,Console Chat
# Logger.chat.addon.yell=4,Console Chat
# Logger.chat.addon.whisper=4,Console Chat
# Logger.chat.addon.party=4,Console Chat
# Logger.chat.addon.raid=4,Console Chat
# Logger.chat.addon.bg=4,Console Chat
# Logger.chat.addon.guild=4,Console Chat
# Logger.chat.addon.guild.officer=4,Console Chat
# Logger.chat.addon.channel=4,Console Chat
# Logger.chat.log=4,Console Server
# Logger.chat.log.addon=4,Console Server
# Logger.chat.system=4,Console Server
# Logger.cheat=4,Console Server
# Logger.commands.ra=4,Console Server
# Logger.condition=4,Console Server
# Logger.dbc=4,Console Server
# Logger.disable=4,Console Server
# Logger.entities.dyobject=4,Console Server
# Logger.entities.faction=4,Console Server
# Logger.entities.gameobject=4,Console Server
# Logger.entities.object=4,Console Server
# Logger.entities.pet=4,Console Server
# Logger.entities.player.auctionhouse=4,Console Server
# Logger.entities.player.character=4,Console Server
# Logger.entities.player.dump=4,Console Server
# Logger.entities.player.items=4,Console Server
# Logger.entities.player.loading=4,Console Server
# Logger.entities.player.mail=4,Console Server
# Logger.entities.player.skills=4,Console Server
# Logger.entities.player.trade=4,Console Server
# Logger.entities.player=4,Console Server
# Logger.entities.transport=4,Console Server
# Logger.entities.unit.ai=4,Console Server
# Logger.entities.unit=4,Console Server
# Logger.entities.vehicle=4,Console Server
# Logger.gameevent=4,Console Server
# Logger.group=4,Console Server
# Logger.guild=4,Console Server
# Logger.instance.save=4,Console Server
# Logger.instance.script=4,Console Server
# Logger.lfg=4,Console Server
# Logger.loot=4,Console Server
# Logger.mail=4,Console Server
# Logger.maps.script=4,Console Server
# Logger.maps=4,Console Server
# Logger.misc=4,Console Server
# Logger.mmaps.tiles=4,Console Server
# Logger.movement.flightpath=4,Console Server
# Logger.movement.motionmaster=4,Console Server
# Logger.movement.splinechain=4,Console Server
# Logger.movement=4,Console Server
# Logger.network.kick=4,Console Server
# Logger.network.opcode=4,Console Server
# Logger.network.soap=4,Console Server
# Logger.network=4,Console Server
# Logger.outdoorpvp=4,Console Server
# Logger.pool=4,Console Server
# Logger.rbac=4,Console Server
# Logger.reputation=4,Console Server
# Logger.scripts.ai.escortai=4,Console Server
# Logger.scripts.ai.followerai=4,Console Server
# Logger.scripts.ai.petai=4,Console Server
# Logger.scripts.ai.sai=4,Console Server
# Logger.scripts.ai=4,Console Server
# Logger.scripts.cos=4,Console Server
# Logger.scripts.midsummer=4,Console Server
# Logger.scripts=4,Console Server
# Logger.server.authserver=4,Console Server
# Logger.spells.aura.effect.nospell=4,Console Server
# Logger.spells.aura.effect=4,Console Server
# Logger.spells.effect.nospell=4,Console Server
# Logger.spells.effect=4,Console Server
# Logger.spells.scripts=4,Console Server
# Logger.spells=4,Console Server
# Logger.sql.dev=4,Console Server Dev
# Logger.sql.driver=4,Console Server
# Logger.vehicles=4,Console Server
# Logger.warden=4,Console Server
# Logger.weather=4,Console Server
#
# Log.Async.Enable
@@ -862,21 +836,12 @@ Metric.Enable = 0
#
# Metric.InfluxDB
# Description: Connection settings for InfluxDB.
#
# For InfluxDB v1:
# Only fill in Metric.InfluxDB.Connection.
#
# Example:
# Metric.InfluxDB.Connection = "hostname;port;database"
#
# For InfluxDB v2:
# Fill in every field.
#
# For InfluxDB v1: Only fill in Metric.InfluxDB.Connection.
# 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
# data from InfluxDB v2.
#
# Example:
# Metric.InfluxDB.Connection = "hostname;port"
# Example: Metric.InfluxDB.Connection = "hostname;port"
# Metric.InfluxDB.v2 = 0 - (Disabled)
# 1 - (Enabled)
# Metric.InfluxDB.Org = "my-org"
@@ -896,7 +861,6 @@ Metric.InfluxDB.Token = ""
# Longer interval means larger batch of data. If the batch
# is too big, it might get rejected.
# Default: 1 second
#
Metric.Interval = 1
@@ -904,7 +868,6 @@ Metric.Interval = 1
# Metric.OverallStatusInterval
# Description: Interval between every gathering of overall worldserver status data in seconds
# Default: 1 second
#
Metric.OverallStatusInterval = 1
@@ -970,7 +933,7 @@ GameType = 0
#
# RealmZone
# 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)
# 3 - (Oceanic - 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.
# 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).
#
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
#
@@ -1802,9 +1856,7 @@ CleanCharacterDB = 0
# CLEANING_FLAG_SPELLS = 0x4
# CLEANING_FLAG_TALENTS = 0x8
# CLEANING_FLAG_QUESTSTATUS = 0x10
#
# Before use this feature, make a backup of your database.
#
# Note: Before use this feature, make a backup of your database.
# Example: 14 - (CLEANING_FLAG_SKILLS + CLEANING_FLAG_SPELLS + CLEANING_FLAG_TALENTS
# 2+4+8 => 14. This will clean up skills, talents and spells will
# 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.
# Default: 1 - (Enabled, enforce valid 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
ValidateSkillLearnedBySpells = 1
@@ -2187,22 +2239,17 @@ Rate.Rest.MaxBonus = 1.5
# Rate.MissChanceMultiplier.Creature
# Rate.MissChanceMultiplier.Player
# Rate.MissChanceMultiplier.OnlyAffectsPlayer
#
# 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 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,
# 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
# 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.
#
# Default: Rate.MissChanceMultiplier.TargetCreature = 11
# Rate.MissChanceMultiplier.TargetPlayer = 7
# Rate.MissChanceMultiplier.OnlyAffectsPlayer = 0
#
Rate.MissChanceMultiplier.TargetCreature = 11
Rate.MissChanceMultiplier.TargetPlayer = 7
@@ -2482,7 +2529,6 @@ Rate.XP.BattlegroundBonus = 1
# Description: Modifies the amount of experience required to level up a pet.
# The lower the rate the less experience is required.
# Default: 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,
# Level 11-19 characters will be affected for 1 minute,
# Level 20-MaxPlayerLevel characters will be affected for 10 minutes)
# Default: 11 - (Enabled, See Example)
# Default: 11 - (Enabled, see Example)
# MaxPlayerLevel+1 - (Disabled)
# -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
# Default: 0 (disabled)
# 1 (enabled)
#
ItemDelete.Vendor = 0
#
# ItemDelete.Quality
# Description: Saving items into database that have quality greater or equal to ItemDelete.Quality
#
# ID | Color | Quality
# 0 | Grey | Poor
# 1 | White | Common
# 2 | Green | Uncommon
# 3 | Blue | Rare
# 4 | Purple| Epic
# 5 | Orange| Legendary
# 6 | Red | Artifact
# 7 | Gold | Bind to Account
#
# | ID | Color | Quality |
# | 0 | Grey | Poor |
# | 1 | White | Common |
# | 2 | Green | Uncommon |
# | 3 | Blue | Rare |
# | 4 | Purple | Epic |
# | 5 | Orange | Legendary |
# | 6 | Red | Artifact |
# | 7 | Gold | Bind to Account |
# Default: 3
#
ItemDelete.Quality = 3
@@ -2762,7 +2804,6 @@ ItemDelete.Quality = 3
# ItemDelete.ItemLevel
# Description: Saving items into database that are Item Levels greater or equal to ItemDelete.ItemLevel
# Default: 80
#
ItemDelete.ItemLevel = 80
@@ -2781,7 +2822,7 @@ ItemDelete.KeepDays = 0
# ITEM
#
# 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
# 1 - On, Enforce DBC Values (default)
@@ -2818,6 +2859,7 @@ Rate.Drop.Item.Artifact = 1
Rate.Drop.Item.Referenced = 1
Rate.Drop.Money = 1
#
# Rate.Drop.Item.ReferencedAmount
# Description: Multiplier for referenced loot amount. Makes many raid bosses (and others) drop additional loot.
# Default: 1
@@ -3254,7 +3296,7 @@ LeaveGroupOnLogout.Enabled = 0
#
# 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.
# Default: 10
#
@@ -3264,7 +3306,7 @@ Group.Raid.LevelRestriction = 10
#
# 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)
# 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.
# Default: 1 - Enable
# 0 - Disable
#
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))
# 0 - (Display no extra information, only "Requirements not met")
# 2 - (Display detailed requirements, all at once, with clickable links)
#
DungeonAccessRequirements.PrintMode = 1
@@ -3421,10 +3461,9 @@ DungeonFinder.OptionsMask = 5
#
# LFG.Location.All
#
# Includes satellite to search for work elsewhere LFG
# Description: Includes satellite to search for work elsewhere LFG
# Default: 0 - Disable
# 1 - Enable
#
LFG.Location.All = 0
@@ -3764,7 +3803,6 @@ Battleground.QueueAnnouncer.Limit.MinPlayers = 3
# Battleground.QueueAnnouncer.SpamProtection.Delay
# Description: Show announce if player rejoined in queue after sec
# Default: 30
#
Battleground.QueueAnnouncer.SpamProtection.Delay = 30
@@ -3781,7 +3819,6 @@ Battleground.QueueAnnouncer.PlayerOnly = 0
# Description: Enabled battleground queue announcements based on timer
# Default: 0 - (Disabled)
# 1 - (Enabled - Set Arena.QueueAnnouncer.Timer)
#
Battleground.QueueAnnouncer.Timed = 0
@@ -3789,7 +3826,6 @@ Battleground.QueueAnnouncer.Timed = 0
# Battleground.QueueAnnouncer.Timer
# Description: Set timer for queue announcements
# Default: 30000 (30 sec)
#
Battleground.QueueAnnouncer.Timer = 30000
@@ -3896,7 +3932,6 @@ Battleground.DisableReadyCheckInBG = 0
# 0 - Battleground.RewardWinnerArenaLast
# 5 - Battleground.RewardLoserHonorFirst
# 5 - Battleground.RewardLoserHonorLast
#
Battleground.RewardWinnerHonorFirst = 30
Battleground.RewardWinnerArenaFirst = 25
@@ -4057,7 +4092,6 @@ Arena.QueueAnnouncer.Enable = 0
# Description: Arena queue announcement type.
# Default: 0 - (System message, Anyone can see it)
# 1 - (Private, Only queued players can see it)
#
Arena.QueueAnnouncer.PlayerOnly = 0
@@ -4068,7 +4102,6 @@ Arena.QueueAnnouncer.PlayerOnly = 0
# 2 - (Announce only the team's name)
# 1 - (Announce only the team's rating)
# 0 - (Do not announce any information about the teams)
#
Arena.QueueAnnouncer.Detail = 3
@@ -4182,8 +4215,6 @@ LevelReq.Mail = 1
# IsContinentTransport.Enabled
# Description: Controls the continent transport (ships, zeppelins etc..)
# Default: 1 - (Enabled)
#
#
IsContinentTransport.Enabled = 1
@@ -4192,10 +4223,8 @@ IsContinentTransport.Enabled = 1
# Description: Should we preload the transport?
# (Not recommended on low-end servers as it consumes 100% more ram)
# and it's not really necessary to be enabled.
#
# Default: 0 - (Disabled)
#
#
IsPreloadedContinentTransport.Enabled = 0
@@ -4235,7 +4264,6 @@ AddonChannel = 1
# "normal" chat messages for sending data to other clients.
# Default: 1 - (Enabled, Blizzlike)
# 0 - (Disabled)
#
ChatFakeMessagePreventing = 1
@@ -4245,12 +4273,10 @@ ChatFakeMessagePreventing = 1
# -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)
# 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
# client locales that are in use on your server.
# If any files are missing, messages with links from clients using those
# locales will likely be blocked by the server.
#
ChatStrictLinkChecking.Severity = 0
@@ -4260,7 +4286,6 @@ ChatStrictLinkChecking.Severity = 0
# is received.
# Default: 0 - (Silently ignore message)
# 1 - (Ignore message and kick player)
#
ChatStrictLinkChecking.Kick = 0
@@ -4334,7 +4359,7 @@ Channel.RestrictedLfg = 1
Channel.SilentlyGMJoin = 0
# 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
# 0 (in-game channel moderator privileges only)
# Default: 1 (enabled for moderators and above)
@@ -4493,7 +4518,7 @@ TalentsInspecting = 1
# ChangeFaction.MaxMoney
# Description: Maximum amount of gold allowed on the character to perform a faction change.
# 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.
ChangeFaction.MaxMoney = 0
@@ -4637,28 +4662,28 @@ PlayerDump.DisallowOverwrite = 1
###################################################################################################
# CUSTOM
#
# ICC Buff
# ICC.Buff.Alliance
# ICC.Buff.Horde
# Description: Specify ICC buff
# (It is necessary to restart the server after changing the values!)
# Default: ICC.Buff.Horde = 73822
# ICC.Buff.Alliance = 73828
#
# Spell IDs for the auras:
# 73816 - 5% buff Horde
# 73818 - 10% buff Horde
# 73819 - 15% buff Horde
# 73820 - 20% buff Horde
# 73821 - 25% buff Horde
# 73822 - 30% buff Horde
# 73762 - 5% buff Alliance
# 73824 - 10% buff Alliance
# 73825 - 15% buff Alliance
# 73826 - 20% buff Alliance
# 73827 - 25% buff Alliance
# 73828 - 30% buff Alliance
# Note: It is necessary to restart the server after changing the values!
# Example: | Spell IDs | Amount | Faction |
# | 73762 | 5% | Alliance |
# | 73824 | 10% | Alliance |
# | 73825 | 15% | Alliance |
# | 73826 | 20% | Alliance |
# | 73827 | 25% | Alliance |
# | 73828 | 30% | Alliance |
# | 73816 | 5% | Horde |
# | 73818 | 10% | Horde |
# | 73819 | 15% | Horde |
# | 73820 | 20% | Horde |
# | 73821 | 25% | Horde |
# | 73822 | 30% | Horde |
# Default: ICC.Buff.Alliance = 73828
# ICC.Buff.Horde = 73822
ICC.Buff.Horde = 73822
ICC.Buff.Alliance = 73828
ICC.Buff.Horde = 73822
#
# WipeGunshipBlizzlike.Enable
@@ -4679,7 +4704,6 @@ Minigob.Manabonk.Enable = 1
# Description: Calculate at loading creature zoneId / areaId and save in creature table
# WARNING: SLOW WORLD SERVER STARTUP. Should only be used for debugging.
# Default: 0 - (Do not show)
#
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
# WARNING: SLOW WORLD SERVER STARTUP. Should only be used for debugging.
# Default: 0 - (Do not show)
#
Calculate.Gameoject.Zone.Area.Data = 0
@@ -4801,7 +4824,6 @@ Respawn.DynamicMinimumGameObject = 10
# When enabled, escort NPCs in spawn groups flagged as ESCORTQUESTNPC
# will use special respawn handling.
# Default: 0 - (Disabled)
#
Respawn.DynamicEscortNPC = 0
@@ -4812,7 +4834,6 @@ Respawn.DynamicEscortNPC = 0
# gameobjects respawn in-place as they always have in AzerothCore.
# Set to 1 to force legacy behavior for all spawns.
# Default: 0 - (Disabled, spawn groups control respawn mode)
#
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_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_MULTISPAWN, "DELETE FROM creature_multispawn WHERE spawnId = ?", CONNECTION_ASYNC);
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_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_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_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_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_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, 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_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_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_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);
@@ -81,6 +81,7 @@ enum WorldDatabaseStatements : uint32
WORLD_UPD_WAYPOINT_SCRIPT_O,
WORLD_SEL_WAYPOINT_SCRIPT_ID_BY_GUID,
WORLD_DEL_CREATURE,
WORLD_DEL_CREATURE_MULTISPAWN,
WORLD_SEL_COMMANDS,
WORLD_SEL_CREATURE_TEMPLATE,
WORLD_SEL_WAYPOINT_SCRIPT_BY_ID,
+3
View File
@@ -42,6 +42,7 @@ void GuardAI::EnterEvadeMode(EvadeReason /*why*/)
me->GetMotionMaster()->MoveIdle();
me->CombatStop(true);
me->GetThreatMgr().ClearAllThreat();
EngagementOver();
return;
}
@@ -51,6 +52,8 @@ void GuardAI::EnterEvadeMode(EvadeReason /*why*/)
me->GetThreatMgr().ClearAllThreat();
me->CombatStop(true);
EngagementOver();
// Remove ChaseMovementGenerator from MotionMaster stack list, and add HomeMovementGenerator instead
if (me->GetMotionMaster()->GetCurrentMovementGeneratorType() == CHASE_MOTION_TYPE)
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
*
* @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);
}
+2 -1
View File
@@ -395,7 +395,8 @@ public:
SpellCastResult DoCastToAllHostilePlayers(uint32 spellid, bool triggered = false);
SpellCastResult DoCastVictim(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.
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 "ObjectMgr.h"
#include "ScriptedCreature.h"
#include "ScriptMgr.h"
#include "SpellMgr.h"
#include "Vehicle.h"
@@ -711,7 +712,7 @@ void SmartAI::JustExitedCombat()
CreatureAI::JustExitedCombat();
}
void SmartAI::EnterEvadeMode(EvadeReason /*why*/)
void SmartAI::EnterEvadeMode(EvadeReason why)
{
if (mSuppressEvade)
return;
@@ -763,6 +764,8 @@ void SmartAI::EnterEvadeMode(EvadeReason /*why*/)
if (!me->HasUnitState(UNIT_STATE_EVADE))
GetScript()->OnReset();
}
sScriptMgr->OnUnitEnterEvadeMode(me, why);
}
void SmartAI::MoveInLineOfSight(Unit* who)
@@ -330,7 +330,7 @@ void SmartAIMgr::CheckIfSmartAIInDatabaseExists()
// check GUID SAI
for (auto const& pair : sObjectMgr->GetAllCreatureData())
{
if (pair.second.id1 != creatureTemplate.Entry)
if (pair.second.id != creatureTemplate.Entry)
continue;
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;
}
else
entry = data->id1;
entry = data->id;
}
else
entry = uint32(e.entryOrGuid);
@@ -216,9 +216,11 @@ void AuctionHouseMgr::SendAuctionSuccessfulMail(AuctionEntry* auction, Character
.AddMoney(profit)
.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)",
auction->Id, owner ? owner->GetName() : "offline", auction->owner.GetCounter(),
auction->bidder.GetCounter(), auction->item_template, auction->itemCount,
CharacterCacheEntry const* sellerCache = sCharacterCache->GetCharacterCacheByGuid(auction->owner);
CharacterCacheEntry const* bidderCache = sCharacterCache->GetCharacterCacheByGuid(auction->bidder);
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());
if (auction->bid >= 500 * GOLD)
@@ -36,7 +36,6 @@ using GameObjectSet = std::set<GameObject*>;
using GameObjectBuilding = std::set<BfWGGameObjectBuilding*>;
using Workshop = std::set<WGWorkshop*>;
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 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_SE,
BATTLEFIELD_WG_WORKSHOP_SW,
BATTLEFIELD_WG_WORKSHOP_KEEP_WEST,
BATTLEFIELD_WG_WORKSHOP_KEEP_EAST,
BATTLEFIELD_WG_WORKSHOP_KEEP_WEST,
};
/// @todo: Handle this with creature_text ?
@@ -1022,9 +1021,11 @@ WintergraspTowerCannonData const TowerCannon[WG_MAX_TOWER_CANNON] =
// Workshop data and elements
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
{
uint8 id;
uint32 worldstate;
uint8 attackText;
uint8 takenText;
@@ -1033,17 +1034,17 @@ struct WGWorkshopData
WGWorkshopData const WorkshopsData[WG_MAX_WORKSHOP] =
{
// 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
{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
{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
{BATTLEFIELD_WG_WORKSHOP_SW, 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},
{WORLD_STATE_BATTLEFIELD_WG_WORKSHOP_SW, BATTLEFIELD_WG_TEXT_WORKSHOP_SW_ATTACK, BATTLEFIELD_WG_TEXT_WORKSHOP_SW_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
@@ -1185,9 +1186,7 @@ struct BfWGGameObjectBuilding
void Init(GameObject* gobj, uint32 type, uint32 worldstate, uint8 damageText, uint8 destroyText)
{
if (!gobj)
{
return;
}
// GameObject associated to object
m_Build = gobj->GetGUID();
@@ -1219,8 +1218,6 @@ struct BfWGGameObjectBuilding
}
m_State = sWorldState->getWorldState(m_WorldState);
if (gobj)
{
switch (m_State)
{
case BATTLEFIELD_WG_OBJECTSTATE_ALLIANCE_INTACT:
@@ -1236,7 +1233,6 @@ struct BfWGGameObjectBuilding
gobj->SetDestructibleState(GO_DESTRUCTIBLE_DAMAGED);
break;
}
}
int32 towerid = -1;
switch (gobj->GetEntry())
@@ -1415,7 +1411,7 @@ struct WGWorkshop
WGWorkshop(BattlefieldWG* _bf, uint8 _workshopId)
{
ASSERT(_bf || _workshopId < WG_MAX_WORKSHOP);
ASSERT(_bf && _workshopId < WG_MAX_WORKSHOP);
bf = _bf;
workshopId = _workshopId;
@@ -1423,6 +1419,9 @@ struct WGWorkshop
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*/)
{
switch (team)
@@ -1438,7 +1437,7 @@ struct WGWorkshop
bf->SendUpdateWorldState(WorkshopsData[workshopId].worldstate, state);
// Found associate graveyard and update it
if (workshopId < BATTLEFIELD_WG_WORKSHOP_KEEP_WEST)
if (IsCapturable())
if (bf->GetGraveyardById(workshopId))
bf->GetGraveyardById(workshopId)->GiveControlTo(team);
@@ -1457,7 +1456,7 @@ struct WGWorkshop
bf->SendWarning(team == TEAM_ALLIANCE ? WorkshopsData[workshopId].takenText : (WorkshopsData[workshopId].takenText + 2));
// Found associate graveyard and update it
if (workshopId < BATTLEFIELD_WG_WORKSHOP_KEEP_WEST)
if (IsCapturable())
if (bf->GetGraveyardById(workshopId))
bf->GetGraveyardById(workshopId)->GiveControlTo(team);
@@ -1475,7 +1474,7 @@ struct WGWorkshop
void UpdateGraveyardAndWorkshop()
{
if (workshopId < BATTLEFIELD_WG_WORKSHOP_KEEP_WEST)
if (IsCapturable())
bf->GetGraveyardById(workshopId)->GiveControlTo(TeamId(teamControl));
else
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 (decreaseInvitedCount && groupInfo->IsInvitedToBGInstanceGUID)
{
if (Battleground* bg = sBattlegroundMgr->GetBattleground(groupInfo->IsInvitedToBGInstanceGUID, groupInfo->BgTypeId))
{
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
m_QueuedPlayers.erase(itr);
+2 -1
View File
@@ -59,7 +59,8 @@
// ... both units must be allowed to enter combat
if (a->IsCombatDisallowed() || b->IsCombatDisallowed())
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;
Player const* playerA = a->GetCharmerOrOwnerPlayerOrPlayerItself();
Player const* playerB = b->GetCharmerOrOwnerPlayerOrPlayerItself();
+2 -2
View File
@@ -1958,7 +1958,7 @@ bool ConditionMgr::isSourceTypeValid(Condition* cond)
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);
return false;
@@ -2322,7 +2322,7 @@ bool ConditionMgr::isConditionTypeValid(Condition* cond)
{
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);
return false;
+6 -6
View File
@@ -1504,7 +1504,6 @@ namespace lfg
if (!gguid)
return;
LfgRolesMap check_roles;
LfgRoleCheckContainer::iterator itRoleCheck = RoleChecksStore.find(gguid);
if (itRoleCheck == RoleChecksStore.end())
return;
@@ -1527,9 +1526,7 @@ namespace lfg
if (itRoles == roleCheck.roles.end())
{
// use temporal var to check roles, CheckGroupRoles modifies the roles
check_roles = roleCheck.roles;
roleCheck.state = CheckGroupRoles(check_roles) ? LFG_ROLECHECK_FINISHED : LFG_ROLECHECK_WRONG_ROLES;
roleCheck.state = CheckGroupRoles(roleCheck.roles) ? LFG_ROLECHECK_FINISHED : LFG_ROLECHECK_WRONG_ROLES;
}
}
@@ -1798,15 +1795,18 @@ namespace lfg
ObjectGuid gguid = grp->GetGUID();
SetState(gguid, LFG_STATE_PROPOSAL);
sGroupMgr->AddGroup(grp);
grp->SetLfgRoles(pguid, proposal.players.find(pguid)->second.role);
}
else if (group != grp)
{
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);
}
}
// pussywizard: crashfix, group wasn't created when iterating players (no player found by guid), proposal is deleted by the calling function
if (!grp)
@@ -1423,7 +1423,7 @@ void Creature::SaveToDB(uint32 mapid, uint8 spawnMask, uint32 phaseMask)
dynamicflags = 0;
}
data.id1 = GetEntry();
data.id = GetEntry();
data.mapid = mapid;
data.phaseMask = phaseMask;
data.displayid = displayId;
@@ -1469,8 +1469,6 @@ void Creature::SaveToDB(uint32 mapid, uint8 spawnMask, uint32 phaseMask)
stmt = WorldDatabase.GetPreparedStatement(WORLD_INS_CREATURE);
stmt->SetData(index++, m_spawnId);
stmt->SetData(index++, GetEntry());
stmt->SetData(index++, 0);
stmt->SetData(index++, 0);
stmt->SetData(index++, uint16(mapid));
stmt->SetData(index++, spawnMask);
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);
// 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))
return false;
@@ -1852,6 +1850,10 @@ void Creature::DeleteFromDB()
stmt->SetData(0, m_spawnId);
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->SetData(0, m_spawnId);
trans->Append(stmt);
@@ -2041,7 +2043,7 @@ void Creature::Respawn(bool force)
if (!allowed && !force) // Will be rechecked on next Update call
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);
CreatureTemplate const* cInfo = sObjectMgr->GetCreatureTemplate(GetEntry());
@@ -2061,7 +2063,7 @@ void Creature::Respawn(bool force)
// Respawn check if spawn has 2 entries
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
m_defaultMovementType = MovementGeneratorType(data->movementType); // Reload Movement Type
LoadEquipment(data->equipmentId); // Reload Equipment
@@ -3169,7 +3171,7 @@ uint32 Creature::GetScriptId() const
if (CreatureData const* creatureData = GetCreatureData())
{
uint32 scriptId = creatureData->ScriptId;
if (scriptId && GetEntry() == creatureData->id1)
if (scriptId && GetEntry() == creatureData->id)
return scriptId;
}
@@ -369,10 +369,9 @@ typedef std::unordered_map<uint32, EquipmentInfoContainerInternal> EquipmentInfo
struct CreatureData : public SpawnData
{
CreatureData() : SpawnData(SPAWN_TYPE_CREATURE) {}
ObjectGuid::LowType spawnId{ 0 }; // mod_playerbots
uint32 id1{0}; // entry in creature_template
uint32 id2{0}; // entry in creature_template
uint32 id3{0}; // entry in creature_template
uint32 id{0}; // entry in creature_template
uint32 id2{0}; // entry in creature_template (from creature_multispawn)
uint32 id3{0}; // entry in creature_template (from creature_multispawn)
uint32 displayid{0};
int8 equipmentId{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
// 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;
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 newXP = curXP + xp + bonus_xp;
while (newXP >= nextLvlXP && level < sWorld->getIntConfig(CONFIG_MAX_PLAYER_LEVEL))
while (newXP >= nextLvlXP && level < maxLevel)
{
newXP -= nextLvlXP;
if (level < sWorld->getIntConfig(CONFIG_MAX_PLAYER_LEVEL))
if (level < maxLevel)
GiveLevel(level + 1);
level = GetLevel();
@@ -11509,6 +11516,17 @@ bool Player::ModifyMoney(int32 amount, bool sendError /*= true*/)
SetMoney (GetMoney() > uint32(-amount) ? GetMoney() + amount : 0);
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))
SetMoney(GetMoney() + amount);
else
+2 -2
View File
@@ -1560,9 +1560,9 @@ public:
void SendQuestReward(Quest const* quest, uint32 XP);
void SendQuestFailed(uint32 questId, InventoryResult reason = EQUIP_ERR_OK);
void SendQuestTimerFailed(uint32 quest_id);
void SendCanTakeQuestResponse(uint32 msg) const;
void SendCanTakeQuestResponse(QuestFailedReason msg) const;
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 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);
+65 -68
View File
@@ -26,6 +26,7 @@
#include "MapMgr.h"
#include "Player.h"
#include "PoolMgr.h"
#include "QuestPackets.h"
#include "ReputationMgr.h"
#include "ScriptMgr.h"
#include "SpellAuraEffects.h"
@@ -991,8 +992,7 @@ bool Player::SatisfyQuestLog(bool msg)
if (msg)
{
WorldPacket data(SMSG_QUESTLOG_FULL, 0);
SendDirectMessage(&data);
SendDirectMessage(WorldPackets::Quest::QuestLogFull().Write());
LOG_DEBUG("network", "WORLD: Sent SMSG_QUESTLOG_FULL");
}
return false;
@@ -2381,13 +2381,13 @@ bool Player::HasQuestForItem(uint32 itemid, uint32 excludeQuestId /* 0 */, bool
void Player::SendQuestComplete(uint32 quest_id)
{
if (quest_id)
{
WorldPacket data(SMSG_QUESTUPDATE_COMPLETE, 4);
data << uint32(quest_id);
SendDirectMessage(&data);
if (!quest_id)
return;
WorldPackets::Quest::QuestUpdateComplete questUpdateComplete;
questUpdateComplete.QuestId = quest_id;
SendDirectMessage(questUpdateComplete.Write());
LOG_DEBUG("network", "WORLD: Sent SMSG_QUESTUPDATE_COMPLETE quest = {}", quest_id);
}
}
void Player::SendQuestReward(Quest const* quest, uint32 XP)
@@ -2395,54 +2395,50 @@ void Player::SendQuestReward(Quest const* quest, uint32 XP)
uint32 questid = quest->GetQuestId();
LOG_DEBUG("network", "WORLD: Sent SMSG_QUESTGIVER_QUEST_COMPLETE quest = {}", questid);
sGameEventMgr->HandleQuestComplete(questid);
WorldPacket data(SMSG_QUESTGIVER_QUEST_COMPLETE, (4 + 4 + 4 + 4 + 4));
data << uint32(questid);
WorldPackets::Quest::QuestGiverQuestComplete questGiverQuestComplete;
questGiverQuestComplete.QuestId = questid;
uint32 rewardMoney = quest->GetRewOrReqMoney(GetLevel());
if (!IsMaxLevel())
{
data << uint32(XP);
data << uint32(quest->GetRewOrReqMoney(GetLevel()));
}
questGiverQuestComplete.Experience = XP;
else
{
data << uint32(0);
data << uint32(quest->GetRewOrReqMoney(GetLevel()) + quest->GetRewMoneyMaxLevel());
}
rewardMoney += quest->GetRewMoneyMaxLevel();
data << uint32(10 * quest->CalculateHonorGain(GetQuestLevel(quest)));
data << uint32(quest->GetBonusTalents()); // bonus talents
data << uint32(quest->GetRewArenaPoints());
SendDirectMessage(&data);
questGiverQuestComplete.RewardMoney = rewardMoney;
questGiverQuestComplete.RewardHonor = 10 * quest->CalculateHonorGain(GetQuestLevel(quest));
questGiverQuestComplete.RewardTalents = quest->GetBonusTalents();
questGiverQuestComplete.RewardArena = quest->GetRewArenaPoints();
SendDirectMessage(questGiverQuestComplete.Write());
}
void Player::SendQuestFailed(uint32 questId, InventoryResult reason)
{
if (questId)
{
WorldPacket data(SMSG_QUESTGIVER_QUEST_FAILED, 4 + 4);
data << uint32(questId);
data << uint32(reason); // failed reason (valid reasons: 4, 16, 50, 17, 74, other values show default message)
SendDirectMessage(&data);
if (!questId)
return;
WorldPackets::Quest::QuestGiverQuestFailed questGiverQuestFailed;
questGiverQuestFailed.QuestId = questId;
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");
}
}
void Player::SendQuestTimerFailed(uint32 quest_id)
{
if (quest_id)
{
WorldPacket data(SMSG_QUESTUPDATE_FAILEDTIMER, 4);
data << uint32(quest_id);
SendDirectMessage(&data);
if (!quest_id)
return;
WorldPackets::Quest::QuestUpdateFailedTimer questUpdateFailedTimer;
questUpdateFailedTimer.QuestId = quest_id;
SendDirectMessage(questUpdateFailedTimer.Write());
LOG_DEBUG("network", "WORLD: Sent SMSG_QUESTUPDATE_FAILEDTIMER");
}
}
void Player::SendCanTakeQuestResponse(uint32 msg) const
void Player::SendCanTakeQuestResponse(QuestFailedReason msg) const
{
WorldPacket data(SMSG_QUESTGIVER_QUEST_INVALID, 4);
data << uint32(msg);
SendDirectMessage(&data);
WorldPackets::Quest::QuestGiverQuestInvalid questGiverQuestInvalid;
questGiverQuestInvalid.FailureReason = msg;
SendDirectMessage(questGiverQuestInvalid.Write());
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()))
ObjectMgr::GetLocaleString(pLocale->Title, loc_idx, strTitle);
WorldPacket data(SMSG_QUEST_CONFIRM_ACCEPT, (4 + quest->GetTitle().size() + 8));
data << uint32(quest->GetQuestId());
data << quest->GetTitle();
data << GetGUID();
pReceiver->SendDirectMessage(&data);
WorldPackets::Quest::QuestConfirmAccept questConfirmAccept;
questConfirmAccept.QuestId = quest->GetQuestId();
questConfirmAccept.QuestTitle = quest->GetTitle();
questConfirmAccept.PlayerGuid = GetGUID();
pReceiver->SendDirectMessage(questConfirmAccept.Write());
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)
{
WorldPacket data(MSG_QUEST_PUSH_RESULT, (8 + 1));
data << player->GetGUID();
data << uint8(msg); // valid values: 0-8
SendDirectMessage(&data);
if (!player)
return;
WorldPackets::Quest::QuestPushResult questPushResult;
questPushResult.PlayerGuid = player->GetGUID();
questPushResult.QuestShareMessage = msg;
SendDirectMessage(questPushResult.Write());
LOG_DEBUG("network", "WORLD: Sent MSG_QUEST_PUSH_RESULT");
}
}
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");
//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)
@@ -2498,14 +2495,14 @@ void Player::SendQuestUpdateAddCreatureOrGo(Quest const* quest, ObjectGuid guid,
// client expected gameobject template id in form (id|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");
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());
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");
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");
data << uint32(quest->GetQuestId());
data << uint32(old_count + add_count);
data << uint32(quest->GetPlayersSlain());
SendDirectMessage(&data);
uint16 log_slot = FindQuestSlot(quest->GetQuestId());
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());
if (attackType != MAX_ATTACK)
UpdateDamageDoneMods(attackType);
UpdateDamageDoneMods(attackType, apply ? -1 : slot);
break;
}
case ITEM_ENCHANTMENT_TYPE_USE_SPELL:
@@ -940,6 +940,11 @@ bool Player::UpdateSkillPro(uint16 SkillId, int32 Chance, uint32 step)
if (!MaxValue || !SkillValue || SkillValue >= MaxValue)
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);
if (Roll <= Chance)
+28 -17
View File
@@ -1555,28 +1555,32 @@ void Unit::CalculateSpellDamageTaken(SpellNonMeleeDamage* damageInfo, int32 dama
{
damageInfo->HitInfo |= SPELL_HIT_TYPE_CRIT;
// Calculate crit bonus
uint32 crit_bonus = damage;
// Apply crit_damage bonus for melee spells
if (Player* modOwner = GetSpellModOwner())
modOwner->ApplySpellMod(spellInfo->Id, SPELLMOD_CRIT_DAMAGE_BONUS, crit_bonus);
damage += crit_bonus;
// Calculate crit bonus (100% for melee/ranged spells)
int32 crit_bonus = damage;
crit_bonus += 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)
critPctDamageMod += victim->GetTotalAuraModifier(SPELL_AURA_MOD_ATTACKER_RANGED_CRIT_DAMAGE);
crit_mod += victim->GetTotalAuraModifier(SPELL_AURA_MOD_ATTACKER_RANGED_CRIT_DAMAGE);
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
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
critPctDamageMod += GetTotalAuraModifierByMiscMask(SPELL_AURA_MOD_CRIT_PERCENT_VERSUS, crTypeMask);
crit_mod += GetTotalAuraModifierByMiscMask(SPELL_AURA_MOD_CRIT_PERCENT_VERSUS, crTypeMask);
if (critPctDamageMod != 0)
AddPct(damage, critPctDamageMod);
if (crit_bonus != 0 && crit_mod != 0.0f)
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
@@ -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_RANGED:
/// @todo: write here full calculation for melee/ranged spells
crit_bonus += damage;
break;
default:
@@ -15799,9 +15802,8 @@ void Unit::_ExitVehicle(Position const* exitPosition)
if (seatAddon)
{
if (seatAddon->ExitParameter == VehicleExitParameters::VehicleExitParamOffset)
{
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);
float floorZ = GetMapHeight(pos.GetPositionX(), pos.GetPositionY(), pos.GetPositionZ());
@@ -15809,6 +15811,15 @@ void Unit::_ExitVehicle(Position const* exitPosition)
if (!isInLoS || pos.GetPositionZ() < floorZ)
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);
+1 -6
View File
@@ -359,12 +359,7 @@ bool Vehicle::AddPassenger(Unit* unit, int8 seatId)
return false;
if (!seat->second.IsEmpty())
{
if (Unit* passenger = ObjectAccessor::GetUnit(*GetBase(), seat->second.Passenger.Guid))
passenger->ExitVehicle();
seat->second.Passenger.Guid.Clear();
}
return false;
ASSERT(seat->second.IsEmpty());
}
+5 -7
View File
@@ -300,7 +300,7 @@ void GameEventMgr::LoadEventVendors()
// Get creature entry
newEntry.Entry = 0;
if (CreatureData const* data = sObjectMgr->GetCreatureData(guid))
newEntry.Entry = data->id1;
newEntry.Entry = data->id;
// Validate vendor item
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>();
uint32 entry = fields[1].Get<uint32>();
uint32 entry2 = fields[2].Get<uint32>();
uint32 entry3 = fields[3].Get<uint32>();
uint16 eventId = fields[4].Get<uint8>();
uint16 eventId = fields[2].Get<uint8>();
if (eventId >= _gameEventModelEquip.size())
{
@@ -626,15 +624,15 @@ void GameEventMgr::LoadEventModelEquipmentChangeData()
ModelEquipList& equiplist = _gameEventModelEquip[eventId];
ModelEquip newModelEquipSet;
newModelEquipSet.ModelId = fields[5].Get<uint32>();
newModelEquipSet.EquipmentId = fields[6].Get<uint8>();
newModelEquipSet.ModelId = fields[3].Get<uint32>();
newModelEquipSet.EquipmentId = fields[4].Get<uint8>();
newModelEquipSet.EquipementIdPrev = 0;
newModelEquipSet.ModelIdPrev = 0;
if (newModelEquipSet.EquipmentId > 0)
{
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.",
guid, newModelEquipSet.EquipmentId);
+199 -137
View File
@@ -1572,7 +1572,7 @@ void ObjectMgr::LoadCreatureMovementOverrides()
"COALESCE(cmo.InteractionPauseTimer, ctm.InteractionPauseTimer) "
"FROM creature_movement_override AS cmo "
"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)
{
LOG_WARN("server.loading", ">> Loaded 0 creature movement overrides. DB table `creature_movement_override` is empty!");
@@ -1978,8 +1978,8 @@ void ObjectMgr::LoadLinkedRespawn()
break;
}
guid = ObjectGuid::Create<HighGuid::Unit>(slave->id1, guidLow);
linkedGuid = ObjectGuid::Create<HighGuid::Unit>(master->id1, linkedGuidLow);
guid = ObjectGuid::Create<HighGuid::Unit>(slave->id, guidLow);
linkedGuid = ObjectGuid::Create<HighGuid::Unit>(master->id, linkedGuidLow);
break;
}
case CREATURE_TO_GO:
@@ -2015,7 +2015,7 @@ void ObjectMgr::LoadLinkedRespawn()
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);
break;
}
@@ -2090,7 +2090,7 @@ void ObjectMgr::LoadLinkedRespawn()
}
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;
}
}
@@ -2109,7 +2109,7 @@ bool ObjectMgr::SetCreatureLinkedRespawn(ObjectGuid::LowType guidLow, ObjectGuid
return false;
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
{
@@ -2140,7 +2140,7 @@ bool ObjectMgr::SetCreatureLinkedRespawn(ObjectGuid::LowType guidLow, ObjectGuid
return false;
}
ObjectGuid linkedGuid = ObjectGuid::Create<HighGuid::Unit>(slave->id1, linkedGuidLow);
ObjectGuid linkedGuid = ObjectGuid::Create<HighGuid::Unit>(slave->id, linkedGuidLow);
_linkedRespawnStore[guid] = linkedGuid;
WorldDatabasePreparedStatement* stmt = WorldDatabase.GetPreparedStatement(WORLD_REP_CREATURE_LINKED_RESPAWN);
@@ -2321,11 +2321,11 @@ void ObjectMgr::LoadCreatures()
{
uint32 oldMSTime = getMSTime();
// 0 1 2 3 4 5 6 7 8 9 10 11
QueryResult result = WorldDatabase.Query("SELECT creature.guid, id1, id2, id3, map, equipment_id, position_x, position_y, position_z, orientation, spawntimesecs, wander_distance, "
// 12 13 14 15 16 17 18 19 20 21 22
// 0 1 2 3 4 5 6 7 8 9
QueryResult result = WorldDatabase.Query("SELECT creature.guid, id, map, equipment_id, position_x, position_y, position_z, orientation, spawntimesecs, wander_distance, "
// 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, "
// 23
// 21
"creature.ScriptName "
"FROM creature "
"LEFT OUTER JOIN game_event_creature ON creature.guid = game_event_creature.guid "
@@ -2356,58 +2356,37 @@ void ObjectMgr::LoadCreatures()
Field* fields = result->Fetch();
ObjectGuid::LowType spawnId = fields[0].Get<uint32>();
uint32 id1 = fields[1].Get<uint32>();
uint32 id2 = fields[2].Get<uint32>();
uint32 id3 = fields[3].Get<uint32>();
uint32 creatureId = fields[1].Get<uint32>();
CreatureTemplate const* cInfo = GetCreatureTemplate(id1);
CreatureTemplate const* cInfo = GetCreatureTemplate(creatureId);
if (!cInfo)
{
LOG_ERROR("sql.sql", "Table `creature` has creature (SpawnId: {}) with non existing creature entry {} in id1 field, skipped.", spawnId, id1);
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);
LOG_ERROR("sql.sql", "Table `creature` has creature (SpawnId: {}) with non existing creature entry {} in `id` field, skipped.", spawnId, creatureId);
continue;
}
CreatureData& data = _creatureDataStore[spawnId];
data.spawnId = spawnId;
data.id1 = id1;
data.id2 = id2;
data.id3 = id3;
data.mapid = fields[4].Get<uint16>();
data.equipmentId = fields[5].Get<int8>();
data.posX = fields[6].Get<float>();
data.posY = fields[7].Get<float>();
data.posZ = fields[8].Get<float>();
data.orientation = fields[9].Get<float>();
data.spawntimesecs = fields[10].Get<uint32>();
data.wander_distance = fields[11].Get<float>();
data.currentwaypoint = fields[12].Get<uint32>();
data.curhealth = fields[13].Get<uint32>();
data.curmana = fields[14].Get<uint32>();
data.movementType = fields[15].Get<uint8>();
data.spawnMask = fields[16].Get<uint8>();
data.phaseMask = fields[17].Get<uint32>();
int16 gameEvent = fields[18].Get<int16>();
uint32 PoolId = fields[19].Get<uint32>();
data.npcflag = fields[20].Get<uint32>();
data.unit_flags = fields[21].Get<uint32>();
data.dynamicflags = fields[22].Get<uint32>();
data.ScriptId = GetScriptId(fields[23].Get<std::string>());
data.id = creatureId;
data.mapid = fields[2].Get<uint16>();
data.equipmentId = fields[3].Get<int8>();
data.posX = fields[4].Get<float>();
data.posY = fields[5].Get<float>();
data.posZ = fields[6].Get<float>();
data.orientation = fields[7].Get<float>();
data.spawntimesecs = fields[8].Get<uint32>();
data.wander_distance = fields[9].Get<float>();
data.currentwaypoint = fields[10].Get<uint32>();
data.curhealth = fields[11].Get<uint32>();
data.curmana = fields[12].Get<uint32>();
data.movementType = fields[13].Get<uint8>();
data.spawnMask = fields[14].Get<uint8>();
data.phaseMask = fields[15].Get<uint32>();
int16 gameEvent = fields[16].Get<int16>();
uint32 PoolId = fields[17].Get<uint32>();
data.npcflag = fields[18].Get<uint32>();
data.unit_flags = fields[19].Get<uint32>();
data.dynamicflags = fields[20].Get<uint32>();
data.ScriptId = GetScriptId(fields[21].Get<std::string>());
data.spawnGroupId = 0;
if (!data.ScriptId)
@@ -2437,10 +2416,10 @@ void ObjectMgr::LoadCreatures()
bool ok = true;
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.",
spawnId, diff + 1, data.id1, data.id2, data.id3);
LOG_ERROR("sql.sql", "Table `creature` have creature (SpawnId: {}) that listed as difficulty {} template (Entry: {}) in `creature_template`, skipped.",
spawnId, diff + 1, data.id);
ok = false;
}
}
@@ -2450,35 +2429,35 @@ void ObjectMgr::LoadCreatures()
// -1 random, 0 no equipment,
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.",
data.id1, data.id2, data.id3, data.equipmentId);
LOG_ERROR("sql.sql", "Table `creature` have creature (Entry: {}) with equipment_id {} not found in table `creature_equip_template`, set to no equipment.",
data.id, data.equipmentId);
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())
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.",
spawnId, data.id1, data.id2, data.id3);
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.id);
}
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;
}
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;
}
else if (data.movementType == RANDOM_MOTION_TYPE)
{
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).",
spawnId, data.id1, data.id2, data.id3);
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.id);
data.movementType = IDLE_MOTION_TYPE;
}
}
@@ -2486,14 +2465,14 @@ void ObjectMgr::LoadCreatures()
{
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;
}
}
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;
}
@@ -2518,6 +2497,72 @@ void ObjectMgr::LoadCreatures()
++count;
} 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", " ");
}
@@ -2531,7 +2576,7 @@ CreatureData const* ObjectMgr::LoadCreatureDataFromDB(ObjectGuid::LowType spawnI
if (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, "
"currentwaypoint, curhealth, curmana, MovementType, spawnMask, phaseMask, "
"creature.npcflag, creature.unit_flags, creature.dynamicflags, creature.ScriptName "
@@ -2541,62 +2586,83 @@ CreatureData const* ObjectMgr::LoadCreatureDataFromDB(ObjectGuid::LowType spawnI
return nullptr;
Field* fields = result->Fetch();
uint32 id1 = fields[1].Get<uint32>();
uint32 id2 = fields[2].Get<uint32>();
uint32 id3 = fields[3].Get<uint32>();
uint32 creatureId = fields[1].Get<uint32>();
CreatureTemplate const* cInfo = GetCreatureTemplate(id1);
CreatureTemplate const* cInfo = GetCreatureTemplate(creatureId);
if (!cInfo)
{
LOG_ERROR("sql.sql", "Table `creature` has creature (SpawnId: {}) with non-existing creature entry {} in id1 field, skipped.", spawnId, id1);
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);
LOG_ERROR("sql.sql", "Table `creature` has creature (SpawnId: {}) with non-existing creature entry {} in `id` field, skipped.", spawnId, creatureId);
return nullptr;
}
CreatureData& creatureData = _creatureDataStore[spawnId];
creatureData.id1 = id1;
creatureData.id2 = id2;
creatureData.id3 = id3;
creatureData.mapid = fields[4].Get<uint16>();
creatureData.equipmentId = fields[5].Get<int8>();
creatureData.posX = fields[6].Get<float>();
creatureData.posY = fields[7].Get<float>();
creatureData.posZ = fields[8].Get<float>();
creatureData.orientation = fields[9].Get<float>();
creatureData.spawntimesecs = fields[10].Get<uint32>();
creatureData.wander_distance = fields[11].Get<float>();
creatureData.currentwaypoint = fields[12].Get<uint32>();
creatureData.curhealth = fields[13].Get<uint32>();
creatureData.curmana = fields[14].Get<uint32>();
creatureData.movementType = fields[15].Get<uint8>();
creatureData.spawnMask = fields[16].Get<uint8>();
creatureData.phaseMask = fields[17].Get<uint32>();
creatureData.npcflag = fields[18].Get<uint32>();
creatureData.unit_flags = fields[19].Get<uint32>();
creatureData.dynamicflags = fields[20].Get<uint32>();
creatureData.ScriptId = GetScriptId(fields[21].Get<std::string>());
creatureData.id = creatureId;
creatureData.mapid = fields[2].Get<uint16>();
creatureData.equipmentId = fields[3].Get<int8>();
creatureData.posX = fields[4].Get<float>();
creatureData.posY = fields[5].Get<float>();
creatureData.posZ = fields[6].Get<float>();
creatureData.orientation = fields[7].Get<float>();
creatureData.spawntimesecs = fields[8].Get<uint32>();
creatureData.wander_distance = fields[9].Get<float>();
creatureData.currentwaypoint = fields[10].Get<uint32>();
creatureData.curhealth = fields[11].Get<uint32>();
creatureData.curmana = fields[12].Get<uint32>();
creatureData.movementType = fields[13].Get<uint8>();
creatureData.spawnMask = fields[14].Get<uint8>();
creatureData.phaseMask = fields[15].Get<uint32>();
creatureData.npcflag = fields[16].Get<uint32>();
creatureData.unit_flags = fields[17].Get<uint32>();
creatureData.dynamicflags = fields[18].Get<uint32>();
creatureData.ScriptId = GetScriptId(fields[19].Get<std::string>());
creatureData.spawnGroupId = 0;
if (!creatureData.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);
if (!mapEntry)
{
@@ -2611,12 +2677,10 @@ CreatureData const* ObjectMgr::LoadCreatureDataFromDB(ObjectGuid::LowType spawnI
bool ok = true;
for (uint32 diff = 0; diff < MAX_DIFFICULTY - 1 && ok; ++diff)
{
if (_difficultyEntries[diff].find(id1) != _difficultyEntries[diff].end() ||
_difficultyEntries[diff].find(id2) != _difficultyEntries[diff].end() ||
_difficultyEntries[diff].find(id3) != _difficultyEntries[diff].end())
if (_difficultyEntries[diff].find(creatureId) != _difficultyEntries[diff].end())
{
LOG_ERROR("sql.sql", "Table `creature` has creature (SpawnId: {}) that is listed as difficulty {} template (Entries: {}, {}, {}) in `creature_template`, skipped.",
spawnId, diff + 1, id1, id2, id3);
LOG_ERROR("sql.sql", "Table `creature` has creature (SpawnId: {}) that is listed as difficulty {} template (Entry: {}) in `creature_template`, skipped.",
spawnId, diff + 1, creatureId);
ok = false;
}
}
@@ -2629,35 +2693,33 @@ CreatureData const* ObjectMgr::LoadCreatureDataFromDB(ObjectGuid::LowType spawnI
if (creatureData.equipmentId != 0)
{
if (!GetEquipmentInfo(id1, creatureData.equipmentId) ||
(id2 && !GetEquipmentInfo(id2, creatureData.equipmentId)) ||
(id3 && !GetEquipmentInfo(id3, creatureData.equipmentId)))
if (!GetEquipmentInfo(creatureId, 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.",
id1, id2, id3, creatureData.equipmentId);
LOG_ERROR("sql.sql", "Table `creature` has creature (Entry: {}) with equipment_id {} not found in table `creature_equip_template`, set to no equipment.",
creatureId, creatureData.equipmentId);
creatureData.equipmentId = 0;
}
}
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.",
spawnId, id1, id2, id3, creatureData.movementType);
LOG_ERROR("sql.sql", "Table `creature` has creature (SpawnId: {} Entry: {}) with wrong movement generator type ({}), set to IDLE.",
spawnId, creatureId, creatureData.movementType);
creatureData.movementType = IDLE_MOTION_TYPE;
}
if (creatureData.wander_distance < 0.0f)
{
LOG_ERROR("sql.sql", "Table `creature` has creature (SpawnId: {} Entries: {}, {}, {}) with `wander_distance`< 0, set to 0.",
spawnId, id1, id2, id3);
LOG_ERROR("sql.sql", "Table `creature` has creature (SpawnId: {} Entry: {}) with `wander_distance`< 0, set to 0.",
spawnId, creatureId);
creatureData.wander_distance = 0.0f;
}
else if (creatureData.movementType == RANDOM_MOTION_TYPE)
{
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).",
spawnId, id1, id2, id3);
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, creatureId);
creatureData.movementType = IDLE_MOTION_TYPE;
}
}
@@ -2665,16 +2727,16 @@ CreatureData const* ObjectMgr::LoadCreatureDataFromDB(ObjectGuid::LowType spawnI
{
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.",
spawnId, id1, id2, id3);
LOG_ERROR("sql.sql", "Table `creature` has creature (SpawnId: {} Entry: {}) with `MovementType`=0 (idle) have `wander_distance`<>0, set to 0.",
spawnId, creatureId);
creatureData.wander_distance = 0.0f;
}
}
if (creatureData.phaseMask == 0)
{
LOG_ERROR("sql.sql", "Table `creature` has creature (SpawnId: {} Entries: {}, {}, {}) with `phaseMask`=0 (not visible for anyone), set to 1.",
spawnId, id1, id2, id3);
LOG_ERROR("sql.sql", "Table `creature` has creature (SpawnId: {} Entry: {}) with `phaseMask`=0 (not visible for anyone), set to 1.",
spawnId, creatureId);
creatureData.phaseMask = 1;
}
@@ -2814,7 +2876,7 @@ ObjectGuid::LowType ObjectMgr::AddCreData(uint32 entry, uint32 mapId, float x, f
CreatureData& data = NewOrExistCreatureData(spawnId);
data.spawnId = spawnId;
data.spawnMask = spawnId;
data.id1 = entry;
data.id = entry;
data.id2 = 0;
data.id3 = 0;
data.mapid = mapId;
+2 -2
View File
@@ -428,7 +428,7 @@ Player* Group::GetInvited(const std::string& name) const
return nullptr;
}
bool Group::AddMember(Player* player)
bool Group::AddMember(Player* player, uint8 roles /* = 0 */)
{
if (!player)
return false;
@@ -456,7 +456,7 @@ bool Group::AddMember(Player* player)
member.name = player->GetName();
member.group = subGroup;
member.flags = 0;
member.roles = 0;
member.roles = roles;
m_memberSlots.push_back(member);
if (!isBGGroup() && !isBFGroup())
+1 -1
View File
@@ -204,7 +204,7 @@ public:
void RemoveInvite(Player* player);
void RemoveAllInvites();
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);
void ChangeLeader(ObjectGuid guid);
void SetLootMethod(LootMethod method);
@@ -132,6 +132,13 @@ void WorldSession::HandleAuctionSellItem(WorldPacket& recvData)
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)
{
recvData >> itemGUIDs[i];
@@ -279,7 +286,7 @@ void WorldSession::HandleAuctionSellItem(WorldPacket& recvData)
return;
}
CreatureTemplate const* auctioneerInfo = sObjectMgr->GetCreatureTemplate(auctioneerData->id1);
CreatureTemplate const* auctioneerInfo = sObjectMgr->GetCreatureTemplate(auctioneerData->id);
if (!auctioneerInfo)
{
LOG_ERROR("network.opcode", "Non existing auctioneer ({})", auctioneer.ToString());
@@ -426,6 +433,12 @@ void WorldSession::HandleAuctionPlaceBid(WorldPacket& recvData)
if (!auctionId || !price)
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);
if (!creature)
{
@@ -578,6 +591,12 @@ void WorldSession::HandleAuctionRemoveItem(WorldPacket& recvData)
recvData >> auctioneer;
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);
if (!creature)
{
+28
View File
@@ -34,6 +34,7 @@
#include "Opcodes.h"
#include "Player.h"
#include "ScriptMgr.h"
#include "SocialMgr.h"
#include "SpellAuraEffects.h"
#include "SpellAuras.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
if (!HasPermission(rbac::RBAC_PERM_SKIP_CHECK_CHAT_CHANNEL_REQ))
{
@@ -401,6 +414,17 @@ void WorldSession::HandleMessagechatOpcode(WorldPacket& recvData)
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 (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())))
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);
}
break;
+40 -2
View File
@@ -89,6 +89,12 @@ void WorldSession::HandleGroupInviteOpcode(WorldPacket& recvData)
if (!sScriptMgr->OnPlayerCanGroupInvite(invitingPlayer, membername))
return;
if (sWorld->getBoolConfig(CONFIG_TRIAL_RESTRICTION_PARTY) && IsTrialAccount())
{
SendPartyResult(PARTY_OP_INVITE, membername, ERR_INVITE_RESTRICTED);
return;
}
if (invitingPlayer->IsSpectator() || invitedPlayer->IsSpectator())
{
SendPartyResult(PARTY_OP_INVITE, membername, ERR_INVITE_RESTRICTED);
@@ -244,6 +250,40 @@ void WorldSession::HandleGroupAcceptOpcode(WorldPacket& recvData)
if (!sScriptMgr->OnPlayerCanGroupAccept(GetPlayer(), group))
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())
{
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;
}
Player* leader = ObjectAccessor::FindConnectedPlayer(group->GetLeaderGUID());
// Forming a new group, create it
if (!group->IsCreated())
{
@@ -21,6 +21,7 @@
#include "Log.h"
#include "ObjectMgr.h"
#include "SocialMgr.h"
#include "World.h"
#include "WorldSession.h"
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());
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 (Guild* guild = sGuildMgr->GetGuildById(GetPlayer()->GetGuildIdInvited()))
guild->HandleAcceptMember(this);
+6
View File
@@ -117,6 +117,12 @@ void WorldSession::HandleSendMail(WorldPacket& recvData)
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))
{
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())
return;
if (sWorld->getBoolConfig(CONFIG_TRIAL_RESTRICTION_GUILD) && IsTrialAccount())
{
Guild::SendCommandResult(this, GUILD_COMMAND_CREATE, ERR_GUILD_PERMISSIONS);
return;
}
charterid = GUILD_CHARTER;
cost = sWorld->getIntConfig(CONFIG_CHARTER_COST_GUILD);
type = GUILD_CHARTER_TYPE;
@@ -455,6 +461,12 @@ void WorldSession::HandlePetitionSignOpcode(WorldPacket& recvData)
return;
}
if (sWorld->getBoolConfig(CONFIG_TRIAL_RESTRICTION_GUILD) && IsTrialAccount())
{
Guild::SendCommandResult(this, GUILD_COMMAND_INVITE, ERR_GUILD_PERMISSIONS);
return;
}
if (_player->GetGuildId())
{
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
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
if (_player->GetGuildId())
{
+25 -41
View File
@@ -27,6 +27,7 @@
#include "Opcodes.h"
#include "Player.h"
#include "QuestDef.h"
#include "QuestPackets.h"
#include "ScriptMgr.h"
#include "World.h"
#include "WorldPacket.h"
@@ -382,29 +383,23 @@ void WorldSession::HandleQuestgiverCancel(WorldPacket& /*recvData*/)
_player->PlayerTalkClass->SendCloseGossip();
}
void WorldSession::HandleQuestLogSwapQuest(WorldPacket& recvData)
void WorldSession::HandleQuestLogSwapQuest(WorldPackets::Quest::QuestLogSwapQuest& packet)
{
uint8 slot1, slot2;
recvData >> slot1 >> slot2;
if (slot1 == slot2 || slot1 >= MAX_QUEST_LOG_SIZE || slot2 >= MAX_QUEST_LOG_SIZE)
if (packet.Slot1 == packet.Slot2 || packet.Slot1 >= MAX_QUEST_LOG_SIZE || packet.Slot2 >= MAX_QUEST_LOG_SIZE)
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;
recvData >> slot;
LOG_DEBUG("network", "WORLD: Received CMSG_QUESTLOG_REMOVE_QUEST slot = {}", packet.Slot);
LOG_DEBUG("network", "WORLD: Received CMSG_QUESTLOG_REMOVE_QUEST slot = {}", slot);
if (slot < MAX_QUEST_LOG_SIZE)
if (packet.Slot < MAX_QUEST_LOG_SIZE)
{
if (uint32 questId = _player->GetQuestSlotQuestId(slot))
if (uint32 questId = _player->GetQuestSlotQuestId(packet.Slot))
{
if (!_player->TakeQuestSourceItem(questId, true))
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);
}
}
void WorldSession::HandleQuestConfirmAccept(WorldPacket& recvData)
void WorldSession::HandleQuestConfirmAccept(WorldPackets::Quest::QuestConfirmAcceptClient& packet)
{
uint32 questId;
recvData >> questId;
LOG_DEBUG("network", "WORLD: Received CMSG_QUEST_CONFIRM_ACCEPT quest = {}", packet.QuestId);
LOG_DEBUG("network", "WORLD: Received CMSG_QUEST_CONFIRM_ACCEPT quest = {}", questId);
if (Quest const* quest = sObjectMgr->GetQuestTemplate(questId))
if (Quest const* quest = sObjectMgr->GetQuestTemplate(packet.QuestId))
{
if (!quest->HasFlag(QUEST_FLAGS_PARTY_ACCEPT))
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;
recvPacket >> questId;
if (!_player->CanShareQuest(questId))
if (!_player->CanShareQuest(packet.QuestId))
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())
{
@@ -561,7 +550,7 @@ void WorldSession::HandlePushQuestToParty(WorldPacket& recvPacket)
continue;
}
if (player->GetQuestStatus(questId) == QUEST_STATUS_COMPLETE)
if (player->GetQuestStatus(packet.QuestId) == QUEST_STATUS_COMPLETE)
{
_player->SendPushToPartyResponse(player, QUEST_PARTY_MSG_FINISH_QUEST);
continue;
@@ -613,21 +602,16 @@ void WorldSession::HandlePushQuestToParty(WorldPacket& recvPacket)
}
}
void WorldSession::HandleQuestPushResult(WorldPacket& recvPacket)
void WorldSession::HandleQuestPushResult(WorldPackets::Quest::QuestPushResultClient& packet)
{
ObjectGuid guid;
uint32 questId;
uint8 msg;
recvPacket >> guid >> questId >> msg;
if (_player->GetDivider() && _player->GetDivider() == guid)
if (_player->GetDivider() && _player->GetDivider() == packet.PlayerGuid)
{
if (Player* player = ObjectAccessor::GetPlayer(*_player, _player->GetDivider()))
{
WorldPacket data(MSG_QUEST_PUSH_RESULT, 8 + 4 + 1);
data << _player->GetGUID();
data << uint8(msg); // valid values: 0-8
player->SendDirectMessage(&data);
WorldPackets::Quest::QuestPushResult questPushResult;
questPushResult.PlayerGuid = _player->GetGUID();
questPushResult.QuestShareMessage = packet.QuestShareMessage;
player->SendDirectMessage(questPushResult.Write());
_player->SetDivider();
}
}
+14
View File
@@ -675,6 +675,13 @@ void WorldSession::HandleInitiateTradeOpcode(WorldPacket& recvPacket)
return;
}
if (sWorld->getBoolConfig(CONFIG_TRIAL_RESTRICTION_TRADE) && IsTrialAccount())
{
info.Status = TRADE_STATUS_TRIAL_ACCOUNT;
SendTradeStatus(info);
return;
}
if (GetPlayer()->IsSpectator())
return;
@@ -722,6 +729,13 @@ void WorldSession::HandleInitiateTradeOpcode(WorldPacket& recvPacket)
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() &&
!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())
{
ASSERT(obj->IsInGrid());
obj->UpdateObjectVisibilityOnCreate();
obj->UpdateObjectVisibility(true);
return true;
}
@@ -344,7 +344,7 @@ bool Map::AddToMap(T* obj, bool checkTransport)
//something, such as vehicle, needs to be update immediately
//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.
if (obj->IsCreature())
+1 -1
View File
@@ -28,7 +28,7 @@ public:
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 void OnCreatureCreate(Creature*) { }
+1 -1
View File
@@ -96,7 +96,7 @@ void OPvPCapturePoint::AddCre(uint32 type, ObjectGuid::LowType guid, uint32 entr
return;
}
entry = data->id1;
entry = data->id;
}
_creatures[type] = guid;
@@ -32,6 +32,7 @@
#include "NPCPackets.h"
#include "PetPackets.h"
#include "QueryPackets.h"
#include "QuestPackets.h"
#include "TotemPackets.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;
}
namespace Quest
{
class QuestPushResultClient;
class QuestGiverQuestAutoLaunch;
class QuestLogSwapQuest;
class QuestLogRemoveQuest;
class QuestConfirmAcceptClient;
class PushQuestToParty;
}
namespace Calendar
{
class GetEvent;
@@ -934,13 +944,13 @@ public: // opcodes handlers
void HandleQuestgiverRequestRewardOpcode(WorldPacket& recvPacket);
void HandleQuestQueryOpcode(WorldPacket& recvPacket);
void HandleQuestgiverCancel(WorldPacket& recvData);
void HandleQuestLogSwapQuest(WorldPacket& recvData);
void HandleQuestLogRemoveQuest(WorldPacket& recvData);
void HandleQuestConfirmAccept(WorldPacket& recvData);
void HandleQuestLogSwapQuest(WorldPackets::Quest::QuestLogSwapQuest& packet);
void HandleQuestLogRemoveQuest(WorldPackets::Quest::QuestLogRemoveQuest& packet);
void HandleQuestConfirmAccept(WorldPackets::Quest::QuestConfirmAcceptClient& packet);
void HandleQuestgiverCompleteQuest(WorldPacket& recvData);
void HandleQuestgiverQuestAutoLaunch(WorldPacket& recvPacket);
void HandlePushQuestToParty(WorldPacket& recvPacket);
void HandleQuestPushResult(WorldPacket& recvPacket);
void HandleQuestgiverQuestAutoLaunch(WorldPackets::Quest::QuestGiverQuestAutoLaunch& packet);
void HandlePushQuestToParty(WorldPackets::Quest::PushQuestToParty& packet);
void HandleQuestPushResult(WorldPackets::Quest::QuestPushResultClient& packet);
void HandleMessagechatOpcode(WorldPacket& recvPacket);
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
--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);
UpdateMaxSessionCounters();
@@ -184,12 +184,7 @@ uint32 GetExplicitDiscoverySpell(uint32 spellId, Player* player)
continue;
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;
}
roll -= item_iter->chance;
}
@@ -847,7 +847,9 @@ void AuraEffect::ApplySpellMod(Unit* target, bool apply)
Aura* aura = iter->second->GetBase();
// 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->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)
{
@@ -4243,6 +4245,8 @@ void AuraEffect::HandleModPercentStat(AuraApplication const* aurApp, uint8 mode,
return;
for (int32 i = STAT_STRENGTH; i < MAX_STATS; ++i)
{
if (GetMiscValue() == i || GetMiscValue() == -1)
{
if (apply)
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);
}
}
}
}
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->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
+21
View File
@@ -33,6 +33,8 @@
#include "Tokenize.h"
#include "World.h"
#include <algorithm>
bool IsPrimaryProfessionSkill(uint32 skill)
{
SkillLineEntry const* pSkill = sSkillLineStore.LookupEntry(skill);
@@ -687,6 +689,24 @@ SpellLearnSkillNode const* SpellMgr::GetSpellLearnSkill(uint32 spell_id) const
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
{
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 46021: // Spectral Realm (SWP)
case 52951: // Chapel Invisibility (DK starting zone)
case 43062: // Alpha Worg: Garwal's Invisibility
break;
default:
spellInfo->AuraInterruptFlags |= AURA_INTERRUPT_FLAG_CAST;
+1
View File
@@ -687,6 +687,7 @@ public:
// Spell learning
[[nodiscard]] SpellLearnSkillNode const* GetSpellLearnSkill(uint32 spell_id) const;
[[nodiscard]] std::vector<uint32> GetSkillRankSpells(uint32 skillId) const;
// Spell target coordinates
[[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<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<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_RACE_LIMIT_ONE_PER_CHARACTER,
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
};
+2 -2
View File
@@ -609,7 +609,7 @@ public:
CreatureData const* spawnpoint = nullptr;
for (auto const& pair : sObjectMgr->GetAllCreatureData())
{
if (pair.second.id1 != entry)
if (pair.second.id != entry)
{
continue;
}
@@ -633,7 +633,7 @@ public:
std::vector<CreatureData const*> spawnpoints;
for (auto const& pair : sObjectMgr->GetAllCreatureData())
{
if (pair.second.id1 != entry)
if (pair.second.id != entry)
{
continue;
}
+15 -1
View File
@@ -309,6 +309,8 @@ public:
static bool HandleLearnAllCraftsCommand(ChatHandler* handler)
{
Player* target = handler->GetSession()->GetPlayer();
for (uint32 i = 0; i < sSkillLineStore.GetNumRows(); ++i)
{
SkillLineEntry const* skillInfo = sSkillLineStore.LookupEntry(i);
@@ -318,7 +320,10 @@ public:
if ((skillInfo->categoryId == SKILL_CATEGORY_PROFESSION || skillInfo->categoryId == SKILL_CATEGORY_SECONDARY) &&
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)
{
// 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();
for (SkillLineAbilityEntry const* skillLine : GetSkillLineAbilitiesBySkillLine(skillId))
+8 -8
View File
@@ -77,19 +77,19 @@ public:
QueryResult result;
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)
creatureCount = (*result)[0].Get<uint64>();
if (handler->GetSession())
{
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 {}",
player->GetPositionX(), player->GetPositionY(), player->GetPositionZ(), uint32(creatureId), uint32(creatureId), uint32(creatureId), count);
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), count);
}
else
result = WorldDatabase.Query("SELECT guid, position_x, position_y, position_z, map FROM creature WHERE id1='{}' OR id2='{}' OR id3='{}' LIMIT {}",
uint32(creatureId), uint32(creatureId), uint32(creatureId), count);
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), count);
if (result)
{
@@ -557,13 +557,13 @@ public:
for (auto const& pair : map->GetCreatureRespawnTimes())
{
CreatureData const* data = sObjectMgr->GetCreatureData(pair.first);
if (!data || (entryFilter && data->id1 != *entryFilter))
if (!data || (entryFilter && data->id != *entryFilter))
continue;
CreatureTemplate const* cTemplate = sObjectMgr->GetCreatureTemplate(data->id1);
CreatureTemplate const* cTemplate = sObjectMgr->GetCreatureTemplate(data->id);
std::string name = cTemplate ? cTemplate->Name : "Unknown";
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;
if (count >= 50)
{
+4 -4
View File
@@ -2492,7 +2492,7 @@ public:
if (isAlive)
{
handler->PSendSysMessage(LANG_RESPAWN_GUID_CREATURE_ALIVE, spawnId, creData->id1);
handler->PSendSysMessage(LANG_RESPAWN_GUID_CREATURE_ALIVE, spawnId, creData->id);
return true;
}
@@ -2508,7 +2508,7 @@ public:
time_t now = GameTime::GetGameTime().count();
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;
}
@@ -2608,7 +2608,7 @@ public:
for (auto const& [spawnId, creature] : map->GetCreatureBySpawnIdStore())
{
CreatureData const* data = sObjectMgr->GetCreatureData(spawnId);
if (!data || data->id1 != entry)
if (!data || data->id != entry)
continue;
if (creature->isDead())
deadCreatures.push_back(creature);
@@ -2624,7 +2624,7 @@ public:
for (auto const& [spawnId, respawnTime] : map->GetCreatureRespawnTimes())
{
CreatureData const* data = sObjectMgr->GetCreatureData(spawnId);
if (!data || data->id1 != entry)
if (!data || data->id != entry)
continue;
if (sPoolMgr->IsPartOfAPool<Creature>(spawnId))
continue;
+10 -12
View File
@@ -229,7 +229,7 @@ public:
{
ObjectGuid::LowType guid = sObjectMgr->GenerateCreatureSpawnId();
CreatureData& data = sObjectMgr->NewOrExistCreatureData(guid);
data.id1 = id;
data.id = id;
data.phaseMask = chr->GetPhaseMaskForSpawn();
data.posX = chr->GetTransOffsetX();
data.posY = chr->GetTransOffsetY();
@@ -677,7 +677,7 @@ public:
uint32 id3 = 0;
if (CreatureData const* cData = target->GetCreatureData())
{
id1 = cData->id1;
id1 = cData->id;
id2 = cData->id2;
id3 = cData->id3;
}
@@ -721,7 +721,7 @@ public:
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)
{
handler->SendErrorMessage(LANG_COMMAND_CREATGUIDNOTFOUND, lowGuid);
@@ -730,8 +730,8 @@ public:
handler->PSendSysMessage("(Not in world - showing DB data)");
uint32 scriptId = cData->ScriptId ? cData->ScriptId : cInfo->ScriptID;
handler->PSendSysMessage(LANG_NPCINFO_CHAR, lowGuid, ObjectGuid::Create<HighGuid::Unit>(cData->id1, lowGuid).ToString(), cData->id1,
cData->id2, cData->id3, cData->displayid, cData->displayid, cInfo->faction,
handler->PSendSysMessage(LANG_NPCINFO_CHAR, lowGuid, ObjectGuid::Create<HighGuid::Unit>(cData->id, lowGuid).ToString(), cData->id,
cData->id, cData->id2, cData->id3, cData->displayid, cData->displayid, cInfo->faction,
cData->npcflag);
handler->PSendSysMessage(LANG_NPCINFO_PHASEMASK, cData->phaseMask);
handler->PSendSysMessage(LANG_NPCINFO_POSITION, cData->posX, cData->posY, cData->posZ);
@@ -763,7 +763,7 @@ public:
uint32 id3 = 0;
if (CreatureData const* cData = target->GetCreatureData())
{
id1 = cData->id1;
id1 = cData->id;
id2 = cData->id2;
id3 = cData->id3;
}
@@ -835,12 +835,10 @@ public:
continue;
uint32 entry = fields[1].Get<uint32>();
//uint32 entry2 = fields[2].Get<uint32>();
//uint32 entry3 = fields[3].Get<uint32>();
float x = fields[4].Get<float>();
float y = fields[5].Get<float>();
float z = fields[6].Get<float>();
uint16 mapId = fields[7].Get<uint16>();
float x = fields[2].Get<float>();
float y = fields[3].Get<float>();
float z = fields[4].Get<float>();
uint16 mapId = fields[5].Get<uint16>();
CreatureTemplate const* creatureTemplate = sObjectMgr->GetCreatureTemplate(entry);
if (!creatureTemplate)
+1 -1
View File
@@ -78,7 +78,7 @@ private:
{
if (CreatureData const* data = sObjectMgr->GetCreatureData(obj.guid))
{
entry = data->id1;
entry = data->id;
mapId = data->mapid;
x = data->posX;
y = data->posY;
+3 -3
View File
@@ -309,7 +309,7 @@ public:
CreatureData const* spawnpoint = nullptr;
for (auto const& pair : sObjectMgr->GetAllCreatureData())
{
if (pair.second.id1 != *creatureId)
if (pair.second.id != *creatureId)
continue;
if (!spawnpoint)
@@ -341,7 +341,7 @@ public:
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);
}
@@ -352,7 +352,7 @@ public:
WorldDatabase.EscapeString(normalizedName);
// 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)
{
handler->SendErrorMessage(LANG_COMMAND_GOCREATNOTFOUND);
+1 -1
View File
@@ -1037,7 +1037,7 @@ public:
if (show == "off")
{
WorldDatabasePreparedStatement* stmt = WorldDatabase.GetPreparedStatement(WORLD_SEL_CREATURE_BY_ID);
stmt->SetArguments(1, 1, 1);
stmt->SetArguments(1, 1);
PreparedQueryResult result = WorldDatabase.Query(stmt);
if (!result)
@@ -38,10 +38,10 @@ struct SHostageInfo
static SHostageInfo HostageInfo[] =
{
{23790, 186648, { -57.0f, 1343.0f, 40.77f, 3.2f } }, // bear
{23999, 187021, { 400.0f, 1414.0f, 74.36f, 3.3f } }, // eagle
{24001, 186672, { -35.0f, 1134.0f, 18.71f, 1.9f } }, // dragonhawk
{24024, 186667, { 413.0f, 1117.0f, 6.32f, 3.1f } } // lynx
{23999, 187021, { 400.0f, 1414.0f, 74.36f, 3.3f } }, // Harkor (Akil'zon)
{23790, 186648, { -57.0f, 1343.0f, 40.77f, 3.2f } }, // Tanzar (Nalorakk)
{24024, 186667, { -35.0f, 1134.0f, 18.71f, 1.9f } }, // Kraz (Jan'alai)
{24001, 186672, { 413.0f, 1117.0f, 6.32f, 3.1f } } // Ashli (Halazzi)
};
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."
static uint32 HostageEntry[] = {23790, 23999, 24024, 24001};
static uint32 ChestEntry[] = {186648, 187021, 186667, 186672};
static uint32 HostageEntry[] = {23999, 23790, 24024, 24001};
static uint32 ChestEntry[] = {187021, 186648, 186667, 186672};
class npc_zulaman_hostage : public CreatureScript
{
@@ -25,8 +25,8 @@
enum DataTypes
{
DATA_NALORAKK = 0,
DATA_AKILZON = 1,
DATA_AKILZON = 0,
DATA_NALORAKK = 1,
DATA_JANALAI = 2,
DATA_HALAZZI = 3,
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.
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);
if (targetGuid.GetEntry() == NPC_VEKLOR)
@@ -585,9 +585,9 @@ enum ShadowPriestSezzizEnum
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_ACOLYTE, { 895.26f, 1199.09f, 8.87f } }, { NPC_SANDFURY_ACOLYTE, { 895.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, { 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_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, { 1895.26f, 1199.09f, 8.87f } }, { NPC_SANDFURY_ACOLYTE, { 1895.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
@@ -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
{
// 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);
}
void JustDied(Unit* /*who*/) override
{
DoCastSelf(SPELL_FLAME_SPHERE_DEATH_EFFECT);
}
void UpdateAI(uint32 diff) override
{
if (moveTimer)
@@ -147,7 +151,7 @@ struct npc_taldaram_flamesphere : public NullCreatureAI
float angle = me->GetAngle(&victimPos) + angleOffset;
float x = me->GetPositionX() + DATA_SPHERE_DISTANCE * cos(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;
}
@@ -84,7 +84,7 @@ public:
uint32 GetCreatureEntry(ObjectGuid::LowType /*guidLow*/, CreatureData const* data) override
{
uint32 entry = data->id1;
uint32 entry = data->id;
switch (entry)
{
case NPC_RESCUED_ALLIANCE_SLAVE:
@@ -1045,7 +1045,7 @@ public:
if (Creature* crusader = ObjectAccessor::GetCreature(*me, instance->GetGuidData(DATA_CAPTAIN_ARNATH + i)))
if (crusader->IsAlive())
{
if (crusader->GetEntry() == crusader->GetCreatureData()->id1)
if (crusader->GetEntry() == crusader->GetCreatureData()->id)
{
crusader->m_Events.AddEventAtOffset(new CaptainSurviveTalk(*crusader), delay);
delay += 6s;
@@ -1235,7 +1235,7 @@ public:
void Reset() override
{
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
@@ -539,7 +539,7 @@ public:
uint32 GetCreatureEntry(ObjectGuid::LowType /*guidLow*/, CreatureData const* data) override
{
uint32 entry = data->id1;
uint32 entry = data->id;
switch (entry)
{
case NPC_HORDE_GUNSHIP_CANNON:
@@ -135,6 +135,22 @@ static uint8 CountAliveBosses(InstanceScript* instance)
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)
{
if (!pInstance || !me)
@@ -186,13 +202,17 @@ struct boss_steelbreaker : public ScriptedAI
void Reset() override
{
me->SetLootMode(0);
RespawnAssemblyOfIron(pInstance, me);
_phase = 0;
events.Reset();
if (!IsAnyAssemblyMemberEngaged(pInstance, me))
{
RespawnAssemblyOfIron(pInstance, me);
if (pInstance)
pInstance->SetBossState(BOSS_ASSEMBLY, NOT_STARTED);
}
}
void JustReachedHome() override
{
@@ -316,11 +336,20 @@ struct boss_steelbreaker : public ScriptedAI
events.Repeat(15s, 20s);
break;
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);
break;
}
case EVENT_OVERWHELMING_POWER:
Talk(SAY_STEELBREAKER_POWER);
me->CastSpell(me->GetVictim(), SPELL_OVERWHELMING_POWER, true);
@@ -367,14 +396,17 @@ struct boss_runemaster_molgeim : public ScriptedAI
void Reset() override
{
me->SetLootMode(0);
RespawnAssemblyOfIron(pInstance, me);
_phase = 0;
events.Reset();
summons.DespawnAll();
if (!IsAnyAssemblyMemberEngaged(pInstance, me))
{
RespawnAssemblyOfIron(pInstance, me);
if (pInstance)
pInstance->SetBossState(BOSS_ASSEMBLY, NOT_STARTED);
}
me->m_Events.AddEventAtOffset(new CastRunesEvent(*me), 8s);
}
@@ -559,7 +591,6 @@ struct boss_stormcaller_brundir : public ScriptedAI
{
SetInvincibility(false);
me->SetLootMode(0);
RespawnAssemblyOfIron(pInstance, me);
_channelTimer = 0;
_phase = 0;
@@ -571,9 +602,14 @@ struct boss_stormcaller_brundir : public ScriptedAI
me->SetDisableGravity(false);
me->SetRegeneratingHealth(true);
me->SetReactState(REACT_AGGRESSIVE);
if (!IsAnyAssemblyMemberEngaged(pInstance, me))
{
RespawnAssemblyOfIron(pInstance, me);
if (pInstance)
pInstance->SetBossState(BOSS_ASSEMBLY, NOT_STARTED);
}
}
void JustReachedHome() override
{
@@ -1445,21 +1445,38 @@ class spell_vehicle_grab_pyrite : public SpellScript
void HandleScript(SpellEffIndex /*effIndex*/)
{
if (Unit* target = GetHitUnit())
if (Unit* seat = GetCaster()->GetVehicleBase())
{
Unit* target = GetHitUnit();
if (!target)
return;
// The grabbing vehicle: the demolisher's mechanic seat, or the chopper itself.
Unit* seat = GetCaster()->GetVehicleBase();
if (!seat)
return;
if (Vehicle* vehicle = seat->GetVehicleKit())
if (Unit* pyrite = vehicle->GetPassenger(1))
pyrite->ExitVehicle();
if (Unit* passenger = vehicle->GetPassenger(1))
{
// 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())
{
// Demolisher: the seat is mounted on a parent vehicle that the pyrite fuels.
GetCaster()->CastSpell(parent, SPELL_ADD_PYRITE, true);
target->CastSpell(seat, GetEffectValue());
if (target->IsCreature())
target->ToCreature()->DespawnOrUnsummon(1300ms);
}
else
{
// Chopper: load the crate into the rear seat so it can be ferried to other vehicles.
target->CastSpell(seat, GetEffectValue());
}
}
@@ -1058,7 +1058,7 @@ struct boss_freya_summons : public ScriptedAI
switch (events.ExecuteEvent())
{
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);
break;
case EVENT_ANCIENT_CONSERVATOR_GRIP:
@@ -77,6 +77,16 @@ enum eEvents
EVENT_GRAB,
};
enum IgnisActions
{
ACTION_CONSTRUCT_SHATTERED = 0,
};
enum IgnisData
{
DATA_SHATTERED = 0,
};
struct npc_ulduar_iron_construct : public ScriptedAI
{
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
{
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 (getMSTimeDiff(lastShatterMSTime, GameTime::GetGameTimeMS().count()) <= 5000)
@@ -247,7 +244,7 @@ struct boss_ignis : public BossAI
uint32 GetData(uint32 id) const override
{
if (id == 1337)
if (id == DATA_SHATTERED)
return (bShattered ? 1 : 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
{
public:
@@ -507,7 +540,7 @@ public:
{
if (!target || !target->IsCreature())
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_grab_initial);
RegisterSpellScript(spell_ignis_slag_pot_aura);
RegisterSpellScript(spell_ignis_brittle_aura);
new achievement_ignis_shattered();
}

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