diff --git a/data/sql/updates/pending_db_world/rev_1787538862556400894.sql b/data/sql/updates/pending_db_world/rev_1787538862556400894.sql new file mode 100644 index 0000000000..0f2b9cf6bd --- /dev/null +++ b/data/sql/updates/pending_db_world/rev_1787538862556400894.sql @@ -0,0 +1,8 @@ +-- +-- Shallow Grave +-- Entry 128403 summons through its WDB-linked trap; entry 128308 is the inert alternative. +DELETE FROM `smart_scripts` WHERE (`entryorguid` = 128308) AND (`source_type` = 1); +DELETE FROM `smart_scripts` WHERE (`entryorguid` = 128403) AND (`source_type` = 1); +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 + (128308, 1, 0, 0, 64, 0, 100, 1, 0, 0, 0, 0, 0, 41, 300000, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Shallow Grave - On Gossip Hello - Despawn after 5 minutes'), + (128403, 1, 0, 0, 64, 0, 100, 1, 0, 0, 0, 0, 0, 41, 300000, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Shallow Grave - On Gossip Hello - Despawn after 5 minutes'); diff --git a/data/sql/updates/pending_db_world/rev_1789853753513859527.sql b/data/sql/updates/pending_db_world/rev_1789853753513859527.sql new file mode 100644 index 0000000000..12e95723a3 --- /dev/null +++ b/data/sql/updates/pending_db_world/rev_1789853753513859527.sql @@ -0,0 +1,25 @@ +-- Zul'Farrak grave summons: use the Loot Normalization (#24398) world loot pools. +-- Gultask's WotLK Classic research (builds 49822, 52237 and 54261): +-- https://github.com/azerothcore/azerothcore-wotlk/pull/27381#issuecomment-5798468763 +-- Keep Troll Sweat, cloth, food, ZF junk and quest drops unchanged. +-- Remove direct world drops supplied by the normalized references to avoid duplicate rolls. +DELETE FROM `creature_loot_template` WHERE `Entry` IN (7276, 7286) AND `Reference` = 0 AND `Item` IN + (1685,3395,3831,3832,3864,3868,3873,3874,3914,3928,4300,4353,4416,4417,4419,4421,4422,4424, + 4637,4638,5974,6149,7084,7085,7086,7453,7909,7910,7975,7989,7990,7992,7993,8029,8386,8387, + 8389,8390,9293,9295,9298,10300,10301,10302,10312,10315,10320,10603,10604,10606,11167,11202, + 11204,11208,11225,12684,13068); +DELETE FROM `creature_loot_template` WHERE `Entry` IN (7276, 7286) AND `Reference` <> 0; +INSERT INTO `creature_loot_template` +(`Entry`, `Item`, `Reference`, `Chance`, `QuestRequired`, `LootMode`, `GroupId`, `MinCount`, `MaxCount`, `Comment`) VALUES + (7276, 1, 1000145, 0, 0, 1, 5, 1, 1, 'Zul''Farrak Dead Hero - World Loot Level 45'), + (7276, 2, 1000146, 0, 0, 1, 5, 1, 1, 'Zul''Farrak Dead Hero - World Loot Level 46'), + (7286, 1, 1000343, 0, 0, 1, 5, 1, 1, 'Zul''Farrak Zombie - World Loot Level 43'), + (7286, 2, 1000344, 0, 0, 1, 5, 1, 1, 'Zul''Farrak Zombie - World Loot Level 44'), + (7286, 209, 1209000, 0.5, 0, 1, 0, 1, 1, 'Zul''Farrak Zombie - Zul''Farrak BoEs'); + +-- Money research from the same sniffs: +-- https://github.com/azerothcore/azerothcore-wotlk/pull/27381#issuecomment-5798878487 +-- Zombie bounds are inferred from comparable level 44 ZF elites; keep Dead Hero money unchanged. +UPDATE `creature_template` SET `mingold` = 186, `maxgold` = 1438 WHERE `entry` = 7286; +-- Observed grave money range, in copper. This addon table has no VerifiedBuild column. +UPDATE `gameobject_template_addon` SET `mingold` = 18, `maxgold` = 131 WHERE `entry` IN (128308, 128403); diff --git a/data/sql/updates/pending_db_world/rev_1790200028364291454.sql b/data/sql/updates/pending_db_world/rev_1790200028364291454.sql new file mode 100644 index 0000000000..2a8b92bcce --- /dev/null +++ b/data/sql/updates/pending_db_world/rev_1790200028364291454.sql @@ -0,0 +1,215 @@ +-- Shallow Grave alternatives from Gultask's Classic sniff research: +-- https://github.com/azerothcore/azerothcore-wotlk/pull/27381#issuecomment-5803161498 +-- Each location has an inert (128308) or trapped (128403) grave, never both. +-- The 75% active / 25% inert weighting is inferred, not an exact measured probability. +-- Preserve existing spawn GUIDs and the one-day respawn; add only the alternate at each location. +DELETE FROM `gameobject` WHERE `guid` BETWEEN 27097 AND 27139 AND `id` IN (128308, 128403); +DELETE FROM `gameobject` WHERE `guid` BETWEEN 43921 AND 43960 AND `id` IN (128308, 128403); +INSERT INTO `gameobject` +(`guid`, `id`, `map`, `zoneId`, `areaId`, `spawnMask`, `phaseMask`, `position_x`, `position_y`, `position_z`, `orientation`, `rotation0`, `rotation1`, `rotation2`, `rotation3`, `spawntimesecs`, `animprogress`, `state`, `VerifiedBuild`) VALUES +(43921, 128308, 209, 1176, 1176, 1, 1, 1832.719, 1032.393, 9.452, 0.5759572982788086, 0, 0, 0.2840147018432617, 0.9588199257850647, 86400, 100, 1, 49822), +(27114, 128403, 209, 1176, 1176, 1, 1, 1832.719, 1032.393, 9.452, 0.5759572982788086, 0, 0, 0.2840147018432617, 0.9588199257850647, 86400, 100, 1, 42940), +(27116, 128308, 209, 1176, 1176, 1, 1, 1836.745, 1029.358, 9.624, 0.9424766302108765, 0, 0, 0.45398998260498047, 0.8910067677497864, 86400, 100, 1, 49345), +(43922, 128403, 209, 1176, 1176, 1, 1, 1836.745, 1029.358, 9.624, 0.9424766302108765, 0, 0, 0.45398998260498047, 0.8910067677497864, 86400, 100, 1, 42940), +(43923, 128308, 209, 1176, 1176, 1, 1, 1839.181, 1036.884, 9.352, 4.607671737670898, 0, 0, -0.7431440353393555, 0.6691314578056335, 86400, 100, 1, 49822), +(27120, 128403, 209, 1176, 1176, 1, 1, 1839.181, 1036.884, 9.352, 4.607671737670898, 0, 0, -0.7431440353393555, 0.6691314578056335, 86400, 100, 1, 42940), +(43924, 128308, 209, 1176, 1176, 1, 1, 1839.422, 1023.486, 9.563, 2.9844982624053955, 0, 0, 0.9969167709350586, 0.07846643030643463, 86400, 100, 1, 44400), +(27102, 128403, 209, 1176, 1176, 1, 1, 1839.422, 1023.486, 9.563, 2.9844982624053955, 0, 0, 0.9969167709350586, 0.07846643030643463, 86400, 100, 1, 42940), +(43925, 128308, 209, 1176, 1176, 1, 1, 1841.083, 1045.543, 9.216, 3.0543265342712402, 0, 0, 0.9990482330322266, 0.04361926019191742, 86400, 100, 1, 49345), +(27115, 128403, 209, 1176, 1176, 1, 1, 1841.083, 1045.543, 9.216, 3.0543265342712402, 0, 0, 0.9990482330322266, 0.04361926019191742, 86400, 100, 1, 42940), +(43926, 128308, 209, 1176, 1176, 1, 1, 1841.821, 1027.759, 9.151, 0.9599299430847168, 0, 0, 0.4617481231689453, 0.8870111107826233, 86400, 100, 1, 49822), +(27119, 128403, 209, 1176, 1176, 1, 1, 1841.821, 1027.759, 9.151, 0.9599299430847168, 0, 0, 0.4617481231689453, 0.8870111107826233, 86400, 100, 1, 42940), +(43927, 128308, 209, 1176, 1176, 1, 1, 1843.193, 1041.155, 8.877, 5.881760597229004, 0, 0, -0.19936752319335938, 0.9799247980117798, 86400, 100, 1, 49345), +(27118, 128403, 209, 1176, 1176, 1, 1, 1843.193, 1041.155, 8.877, 5.881760597229004, 0, 0, -0.19936752319335938, 0.9799247980117798, 86400, 100, 1, 42940), +(43928, 128308, 209, 1176, 1176, 1, 1, 1845.539, 1024.121, 9.08, 0.9773830771446228, 0, 0, 0.4694709777832031, 0.8829479217529297, 86400, 100, 1, 49822), +(27121, 128403, 209, 1176, 1176, 1, 1, 1845.539, 1024.121, 9.08, 0.9773830771446228, 0, 0, 0.4694709777832031, 0.8829479217529297, 86400, 100, 1, 42940), +(43929, 128308, 209, 1176, 1176, 1, 1, 1845.959, 1047.25, 8.877, 5.829400539398193, 0, 0, -0.22495079040527344, 0.9743701219558716, 86400, 100, 1, 44400), +(27133, 128403, 209, 1176, 1176, 1, 1, 1845.959, 1047.25, 8.877, 5.829400539398193, 0, 0, -0.22495079040527344, 0.9743701219558716, 86400, 100, 1, 42940), +(43930, 128308, 209, 1176, 1176, 1, 1, 1852.408, 1024.781, 8.877, 3.787367343902588, 0, 0, -0.9483232498168945, 0.31730589270591736, 86400, 100, 1, 49822), +(27117, 128403, 209, 1176, 1176, 1, 1, 1852.408, 1024.781, 8.877, 3.787367343902588, 0, 0, -0.9483232498168945, 0.31730589270591736, 86400, 100, 1, 42940), +(43931, 128308, 209, 1176, 1176, 1, 1, 1858.698, 1053.75, 8.877, 3.5430235862731934, 0, 0, -0.979924201965332, 0.1993703842163086, 86400, 100, 1, 44400), +(27134, 128403, 209, 1176, 1176, 1, 1, 1858.698, 1053.75, 8.877, 3.5430235862731934, 0, 0, -0.979924201965332, 0.1993703842163086, 86400, 100, 1, 42940), +(43932, 128308, 209, 1176, 1176, 1, 1, 1861.116, 1019.389, 8.877, 5.096362113952637, 0, 0, -0.5591926574707031, 0.8290377259254456, 86400, 100, 1, 49822), +(27103, 128403, 209, 1176, 1176, 1, 1, 1861.116, 1019.389, 8.877, 5.096362113952637, 0, 0, -0.5591926574707031, 0.8290377259254456, 86400, 100, 1, 42940), +(43933, 128308, 209, 1176, 1176, 1, 1, 1862.12, 1050.756, 9.057, 3.892086982727051, 0, 0, -0.9304170608520508, 0.3665024936199188, 86400, 100, 1, 42940), +(27135, 128403, 209, 1176, 1176, 1, 1, 1862.12, 1050.756, 9.057, 3.892086982727051, 0, 0, -0.9304170608520508, 0.3665024936199188, 86400, 100, 1, 44400), +(27122, 128308, 209, 1176, 1176, 1, 1, 1863.409, 1044.78, 8.877, 4.572763919830322, 0, 0, -0.7547092437744141, 0.6560594439506531, 86400, 100, 1, 42940), +(43934, 128403, 209, 1176, 1176, 1, 1, 1863.409, 1044.78, 8.877, 4.572763919830322, 0, 0, -0.7547092437744141, 0.6560594439506531, 86400, 100, 1, 49345), +(43935, 128308, 209, 1176, 1176, 1, 1, 1865.033, 1012.491, 8.877, 1.0471975803375244, 0, 0, 0.5, 0.8660253882408142, 86400, 100, 1, 52237), +(27105, 128403, 209, 1176, 1176, 1, 1, 1865.033, 1012.491, 8.877, 1.0471975803375244, 0, 0, 0.5, 0.8660253882408142, 86400, 100, 1, 42940), +(43936, 128308, 209, 1176, 1176, 1, 1, 1866.235, 1008.397, 8.877, 0.6632232069969177, 0, 0, 0.32556724548339844, 0.9455188512802124, 86400, 100, 1, 44400), +(27104, 128403, 209, 1176, 1176, 1, 1, 1866.235, 1008.397, 8.877, 0.6632232069969177, 0, 0, 0.32556724548339844, 0.9455188512802124, 86400, 100, 1, 42940), +(27136, 128308, 209, 1176, 1176, 1, 1, 1866.326, 1049.636, 9.015, 1.1170092821121216, 0, 0, 0.5299186706542969, 0.8480484485626221, 86400, 100, 1, 44400), +(43937, 128403, 209, 1176, 1176, 1, 1, 1866.326, 1049.636, 9.015, 1.1170092821121216, 0, 0, 0.5299186706542969, 0.8480484485626221, 86400, 100, 1, 42940), +(43938, 128308, 209, 1176, 1176, 1, 1, 1867.745, 1041.844, 8.877, 5.846853733062744, 0, 0, -0.21643924713134766, 0.976296067237854, 86400, 100, 1, 49822), +(27126, 128403, 209, 1176, 1176, 1, 1, 1867.745, 1041.844, 8.877, 5.846853733062744, 0, 0, -0.21643924713134766, 0.976296067237854, 86400, 100, 1, 42940), +(27127, 128308, 209, 1176, 1176, 1, 1, 1869.284, 1020.079, 8.877, 5.794494152069092, 0, 0, -0.24192142486572266, 0.9702958464622498, 86400, 100, 1, 49345), +(43939, 128403, 209, 1176, 1176, 1, 1, 1869.284, 1020.079, 8.877, 5.794494152069092, 0, 0, -0.24192142486572266, 0.9702958464622498, 86400, 100, 1, 42940), +(43940, 128308, 209, 1176, 1176, 1, 1, 1871.077, 1046.756, 9.034, 4.118979454040527, 0, 0, -0.8829469680786133, 0.4694727063179016, 86400, 100, 1, 52237), +(27138, 128403, 209, 1176, 1176, 1, 1, 1871.077, 1046.756, 9.034, 4.118979454040527, 0, 0, -0.8829469680786133, 0.4694727063179016, 86400, 100, 1, 42940), +(43941, 128308, 209, 1176, 1176, 1, 1, 1871.296, 1008.323, 8.877, 1.08210289478302, 0, 0, 0.5150375366210938, 0.8571676015853882, 86400, 100, 1, 54558), +(27113, 128403, 209, 1176, 1176, 1, 1, 1871.296, 1008.323, 8.877, 1.08210289478302, 0, 0, 0.5150375366210938, 0.8571676015853882, 86400, 100, 1, 42940), +(43942, 128308, 209, 1176, 1176, 1, 1, 1871.855, 994.256, 8.882, 0.5061445832252502, 0, 0, 0.2503795623779297, 0.9681477546691895, 86400, 100, 1, 49822), +(27111, 128403, 209, 1176, 1176, 1, 1, 1871.855, 994.256, 8.882, 0.5061445832252502, 0, 0, 0.2503795623779297, 0.9681477546691895, 86400, 100, 1, 42940), +(43943, 128308, 209, 1176, 1176, 1, 1, 1875.847, 1044.261, 9.078, 0.855210542678833, 0, 0, 0.41469287872314453, 0.909961462020874, 86400, 100, 1, 52237), +(27139, 128403, 209, 1176, 1176, 1, 1, 1875.847, 1044.261, 9.078, 0.855210542678833, 0, 0, 0.41469287872314453, 0.909961462020874, 86400, 100, 1, 42940), +(27101, 128308, 209, 1176, 1176, 1, 1, 1876.138, 976.482, 9.052, 1.0122894048690796, 0, 0, 0.48480892181396484, 0.8746200799942017, 86400, 100, 1, 52237), +(43944, 128403, 209, 1176, 1176, 1, 1, 1876.138, 976.482, 9.052, 1.0122894048690796, 0, 0, 0.48480892181396484, 0.8746200799942017, 86400, 100, 1, 42940), +(43945, 128308, 209, 1176, 1176, 1, 1, 1876.44, 972.207, 9.912, 2.8448827266693115, 0, 0, 0.9890155792236328, 0.14781136810779572, 86400, 100, 1, 42940), +(27099, 128403, 209, 1176, 1176, 1, 1, 1876.44, 972.207, 9.912, 2.8448827266693115, 0, 0, 0.9890155792236328, 0.14781136810779572, 86400, 100, 1, 49345), +(43946, 128308, 209, 1176, 1176, 1, 1, 1879.89, 974.651, 8.877, 1.0471975803375244, 0, 0, 0.5, 0.8660253882408142, 86400, 100, 1, 42940), +(27098, 128403, 209, 1176, 1176, 1, 1, 1879.89, 974.651, 8.877, 1.0471975803375244, 0, 0, 0.5, 0.8660253882408142, 86400, 100, 1, 49345), +(43947, 128308, 209, 1176, 1176, 1, 1, 1881.465, 1041.431, 8.877, 4.084071159362793, 0, 0, -0.8910064697265625, 0.453990638256073, 86400, 100, 1, 42940), +(27137, 128403, 209, 1176, 1176, 1, 1, 1881.465, 1041.431, 8.877, 4.084071159362793, 0, 0, -0.8910064697265625, 0.453990638256073, 86400, 100, 1, 44400), +(43948, 128308, 209, 1176, 1176, 1, 1, 1881.989, 1034.684, 8.877, 5.829400539398193, 0, 0, -0.22495079040527344, 0.9743701219558716, 86400, 100, 1, 54261), +(27131, 128403, 209, 1176, 1176, 1, 1, 1881.989, 1034.684, 8.877, 5.829400539398193, 0, 0, -0.22495079040527344, 0.9743701219558716, 86400, 100, 1, 42940), +(43949, 128308, 209, 1176, 1176, 1, 1, 1882.703, 971.825, 8.877, 0.9948371648788452, 0, 0, 0.4771585464477539, 0.8788172602653503, 86400, 100, 1, 44400), +(27100, 128403, 209, 1176, 1176, 1, 1, 1882.703, 971.825, 8.877, 0.9948371648788452, 0, 0, 0.4771585464477539, 0.8788172602653503, 86400, 100, 1, 42940), +(43950, 128308, 209, 1176, 1176, 1, 1, 1887.458, 967.143, 8.985, 5.969027042388916, 0, 0, -0.1564340591430664, 0.9876883625984192, 86400, 100, 1, 42940), +(27097, 128403, 209, 1176, 1176, 1, 1, 1887.458, 967.143, 8.985, 5.969027042388916, 0, 0, -0.1564340591430664, 0.9876883625984192, 86400, 100, 1, 49345), +(43951, 128308, 209, 1176, 1176, 1, 1, 1887.94, 1029.084, 9.148, 0.855210542678833, 0, 0, 0.41469287872314453, 0.909961462020874, 86400, 100, 1, 49822), +(27128, 128403, 209, 1176, 1176, 1, 1, 1887.94, 1029.084, 9.148, 0.855210542678833, 0, 0, 0.41469287872314453, 0.909961462020874, 86400, 100, 1, 42940), +(43952, 128308, 209, 1176, 1176, 1, 1, 1888.363, 1037.002, 8.921, 4.293513298034668, 0, 0, -0.8386697769165039, 0.5446402430534363, 86400, 100, 1, 42940), +(27130, 128403, 209, 1176, 1176, 1, 1, 1888.363, 1037.002, 8.921, 4.293513298034668, 0, 0, -0.8386697769165039, 0.5446402430534363, 86400, 100, 1, 44400), +(27110, 128308, 209, 1176, 1176, 1, 1, 1888.636, 977.75, 8.877, 3.7175536155700684, 0, 0, -0.9588193893432617, 0.2840164601802826, 86400, 100, 1, 49822), +(43953, 128403, 209, 1176, 1176, 1, 1, 1888.636, 977.75, 8.877, 3.7175536155700684, 0, 0, -0.9588193893432617, 0.2840164601802826, 86400, 100, 1, 42940), +(43954, 128308, 209, 1176, 1176, 1, 1, 1890.123, 989.759, 8.877, 2.7052552700042725, 0, 0, 0.9762954711914062, 0.2164420634508133, 86400, 100, 1, 49345), +(27106, 128403, 209, 1176, 1176, 1, 1, 1890.123, 989.759, 8.877, 2.7052552700042725, 0, 0, 0.9762954711914062, 0.2164420634508133, 86400, 100, 1, 42940), +(27112, 128308, 209, 1176, 1176, 1, 1, 1890.268, 970.862, 9.014, 2.565631866455078, 0, 0, 0.9588193893432617, 0.2840164601802826, 86400, 100, 1, 42940), +(43955, 128403, 209, 1176, 1176, 1, 1, 1890.268, 970.862, 9.014, 2.565631866455078, 0, 0, 0.9588193893432617, 0.2840164601802826, 86400, 100, 1, 44400), +(43956, 128308, 209, 1176, 1176, 1, 1, 1891.598, 1023.06, 9.371, 2.792518377304077, 0, 0, 0.984807014465332, 0.1736523061990738, 86400, 100, 1, 49822), +(27132, 128403, 209, 1176, 1176, 1, 1, 1891.598, 1023.06, 9.371, 2.792518377304077, 0, 0, 0.984807014465332, 0.1736523061990738, 86400, 100, 1, 42940), +(43957, 128308, 209, 1176, 1176, 1, 1, 1892.571, 1006.369, 9.068, 4.1538848876953125, 0, 0, -0.8746194839477539, 0.4848100543022156, 86400, 100, 1, 49345), +(27129, 128403, 209, 1176, 1176, 1, 1, 1892.571, 1006.369, 9.068, 4.1538848876953125, 0, 0, -0.8746194839477539, 0.4848100543022156, 86400, 100, 1, 42940), +(43958, 128308, 209, 1176, 1176, 1, 1, 1894.394, 977.006, 8.926, 6.003933906555176, 0, 0, -0.13917255401611328, 0.9902681708335876, 86400, 100, 1, 42940), +(27108, 128403, 209, 1176, 1176, 1, 1, 1894.394, 977.006, 8.926, 6.003933906555176, 0, 0, -0.13917255401611328, 0.9902681708335876, 86400, 100, 1, 44400), +(27107, 128308, 209, 1176, 1176, 1, 1, 1897.079, 980.836, 8.959, 3.298687219619751, 0, 0, -0.9969167709350586, 0.07846643030643463, 86400, 100, 1, 42940), +(43959, 128403, 209, 1176, 1176, 1, 1, 1897.079, 980.836, 8.959, 3.298687219619751, 0, 0, -0.9969167709350586, 0.07846643030643463, 86400, 100, 1, 49345), +(27109, 128308, 209, 1176, 1176, 1, 1, 1897.983, 985.276, 8.898, 2.757613182067871, 0, 0, 0.9816265106201172, 0.19081240892410278, 86400, 100, 1, 59415), +(43960, 128403, 209, 1176, 1176, 1, 1, 1897.983, 985.276, 8.898, 2.757613182067871, 0, 0, 0.9816265106201172, 0.19081240892410278, 86400, 100, 1, 42940); + +DELETE FROM `pool_template` WHERE `entry` BETWEEN 1121 AND 1160; +INSERT INTO `pool_template` (`entry`, `max_limit`, `description`) VALUES +(1121, 1, 'Zul''Farrak - Shallow Grave 1'), +(1122, 1, 'Zul''Farrak - Shallow Grave 2'), +(1123, 1, 'Zul''Farrak - Shallow Grave 3'), +(1124, 1, 'Zul''Farrak - Shallow Grave 4'), +(1125, 1, 'Zul''Farrak - Shallow Grave 5'), +(1126, 1, 'Zul''Farrak - Shallow Grave 6'), +(1127, 1, 'Zul''Farrak - Shallow Grave 7'), +(1128, 1, 'Zul''Farrak - Shallow Grave 8'), +(1129, 1, 'Zul''Farrak - Shallow Grave 9'), +(1130, 1, 'Zul''Farrak - Shallow Grave 10'), +(1131, 1, 'Zul''Farrak - Shallow Grave 11'), +(1132, 1, 'Zul''Farrak - Shallow Grave 12'), +(1133, 1, 'Zul''Farrak - Shallow Grave 13'), +(1134, 1, 'Zul''Farrak - Shallow Grave 14'), +(1135, 1, 'Zul''Farrak - Shallow Grave 15'), +(1136, 1, 'Zul''Farrak - Shallow Grave 16'), +(1137, 1, 'Zul''Farrak - Shallow Grave 17'), +(1138, 1, 'Zul''Farrak - Shallow Grave 18'), +(1139, 1, 'Zul''Farrak - Shallow Grave 19'), +(1140, 1, 'Zul''Farrak - Shallow Grave 20'), +(1141, 1, 'Zul''Farrak - Shallow Grave 21'), +(1142, 1, 'Zul''Farrak - Shallow Grave 22'), +(1143, 1, 'Zul''Farrak - Shallow Grave 23'), +(1144, 1, 'Zul''Farrak - Shallow Grave 24'), +(1145, 1, 'Zul''Farrak - Shallow Grave 25'), +(1146, 1, 'Zul''Farrak - Shallow Grave 26'), +(1147, 1, 'Zul''Farrak - Shallow Grave 27'), +(1148, 1, 'Zul''Farrak - Shallow Grave 28'), +(1149, 1, 'Zul''Farrak - Shallow Grave 29'), +(1150, 1, 'Zul''Farrak - Shallow Grave 30'), +(1151, 1, 'Zul''Farrak - Shallow Grave 31'), +(1152, 1, 'Zul''Farrak - Shallow Grave 32'), +(1153, 1, 'Zul''Farrak - Shallow Grave 33'), +(1154, 1, 'Zul''Farrak - Shallow Grave 34'), +(1155, 1, 'Zul''Farrak - Shallow Grave 35'), +(1156, 1, 'Zul''Farrak - Shallow Grave 36'), +(1157, 1, 'Zul''Farrak - Shallow Grave 37'), +(1158, 1, 'Zul''Farrak - Shallow Grave 38'), +(1159, 1, 'Zul''Farrak - Shallow Grave 39'), +(1160, 1, 'Zul''Farrak - Shallow Grave 40'); + +DELETE FROM `pool_gameobject` WHERE `pool_entry` BETWEEN 1121 AND 1160; +INSERT INTO `pool_gameobject` (`guid`, `pool_entry`, `chance`, `description`) VALUES +(43921, 1121, 25, 'Zul''Farrak - Inert Shallow Grave'), +(27114, 1121, 75, 'Zul''Farrak - Trapped Shallow Grave'), +(27116, 1122, 25, 'Zul''Farrak - Inert Shallow Grave'), +(43922, 1122, 75, 'Zul''Farrak - Trapped Shallow Grave'), +(43923, 1123, 25, 'Zul''Farrak - Inert Shallow Grave'), +(27120, 1123, 75, 'Zul''Farrak - Trapped Shallow Grave'), +(43924, 1124, 25, 'Zul''Farrak - Inert Shallow Grave'), +(27102, 1124, 75, 'Zul''Farrak - Trapped Shallow Grave'), +(43925, 1125, 25, 'Zul''Farrak - Inert Shallow Grave'), +(27115, 1125, 75, 'Zul''Farrak - Trapped Shallow Grave'), +(43926, 1126, 25, 'Zul''Farrak - Inert Shallow Grave'), +(27119, 1126, 75, 'Zul''Farrak - Trapped Shallow Grave'), +(43927, 1127, 25, 'Zul''Farrak - Inert Shallow Grave'), +(27118, 1127, 75, 'Zul''Farrak - Trapped Shallow Grave'), +(43928, 1128, 25, 'Zul''Farrak - Inert Shallow Grave'), +(27121, 1128, 75, 'Zul''Farrak - Trapped Shallow Grave'), +(43929, 1129, 25, 'Zul''Farrak - Inert Shallow Grave'), +(27133, 1129, 75, 'Zul''Farrak - Trapped Shallow Grave'), +(43930, 1130, 25, 'Zul''Farrak - Inert Shallow Grave'), +(27117, 1130, 75, 'Zul''Farrak - Trapped Shallow Grave'), +(43931, 1131, 25, 'Zul''Farrak - Inert Shallow Grave'), +(27134, 1131, 75, 'Zul''Farrak - Trapped Shallow Grave'), +(43932, 1132, 25, 'Zul''Farrak - Inert Shallow Grave'), +(27103, 1132, 75, 'Zul''Farrak - Trapped Shallow Grave'), +(43933, 1133, 25, 'Zul''Farrak - Inert Shallow Grave'), +(27135, 1133, 75, 'Zul''Farrak - Trapped Shallow Grave'), +(27122, 1134, 25, 'Zul''Farrak - Inert Shallow Grave'), +(43934, 1134, 75, 'Zul''Farrak - Trapped Shallow Grave'), +(43935, 1135, 25, 'Zul''Farrak - Inert Shallow Grave'), +(27105, 1135, 75, 'Zul''Farrak - Trapped Shallow Grave'), +(43936, 1136, 25, 'Zul''Farrak - Inert Shallow Grave'), +(27104, 1136, 75, 'Zul''Farrak - Trapped Shallow Grave'), +(27136, 1137, 25, 'Zul''Farrak - Inert Shallow Grave'), +(43937, 1137, 75, 'Zul''Farrak - Trapped Shallow Grave'), +(43938, 1138, 25, 'Zul''Farrak - Inert Shallow Grave'), +(27126, 1138, 75, 'Zul''Farrak - Trapped Shallow Grave'), +(27127, 1139, 25, 'Zul''Farrak - Inert Shallow Grave'), +(43939, 1139, 75, 'Zul''Farrak - Trapped Shallow Grave'), +(43940, 1140, 25, 'Zul''Farrak - Inert Shallow Grave'), +(27138, 1140, 75, 'Zul''Farrak - Trapped Shallow Grave'), +(43941, 1141, 25, 'Zul''Farrak - Inert Shallow Grave'), +(27113, 1141, 75, 'Zul''Farrak - Trapped Shallow Grave'), +(43942, 1142, 25, 'Zul''Farrak - Inert Shallow Grave'), +(27111, 1142, 75, 'Zul''Farrak - Trapped Shallow Grave'), +(43943, 1143, 25, 'Zul''Farrak - Inert Shallow Grave'), +(27139, 1143, 75, 'Zul''Farrak - Trapped Shallow Grave'), +(27101, 1144, 25, 'Zul''Farrak - Inert Shallow Grave'), +(43944, 1144, 75, 'Zul''Farrak - Trapped Shallow Grave'), +(43945, 1145, 25, 'Zul''Farrak - Inert Shallow Grave'), +(27099, 1145, 75, 'Zul''Farrak - Trapped Shallow Grave'), +(43946, 1146, 25, 'Zul''Farrak - Inert Shallow Grave'), +(27098, 1146, 75, 'Zul''Farrak - Trapped Shallow Grave'), +(43947, 1147, 25, 'Zul''Farrak - Inert Shallow Grave'), +(27137, 1147, 75, 'Zul''Farrak - Trapped Shallow Grave'), +(43948, 1148, 25, 'Zul''Farrak - Inert Shallow Grave'), +(27131, 1148, 75, 'Zul''Farrak - Trapped Shallow Grave'), +(43949, 1149, 25, 'Zul''Farrak - Inert Shallow Grave'), +(27100, 1149, 75, 'Zul''Farrak - Trapped Shallow Grave'), +(43950, 1150, 25, 'Zul''Farrak - Inert Shallow Grave'), +(27097, 1150, 75, 'Zul''Farrak - Trapped Shallow Grave'), +(43951, 1151, 25, 'Zul''Farrak - Inert Shallow Grave'), +(27128, 1151, 75, 'Zul''Farrak - Trapped Shallow Grave'), +(43952, 1152, 25, 'Zul''Farrak - Inert Shallow Grave'), +(27130, 1152, 75, 'Zul''Farrak - Trapped Shallow Grave'), +(27110, 1153, 25, 'Zul''Farrak - Inert Shallow Grave'), +(43953, 1153, 75, 'Zul''Farrak - Trapped Shallow Grave'), +(43954, 1154, 25, 'Zul''Farrak - Inert Shallow Grave'), +(27106, 1154, 75, 'Zul''Farrak - Trapped Shallow Grave'), +(27112, 1155, 25, 'Zul''Farrak - Inert Shallow Grave'), +(43955, 1155, 75, 'Zul''Farrak - Trapped Shallow Grave'), +(43956, 1156, 25, 'Zul''Farrak - Inert Shallow Grave'), +(27132, 1156, 75, 'Zul''Farrak - Trapped Shallow Grave'), +(43957, 1157, 25, 'Zul''Farrak - Inert Shallow Grave'), +(27129, 1157, 75, 'Zul''Farrak - Trapped Shallow Grave'), +(43958, 1158, 25, 'Zul''Farrak - Inert Shallow Grave'), +(27108, 1158, 75, 'Zul''Farrak - Trapped Shallow Grave'), +(27107, 1159, 25, 'Zul''Farrak - Inert Shallow Grave'), +(43959, 1159, 75, 'Zul''Farrak - Trapped Shallow Grave'), +(27109, 1160, 25, 'Zul''Farrak - Inert Shallow Grave'), +(43960, 1160, 75, 'Zul''Farrak - Trapped Shallow Grave'); diff --git a/e2e/README.md b/e2e/README.md index a783aacb06..4727bc10bd 100644 --- a/e2e/README.md +++ b/e2e/README.md @@ -171,6 +171,24 @@ go test -tags=e2e ./local/... -count=1 -v -timeout 30m -parallel 1 --- +### Chest regression checks (PR #27381) + +`world/chests` covers Cat Figurine reopening, Ice Chest party rolls and reward attribution, +and both Shallow Grave variants: inert graves summon nothing; trapped graves do not summon +again on reopening. Grave checks include full bags, another looter, consuming all loot, +and replaying an opening request after despawn. Live execution is still pending. + +```bash +go test -tags=e2e ./suites/world/chests -count=2 -v -p 1 -parallel 1 -timeout 20m +``` + +Use a disposable realm with the pending migrations applied. Tests create temporary fixtures +through cleanup-enabled spawn helpers, not the dungeon's real graves. They sample up to +48 fresh graves for item loot and a positive summon from the trapped variant; inability to +obtain that fixture fails as a precondition. Initial empty-loot coverage is not claimed: +the grave money range is now nonzero. Exact summon/drop probabilities and the dungeon's +pool selection are not asserted by these lifecycle tests. + ## Parallelism and isolation ### Model diff --git a/e2e/suites/world/chests/chests_test.go b/e2e/suites/world/chests/chests_test.go new file mode 100644 index 0000000000..af5ae47b76 --- /dev/null +++ b/e2e/suites/world/chests/chests_test.go @@ -0,0 +1,417 @@ +//go:build e2e + +package chests_test + +import ( + "encoding/binary" + "fmt" + "reflect" + "testing" + "time" + + "github.com/azerothcore/AzerothGhost/client" + "github.com/azerothcore/AzerothGhost/e2e/e2eharness" + "github.com/azerothcore/azerothcore-wotlk/e2e/internal/meta" + _ "github.com/go-sql-driver/mysql" +) + +// PR: https://github.com/azerothcore/azerothcore-wotlk/pull/27381 +// Use fresh, cleaned-up spawns at an isolation pad, never the dungeon's real graves. +// These are chest lifecycle tests, not tests of Ahune or Zum'rah's encounter. +func chestBots(t *testing.T) (actor, mate, observer *e2eharness.ScenarioBot) { + t.Helper() + bots := e2eharness.NewScenario(t, e2eharness.ScenarioOpts{ + Prefix: "Chests", Count: 3, Race: e2eharness.RaceHuman, Class: e2eharness.ClassWarrior, Level: 80, + }) + actor, mate, observer = bots[0], bots[1], bots[2] + pad := e2eharness.PackagePad(t) + e2eharness.FormPartyAtPad(t, pad, actor, mate) + observer.TeleportPad(t, pad) + // The Ice Chest's bunny is an invisible trigger; a separate GM observes it. + // Neither looter gets GM-mode loot privileges. + observer.GM(t, ".gm on") + observer.FlushWorld(t) + actor.CombatReady(t) + mate.CombatReady(t) + return +} + +func spawnChest(t *testing.T, observer, actor *e2eharness.ScenarioBot, entry uint32) uint64 { + t.Helper() + if observer.SpawnGameObject(t, entry) == 0 { + e2eharness.Preconditionf(t, "cannot resolve cleanup spawn ID for chest %d", entry) + } + return actor.WaitGameObject(t, entry, 10*time.Second) +} + +type chestLoot struct { + gold uint32 + items []client.LootItem +} + +// v1.0.8 has no gameobject-target cast helper. Use its raw packet API, with the +// same CMSG_CAST_SPELL layout read by WorldSession::HandleCastSpellOpcode. +// CMSG_LOOT bypasses Spell::SendLoot; CMSG_GAMEOBJ_USE alone does not open a chest. +func castOpening(t *testing.T, bot *e2eharness.ScenarioBot, guid uint64, spell uint32) { + t.Helper() + payload := make([]byte, 11) // cast count, spell, cast flags, target flags, packed-GUID mask + binary.LittleEndian.PutUint32(payload[1:5], spell) + binary.LittleEndian.PutUint32(payload[6:10], 0x0800) // TARGET_FLAG_GAMEOBJECT + for i := uint(0); i < 8; i++ { + if b := byte(guid >> (8 * i)); b != 0 { + payload[10] |= 1 << i + payload = append(payload, b) + } + } + if err := bot.World.SendPacketRaw(client.CmsgCastSpell, payload); err != nil { + e2eharness.HarnessFailf(t, "Opening packet: %v", err) + } +} + +func openChest(t *testing.T, bot *e2eharness.ScenarioBot, guid uint64, spell uint32) chestLoot { + t.Helper() + packets := make(chan []byte, 1) + cancel := bot.World.AddPacketHook(func(op uint16, data []byte) { + if op == client.SmsgLootResponse && len(data) >= 8 && binary.LittleEndian.Uint64(data) == guid { + select { + case packets <- append([]byte(nil), data...): + default: + } + } + }) + defer cancel() + castOpening(t, bot, guid, spell) + select { + case data := <-packets: + if len(data) < 14 || len(data) < 14+22*int(data[13]) { + e2eharness.Assertf(t, "chest 0x%X rejected opening or returned malformed loot: %x", guid, data) + } + result := chestLoot{gold: binary.LittleEndian.Uint32(data[9:13])} + for i := 0; i < int(data[13]); i++ { + row := data[14+i*22:] + result.items = append(result.items, client.LootItem{ + Index: row[0], ItemID: binary.LittleEndian.Uint32(row[1:5]), Quantity: binary.LittleEndian.Uint32(row[5:9]), + }) + } + return result + case <-time.After(12 * time.Second): + e2eharness.Assertf(t, "chest 0x%X: no loot response to Opening spell %d", guid, spell) + return chestLoot{} + } +} + +func closeChest(t *testing.T, bot *e2eharness.ScenarioBot, guid uint64) { + t.Helper() + ack := make(chan struct{}, 1) + cancel := bot.World.AddPacketHook(func(op uint16, data []byte) { + if op == client.SmsgLootReleaseResponse && len(data) >= 8 && binary.LittleEndian.Uint64(data) == guid { + select { + case ack <- struct{}{}: + default: + } + } + }) + defer cancel() + bot.LootRelease(t, guid) + select { + case <-ack: + case <-time.After(5 * time.Second): + e2eharness.HarnessFailf(t, "no loot release acknowledgement for 0x%X", guid) + } +} + +func waitChestGone(t *testing.T, bot *e2eharness.ScenarioBot, guid uint64) { + t.Helper() + deadline := time.Now().Add(5 * time.Second) + for bot.World.GetObject(guid) != nil { + if time.Now().After(deadline) { + e2eharness.Assertf(t, "consumed/deleted chest 0x%X remained visible", guid) + } + time.Sleep(25 * time.Millisecond) + } +} + +// Count GUIDs, not surviving creatures: dying/despawning must not conceal a +// repeated summon. Only new units of the fixture's entries are cleaned up. +type summonObserver struct { + bot *e2eharness.ScenarioBot + entries map[uint32]bool + seen map[uint64]bool + owned map[uint64]bool +} + +func observeSummons(t *testing.T, bot *e2eharness.ScenarioBot, entries ...uint32) *summonObserver { + t.Helper() + o := &summonObserver{bot: bot, entries: map[uint32]bool{}, seen: map[uint64]bool{}, owned: map[uint64]bool{}} + for _, entry := range entries { + o.entries[entry] = true + } + o.scan() + o.owned = map[uint64]bool{} // existing units are not ours + t.Cleanup(func() { o.clean(t) }) + return o +} + +func (o *summonObserver) scan() { + for _, unit := range o.bot.World.GetNearbyUnits(100) { + if o.entries[unit.Entry] && !o.seen[unit.GUID] { + o.seen[unit.GUID], o.owned[unit.GUID] = true, true + } + } +} + +func (o *summonObserver) watch(duration time.Duration) { + deadline := time.Now().Add(duration) + for time.Now().Before(deadline) { + o.scan() + time.Sleep(25 * time.Millisecond) + } + o.scan() +} + +func (o *summonObserver) clean(t *testing.T) { + t.Helper() + o.scan() + for guid := range o.owned { + if o.bot.World.GetObject(guid) != nil { + if err := o.bot.World.SetTarget(guid); err != nil { + e2eharness.HarnessFailf(t, "select fixture summon: %v", err) + } + o.bot.FlushWorld(t) + o.bot.GM(t, ".npc delete") + o.bot.FlushWorld(t) + } + delete(o.owned, guid) + } +} + +func (o *summonObserver) unchanged(t *testing.T, baseline int) { + t.Helper() + // A bounded negative observation window also lets delayed object updates arrive. + o.watch(2 * time.Second) + if len(o.seen) != baseline { + e2eharness.Assertf(t, "reopening summoned new creatures: distinct GUIDs %d -> %d", baseline, len(o.seen)) + } +} + +func TestChests_CatFigurineReopen(t *testing.T) { + meta.Begin(t, meta.TestMeta{Tags: []string{"med", "loot", "serial"}, Runtime: "med", Category: "world/chests"}) + actor, _, observer := chestBots(t) + o := observeSummons(t, observer, 3619) + guid := spawnChest(t, observer, actor, 13873) + before := len(o.seen) + first := openChest(t, actor, guid, 3365) // lock type 5, Opening + o.watch(2 * time.Second) + baseline := len(o.seen) + if baseline == before || len(first.items) != 1 || first.items[0].ItemID != 5329 { + e2eharness.Assertf(t, "Cat Figurine must summon Ghost Saber(s) and offer item 5329: summons=%d loot=%+v", baseline-before, first) + } + // Existing SAI also casts 5968; this test deliberately does not assert one saber. + closeChest(t, actor, guid) + o.clean(t) + for i := 0; i < 3; i++ { + got := openChest(t, actor, guid, 3365) + if !reflect.DeepEqual(got, first) { + e2eharness.Assertf(t, "unclaimed figurine loot changed on reopen: %+v -> %+v", first, got) + } + o.unchanged(t, baseline) + closeChest(t, actor, guid) + } + t.Log("PASS Cat Figurine summons on first opening; unclaimed loot survives without repeat summons") +} + +func fillBackpack(t *testing.T, bot *e2eharness.ScenarioBot) { + t.Helper() + bot.Save(t) + var bags, used int + if err := bot.CharDB.QueryRow("SELECT COUNT(*) FROM character_inventory WHERE guid = ? AND bag = 0 AND slot BETWEEN 19 AND 22", bot.GUID).Scan(&bags); err != nil { + e2eharness.HarnessFailf(t, "bag query: %v", err) + } + if bags != 0 { + e2eharness.Preconditionf(t, "full-bag fixture expects a fresh character without equipped bags") + } + if err := bot.CharDB.QueryRow("SELECT COUNT(*) FROM character_inventory WHERE guid = ? AND bag = 0 AND slot BETWEEN 23 AND 38", bot.GUID).Scan(&used); err != nil { + e2eharness.HarnessFailf(t, "backpack query: %v", err) + } + if used < 16 { + if err := bot.World.SetTarget(0); err != nil { + e2eharness.HarnessFailf(t, "clear item-command target: %v", err) + } + bot.FlushWorld(t) + bot.GM(t, fmt.Sprintf(".additem 38 %d", 16-used)) // non-stackable Recruit's Shirt + bot.FlushWorld(t) + } +} + +func assertBagFull(t *testing.T, bot *e2eharness.ScenarioBot, slot uint8) { + t.Helper() + ch := make(chan byte, 1) + cancel := bot.World.AddPacketHook(func(op uint16, data []byte) { + if op == 0x0112 && len(data) > 0 { // SMSG_INVENTORY_CHANGE_FAILURE + select { + case ch <- data[0]: + default: + } + } + }) + defer cancel() + bot.LootTakeItem(t, slot) + select { + case reason := <-ch: + if reason != 50 { // EQUIP_ERR_INVENTORY_FULL + e2eharness.Preconditionf(t, "expected inventory-full error, got %d", reason) + } + case <-time.After(5 * time.Second): + e2eharness.Assertf(t, "taking grave loot with full bags did not return inventory-full error") + } +} + +// Issues: https://github.com/azerothcore/azerothcore-wotlk/issues/24692 +// +// https://github.com/azerothcore/azerothcore-wotlk/issues/12433 +func TestAC_24692_ShallowGraveLifecycle(t *testing.T) { + meta.Begin(t, meta.TestMeta{Tags: []string{"med", "loot", "issue", "serial"}, Runtime: "med", Category: "world/chests", Issue: 24692}) + for _, entry := range []uint32{128308, 128403} { + t.Run(fmt.Sprint(entry), func(t *testing.T) { + actor, mate, observer := chestBots(t) + actor.SetLootMethod(t, client.LootMethodFreeForAll, 0, e2eharness.LootThresholdUncommon) + actor.WaitLootMethod(t, client.LootMethodFreeForAll, 5*time.Second) + fillBackpack(t, actor) + o := observeSummons(t, observer, 7286, 7276) + covered := false + // Sample stock item loot and, for the trapped variant, a positive summon + // roll. Money is now always present; an initially empty grave is not a fixture. + for attempt := 0; attempt < 48 && !covered; attempt++ { + sid := observer.SpawnGameObject(t, entry) + if sid == 0 { + e2eharness.Preconditionf(t, "cannot resolve grave spawn for cleanup") + } + guid := actor.WaitGameObject(t, entry, 10*time.Second) + before := len(o.seen) + first := openChest(t, actor, guid, 6247) // lock type 10, Opening (no text) + o.watch(2 * time.Second) + baseline := len(o.seen) + if entry == 128308 && baseline != before { + e2eharness.ConfirmedBugf(t, 24692, "inert grave summoned %d creatures", baseline-before) + } + if len(first.items) == 0 || (entry == 128403 && baseline == before) { + closeChest(t, actor, guid) + o.clean(t) + observer.DespawnGameObjectSpawn(t, sid) + waitChestGone(t, actor, guid) + continue + } + assertBagFull(t, actor, first.items[0].Index) + closeChest(t, actor, guid) + o.clean(t) + for reopen := 0; reopen < 6; reopen++ { + looter := actor + if reopen%2 == 1 { + looter = mate + } + got := openChest(t, looter, guid, 6247) + if !reflect.DeepEqual(got, first) { + e2eharness.Assertf(t, "grave %d changed unclaimed loot: %+v -> %+v", entry, first, got) + } + closeChest(t, looter, guid) + o.unchanged(t, baseline) + } + // A different player can still collect everything left by full bags. + loot := openChest(t, mate, guid, 6247) + for _, item := range loot.items { + count := mate.InventoryCount(t, item.ItemID) + mate.LootTakeItem(t, item.Index) + mate.FlushWorld(t) + if got := mate.InventoryCount(t, item.ItemID); got != count+int(item.Quantity) { + e2eharness.Assertf(t, "grave %d item %d was not credited: %d -> %d", entry, item.ItemID, count, got) + } + } + if loot.gold != 0 { + if err := mate.World.SendPacketRaw(0x015E, nil); err != nil { // CMSG_LOOT_MONEY + e2eharness.HarnessFailf(t, "loot grave money: %v", err) + } + mate.FlushWorld(t) + } + closeChest(t, mate, guid) + waitChestGone(t, mate, guid) + // A stale opening request must not produce another wave after consumption. + castOpening(t, mate, guid, 6247) + mate.FlushWorld(t) + o.unchanged(t, baseline) + covered = true + observer.DespawnGameObjectSpawn(t, sid) + waitChestGone(t, actor, guid) + t.Logf("PASS grave %d initial summons=%d; six reopens and a consumed-GUID request added none", entry, baseline-before) + } + if !covered { + e2eharness.Preconditionf(t, "48 fresh graves did not produce an item-loot fixture with the required active/inert behavior") + } + }) + } +} + +func TestChests_IceChestGroupLoot(t *testing.T) { + meta.Begin(t, meta.TestMeta{Tags: []string{"med", "loot", "multi_bot", "serial"}, Runtime: "med", Category: "world/chests"}) + actor, mate, observer := chestBots(t) + actor.SetLootMethod(t, client.LootMethodGroupLoot, 0, e2eharness.LootThresholdUncommon) + actor.WaitLootMethod(t, client.LootMethodGroupLoot, 5*time.Second) + o := observeSummons(t, observer, 26391) + guid := spawnChest(t, observer, actor, 187892) + waitActor, cancelActor := actor.ArmLootStartRoll() + defer cancelActor() + waitMate, cancelMate := mate.ArmLootStartRoll() + defer cancelMate() + before := len(o.seen) + loot := openChest(t, actor, guid, 6247) + var reward uint32 + for _, item := range loot.items { + if item.ItemID >= 54801 && item.ItemID <= 54805 { + reward = item.ItemID + } + } + if reward == 0 { + e2eharness.Assertf(t, "Ice Chest did not offer one of its guaranteed cloaks: %+v", loot.items) + } + roll, ok := waitActor(reward, 10*time.Second) + if !ok { + e2eharness.Assertf(t, "Ice Chest did not start a group roll for its epic reward") + } + otherRoll, ok := waitMate(roll.ItemID, 10*time.Second) + if !ok || otherRoll.ItemGUID != roll.ItemGUID || otherRoll.ItemSlot != roll.ItemSlot { + e2eharness.Assertf(t, "party members did not receive the same Ice Chest roll") + } + o.watch(time.Second) + baseline := len(o.seen) + if baseline-before != 1 { + e2eharness.Assertf(t, "Ice Chest should summon its bunny on initial opening: got %d", baseline-before) + } + closeChest(t, actor, guid) + openChest(t, mate, guid, 6247) + closeChest(t, mate, guid) + openChest(t, actor, guid, 6247) + closeChest(t, actor, guid) + o.unchanged(t, baseline) + // The trap has a long stock cooldown; repeat-open observations here cannot + // prove the once-per-generation guard by themselves. The reward is the oracle. + count := actor.InventoryCount(t, roll.ItemID) + won, _, cancel := actor.ArmLootRollOutcome(roll.ItemID) + defer cancel() + mate.RollGreed(t, roll) + actor.RollNeed(t, roll) + select { + case result := <-won: + if result.WinnerGUID != actor.GUID { + e2eharness.Assertf(t, "Need did not beat Greed for Ice Chest item %d", roll.ItemID) + } + case <-time.After(10 * time.Second): + e2eharness.Assertf(t, "Ice Chest roll did not resolve after both votes") + } + deadline := time.Now().Add(5 * time.Second) + for actor.InventoryCount(t, roll.ItemID) != count+1 { + if time.Now().After(deadline) { + e2eharness.Assertf(t, "Ice Chest roll winner did not receive exactly one item %d", roll.ItemID) + } + time.Sleep(100 * time.Millisecond) + } + t.Logf("PASS Ice Chest first-open bunny, shared roll, second-player reopening and reward %d", roll.ItemID) +} diff --git a/src/server/game/Entities/Player/Player.cpp b/src/server/game/Entities/Player/Player.cpp index e2e2b21b19..6049b563cf 100644 --- a/src/server/game/Entities/Player/Player.cpp +++ b/src/server/game/Entities/Player/Player.cpp @@ -8100,6 +8100,11 @@ void Player::SendLoot(ObjectGuid guid, LootType loot_type) } go->SetLootState(GO_ACTIVATED, this); + + // Trigger chest traps once per loot generation, including when the generated loot is empty. + if (go->GetGoType() == GAMEOBJECT_TYPE_CHEST) + if (uint32 trapEntry = go->GetGOInfo()->chest.linkedTrapId) + go->TriggeringLinkedGameObject(trapEntry, this); } if (go->getLootState() == GO_ACTIVATED) diff --git a/src/server/game/Spells/SpellEffects.cpp b/src/server/game/Spells/SpellEffects.cpp index 306a7a44e9..735bb6965b 100644 --- a/src/server/game/Spells/SpellEffects.cpp +++ b/src/server/game/Spells/SpellEffects.cpp @@ -2184,11 +2184,6 @@ void Spell::SendLoot(ObjectGuid guid, LootType loottype) gameObjTarget->TriggeringLinkedGameObject(trapEntry, unitCaster); return; - case GAMEOBJECT_TYPE_CHEST: - // triggering linked GO - if (uint32 trapEntry = gameObjTarget->GetGOInfo()->chest.linkedTrapId) - gameObjTarget->TriggeringLinkedGameObject(trapEntry, unitCaster); - // Don't return, let loots been taken default: break;